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

Attributes included from Settings

#delim

Instance Method Summary collapse

Methods included from RelationsManagement

#attach_class_redis_object_relation, #attach_instance_redis_object_relation, included

Methods included from Settings

#default_suffix

Instance Attribute Details

#dump_methodObject



231
232
233
# File 'lib/familia/horreum/class_methods.rb', line 231

def dump_method
  @dump_method || :to_json # Familia.dump_method
end

#load_methodObject



235
236
237
# File 'lib/familia/horreum/class_methods.rb', line 235

def load_method
  @load_method || :from_json # Familia.load_method
end

#parentObject

Returns the value of attribute parent.



37
38
39
# File 'lib/familia/horreum/class_methods.rb', line 37

def parent
  @parent
end

#redisObject



40
41
42
# File 'lib/familia/horreum/class_methods.rb', line 40

def redis
  @redis || Familia.redis(uri || db)
end

Instance Method Details

#all(suffix = :object) ⇒ Object



104
105
106
107
# File 'lib/familia/horreum/class_methods.rb', line 104

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

Returns:

  • (Boolean)


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

def any?(filter = '*')
  size(filter) > 0
end

#class_redis_typesObject



66
67
68
69
# File 'lib/familia/horreum/class_methods.rb', line 66

def class_redis_types
  @class_redis_types ||= {}
  @class_redis_types
end

#class_redis_types?(name) ⇒ Boolean

Returns:

  • (Boolean)


71
72
73
# File 'lib/familia/horreum/class_methods.rb', line 71

def class_redis_types?(name)
  class_redis_types.key? name.to_s.to_sym
end

#create(*args) ⇒ Object



127
128
129
130
131
132
133
# File 'lib/familia/horreum/class_methods.rb', line 127

def create *args
  me = from_array(*args)
  raise "#{self} exists: #{me.rediskey}" if me.exists?

  me.save
  me
end

#db(v = nil) ⇒ Object



94
95
96
97
# File 'lib/familia/horreum/class_methods.rb', line 94

def db(v = nil)
  @db = v unless v.nil?
  @db || parent&.db
end

#defined_fieldsObject



84
85
86
87
# File 'lib/familia/horreum/class_methods.rb', line 84

def defined_fields
  @defined_fields ||= {}
  @defined_fields
end

#destroy!(identifier, suffix = :object) ⇒ Object



193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/familia/horreum/class_methods.rb', line 193

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

Returns:

  • (Boolean)


180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/familia/horreum/class_methods.rb', line 180

def exists?(identifier, suffix = :object)
  return false if identifier.to_s.empty?

  objkey = rediskey identifier, suffix

  ret = redis.exists objkey
  if Familia.debug?
    Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}",
                  caller
  end
  ret.positive?
end

#field(name) ⇒ Object

Define a field for the class. This will create getter and setter instance methods just like any “attr_accessor” methods.



54
55
56
57
# File 'lib/familia/horreum/class_methods.rb', line 54

def field(name)
  fields << name
  attr_accessor name
end

#fieldsObject

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]`.



61
62
63
64
# File 'lib/familia/horreum/class_methods.rb', line 61

def fields
  @fields ||= []
  @fields
end

#find(suffix = '*') ⇒ Object



206
207
208
# File 'lib/familia/horreum/class_methods.rb', line 206

def find(suffix = '*')
  redis.keys(rediskey('*', suffix)) || []
end

#from_key(objkey) ⇒ Object

Raises:

  • (ArgumentError)


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/familia/horreum/class_methods.rb', line 148

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 :LOAD, redis, objkey, caller if Familia.debug?

  # This is 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 :HGETALL, redis, "#{objkey}: #{obj.inspect}", caller if Familia.debug?

  new(**obj)
end

#from_redis(identifier, suffix = :object) ⇒ Object



171
172
173
174
175
176
177
178
# File 'lib/familia/horreum/class_methods.rb', line 171

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 :FROMREDIS, 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.



47
48
49
50
# File 'lib/familia/horreum/class_methods.rb', line 47

def identifier(val = nil)
  @identifier = val if val
  @identifier
end

#multiget(*ids) ⇒ Object



135
136
137
138
# File 'lib/familia/horreum/class_methods.rb', line 135

def multiget(*ids)
  ids = rawmultiget(*ids)
  ids.filter_map { |json| from_json(json) }
end

#prefix(a = nil) ⇒ Object



122
123
124
125
# File 'lib/familia/horreum/class_methods.rb', line 122

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



210
211
212
213
214
215
# File 'lib/familia/horreum/class_methods.rb', line 210

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



140
141
142
143
144
145
146
# File 'lib/familia/horreum/class_methods.rb', line 140

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

Returns:

  • (Boolean)


75
76
77
# File 'lib/familia/horreum/class_methods.rb', line 75

def redis_object?(name)
  redis_types.key? name.to_s.to_sym
end

#redis_typesObject



79
80
81
82
# File 'lib/familia/horreum/class_methods.rb', line 79

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.

Raises:



223
224
225
226
227
228
229
# File 'lib/familia/horreum/class_methods.rb', line 223

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



113
114
115
# File 'lib/familia/horreum/class_methods.rb', line 113

def size(filter = '*')
  redis.keys(rediskey(filter)).compact.size
end

#suffix(a = nil, &blk) ⇒ Object



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

def suffix(a = nil, &blk)
  @suffix = a || blk if a || !blk.nil?
  @suffix || Familia.default_suffix
end

#ttl(v = nil) ⇒ Object



89
90
91
92
# File 'lib/familia/horreum/class_methods.rb', line 89

def ttl(v = nil)
  @ttl = v unless v.nil?
  @ttl || parent&.ttl
end

#uri(v = nil) ⇒ Object



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

def uri(v = nil)
  @uri = v unless v.nil?
  @uri || parent&.uri
end