Module: Familia::Horreum::DatabaseCommands

Included in:
Familia::Horreum
Defined in:
lib/familia/horreum/database_commands.rb

Overview

DatabaseCommands - Instance-level methods for horreum models that call Database commands

NOTE: There is no hgetall for Horreum. This is because Horreum is a single hash in Database that we aren't meant to have be working on in memory for more than, making changes -> committing. To emphasize this, instead of "refreshing" the object with hgetall, just load the object again.

Instance Method Summary collapse

Instance Method Details

#current_expirationInteger

Retrieves the remaining time to live (TTL) for the object's dbkey.

This method accesses the objects Database client to obtain the TTL of dbkey. If debugging is enabled, it logs the TTL retrieval operation using Familia.trace.

Returns:

  • (Integer)

    The TTL of the key in seconds. Returns -1 if the key does not exist or has no associated expire time.



90
91
92
93
# File 'lib/familia/horreum/database_commands.rb', line 90

def current_expiration
  Familia.trace :CURRENT_EXPIRATION, nil, self.class.uri if Familia.debug?
  dbclient.ttl dbkey
end

#data_typeString

Returns the Redis data type of the key.

Returns:

  • (String)

    The data type (e.g., 'hash', 'string', 'list')



108
109
110
111
# File 'lib/familia/horreum/database_commands.rb', line 108

def data_type
  Familia.trace :DATATYPE, nil, self.class.uri if Familia.debug?
  dbclient.type dbkey(suffix)
end

#decr(field) ⇒ Integer Also known as: decrement

Decrements the integer value of a hash field by 1.

Parameters:

  • field (String)

    The field name

Returns:

  • (Integer)

    The value after decrementing



224
225
226
# File 'lib/familia/horreum/database_commands.rb', line 224

def decr(field)
  dbclient.hdecr field
end

#decrby(field, decrement) ⇒ Integer Also known as: decrementby

Decrements the integer value of a hash field by the given amount.

Parameters:

  • field (String)

    The field name

  • decrement (Integer)

    The decrement value

Returns:

  • (Integer)

    The value after decrementing



215
216
217
# File 'lib/familia/horreum/database_commands.rb', line 215

def decrby(field, decrement)
  dbclient.decrby dbkey(suffix), field, decrement
end

#delete!Boolean Also known as: clear

Deletes the dbkey for this horreum :object.

It does not delete the related fields keys. See destroy!

Returns:

  • (Boolean)

    true if the key was deleted, false otherwise



252
253
254
255
256
257
# File 'lib/familia/horreum/database_commands.rb', line 252

def delete!
  Familia.trace :DELETE!, nil, self.class.uri if Familia.debug?

  # Delete the main object key
  dbclient.del dbkey
end

#discardString

Flushes all previously queued commands in a transaction and all watched keys

NOTE: This command operates on the connection itself; not a specific key

Returns:

  • (String)

    'OK' always



307
# File 'lib/familia/horreum/database_commands.rb', line 307

def discard(...) = dbclient.discard(...)

#echo(*args) ⇒ String

Echoes a message through the Redis connection.

Parameters:

  • args (Array)

    Arguments to join and echo

Returns:

  • (String)

    The echoed message



313
314
315
# File 'lib/familia/horreum/database_commands.rb', line 313

def echo(*args)
  dbclient.echo "[#{self.class}] #{args.join(' ')}"
end

#exists?(check_size: true) ⇒ Boolean

Note:

The default behavior maintains backward compatibility by treating empty hashes as non-existent. Use check_size: false for pure key existence checking.

Checks if the calling object's key exists in the database.

Examples:

Check existence with size validation (default behavior)

some_object.exists?                    # => false for empty hashes
some_object.exists?(check_size: true)  # => false for empty hashes

Check existence only

some_object.exists?(check_size: false)  # => true for empty hashes

Parameters:

  • check_size (Boolean) (defaults to: true)

    When true (default), also verifies the hash has a non-zero size. When false, only checks key existence regardless of content.

Returns:

  • (Boolean)

    Returns true if the key exists in the database. When check_size is true, also requires the hash to have at least one field.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/familia/horreum/database_commands.rb', line 45

def exists?(check_size: true)
  key_exists = self.class.exists?(identifier)
  return key_exists unless check_size

  # Handle Redis::Future in transactions - skip size check
  if key_exists.is_a?(Redis::Future)
    return key_exists
  end

  current_size = size
  # Handle Redis::Future from size call too
  if current_size.is_a?(Redis::Future)
    return current_size
  end

  key_exists && !current_size.zero?
end

#expire(default_expiration = nil) ⇒ Integer

Sets a timeout on key. After the timeout has expired, the key will automatically be deleted. Returns 1 if the timeout was set, 0 if key does not exist or the timeout could not be set.

Parameters:

  • default_expiration (Integer) (defaults to: nil)

    TTL in seconds (uses class default if nil)

Returns:

  • (Integer)

    1 if timeout was set, 0 otherwise



77
78
79
80
81
# File 'lib/familia/horreum/database_commands.rb', line 77

def expire(default_expiration = nil)
  default_expiration ||= self.class.default_expiration
  Familia.trace :EXPIRE, nil, default_expiration if Familia.debug?
  dbclient.expire dbkey, default_expiration.to_i
end

#field_countInteger Also known as: size, length

Returns the number of fields in the main object hash

Returns:

  • (Integer)

    number of fields



65
66
67
# File 'lib/familia/horreum/database_commands.rb', line 65

def field_count
  dbclient.hlen dbkey
end

#hget(field) ⇒ String?

Gets the value of a hash field.

Parameters:

  • field (String)

    The field name

Returns:

  • (String, nil)

    The value of the field, or nil if field doesn't exist



127
128
129
130
# File 'lib/familia/horreum/database_commands.rb', line 127

def hget(field)
  Familia.trace :HGET, nil, field if Familia.debug?
  dbclient.hget dbkey(suffix), field
end

#hgetallHash Also known as: all

Note:

For parity with DataType#hgetall

Returns all fields and values in the hash.

Returns:

  • (Hash)

    All field-value pairs in the hash



117
118
119
120
# File 'lib/familia/horreum/database_commands.rb', line 117

def hgetall
  Familia.trace :HGETALL, nil, self.class.uri if Familia.debug?
  dbclient.hgetall dbkey(suffix)
end

#hkeysArray<String>

Returns all field names in the hash.

Returns:

  • (Array<String>)

    Array of field names



169
170
171
172
# File 'lib/familia/horreum/database_commands.rb', line 169

def hkeys
  Familia.trace :HKEYS, nil, self.class.uri if Familia.debug?
  dbclient.hkeys dbkey(suffix)
end

#hmset(hsh = {}) ⇒ String

Sets multiple hash fields to multiple values.

Parameters:

  • hsh (Hash) (defaults to: {})

    Hash of field-value pairs to set

Returns:

  • (String)

    'OK' on success



160
161
162
163
164
# File 'lib/familia/horreum/database_commands.rb', line 160

def hmset(hsh = {})
  hsh ||= to_h_for_storage
  Familia.trace :HMSET, nil, hsh if Familia.debug?
  dbclient.hmset dbkey(suffix), hsh
end

#hset(field, value) ⇒ Integer

Sets the value of a hash field.

Parameters:

  • field (String)

    The field name

  • value (String)

    The value to set

Returns:

  • (Integer)

    The number of fields that were added to the hash. If the field already exists, this will return 0.



138
139
140
141
# File 'lib/familia/horreum/database_commands.rb', line 138

def hset(field, value)
  Familia.trace :HSET, nil, field if Familia.debug?
  dbclient.hset dbkey, field, value
end

#hsetnx(field, value) ⇒ Integer

Sets field in the hash stored at key to value, only if field does not yet exist. If key does not exist, a new key holding a hash is created. If field already exists, this operation has no effect.

Parameters:

  • field (String)

    The field to set in the hash

  • value (String)

    The value to set for the field

Returns:

  • (Integer)

    1 if the field is a new field in the hash and the value was set, 0 if the field already exists in the hash and no operation was performed



151
152
153
154
# File 'lib/familia/horreum/database_commands.rb', line 151

def hsetnx(field, value)
  Familia.trace :HSETNX, nil, field if Familia.debug?
  dbclient.hsetnx dbkey, field, value
end

#hstrlen(field) ⇒ Integer Also known as: hstrlength

Returns the string length of the value associated with field in the hash.

Parameters:

  • field (String)

    The field name

Returns:

  • (Integer)

    The string length of the field value, or 0 if field doesn't exist



233
234
235
# File 'lib/familia/horreum/database_commands.rb', line 233

def hstrlen(field)
  dbclient.hstrlen dbkey(suffix), field
end

#hvalsArray<String>

Returns all values in the hash.

Returns:

  • (Array<String>)

    Array of values



177
178
179
# File 'lib/familia/horreum/database_commands.rb', line 177

def hvals
  dbclient.hvals dbkey(suffix)
end

#incr(field) ⇒ Integer Also known as: increment

Increments the integer value of a hash field by 1.

Parameters:

  • field (String)

    The field name

Returns:

  • (Integer)

    The value after incrementing



185
186
187
# File 'lib/familia/horreum/database_commands.rb', line 185

def incr(field)
  dbclient.hincrby dbkey(suffix), field, 1
end

#incrby(field, increment) ⇒ Integer Also known as: incrementby

Increments the integer value of a hash field by the given amount.

Parameters:

  • field (String)

    The field name

  • increment (Integer)

    The increment value

Returns:

  • (Integer)

    The value after incrementing



195
196
197
# File 'lib/familia/horreum/database_commands.rb', line 195

def incrby(field, increment)
  dbclient.hincrby dbkey(suffix), field, increment
end

#incrbyfloat(field, increment) ⇒ Float Also known as: incrementbyfloat

Increments the float value of a hash field by the given amount.

Parameters:

  • field (String)

    The field name

  • increment (Float)

    The increment value

Returns:

  • (Float)

    The value after incrementing



205
206
207
# File 'lib/familia/horreum/database_commands.rb', line 205

def incrbyfloat(field, increment)
  dbclient.hincrbyfloat dbkey(suffix), field, increment
end

#key?(field) ⇒ Boolean Also known as: has_key?

Determines if a hash field exists.

Parameters:

  • field (String)

    The field name

Returns:

  • (Boolean)

    true if the field exists, false otherwise



242
243
244
# File 'lib/familia/horreum/database_commands.rb', line 242

def key?(field)
  dbclient.hexists dbkey(suffix), field
end

#move(logical_database) ⇒ Boolean

Moves the object's key to a different logical database.

Parameters:

  • logical_database (Integer)

    The target database number

Returns:

  • (Boolean)

    true if the key was moved successfully



25
26
27
# File 'lib/familia/horreum/database_commands.rb', line 25

def move(logical_database)
  dbclient.move dbkey, logical_database
end

#remove_field(field) ⇒ Integer Also known as: remove

Removes a field from the hash stored at the dbkey.

Parameters:

  • field (String)

    The field to remove from the hash.

Returns:

  • (Integer)

    The number of fields that were removed from the hash (0 or 1).



99
100
101
102
# File 'lib/familia/horreum/database_commands.rb', line 99

def remove_field(field)
  Familia.trace :HDEL, nil, field if Familia.debug?
  dbclient.hdel dbkey, field
end

#unwatchString

Flushes all the previously watched keys for a transaction.

If a transaction completes successfully or discard is called, there's no need to manually call unwatch.

NOTE: This command operates on the connection itself; not a specific key

Returns:

  • (String)

    'OK' always, regardless of whether the key was watched or not



300
# File 'lib/familia/horreum/database_commands.rb', line 300

def unwatch(...) = dbclient.unwatch(...)

#watchString

Watches the key for changes during a MULTI/EXEC transaction.

Decision Matrix:

| Scenario | Use | Why | |----------|-----|-----| | First-one-wins / idempotency | SET NX | Atomic claim, no read needed | | Distributed lock acquisition | SET NX EX | Claim with automatic expiry | | Read value, update conditionally | WATCH | Decision depends on current state | | Compare-and-swap operations | WATCH | Need optimistic locking | | Version-based updates | WATCH | Must detect concurrent changes | | Status transitions (pending→processing) | WATCH | Must verify current state | | Batch field updates | MULTI only | No conditional logic | | Increment + timestamp together | MULTI only | Concurrent increments OK | | Save object atomically | MULTI only | Just need atomicity | | Update indexes with save | MULTI only | No state checking needed |

If you don't need to read before deciding, WATCH adds complexity without benefit. SET NX handles the "claim" pattern in one atomic shot.

Parameters:

  • suffix_override (String, nil)

    Optional suffix override

Returns:

  • (String)

    'OK' on success



282
283
284
285
286
287
288
289
290
# File 'lib/familia/horreum/database_commands.rb', line 282

def watch(...)
  raise ArgumentError, 'Block required' unless block_given?

  # Forward all arguments including the block to the watch command
  dbclient.watch(dbkey, ...)

rescue Redis::BaseError => e
  raise OptimisticLockError, "Redis error: #{e.message}"
end