Module: Familia::Horreum::ClassMethods
- Includes:
- RelationsManagement, Settings
- Defined in:
- lib/familia/horreum/class_methods.rb
Overview
ClassMethods: Provides class-level functionality for Horreum
This module is extended into classes that include Familia::Horreum, providing methods for Redis operations and object management.
Key features:
-
Includes RelationsManagement for Redis-type field handling
-
Defines methods for managing fields, identifiers, and Redis keys
-
Provides utility methods for working with Redis objects
Instance Attribute Summary collapse
- #dump_method ⇒ Object
- #load_method ⇒ Object
-
#parent ⇒ Object
Returns the value of attribute parent.
- #redis ⇒ Object
Attributes included from Settings
Instance Method Summary collapse
- #all(suffix = :object) ⇒ Object
- #any?(filter = '*') ⇒ Boolean
- #class_redis_types ⇒ Object
- #class_redis_types?(name) ⇒ Boolean
- #create(*args) ⇒ Object
- #db(v = nil) ⇒ Object
- #destroy!(identifier, suffix = :object) ⇒ Object
- #exists?(identifier, suffix = :object) ⇒ Boolean
-
#fast_writer!(name) ⇒ Object
The return value from redis client for hset command.
-
#field(name) ⇒ Object
Define a field for the class.
-
#fields ⇒ Object
Returns the list of field names defined for the class in the order that they were defined.
- #find(suffix = '*') ⇒ Object
-
#from_key(objkey) ⇒ Object?
Retrieves and instantiates an object from Redis using the full object key.
-
#from_redis(identifier, suffix = :object) ⇒ Object?
Retrieves and instantiates an object from Redis using its identifier.
-
#identifier(val = nil) ⇒ Object
The object field or instance method to call to get the unique identifier for that instance.
- #multiget(*ids) ⇒ Object
- #prefix(a = nil) ⇒ Object
- #qstamp(quantum = nil, pattern = nil, now = Familia.now) ⇒ Object
- #rawmultiget(*ids) ⇒ Object
- #redis_object?(name) ⇒ Boolean
- #redis_types ⇒ Object
-
#rediskey(identifier, suffix = self.suffix) ⇒ Object
identifiercan be a value or an Array of values used to create the index. - #size(filter = '*') ⇒ Object
- #suffix(a = nil, &blk) ⇒ Object
- #ttl(v = nil) ⇒ Object
- #uri(v = nil) ⇒ Object
Methods included from RelationsManagement
#attach_class_redis_object_relation, #attach_instance_redis_object_relation, included
Methods included from Settings
Instance Attribute Details
#dump_method ⇒ Object
282 283 284 |
# File 'lib/familia/horreum/class_methods.rb', line 282 def dump_method @dump_method || :to_json # Familia.dump_method end |
#load_method ⇒ Object
286 287 288 |
# File 'lib/familia/horreum/class_methods.rb', line 286 def load_method @load_method || :from_json # Familia.load_method end |
#parent ⇒ Object
Returns the value of attribute parent.
36 37 38 |
# File 'lib/familia/horreum/class_methods.rb', line 36 def parent @parent end |
Instance Method Details
#all(suffix = :object) ⇒ Object
111 112 113 114 |
# File 'lib/familia/horreum/class_methods.rb', line 111 def all(suffix = :object) # objects that could not be parsed will be nil keys(suffix).filter_map { |k| from_key(k) } end |
#any?(filter = '*') ⇒ Boolean
116 117 118 |
# File 'lib/familia/horreum/class_methods.rb', line 116 def any?(filter = '*') size(filter) > 0 end |
#class_redis_types ⇒ Object
78 79 80 81 |
# File 'lib/familia/horreum/class_methods.rb', line 78 def class_redis_types @class_redis_types ||= {} @class_redis_types end |
#class_redis_types?(name) ⇒ Boolean
83 84 85 |
# File 'lib/familia/horreum/class_methods.rb', line 83 def class_redis_types?(name) class_redis_types.key? name.to_s.to_sym end |
#create(*args) ⇒ Object
134 135 136 137 138 139 140 |
# File 'lib/familia/horreum/class_methods.rb', line 134 def create *args me = from_array(*args) raise "#{self} exists: #{me.rediskey}" if me.exists? me.save me end |
#db(v = nil) ⇒ Object
101 102 103 104 |
# File 'lib/familia/horreum/class_methods.rb', line 101 def db(v = nil) @db = v unless v.nil? @db || parent&.db end |
#destroy!(identifier, suffix = :object) ⇒ Object
244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/familia/horreum/class_methods.rb', line 244 def destroy!(identifier, suffix = :object) return false if identifier.to_s.empty? objkey = rediskey identifier, suffix ret = redis.del objkey if Familia.debug? Familia.trace :DELETED, redis, "#{objkey}: #{ret.inspect}", caller end ret.positive? end |
#exists?(identifier, suffix = :object) ⇒ Boolean
234 235 236 237 238 239 240 241 242 |
# File 'lib/familia/horreum/class_methods.rb', line 234 def exists?(identifier, suffix = :object) return false if identifier.to_s.empty? objkey = rediskey identifier, suffix ret = redis.exists objkey Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}", caller if Familia.debug? ret.positive? end |
#fast_writer!(name) ⇒ Object
Returns The return value from redis client for hset command.
62 63 64 65 66 67 68 69 |
# File 'lib/familia/horreum/class_methods.rb', line 62 def fast_writer!(name) define_method :"#{name}!" do |value| prepared = to_redis(value) Familia.ld "[.fast_writer!] #{name} val: #{value.class} prepared: #{prepared.class}" send :"#{name}=", value # use the existing accessor hset name, prepared # persist to Redis without delay end end |
#field(name) ⇒ Object
Define a field for the class. This will create getter and setter instance methods just like any “attr_accessor” methods.
53 54 55 56 57 58 59 |
# File 'lib/familia/horreum/class_methods.rb', line 53 def field(name) fields << name attr_accessor name # Every field gets a fast writer method for immediately persisting fast_writer! name end |
#fields ⇒ Object
Returns the list of field names defined for the class in the order that they were defined. i.e. ‘field :a; field :b; fields => [:a, :b]`.
73 74 75 76 |
# File 'lib/familia/horreum/class_methods.rb', line 73 def fields @fields ||= [] @fields end |
#find(suffix = '*') ⇒ Object
257 258 259 |
# File 'lib/familia/horreum/class_methods.rb', line 257 def find(suffix = '*') redis.keys(rediskey('*', suffix)) || [] end |
#from_key(objkey) ⇒ Object?
Retrieves and instantiates an object from Redis using the full object key.
This method performs a two-step process to safely retrieve and instantiate objects:
-
It first checks if the key exists in Redis. This is crucial because:
-
It provides a definitive answer about the object’s existence.
-
It prevents ambiguity that could arise from ‘hgetall` returning an empty hash for non-existent keys, which could lead to the creation of “empty” objects.
-
-
If the key exists, it retrieves the object’s data and instantiates it.
This approach ensures that we only attempt to instantiate objects that actually exist in Redis, improving reliability and simplifying debugging.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/familia/horreum/class_methods.rb', line 183 def from_key(objkey) raise ArgumentError, 'Empty key' if objkey.to_s.empty? # We use a lower-level method here b/c we're working with the # full key and not just the identifier. does_exist = redis.exists(objkey).positive? Familia.ld "[.from_key] #{self} from key #{objkey} (exists: #{does_exist})" Familia.trace :FROM_KEY, redis, objkey, caller if Familia.debug? # This is the reason for calling exists first. We want to definitively # and without any ambiguity know if the object exists in Redis. If it # doesn't, we return nil. If it does, we proceed to load the object. # Otherwise, hgetall will return an empty hash, which will be passed to # the constructor, which will then be annoying to debug. return unless does_exist obj = redis.hgetall(objkey) # horreum objects are persisted as redis hashes Familia.trace :FROM_KEY2, redis, "#{objkey}: #{obj.inspect}", caller if Familia.debug? new(**obj) end |
#from_redis(identifier, suffix = :object) ⇒ Object?
Retrieves and instantiates an object from Redis using its identifier.
This method constructs the full Redis key using the provided identifier and suffix, then delegates to ‘from_key` for the actual retrieval and instantiation.
It’s a higher-level method that abstracts away the key construction, making it easier to retrieve objects when you only have their identifier.
225 226 227 228 229 230 231 232 |
# File 'lib/familia/horreum/class_methods.rb', line 225 def from_redis(identifier, suffix = :object) return nil if identifier.to_s.empty? objkey = rediskey(identifier, suffix) Familia.ld "[.from_redis] #{self} from key #{objkey})" Familia.trace :FROM_REDIS, Familia.redis(uri), objkey, caller(1..1).first if Familia.debug? from_key objkey end |
#identifier(val = nil) ⇒ Object
The object field or instance method to call to get the unique identifier for that instance. The value returned by this method will be used to generate the key for the object in Redis.
46 47 48 49 |
# File 'lib/familia/horreum/class_methods.rb', line 46 def identifier(val = nil) @identifier = val if val @identifier end |
#multiget(*ids) ⇒ Object
142 143 144 145 |
# File 'lib/familia/horreum/class_methods.rb', line 142 def multiget(*ids) ids = rawmultiget(*ids) ids.filter_map { |json| from_json(json) } end |
#prefix(a = nil) ⇒ Object
129 130 131 132 |
# File 'lib/familia/horreum/class_methods.rb', line 129 def prefix(a = nil) @prefix = a if a @prefix || name.downcase.gsub('::', Familia.delim).to_sym end |
#qstamp(quantum = nil, pattern = nil, now = Familia.now) ⇒ Object
261 262 263 264 265 266 |
# File 'lib/familia/horreum/class_methods.rb', line 261 def qstamp(quantum = nil, pattern = nil, now = Familia.now) quantum ||= ttl || 10.minutes pattern ||= '%H%M' rounded = now - (now % quantum) Time.at(rounded).utc.strftime(pattern) end |
#rawmultiget(*ids) ⇒ Object
147 148 149 150 151 152 153 |
# File 'lib/familia/horreum/class_methods.rb', line 147 def rawmultiget(*ids) ids.collect! { |objid| rediskey(objid) } return [] if ids.compact.empty? Familia.trace :MULTIGET, redis, "#{ids.size}: #{ids}", caller if Familia.debug? redis.mget(*ids) end |
#redis_object?(name) ⇒ Boolean
87 88 89 |
# File 'lib/familia/horreum/class_methods.rb', line 87 def redis_object?(name) redis_types.key? name.to_s.to_sym end |
#redis_types ⇒ Object
91 92 93 94 |
# File 'lib/familia/horreum/class_methods.rb', line 91 def redis_types @redis_types ||= {} @redis_types end |
#rediskey(identifier, suffix = self.suffix) ⇒ Object
identifier can be a value or an Array of values used to create the index. We don’t enforce a default suffix; that’s left up to the instance. The suffix is used to differentiate between different types of objects.
A nil suffix will not be included in the key.
274 275 276 277 278 279 280 |
# File 'lib/familia/horreum/class_methods.rb', line 274 def rediskey(identifier, suffix = self.suffix) Familia.ld "[.rediskey] #{identifier} for #{self} (suffix:#{suffix})" raise NoIdentifier, self if identifier.to_s.empty? identifier &&= identifier.to_s Familia.rediskey(prefix, identifier, suffix) end |
#size(filter = '*') ⇒ Object
120 121 122 |
# File 'lib/familia/horreum/class_methods.rb', line 120 def size(filter = '*') redis.keys(rediskey(filter)).compact.size end |
#suffix(a = nil, &blk) ⇒ Object
124 125 126 127 |
# File 'lib/familia/horreum/class_methods.rb', line 124 def suffix(a = nil, &blk) @suffix = a || blk if a || !blk.nil? @suffix || Familia.default_suffix end |
#ttl(v = nil) ⇒ Object
96 97 98 99 |
# File 'lib/familia/horreum/class_methods.rb', line 96 def ttl(v = nil) @ttl = v unless v.nil? @ttl || parent&.ttl end |
#uri(v = nil) ⇒ Object
106 107 108 109 |
# File 'lib/familia/horreum/class_methods.rb', line 106 def uri(v = nil) @uri = v unless v.nil? @uri || parent&.uri end |