familia 1.0.0.pre.rc2 → 1.0.0.pre.rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +198 -48
- data/VERSION.yml +1 -1
- data/lib/familia/base.rb +29 -1
- data/lib/familia/features/expiration.rb +90 -0
- data/lib/familia/features/quantization.rb +56 -0
- data/lib/familia/features/safe_dump.rb +2 -33
- data/lib/familia/features.rb +5 -4
- data/lib/familia/horreum/class_methods.rb +172 -53
- data/lib/familia/horreum/commands.rb +43 -5
- data/lib/familia/horreum/relations_management.rb +2 -2
- data/lib/familia/horreum/serialization.rb +172 -47
- data/lib/familia/horreum/settings.rb +0 -8
- data/lib/familia/horreum/utils.rb +0 -1
- data/lib/familia/horreum.rb +1 -1
- data/lib/familia/logging.rb +26 -4
- data/lib/familia/redistype/serialization.rb +60 -38
- data/lib/familia/redistype.rb +45 -17
- data/lib/familia/settings.rb +11 -1
- data/lib/familia/tools.rb +68 -0
- data/lib/familia/types/hashkey.rb +9 -8
- data/lib/familia/types/list.rb +4 -2
- data/lib/familia/types/sorted_set.rb +12 -12
- data/lib/familia/types/string.rb +1 -1
- data/lib/familia/types/unsorted_set.rb +2 -2
- data/lib/familia/utils.rb +106 -51
- data/lib/familia/version.rb +2 -2
- data/try/10_familia_try.rb +4 -4
- data/try/20_redis_type_try.rb +9 -6
- data/try/26_redis_bool_try.rb +1 -1
- data/try/27_redis_horreum_try.rb +1 -1
- data/try/30_familia_object_try.rb +3 -2
- data/try/40_customer_try.rb +3 -3
- data/try/test_helpers.rb +9 -2
- metadata +5 -5
- data/lib/familia/features/api_version.rb +0 -19
- data/lib/familia/features/atomic_saves.rb +0 -8
- data/lib/familia/features/quantizer.rb +0 -35
@@ -36,20 +36,40 @@ module Familia
|
|
36
36
|
attr_accessor :parent
|
37
37
|
attr_writer :redis, :dump_method, :load_method
|
38
38
|
|
39
|
+
# Returns the Redis connection for the class.
|
40
|
+
#
|
41
|
+
# This method retrieves the Redis connection instance for the class. If no
|
42
|
+
# connection is set, it initializes a new connection using the provided URI
|
43
|
+
# or database configuration.
|
44
|
+
#
|
45
|
+
# @return [Redis] the Redis connection instance.
|
46
|
+
#
|
39
47
|
def redis
|
40
48
|
@redis || Familia.redis(uri || db)
|
41
49
|
end
|
42
50
|
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
51
|
+
# Sets or retrieves the unique identifier for the class.
|
52
|
+
#
|
53
|
+
# This method defines or returns the unique identifier used to generate the
|
54
|
+
# Redis key for the object. If a value is provided, it sets the identifier;
|
55
|
+
# otherwise, it returns the current identifier.
|
56
|
+
#
|
57
|
+
# @param [Object] val the value to set as the identifier (optional).
|
58
|
+
# @return [Object] the current identifier.
|
59
|
+
#
|
46
60
|
def identifier(val = nil)
|
47
61
|
@identifier = val if val
|
48
62
|
@identifier
|
49
63
|
end
|
50
64
|
|
51
|
-
#
|
52
|
-
#
|
65
|
+
# Defines a field for the class and creates accessor methods.
|
66
|
+
#
|
67
|
+
# This method defines a new field for the class, creating getter and setter
|
68
|
+
# instance methods similar to `attr_accessor`. It also generates a fast
|
69
|
+
# writer method for immediate persistence to Redis.
|
70
|
+
#
|
71
|
+
# @param [Symbol, String] name the name of the field to define.
|
72
|
+
#
|
53
73
|
def field(name)
|
54
74
|
fields << name
|
55
75
|
attr_accessor name
|
@@ -58,13 +78,44 @@ module Familia
|
|
58
78
|
fast_writer! name
|
59
79
|
end
|
60
80
|
|
61
|
-
#
|
81
|
+
# Defines a writer method with a bang (!) suffix for a given attribute name.
|
82
|
+
#
|
83
|
+
# The dynamically defined method performs the following:
|
84
|
+
# - Checks if the correct number of arguments is provided (exactly one).
|
85
|
+
# - Converts the provided value to a format suitable for Redis storage.
|
86
|
+
# - Uses the existing accessor method to set the attribute value.
|
87
|
+
# - Persists the value to Redis immediately using the hset command.
|
88
|
+
# - Includes custom error handling to raise an ArgumentError if the wrong number of arguments is given.
|
89
|
+
# - Raises a custom error message if an exception occurs during the execution of the method.
|
90
|
+
#
|
91
|
+
# @param [Symbol, String] name the name of the attribute for which the writer method is defined.
|
92
|
+
# @raise [ArgumentError] if the wrong number of arguments is provided.
|
93
|
+
# @raise [RuntimeError] if an exception occurs during the execution of the method.
|
94
|
+
#
|
62
95
|
def fast_writer!(name)
|
63
|
-
define_method :"#{name}!" do |
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
96
|
+
define_method :"#{name}!" do |*args|
|
97
|
+
# Check if the correct number of arguments is provided (exactly one).
|
98
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" if args.size != 1
|
99
|
+
|
100
|
+
value = args.first
|
101
|
+
|
102
|
+
begin
|
103
|
+
# Trace the operation if debugging is enabled.
|
104
|
+
Familia.trace :FAST_WRITER, redis, "#{name}: #{value.inspect}", caller(1..1) if Familia.debug?
|
105
|
+
|
106
|
+
# Convert the provided value to a format suitable for Redis storage.
|
107
|
+
prepared = to_redis(value)
|
108
|
+
Familia.ld "[.fast_writer!] #{name} val: #{value.class} prepared: #{prepared.class}"
|
109
|
+
|
110
|
+
# Use the existing accessor method to set the attribute value.
|
111
|
+
send :"#{name}=", value
|
112
|
+
|
113
|
+
# Persist the value to Redis immediately using the hset command.
|
114
|
+
hset name, prepared
|
115
|
+
rescue Familia::Problem => e
|
116
|
+
# Raise a custom error message if an exception occurs during the execution of the method.
|
117
|
+
raise "#{name}! method failed: #{e.message}", e.backtrace
|
118
|
+
end
|
68
119
|
end
|
69
120
|
end
|
70
121
|
|
@@ -93,11 +144,6 @@ module Familia
|
|
93
144
|
@redis_types
|
94
145
|
end
|
95
146
|
|
96
|
-
def ttl(v = nil)
|
97
|
-
@ttl = v unless v.nil?
|
98
|
-
@ttl || parent&.ttl
|
99
|
-
end
|
100
|
-
|
101
147
|
def db(v = nil)
|
102
148
|
@db = v unless v.nil?
|
103
149
|
@db || parent&.db
|
@@ -108,9 +154,10 @@ module Familia
|
|
108
154
|
@uri || parent&.uri
|
109
155
|
end
|
110
156
|
|
111
|
-
def all(suffix =
|
157
|
+
def all(suffix = nil)
|
158
|
+
suffix ||= self.suffix
|
112
159
|
# objects that could not be parsed will be nil
|
113
|
-
keys(suffix).filter_map { |k|
|
160
|
+
keys(suffix).filter_map { |k| from_rediskey(k) }
|
114
161
|
end
|
115
162
|
|
116
163
|
def any?(filter = '*')
|
@@ -131,12 +178,48 @@ module Familia
|
|
131
178
|
@prefix || name.downcase.gsub('::', Familia.delim).to_sym
|
132
179
|
end
|
133
180
|
|
134
|
-
|
135
|
-
|
136
|
-
|
181
|
+
# Creates and persists a new instance of the class.
|
182
|
+
#
|
183
|
+
# @param *args [Array] Variable number of positional arguments to be passed
|
184
|
+
# to the constructor.
|
185
|
+
# @param **kwargs [Hash] Keyword arguments to be passed to the constructor.
|
186
|
+
# @return [Object] The newly created and persisted instance.
|
187
|
+
# @raise [Familia::Problem] If an instance with the same identifier already
|
188
|
+
# exists.
|
189
|
+
#
|
190
|
+
# This method serves as a factory method for creating and persisting new
|
191
|
+
# instances of the class. It combines object instantiation, existence
|
192
|
+
# checking, and persistence in a single operation.
|
193
|
+
#
|
194
|
+
# The method is flexible in accepting both positional and keyword arguments:
|
195
|
+
# - Positional arguments (*args) are passed directly to the constructor.
|
196
|
+
# - Keyword arguments (**kwargs) are passed as a hash to the constructor.
|
197
|
+
#
|
198
|
+
# After instantiation, the method checks if an object with the same
|
199
|
+
# identifier already exists. If it does, a Familia::Problem exception is
|
200
|
+
# raised to prevent overwriting existing data.
|
201
|
+
#
|
202
|
+
# Finally, the method saves the new instance returns it.
|
203
|
+
#
|
204
|
+
# @example Creating an object with keyword arguments
|
205
|
+
# User.create(name: "John", age: 30)
|
206
|
+
#
|
207
|
+
# @example Creating an object with positional and keyword arguments (not recommended)
|
208
|
+
# Product.create("SKU123", name: "Widget", price: 9.99)
|
209
|
+
#
|
210
|
+
# @note The behavior of this method depends on the implementation of #new,
|
211
|
+
# #exists?, and #save in the class and its superclasses.
|
212
|
+
#
|
213
|
+
# @see #new
|
214
|
+
# @see #exists?
|
215
|
+
# @see #save
|
216
|
+
#
|
217
|
+
def create *args, **kwargs
|
218
|
+
fobj = new(*args, **kwargs)
|
219
|
+
raise Familia::Problem, "#{self} already exists: #{fobj.rediskey}" if fobj.exists?
|
137
220
|
|
138
|
-
|
139
|
-
|
221
|
+
fobj.save
|
222
|
+
fobj
|
140
223
|
end
|
141
224
|
|
142
225
|
def multiget(*ids)
|
@@ -148,7 +231,7 @@ module Familia
|
|
148
231
|
ids.collect! { |objid| rediskey(objid) }
|
149
232
|
return [] if ids.compact.empty?
|
150
233
|
|
151
|
-
Familia.trace :MULTIGET, redis, "#{ids.size}: #{ids}", caller if Familia.debug?
|
234
|
+
Familia.trace :MULTIGET, redis, "#{ids.size}: #{ids}", caller(1..1) if Familia.debug?
|
152
235
|
redis.mget(*ids)
|
153
236
|
end
|
154
237
|
|
@@ -177,18 +260,18 @@ module Familia
|
|
177
260
|
# debugging.
|
178
261
|
#
|
179
262
|
# @example
|
180
|
-
# User.
|
263
|
+
# User.from_rediskey("user:123") # Returns a User instance if it exists,
|
181
264
|
# nil otherwise
|
182
265
|
#
|
183
|
-
def
|
266
|
+
def from_rediskey(objkey)
|
184
267
|
raise ArgumentError, 'Empty key' if objkey.to_s.empty?
|
185
268
|
|
186
269
|
# We use a lower-level method here b/c we're working with the
|
187
270
|
# full key and not just the identifier.
|
188
271
|
does_exist = redis.exists(objkey).positive?
|
189
272
|
|
190
|
-
Familia.ld "[.
|
191
|
-
Familia.trace :FROM_KEY, redis, objkey, caller if Familia.debug?
|
273
|
+
Familia.ld "[.from_rediskey] #{self} from key #{objkey} (exists: #{does_exist})"
|
274
|
+
Familia.trace :FROM_KEY, redis, objkey, caller(1..1) if Familia.debug?
|
192
275
|
|
193
276
|
# This is the reason for calling exists first. We want to definitively
|
194
277
|
# and without any ambiguity know if the object exists in Redis. If it
|
@@ -198,7 +281,7 @@ module Familia
|
|
198
281
|
return unless does_exist
|
199
282
|
|
200
283
|
obj = redis.hgetall(objkey) # horreum objects are persisted as redis hashes
|
201
|
-
Familia.trace :FROM_KEY2, redis, "#{objkey}: #{obj.inspect}", caller if Familia.debug?
|
284
|
+
Familia.trace :FROM_KEY2, redis, "#{objkey}: #{obj.inspect}", caller(1..1) if Familia.debug?
|
202
285
|
|
203
286
|
new(**obj)
|
204
287
|
end
|
@@ -212,7 +295,7 @@ module Familia
|
|
212
295
|
# @return [Object, nil] An instance of the class if found, nil otherwise.
|
213
296
|
#
|
214
297
|
# This method constructs the full Redis key using the provided identifier
|
215
|
-
# and suffix, then delegates to `
|
298
|
+
# and suffix, then delegates to `from_rediskey` for the actual retrieval and
|
216
299
|
# instantiation.
|
217
300
|
#
|
218
301
|
# It's a higher-level method that abstracts away the key construction,
|
@@ -220,59 +303,95 @@ module Familia
|
|
220
303
|
# identifier.
|
221
304
|
#
|
222
305
|
# @example
|
223
|
-
# User.
|
306
|
+
# User.from_identifier(123) # Equivalent to User.from_rediskey("user:123:object")
|
224
307
|
#
|
225
|
-
def
|
308
|
+
def from_identifier(identifier, suffix = nil)
|
309
|
+
suffix ||= self.suffix
|
226
310
|
return nil if identifier.to_s.empty?
|
227
311
|
|
228
312
|
objkey = rediskey(identifier, suffix)
|
229
|
-
|
230
|
-
Familia.
|
231
|
-
|
313
|
+
|
314
|
+
Familia.ld "[.from_identifier] #{self} from key #{objkey})"
|
315
|
+
Familia.trace :FROM_IDENTIFIER, Familia.redis(uri), objkey, caller(1..1).first if Familia.debug?
|
316
|
+
from_rediskey objkey
|
232
317
|
end
|
318
|
+
alias load from_identifier
|
233
319
|
|
234
|
-
|
320
|
+
# Checks if an object with the given identifier exists in Redis.
|
321
|
+
#
|
322
|
+
# @param identifier [String, Integer] The unique identifier for the object.
|
323
|
+
# @param suffix [Symbol, nil] The suffix to use in the Redis key (default: class suffix).
|
324
|
+
# @return [Boolean] true if the object exists, false otherwise.
|
325
|
+
#
|
326
|
+
# This method constructs the full Redis key using the provided identifier and suffix,
|
327
|
+
# then checks if the key exists in Redis.
|
328
|
+
#
|
329
|
+
# @example
|
330
|
+
# User.exists?(123) # Returns true if user:123:object exists in Redis
|
331
|
+
#
|
332
|
+
def exists?(identifier, suffix = nil)
|
333
|
+
suffix ||= self.suffix
|
235
334
|
return false if identifier.to_s.empty?
|
236
335
|
|
237
336
|
objkey = rediskey identifier, suffix
|
238
337
|
|
239
338
|
ret = redis.exists objkey
|
240
|
-
Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}", caller if Familia.debug?
|
241
|
-
|
339
|
+
Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
|
340
|
+
|
341
|
+
ret.positive? # differs from redis API but I think it's okay bc `exists?` is a predicate method.
|
242
342
|
end
|
243
343
|
|
244
|
-
|
344
|
+
# Destroys an object in Redis with the given identifier.
|
345
|
+
#
|
346
|
+
# @param identifier [String, Integer] The unique identifier for the object to destroy.
|
347
|
+
# @param suffix [Symbol, nil] The suffix to use in the Redis key (default: class suffix).
|
348
|
+
# @return [Boolean] true if the object was successfully destroyed, false otherwise.
|
349
|
+
#
|
350
|
+
# This method constructs the full Redis key using the provided identifier and suffix,
|
351
|
+
# then removes the corresponding key from Redis.
|
352
|
+
#
|
353
|
+
# @example
|
354
|
+
# User.destroy!(123) # Removes user:123:object from Redis
|
355
|
+
#
|
356
|
+
def destroy!(identifier, suffix = nil)
|
357
|
+
suffix ||= self.suffix
|
245
358
|
return false if identifier.to_s.empty?
|
246
359
|
|
247
360
|
objkey = rediskey identifier, suffix
|
248
361
|
|
249
362
|
ret = redis.del objkey
|
250
|
-
if Familia.debug?
|
251
|
-
Familia.trace :DELETED, redis, "#{objkey}: #{ret.inspect}",
|
252
|
-
caller
|
253
|
-
end
|
363
|
+
Familia.trace :DELETED, redis, "#{objkey}: #{ret.inspect}", caller(1..1) if Familia.debug?
|
254
364
|
ret.positive?
|
255
365
|
end
|
256
366
|
|
367
|
+
# Finds all keys in Redis matching the given suffix pattern.
|
368
|
+
#
|
369
|
+
# @param suffix [String] The suffix pattern to match (default: '*').
|
370
|
+
# @return [Array<String>] An array of matching Redis keys.
|
371
|
+
#
|
372
|
+
# This method searches for all Redis keys that match the given suffix pattern.
|
373
|
+
# It uses the class's rediskey method to construct the search pattern.
|
374
|
+
#
|
375
|
+
# @example
|
376
|
+
# User.find # Returns all keys matching user:*:object
|
377
|
+
# User.find('active') # Returns all keys matching user:*:active
|
378
|
+
#
|
257
379
|
def find(suffix = '*')
|
258
380
|
redis.keys(rediskey('*', suffix)) || []
|
259
381
|
end
|
260
382
|
|
261
|
-
def qstamp(quantum = nil, pattern = nil, now = Familia.now)
|
262
|
-
quantum ||= ttl || 10.minutes
|
263
|
-
pattern ||= '%H%M'
|
264
|
-
rounded = now - (now % quantum)
|
265
|
-
Time.at(rounded).utc.strftime(pattern)
|
266
|
-
end
|
267
|
-
|
268
383
|
# +identifier+ can be a value or an Array of values used to create the index.
|
269
384
|
# We don't enforce a default suffix; that's left up to the instance.
|
270
385
|
# The suffix is used to differentiate between different types of objects.
|
271
386
|
#
|
272
|
-
#
|
273
|
-
#
|
387
|
+
# +suffix+ If a nil value is explicitly passed in, it won't appear in the redis
|
388
|
+
# key that's returned. If no suffix is passed in, the class' suffix is used
|
389
|
+
# as the default (via the class method self.suffix). It's an important
|
390
|
+
# distinction b/c passing in an explicitly nil is how RedisType objects
|
391
|
+
# at the class level are created without the global default 'object'
|
392
|
+
# suffix. See RedisType#rediskey "parent_class?" for more details.
|
274
393
|
def rediskey(identifier, suffix = self.suffix)
|
275
|
-
Familia.ld "[.rediskey] #{identifier} for #{self} (suffix:#{suffix})"
|
394
|
+
# Familia.ld "[.rediskey] #{identifier} for #{self} (suffix:#{suffix})"
|
276
395
|
raise NoIdentifier, self if identifier.to_s.empty?
|
277
396
|
|
278
397
|
identifier &&= identifier.to_s
|
@@ -19,29 +19,42 @@ module Familia
|
|
19
19
|
module Commands
|
20
20
|
|
21
21
|
def exists?
|
22
|
-
|
23
|
-
|
22
|
+
# Trace output comes from the class method
|
23
|
+
self.class.exists? identifier, suffix
|
24
24
|
end
|
25
25
|
|
26
|
+
# Sets a timeout on key. After the timeout has expired, the key will automatically be deleted.
|
27
|
+
# Returns 1 if the timeout was set, 0 if key does not exist or the timeout could not be set.
|
28
|
+
#
|
26
29
|
def expire(ttl = nil)
|
27
30
|
ttl ||= self.class.ttl
|
31
|
+
Familia.trace :EXPIRE, redis, ttl, caller(1..1) if Familia.debug?
|
28
32
|
redis.expire rediskey, ttl.to_i
|
29
33
|
end
|
30
34
|
|
31
35
|
def realttl
|
36
|
+
Familia.trace :REALTTL, redis, redisuri, caller(1..1) if Familia.debug?
|
32
37
|
redis.ttl rediskey
|
33
38
|
end
|
34
39
|
|
40
|
+
# Deletes a field from the hash stored at the Redis key.
|
41
|
+
#
|
42
|
+
# @param field [String] The field to delete from the hash.
|
43
|
+
# @return [Integer] The number of fields that were removed from the hash (0 or 1).
|
44
|
+
# @note This method is destructive, as indicated by the bang (!).
|
35
45
|
def hdel!(field)
|
46
|
+
Familia.trace :HDEL, redis, field, caller(1..1) if Familia.debug?
|
36
47
|
redis.hdel rediskey, field
|
37
48
|
end
|
38
49
|
|
39
50
|
def redistype
|
51
|
+
Familia.trace :REDISTYPE, redis, redisuri, caller(1..1) if Familia.debug?
|
40
52
|
redis.type rediskey(suffix)
|
41
53
|
end
|
42
54
|
|
43
55
|
# Parity with RedisType#rename
|
44
56
|
def rename(newkey)
|
57
|
+
Familia.trace :RENAME, redis, "#{rediskey} -> #{newkey}", caller(1..1) if Familia.debug?
|
45
58
|
redis.rename rediskey, newkey
|
46
59
|
end
|
47
60
|
|
@@ -53,13 +66,14 @@ module Familia
|
|
53
66
|
alias all hgetall
|
54
67
|
|
55
68
|
def hget(field)
|
69
|
+
Familia.trace :HGET, redis, field, caller(1..1) if Familia.debug?
|
56
70
|
redis.hget rediskey(suffix), field
|
57
71
|
end
|
58
72
|
|
59
73
|
# @return The number of fields that were added to the hash. If the
|
60
74
|
# field already exists, this will return 0.
|
61
75
|
def hset(field, value)
|
62
|
-
Familia.trace :HSET, redis,
|
76
|
+
Familia.trace :HSET, redis, field, caller(1..1) if Familia.debug?
|
63
77
|
redis.hset rediskey, field, value
|
64
78
|
end
|
65
79
|
|
@@ -76,21 +90,45 @@ module Familia
|
|
76
90
|
redis.hvals rediskey(suffix)
|
77
91
|
end
|
78
92
|
|
79
|
-
def
|
93
|
+
def incr(field)
|
94
|
+
redis.hincrby rediskey(suffix), field, 1
|
95
|
+
end
|
96
|
+
alias increment incr
|
97
|
+
|
98
|
+
def incrby(field, increment)
|
80
99
|
redis.hincrby rediskey(suffix), field, increment
|
81
100
|
end
|
101
|
+
alias incrementby incrby
|
82
102
|
|
83
|
-
def
|
103
|
+
def incrbyfloat(field, increment)
|
84
104
|
redis.hincrbyfloat rediskey(suffix), field, increment
|
85
105
|
end
|
106
|
+
alias incrementbyfloat incrbyfloat
|
107
|
+
|
108
|
+
def decrby(field, decrement)
|
109
|
+
redis.decrby rediskey(suffix), field, decrement
|
110
|
+
end
|
111
|
+
alias decrementby decrby
|
112
|
+
|
113
|
+
def decr(field)
|
114
|
+
redis.hdecr field
|
115
|
+
end
|
116
|
+
alias decrement decr
|
86
117
|
|
87
118
|
def hlen
|
88
119
|
redis.hlen rediskey(suffix)
|
89
120
|
end
|
121
|
+
alias hlength hlen
|
90
122
|
|
91
123
|
def hstrlen(field)
|
92
124
|
redis.hstrlen rediskey(suffix), field
|
93
125
|
end
|
126
|
+
alias hstrlength hstrlen
|
127
|
+
|
128
|
+
def key?(field)
|
129
|
+
redis.hexists rediskey(suffix), field
|
130
|
+
end
|
131
|
+
alias has_key? key?
|
94
132
|
|
95
133
|
def delete!
|
96
134
|
Familia.trace :DELETE!, redis, redisuri, caller(1..1) if Familia.debug?
|
@@ -81,7 +81,7 @@ module Familia
|
|
81
81
|
|
82
82
|
# Creates an instance-level relation
|
83
83
|
def attach_instance_redis_object_relation(name, klass, opts)
|
84
|
-
Familia.ld "[
|
84
|
+
Familia.ld "[#{self}##{name}] Attaching instance-level #{klass} #{opts}"
|
85
85
|
raise ArgumentError, "Name is blank (#{klass})" if name.to_s.empty?
|
86
86
|
|
87
87
|
name = name.to_s.to_sym
|
@@ -106,7 +106,7 @@ module Familia
|
|
106
106
|
|
107
107
|
# Creates a class-level relation
|
108
108
|
def attach_class_redis_object_relation(name, klass, opts)
|
109
|
-
Familia.ld "[#{self}] Attaching class-level #{
|
109
|
+
Familia.ld "[#{self}.#{name}] Attaching class-level #{klass} #{opts}"
|
110
110
|
raise ArgumentError, 'Name is blank (klass)' if name.to_s.empty?
|
111
111
|
|
112
112
|
name = name.to_s.to_sym
|