Class: Familia::StringKey

Inherits:
DataType show all
Includes:
DataType::ScalarBase
Defined in:
lib/familia/data_type/types/stringkey.rb

Direct Known Subclasses

Counter, Lock

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, #logical_database, #prefix, #raise_on_unsaved_parent_write, #schema_path, #schema_validator, #schemas, #strict_write_order, #suffix, #transaction_mode

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DataType::ScalarBase

#scalar_type?

Methods included from Features::Autoloader

autoload_files, included, normalize_to_config_name

Methods included from DataType::Serialization

#deserialize_values, #deserialize_values_with_nil, #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, #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.

Class Method Details

.bitop(operation, destkey, *keys, client: nil) ⇒ Integer

Note:

This method is a raw Redis passthrough and does NOT apply Familia's serialization pipeline or expiration hooks. All key arguments are used as-is (no dbkey prefixing). The destination key will not have a TTL applied automatically.

Perform bitwise operations between strings and store result

Examples:

StringKey.bitop(:and, 'result', 'key1', 'key2')

Parameters:

  • operation (String, Symbol)

    Bitwise operation: AND, OR, XOR, NOT

  • destkey (String)

    Destination key to store result

  • keys (Array<String>)

    Source keys for the operation

  • client (Redis, nil) (defaults to: nil)

    Optional Redis client (uses Familia.dbclient if nil)

Returns:

  • (Integer)

    Size of the resulting string in bytes



312
313
314
315
# File 'lib/familia/data_type/types/stringkey.rb', line 312

def bitop(operation, destkey, *keys, client: nil)
  client ||= Familia.dbclient
  client.bitop(operation.to_s.upcase, destkey, *keys)
end

.mget(*keys, client: nil) ⇒ Array

Note:

This method is a raw Redis passthrough and does NOT apply Familia's serialization pipeline. Keys are used as-is (no dbkey prefixing) and returned values are raw Redis strings. If you need round-trip compatibility with StringKey instances, read via the instance #value getter or a Familia.dbclient.get(instance.dbkey) call.

Get values for multiple keys

Examples:

StringKey.mget('user:1:name', 'user:2:name')

Parameters:

  • keys (Array<String>)

    Full Redis key names

  • client (Redis, nil) (defaults to: nil)

    Optional Redis client (uses Familia.dbclient if nil)

Returns:

  • (Array)

    Values for each key (nil for non-existent keys)



259
260
261
262
# File 'lib/familia/data_type/types/stringkey.rb', line 259

def mget(*keys, client: nil)
  client ||= Familia.dbclient
  client.mget(*keys)
end

.mset(hash, client: nil) ⇒ String

Note:

This method is a raw Redis passthrough and does NOT apply Familia's serialization pipeline or expiration hooks. Keys are used as-is (no dbkey prefixing) and values are written as-is. For round-trip compatibility with StringKey instances, write via the instance #value= setter (which calls serialize_value and update_expiration).

Set multiple keys atomically. Keys and values are extracted from the hash for the Redis MSET command.

Examples:

StringKey.mset('user:1:name' => 'Alice', 'user:2:name' => 'Bob')

Parameters:

  • hash (Hash)

    Key-value pairs to set

  • client (Redis, nil) (defaults to: nil)

    Optional Redis client (uses Familia.dbclient if nil)

Returns:

  • (String)

    "OK" on success



277
278
279
280
# File 'lib/familia/data_type/types/stringkey.rb', line 277

def mset(hash, client: nil)
  client ||= Familia.dbclient
  client.mset(*hash.flatten)
end

.msetnx(hash, client: nil) ⇒ Boolean

Note:

This method is a raw Redis passthrough and does NOT apply Familia's serialization pipeline or expiration hooks. Keys are used as-is (no dbkey prefixing) and values are written as-is. For round-trip compatibility with StringKey instances, write via the instance #value= setter (which calls serialize_value and update_expiration).

Set multiple keys only if none of them exist. Keys and values are extracted from the hash for the Redis MSETNX command.

Examples:

StringKey.msetnx('user:1:name' => 'Alice', 'user:2:name' => 'Bob')

Parameters:

  • hash (Hash)

    Key-value pairs to set

  • client (Redis, nil) (defaults to: nil)

    Optional Redis client (uses Familia.dbclient if nil)

Returns:

  • (Boolean)

    true if all keys were set, false if none were set



295
296
297
298
# File 'lib/familia/data_type/types/stringkey.rb', line 295

def msetnx(hash, client: nil)
  client ||= Familia.dbclient
  client.msetnx(*hash.flatten)
end

Instance Method Details

#append(val) ⇒ Object Also known as: <<



116
117
118
119
120
# File 'lib/familia/data_type/types/stringkey.rb', line 116

def append(val)
  ret = dbclient.append dbkey, val
  update_expiration
  ret
end

#bitcount(start_pos = nil, end_pos = nil) ⇒ Integer

Count the number of set bits (population counting)

Parameters:

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

    Start byte position (optional)

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

    End byte position (optional)

Returns:

  • (Integer)

    Number of bits set to 1



203
204
205
206
207
208
209
210
211
# File 'lib/familia/data_type/types/stringkey.rb', line 203

def bitcount(start_pos = nil, end_pos = nil)
  if start_pos && end_pos
    dbclient.bitcount(dbkey, start_pos, end_pos)
  elsif start_pos
    dbclient.bitcount(dbkey, start_pos)
  else
    dbclient.bitcount(dbkey)
  end
end

#bitfield(*args) ⇒ Array

Perform bitfield operations on this string

Examples:

Get an unsigned 8-bit integer at offset 0

str.bitfield('GET', 'u8', 0)

Set and increment

str.bitfield('SET', 'u8', 0, 100, 'INCRBY', 'i5', 100, 1)

Parameters:

  • args (Array)

    Bitfield subcommands and arguments

Returns:

  • (Array)

    Results of the bitfield operations



235
236
237
238
239
# File 'lib/familia/data_type/types/stringkey.rb', line 235

def bitfield(*args)
  ret = dbclient.bitfield(dbkey, *args)
  update_expiration
  ret
end

#bitpos(bit, start_pos = nil, end_pos = nil) ⇒ Integer

Find the position of the first bit set to 0 or 1

Parameters:

  • bit (Integer)

    The bit value to search for (0 or 1)

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

    Start byte position (optional)

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

    End byte position (optional)

Returns:

  • (Integer)

    Position of the first bit, or -1 if not found



218
219
220
221
222
223
224
225
226
# File 'lib/familia/data_type/types/stringkey.rb', line 218

def bitpos(bit, start_pos = nil, end_pos = nil)
  if start_pos && end_pos
    dbclient.bitpos(dbkey, bit, start_pos, end_pos)
  elsif start_pos
    dbclient.bitpos(dbkey, bit, start_pos)
  else
    dbclient.bitpos(dbkey, bit)
  end
end

#char_countInteger Also known as: size, length

Returns the number of characters in the stored value.

Reads from #value rather than #to_s: #to_s follows Familia::Base's "never nil" contract and falls back to an inspect-style string (e.g. "#Familia::StringKey:0x...") when there's no value, which made #char_count/#size/#empty? report a nonsense non-zero count for a deleted/never-created key.

Returns:

  • (Integer)

    number of characters



42
43
44
# File 'lib/familia/data_type/types/stringkey.rb', line 42

def char_count
  value.to_s.size
end

#decrementObject Also known as: decr



102
103
104
105
106
# File 'lib/familia/data_type/types/stringkey.rb', line 102

def decrement
  ret = dbclient.decr dbkey
  update_expiration
  ret
end

#decrementby(val) ⇒ Object Also known as: decrby



109
110
111
112
113
# File 'lib/familia/data_type/types/stringkey.rb', line 109

def decrementby(val)
  ret = dbclient.decrby dbkey, val.to_i
  update_expiration
  ret
end

#delObject



241
242
243
244
# File 'lib/familia/data_type/types/stringkey.rb', line 241

def del
  ret = dbclient.del dbkey
  Familia.positive?(ret)
end

#deserialize_value(val) ⇒ Object

StringKey returns raw values (not JSON parsed)



27
28
29
30
31
# File 'lib/familia/data_type/types/stringkey.rb', line 27

def deserialize_value(val)
  return val if val.is_a?(Redis::Future)
  return @opts[:default] if val.nil?
  val
end

#empty?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/familia/data_type/types/stringkey.rb', line 48

def empty?
  char_count.zero?
end

#getbit(offset) ⇒ Object



123
124
125
# File 'lib/familia/data_type/types/stringkey.rb', line 123

def getbit(offset)
  dbclient.getbit dbkey, offset
end

#getdelString?

Atomically get and delete the value

Returns:

  • (String, nil)

    The value before deletion, or nil if key didn't exist



151
152
153
# File 'lib/familia/data_type/types/stringkey.rb', line 151

def getdel
  dbclient.getdel(dbkey)
end

#getex(ex: nil, px: nil, exat: nil, pxat: nil, persist: false) ⇒ String?

Get value and optionally set expiration atomically

Parameters:

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

    Set expiration in seconds

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

    Set expiration in milliseconds

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

    Set expiration at Unix timestamp (seconds)

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

    Set expiration at Unix timestamp (milliseconds)

  • persist (Boolean) (defaults to: false)

    Remove existing expiration

Returns:

  • (String, nil)

    The value



162
163
164
165
166
167
168
169
170
171
# File 'lib/familia/data_type/types/stringkey.rb', line 162

def getex(ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
  options = {}
  options[:ex] = ex if ex
  options[:px] = px if px
  options[:exat] = exat if exat
  options[:pxat] = pxat if pxat
  options[:persist] = persist if persist

  dbclient.getex(dbkey, **options)
end

#getrange(spoint, epoint) ⇒ Object



133
134
135
# File 'lib/familia/data_type/types/stringkey.rb', line 133

def getrange(spoint, epoint)
  dbclient.getrange dbkey, spoint, epoint
end

#getset(val) ⇒ Object



143
144
145
146
147
# File 'lib/familia/data_type/types/stringkey.rb', line 143

def getset(val)
  ret = dbclient.getset dbkey, val
  update_expiration
  ret
end

#incrbyfloat(val) ⇒ Float Also known as: incrfloat

Increment value by a float amount

Parameters:

  • val (Float, Numeric)

    The amount to increment by

Returns:

  • (Float)

    The new value after increment



176
177
178
179
180
# File 'lib/familia/data_type/types/stringkey.rb', line 176

def incrbyfloat(val)
  ret = dbclient.incrbyfloat(dbkey, val.to_f)
  update_expiration
  ret
end

#incrementObject Also known as: incr



88
89
90
91
92
# File 'lib/familia/data_type/types/stringkey.rb', line 88

def increment
  ret = dbclient.incr(dbkey)
  update_expiration
  ret
end

#incrementby(val) ⇒ Object Also known as: incrby



95
96
97
98
99
# File 'lib/familia/data_type/types/stringkey.rb', line 95

def incrementby(val)
  ret = dbclient.incrby(dbkey, val.to_i)
  update_expiration
  ret
end

#initObject



9
# File 'lib/familia/data_type/types/stringkey.rb', line 9

def init; end

#psetex(milliseconds, val) ⇒ String

Set value with expiration in milliseconds

Parameters:

  • milliseconds (Integer)

    Expiration time in milliseconds

  • val (Object)

    The value to set

Returns:

  • (String)

    "OK" on success



195
196
197
# File 'lib/familia/data_type/types/stringkey.rb', line 195

def psetex(milliseconds, val)
  dbclient.psetex(dbkey, milliseconds.to_i, serialize_value(val))
end

#serialize_value(val) ⇒ Object

StringKey uses raw string serialization (not JSON) because Redis string operations like INCR, DECR, APPEND operate on raw values. This overrides the base JSON serialization from DataType.



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/familia/data_type/types/stringkey.rb', line 14

def serialize_value(val)
  Familia.trace :TOREDIS, nil, "#{val}<#{val.class}>" if Familia.debug?

  # Handle Familia object references - extract identifier
  if val.is_a?(Familia::Base) || (val.is_a?(Class) && val.ancestors.include?(Familia::Base))
    return val.is_a?(Class) ? val.name : val.identifier
  end

  # StringKey uses raw string conversion for Redis compatibility
  val.to_s
end

#setbit(offset, val) ⇒ Object



127
128
129
130
131
# File 'lib/familia/data_type/types/stringkey.rb', line 127

def setbit(offset, val)
  ret = dbclient.setbit dbkey, offset, val
  update_expiration
  ret
end

#setex(seconds, val) ⇒ String

Set value with expiration in seconds

Parameters:

  • seconds (Integer)

    Expiration time in seconds

  • val (Object)

    The value to set

Returns:

  • (String)

    "OK" on success



187
188
189
# File 'lib/familia/data_type/types/stringkey.rb', line 187

def setex(seconds, val)
  dbclient.setex(dbkey, seconds.to_i, serialize_value(val))
end

#setnx(val) ⇒ Object



82
83
84
85
86
# File 'lib/familia/data_type/types/stringkey.rb', line 82

def setnx(val)
  ret = dbclient.setnx(dbkey, serialize_value(val))
  update_expiration
  ret
end

#setrange(offset, val) ⇒ Object



137
138
139
140
141
# File 'lib/familia/data_type/types/stringkey.rb', line 137

def setrange(offset, val)
  ret = dbclient.setrange dbkey, offset, val
  update_expiration
  ret
end

#to_iObject



66
67
68
# File 'lib/familia/data_type/types/stringkey.rb', line 66

def to_i
  value.to_i
end

#to_sObject



60
61
62
63
64
# File 'lib/familia/data_type/types/stringkey.rb', line 60

def to_s
  return super if value.to_s.empty?

  value.to_s
end

#valueObject Also known as: content, get



52
53
54
55
56
# File 'lib/familia/data_type/types/stringkey.rb', line 52

def value
  echo :value, Familia.pretty_stack(limit: 1) if Familia.debug
  dbclient.setnx dbkey, @opts[:default] if @opts[:default]
  deserialize_value dbclient.get(dbkey)
end

#value=(val) ⇒ Object Also known as: replace, set

Note:

This method executes a Redis SET 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.



73
74
75
76
77
78
# File 'lib/familia/data_type/types/stringkey.rb', line 73

def value=(val)
  warn_if_dirty!
  ret = dbclient.set(dbkey, serialize_value(val))
  update_expiration
  ret
end