Class: Familia::HashKey

Inherits:
DataType show all
Includes:
DataType::CollectionBase
Defined in:
lib/familia/data_type/types/hashkey.rb

Constant Summary collapse

CLAIM_FIELD_SCRIPT =

Server-side compare-and-set: HSET the field only if it is unclaimed or already holds a value this caller owns.

KEYS[1] is the hash; ARGV[1] the field; ARGV[2] the canonical value to write; ARGV[3..] additional values that count as "already ours" (see #claim_field for the legacy-encoding case). A stored value matching a non-canonical variant is rewritten to the canonical form, so a claim doubles as an in-place normalization.

Returns:

  • (Integer, String)

    0 when newly created, 1 when re-affirmed, or the conflicting stored value.

<<~LUA
  local current = redis.call('HGET', KEYS[1], ARGV[1])
  if current == false then
    redis.call('HSET', KEYS[1], ARGV[1], ARGV[2])
    return 0
  end
  for i = 2, #ARGV do
    if current == ARGV[i] then
      if current ~= ARGV[2] then
        redis.call('HSET', KEYS[1], ARGV[1], ARGV[2])
      end
      return 1
    end
  end
  return current
LUA
RELEASE_FIELD_SCRIPT =

Server-side compare-and-delete: HDEL the field only if it holds a value this caller owns. Same KEYS/ARGV shape as CLAIM_FIELD_SCRIPT minus the normalization (there is nothing to normalize on the way out).

Returns:

  • (Integer)

    1 when deleted, 0 when the field was absent or owned by someone else.

<<~LUA
  local current = redis.call('HGET', KEYS[1], ARGV[1])
  if current == false then
    return 0
  end
  for i = 2, #ARGV do
    if current == ARGV[i] then
      return redis.call('HDEL', KEYS[1], ARGV[1])
    end
  end
  return 0
LUA

Instance Attribute Summary collapse

Attributes included from Settings

#current_key_version, #default_expiration, #delim, #encryption_hkdf_salt, #encryption_hkdf_salt_history, #encryption_keys, #encryption_personalization, #encryption_personalization_history, #logical_database, #prefix, #raise_on_unsaved_parent_write, #schema_path, #schema_validator, #schemas, #strict_write_order, #suffix, #transaction_mode

Instance Method Summary collapse

Methods included from DataType::CollectionBase

#collection_type?, #each_record

Methods included from Features::Autoloader

autoload_files, included, normalize_to_config_name

Methods included from DataType::Serialization

#deserialize_value, #deserialize_values, #deserialize_values_with_nil, #serialize_value, #strip_legacy_json_encoding

Methods included from DataType::DatabaseCommands

#current_expiration, #delete!, #echo, #exists?, #expire, #expireat, #move, #persist, #rename, #renamenx, #type

Methods included from DataType::Connection

#dbclient, #dbkey, #uri

Methods included from Connection::Behavior

#connect, #create_dbclient, #multi, #normalize_uri, #pipeline, #pipelined, #transaction, #uri=, #url, #url=

Methods included from Settings

#configure, #default_suffix, #dirty_write_warnings, #dirty_write_warnings=, #pipelined_mode, #pipelined_mode=

Methods included from Base

add_feature, #as_json, #expired?, #expires?, find_feature, #generate_id, #to_json, #to_s, #ttl, #update_expiration, #uuid

Constructor Details

This class inherits a constructor from Familia::DataType

Instance Attribute Details

#features_enabledObject (readonly) Originally defined in module Features

Returns the value of attribute features_enabled.

#logical_database(val = nil) ⇒ Object Originally defined in module DataType::ClassMethods

#parentObject Originally defined in module DataType::ClassMethods

Returns the value of attribute parent.

#prefixObject Originally defined in module DataType::ClassMethods

Returns the value of attribute prefix.

#suffixObject Originally defined in module DataType::ClassMethods

Returns the value of attribute suffix.

#uri(val = nil) ⇒ Object Originally defined in module DataType::ClassMethods

Returns the value of attribute uri.

Instance Method Details

#[](field) ⇒ Object Also known as: get



161
162
163
# File 'lib/familia/data_type/types/hashkey.rb', line 161

def [](field)
  deserialize_value dbclient.hget(dbkey, field.to_s)
end

#[]=(field, val) ⇒ Object Also known as: put, store, add

Note:

This method executes a Redis HSET immediately, unlike scalar field setters which are deferred until save. If the parent object has unsaved scalar field changes, consider calling save first to avoid split-brain state.

+return+ [Integer] Returns 1 if the field is new and added, 0 if the field already existed and the value was updated.



144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/familia/data_type/types/hashkey.rb', line 144

def []=(field, val)
  warn_if_dirty!
  ret = dbclient.hset dbkey, field.to_s, serialize_value(val)
  update_expiration
  ret
rescue TypeError => e
  Familia.error "[hset]= #{e.message}"
  Familia.debug "[hset]= #{dbkey} #{field}=#{val}"
  echo :hset, Familia.pretty_stack(limit: 1) if Familia.debug # logs via echo to the db and back
  klass = val.class
  msg = "Cannot store #{field} => #{val.inspect} (#{klass}) in #{dbkey}"
  raise e.class, msg
end

#claim_field(field, val) ⇒ Symbol, Object

Claims +field+ for +val+, atomically, server-side.

This is the enforcement half of the unique-index invariant (ADR-0002). A read-then-HSET pair cannot enforce uniqueness on a key many writers share -- two savers both read "unclaimed" and the second HSET silently overwrites the first. WATCH does not help either: its granularity is the whole key, so every concurrent save of the class aborts every other one. A single EVAL checks and writes at field granularity, conflicting only on a genuine collision, and touches exactly one key (so it is Redis Cluster safe).

Parameters:

  • field (String, Symbol)

    hash field (an indexed value)

  • val (Object)

    the value to claim it for (an identifier)

Returns:

  • (Symbol, Object)

    +:created+ when the field was unclaimed, +:owned+ when it already held an equivalent value (normalized to the canonical form as a side effect), or the deserialized conflicting value when another owner holds it.

Raises:

  • (Familia::OperationModeError)

    when called inside a transaction or pipeline, where EVAL returns a Future and its verdict cannot be acted on.

See Also:



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/familia/data_type/types/hashkey.rb', line 78

def claim_field(field, val)
  if Fiber[:familia_transaction] || Fiber[:familia_pipeline]
    raise Familia::OperationModeError, <<~ERROR_MESSAGE
      Cannot claim_field inside a transaction or pipeline: EVAL returns a Future there, so the claim's verdict cannot be checked. Claim before opening the MULTI (see ADR-0002).
    ERROR_MESSAGE
  end

  result = dbclient.eval(
    CLAIM_FIELD_SCRIPT, keys: [dbkey], argv: [field.to_s, *ownership_variants(val)]
  )

  return deserialize_value(result) unless result.is_a?(Integer)

  update_expiration
  result.zero? ? :created : :owned
end

#decrement(field, by = 1) ⇒ Object Also known as: decr, decrby



238
239
240
# File 'lib/familia/data_type/types/hashkey.rb', line 238

def decrement(field, by = 1)
  increment field, -by
end

#each(matching: nil, batch_size: 100) {|field, value| ... } ⇒ Enumerator, self

Note:

Pattern matches field names only (plain strings). To filter on values, use Enumerable#select instead.

Iterates over field-value pairs in the hash.

Uses HSCAN for memory-efficient iteration. Optionally filters by field name pattern using Redis MATCH.

Examples:

Iterate all pairs

settings.each { |field, value| puts "#{field}: #{value}" }

Filter by field name pattern

settings.each(matching: "cache_*") { |f, v| puts "#{f}: #{v}" }

Parameters:

  • matching (String, nil) (defaults to: nil)

    Optional glob-style pattern to filter field names (e.g., "user:", "_count"). Pattern is passed to Redis HSCAN MATCH and matches against field names (plain strings, not JSON-encoded).

  • batch_size (Integer) (defaults to: 100)

    Number of elements to fetch per HSCAN iteration

Yields:

  • (field, value)

    Each field-value pair (values are deserialized)

Returns:

  • (Enumerator, self)

    Returns Enumerator if no block given, self otherwise



283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/familia/data_type/types/hashkey.rb', line 283

def each(matching: nil, batch_size: 100, &block)
  return to_enum(:each, matching: matching, batch_size: batch_size) unless block

  cursor = 0
  loop do
    new_cursor, pairs = scan(cursor, match: matching, count: batch_size)
    pairs.each(&block)
    cursor = new_cursor
    break if cursor.zero?
  end

  self
end

#empty?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/familia/data_type/types/hashkey.rb', line 134

def empty?
  field_count.zero?
end

#expire_fields(seconds, *fields) ⇒ Array<Integer> Also known as: hexpire

Note:

Requires Redis 7.4+

Sets expiration time in seconds on one or more hash fields.

Examples:

Set 1 hour TTL on specific fields

my_hash.expire_fields(3600, 'session_token', 'temp_data')

Parameters:

  • seconds (Integer)

    TTL in seconds

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of results for each field: -2 if field does not exist, 1 if expiration was set, 0 if expiration was not set (e.g., field has no expiration)



409
410
411
# File 'lib/familia/data_type/types/hashkey.rb', line 409

def expire_fields(seconds, *fields)
  call_hash_field_command('HEXPIRE', seconds, fields: fields)
end

#expireat_fields(unix_time, *fields) ⇒ Array<Integer> Also known as: hexpireat

Note:

Requires Redis 7.4+

Sets absolute expiration time (Unix timestamp in seconds) on hash fields.

Examples:

Expire fields at midnight tonight

midnight = Time.now.to_i + (24 * 60 * 60)
my_hash.expireat_fields(midnight, 'daily_counter')

Parameters:

  • unix_time (Integer)

    Absolute Unix timestamp in seconds

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of results for each field



438
439
440
# File 'lib/familia/data_type/types/hashkey.rb', line 438

def expireat_fields(unix_time, *fields)
  call_hash_field_command('HEXPIREAT', unix_time, fields: fields)
end

#expiretime_fields(*fields) ⇒ Array<Integer> Also known as: hexpiretime

Note:

Requires Redis 7.4+

Returns the absolute Unix expiration timestamp in seconds for hash fields.

Examples:

Get expiration timestamp

my_hash.expiretime_fields('session')  #=> [1700000000]

Parameters:

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of timestamps for each field: -2 if field does not exist, -1 if field has no expiration, otherwise the absolute Unix timestamp in seconds



510
511
512
# File 'lib/familia/data_type/types/hashkey.rb', line 510

def expiretime_fields(*fields)
  call_hash_field_command('HEXPIRETIME', fields: fields)
end

#fetch(field, default = nil) ⇒ Object



166
167
168
169
170
171
172
173
174
175
# File 'lib/familia/data_type/types/hashkey.rb', line 166

def fetch(field, default = nil)
  ret = self[field.to_s]
  if ret.nil?
    raise IndexError, "No such index for: #{field}" if default.nil?

    default
  else
    ret
  end
end

#field_countInteger Also known as: size, length, count

Returns the number of fields in the hash

Returns:

  • (Integer)

    number of fields



127
128
129
# File 'lib/familia/data_type/types/hashkey.rb', line 127

def field_count
  dbclient.hlen dbkey
end

#hgetallObject Also known as: all



185
186
187
188
189
# File 'lib/familia/data_type/types/hashkey.rb', line 185

def hgetall
  dbclient.hgetall(dbkey).transform_values do |v|
    deserialize_value v
  end
end

#hsetnx(field, val) ⇒ Integer

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

Parameters:

  • field (String)

    The field name

  • val (Object)

    The value to set

Returns:

  • (Integer)

    1 if field is a new field and value was set, 0 if field already exists



197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/familia/data_type/types/hashkey.rb', line 197

def hsetnx(field, val)
  warn_if_dirty!
  ret = dbclient.hsetnx dbkey, field.to_s, serialize_value(val)
  update_expiration if ret == 1
  ret
rescue TypeError => e
  Familia.error "[hsetnx] #{e.message}"
  Familia.debug "[hsetnx] #{dbkey} #{field}=#{val}"
  echo :hsetnx, Familia.pretty_stack(limit: 1) if Familia.debug # logs via echo to the db and back
  klass = val.class
  msg = "Cannot store #{field} => #{val.inspect} (#{klass}) in #{dbkey}"
  raise e.class, msg
end

#incrbyfloat(field, by) ⇒ Float Also known as: incrfloat

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

Examples:

my_hash.incrbyfloat('temperature', 0.5)  #=> 23.5
my_hash.incrbyfloat('temperature', -1.2) #=> 22.3

Parameters:

  • field (String)

    The field name

  • by (Float, Integer)

    The amount to increment by (can be negative)

Returns:

  • (Float)

    The new value after incrementing



339
340
341
342
343
# File 'lib/familia/data_type/types/hashkey.rb', line 339

def incrbyfloat(field, by)
  ret = dbclient.hincrbyfloat(dbkey, field.to_s, by).to_f
  update_expiration
  ret
end

#increment(field, by = 1) ⇒ Object Also known as: incr, incrby



230
231
232
233
234
# File 'lib/familia/data_type/types/hashkey.rb', line 230

def increment(field, by = 1)
  ret = dbclient.hincrby(dbkey, field.to_s, by).to_i
  update_expiration
  ret
end

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

Returns:

  • (Boolean)


211
212
213
# File 'lib/familia/data_type/types/hashkey.rb', line 211

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

#keysObject



177
178
179
# File 'lib/familia/data_type/types/hashkey.rb', line 177

def keys
  dbclient.hkeys dbkey
end

#persist_fields(*fields) ⇒ Array<Integer> Also known as: hpersist

Note:

Requires Redis 7.4+

Removes expiration from one or more hash fields.

Examples:

Remove expiration from fields

my_hash.persist_fields('important_data')  #=> [1]

Parameters:

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of results for each field: -2 if field does not exist, -1 if field has no expiration, 1 if expiration was removed



495
496
497
# File 'lib/familia/data_type/types/hashkey.rb', line 495

def persist_fields(*fields)
  call_hash_field_command('HPERSIST', fields: fields)
end

#pexpire_fields(milliseconds, *fields) ⇒ Array<Integer> Also known as: hpexpire

Note:

Requires Redis 7.4+

Sets expiration time in milliseconds on one or more hash fields.

Examples:

Set 500ms TTL on a field

my_hash.pexpire_fields(500, 'rate_limit_counter')

Parameters:

  • milliseconds (Integer)

    TTL in milliseconds

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of results for each field



423
424
425
# File 'lib/familia/data_type/types/hashkey.rb', line 423

def pexpire_fields(milliseconds, *fields)
  call_hash_field_command('HPEXPIRE', milliseconds, fields: fields)
end

#pexpireat_fields(unix_time_ms, *fields) ⇒ Array<Integer> Also known as: hpexpireat

Note:

Requires Redis 7.4+

Sets absolute expiration time (Unix timestamp in milliseconds) on hash fields.

Examples:

Expire field at a precise millisecond

my_hash.pexpireat_fields(1700000000000, 'precise_data')

Parameters:

  • unix_time_ms (Integer)

    Absolute Unix timestamp in milliseconds

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of results for each field



452
453
454
# File 'lib/familia/data_type/types/hashkey.rb', line 452

def pexpireat_fields(unix_time_ms, *fields)
  call_hash_field_command('HPEXPIREAT', unix_time_ms, fields: fields)
end

#pexpiretime_fields(*fields) ⇒ Array<Integer> Also known as: hpexpiretime

Note:

Requires Redis 7.4+

Returns the absolute Unix expiration timestamp in milliseconds for hash fields.

Examples:

Get precise expiration timestamp

my_hash.pexpiretime_fields('session')  #=> [1700000000000]

Parameters:

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of timestamps in milliseconds



523
524
525
# File 'lib/familia/data_type/types/hashkey.rb', line 523

def pexpiretime_fields(*fields)
  call_hash_field_command('HPEXPIRETIME', fields: fields)
end

#pttl_fields(*fields) ⇒ Array<Integer> Also known as: hpttl

Note:

Requires Redis 7.4+

Returns the remaining TTL in milliseconds for one or more hash fields.

Examples:

Check remaining TTL in milliseconds

my_hash.pttl_fields('rate_limit')  #=> [450]

Parameters:

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of TTL values in milliseconds



480
481
482
# File 'lib/familia/data_type/types/hashkey.rb', line 480

def pttl_fields(*fields)
  call_hash_field_command('HPTTL', fields: fields)
end

#randfield(count = nil, withvalues: false) ⇒ String, ... Also known as: hrandfield

Returns one or more random fields from the hash.

Examples:

Get a single random field

my_hash.randfield  #=> "some_field"

Get 3 distinct random fields

my_hash.randfield(3)  #=> ["field1", "field2", "field3"]

Get 2 random fields with values

my_hash.randfield(2, withvalues: true)  #=> [["field1", value1], ["field2", value2]]

Parameters:

  • count (Integer, nil) (defaults to: nil)

    Number of fields to return. If nil, returns a single field. If positive, returns distinct fields. If negative, allows duplicates.

  • withvalues (Boolean) (defaults to: false)

    If true, returns fields with their values

Returns:

  • (String, Array<String>, Array<Array>)

    Depending on arguments:

    • No count: single field name (or nil if hash is empty)
    • With count: array of field names
    • With count and withvalues: array of [field, value] pairs


377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/familia/data_type/types/hashkey.rb', line 377

def randfield(count = nil, withvalues: false)
  if count.nil?
    dbclient.hrandfield(dbkey)
  elsif withvalues
    pairs = dbclient.hrandfield(dbkey, count, withvalues: true)
    # pairs is array of [field, value, field, value, ...]
    # Convert to array of [field, deserialized_value] pairs
    pairs.each_slice(2).map { |field, val| [field, deserialize_value(val)] }
  else
    dbclient.hrandfield(dbkey, count)
  end
end

#refreshself

The friendly neighborhood refresh method!

This method is like refresh! but with better manners - it returns self so you can chain it with other methods. It's perfect for when you want to refresh your hash and immediately do something with it.

Examples:

Refresh and chain

my_hash.refresh.keys  # Refresh and get all keys
my_hash.refresh['field']  # Refresh and get a specific field

Returns:

  • (self)

    Returns the refreshed hash, ready for more adventures!

Raises:

See Also:



629
630
631
632
# File 'lib/familia/data_type/types/hashkey.rb', line 629

def refresh
  refresh!
  self
end

#refresh!void

Note:

This operation is atomic - it either succeeds completely or fails safely. Any unsaved changes to the hash will be overwritten.

This method returns an undefined value.

The Great Database Refresh-o-matic 3000 for HashKey!

This method performs a complete refresh of the hash's state from the database. It's like giving your hash a memory transfusion - out with the old state, in with the fresh data straight from Valkey/Redis!

Examples:

Basic usage

my_hash.refresh!  # ZAP! Fresh data loaded

With error handling

begin
  my_hash.refresh!
rescue Familia::KeyNotFoundError
  puts "Oops! Our hash seems to have vanished into the Database void!"
end

Raises:



602
603
604
605
606
607
608
609
610
611
# File 'lib/familia/data_type/types/hashkey.rb', line 602

def refresh!
  Familia.trace :REFRESH, nil, self.class.uri if Familia.debug?
  raise Familia::KeyNotFoundError, dbkey unless dbclient.exists(dbkey)

  fields = hgetall
  Familia.debug "[refresh!] #{self.class} #{dbkey} #{fields.keys}"

  # For HashKey, we update by merging the fresh data
  update(fields)
end

#release_field(field, val) ⇒ Integer, Redis::Future

Deletes +field+ only if it currently holds a value equivalent to +val+.

The release half of the claim protocol. An unconditional HDEL lets a stale in-memory value delete a claim another record now legitimately owns -- the same shared-key overwrite #claim_field closes on the way in.

Unlike #claim_field this is safe to queue inside a MULTI: it needs no return value to decide anything, so the script's atomicity still holds at EXEC time.

Parameters:

  • field (String, Symbol)

    hash field to release

  • val (Object)

    the value the caller believes it owns

Returns:

  • (Integer, Redis::Future)

    1 if deleted, 0 if absent or not ours

See Also:



111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/familia/data_type/types/hashkey.rb', line 111

def release_field(field, val)
  ret = dbclient.eval(
    RELEASE_FIELD_SCRIPT, keys: [dbkey], argv: [field.to_s, *ownership_variants(val)]
  )
  # Refreshed unconditionally, matching #[]= -- a release that deleted
  # nothing still touched the index key's TTL policy, and `ret` cannot gate
  # it anyway: inside a MULTI (where this method is explicitly safe to
  # queue) `ret` is a Future with no value to test. On an index hash with
  # no default_expiration -- the usual case -- this returns early without
  # issuing a command.
  update_expiration
  ret
end

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

Removes a field from the hash

Parameters:

  • field (String)

    The field to remove

Returns:

  • (Integer)

    The number of fields that were removed (0 or 1)



221
222
223
224
225
226
# File 'lib/familia/data_type/types/hashkey.rb', line 221

def remove_field(field)
  warn_if_dirty!
  ret = dbclient.hdel dbkey, field.to_s
  update_expiration
  ret
end

#scan(cursor = 0, match: nil, count: nil) ⇒ Array<Integer, Hash> Also known as: hscan

Incrementally iterates over fields in the hash using cursor-based iteration. This is more memory-efficient than hgetall for large hashes.

Examples:

Basic iteration

cursor = 0
loop do
  cursor, results = my_hash.scan(cursor)
  results.each { |field, value| puts "#{field}: #{value}" }
  break if cursor == 0
end

With pattern matching

cursor, results = my_hash.scan(0, match: "user:*", count: 100)

Parameters:

  • cursor (Integer) (defaults to: 0)

    The cursor position to start from (0 for initial call)

  • match (String, nil) (defaults to: nil)

    Optional glob-style pattern to filter field names

  • count (Integer, nil) (defaults to: nil)

    Optional hint for number of elements to return per call

Returns:

  • (Array<Integer, Hash>)

    A two-element array: [new_cursor, => value, ...] When new_cursor is 0, iteration is complete.



316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/familia/data_type/types/hashkey.rb', line 316

def scan(cursor = 0, match: nil, count: nil)
  opts = {}
  opts[:match] = match if match
  opts[:count] = count if count

  new_cursor, pairs = dbclient.hscan(dbkey, cursor, **opts)

  # pairs is an array of [field, value] pairs, convert to hash with deserialization
  result_hash = pairs.to_h.transform_values { |v| deserialize_value(v) }

  [new_cursor.to_i, result_hash]
end

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

Returns the string length of the value associated with field.

Examples:

my_hash['name'] = 'Alice'
my_hash.strlen('name')  #=> 7 (includes JSON quotes: "Alice")

Parameters:

  • field (String)

    The field name

Returns:

  • (Integer)

    The length of the value in bytes, or 0 if field does not exist



354
355
356
# File 'lib/familia/data_type/types/hashkey.rb', line 354

def strlen(field)
  dbclient.hstrlen(dbkey, field.to_s)
end

#ttl_fields(*fields) ⇒ Array<Integer> Also known as: httl

Note:

Requires Redis 7.4+

Returns the remaining TTL in seconds for one or more hash fields.

Examples:

Check remaining TTL on fields

my_hash.ttl_fields('session_token', 'temp_data')  #=> [3600, -1]

Parameters:

  • fields (Array<String>)

    One or more field names

Returns:

  • (Array<Integer>)

    Array of TTL values for each field: -2 if field does not exist, -1 if field has no expiration, otherwise the TTL in seconds



467
468
469
# File 'lib/familia/data_type/types/hashkey.rb', line 467

def ttl_fields(*fields)
  call_hash_field_command('HTTL', fields: fields)
end

#update(hsh = {}) ⇒ Object Also known as: merge!

Raises:

  • (ArgumentError)


244
245
246
247
248
249
250
251
252
253
# File 'lib/familia/data_type/types/hashkey.rb', line 244

def update(hsh = {})
  warn_if_dirty!
  raise ArgumentError, 'Argument to bulk_set must be a hash' unless hsh.is_a?(Hash)

  data = hsh.inject([]) { |ret, pair| ret << [pair[0], serialize_value(pair[1])] }.flatten

  ret = dbclient.hmset(dbkey, *data)
  update_expiration
  ret
end

#valuesObject



181
182
183
# File 'lib/familia/data_type/types/hashkey.rb', line 181

def values
  dbclient.hvals(dbkey).map { |v| deserialize_value v }
end

#values_at(*fields) ⇒ Object



256
257
258
259
260
# File 'lib/familia/data_type/types/hashkey.rb', line 256

def values_at *fields
  string_fields = fields.flatten.compact.map(&:to_s)
  elements = dbclient.hmget(dbkey, *string_fields)
  deserialize_values(*elements)
end