familia 1.0.0.pre.rc3 → 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 +112 -46
- data/lib/familia/horreum/commands.rb +9 -3
- data/lib/familia/horreum/relations_management.rb +2 -2
- data/lib/familia/horreum/serialization.rb +23 -42
- 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 +5 -5
- data/lib/familia/types/list.rb +2 -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
@@ -95,15 +95,13 @@ module Familia
|
|
95
95
|
def fast_writer!(name)
|
96
96
|
define_method :"#{name}!" do |*args|
|
97
97
|
# Check if the correct number of arguments is provided (exactly one).
|
98
|
-
if args.size != 1
|
99
|
-
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)"
|
100
|
-
end
|
98
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" if args.size != 1
|
101
99
|
|
102
100
|
value = args.first
|
103
101
|
|
104
102
|
begin
|
105
103
|
# Trace the operation if debugging is enabled.
|
106
|
-
Familia.trace :FAST_WRITER, redis, "#{name}: #{value.inspect}", caller if Familia.debug?
|
104
|
+
Familia.trace :FAST_WRITER, redis, "#{name}: #{value.inspect}", caller(1..1) if Familia.debug?
|
107
105
|
|
108
106
|
# Convert the provided value to a format suitable for Redis storage.
|
109
107
|
prepared = to_redis(value)
|
@@ -146,11 +144,6 @@ module Familia
|
|
146
144
|
@redis_types
|
147
145
|
end
|
148
146
|
|
149
|
-
def ttl(v = nil)
|
150
|
-
@ttl = v unless v.nil?
|
151
|
-
@ttl || parent&.ttl
|
152
|
-
end
|
153
|
-
|
154
147
|
def db(v = nil)
|
155
148
|
@db = v unless v.nil?
|
156
149
|
@db || parent&.db
|
@@ -161,9 +154,10 @@ module Familia
|
|
161
154
|
@uri || parent&.uri
|
162
155
|
end
|
163
156
|
|
164
|
-
def all(suffix =
|
157
|
+
def all(suffix = nil)
|
158
|
+
suffix ||= self.suffix
|
165
159
|
# objects that could not be parsed will be nil
|
166
|
-
keys(suffix).filter_map { |k|
|
160
|
+
keys(suffix).filter_map { |k| from_rediskey(k) }
|
167
161
|
end
|
168
162
|
|
169
163
|
def any?(filter = '*')
|
@@ -184,12 +178,48 @@ module Familia
|
|
184
178
|
@prefix || name.downcase.gsub('::', Familia.delim).to_sym
|
185
179
|
end
|
186
180
|
|
187
|
-
|
188
|
-
|
189
|
-
|
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?
|
190
220
|
|
191
|
-
|
192
|
-
|
221
|
+
fobj.save
|
222
|
+
fobj
|
193
223
|
end
|
194
224
|
|
195
225
|
def multiget(*ids)
|
@@ -201,7 +231,7 @@ module Familia
|
|
201
231
|
ids.collect! { |objid| rediskey(objid) }
|
202
232
|
return [] if ids.compact.empty?
|
203
233
|
|
204
|
-
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?
|
205
235
|
redis.mget(*ids)
|
206
236
|
end
|
207
237
|
|
@@ -230,18 +260,18 @@ module Familia
|
|
230
260
|
# debugging.
|
231
261
|
#
|
232
262
|
# @example
|
233
|
-
# User.
|
263
|
+
# User.from_rediskey("user:123") # Returns a User instance if it exists,
|
234
264
|
# nil otherwise
|
235
265
|
#
|
236
|
-
def
|
266
|
+
def from_rediskey(objkey)
|
237
267
|
raise ArgumentError, 'Empty key' if objkey.to_s.empty?
|
238
268
|
|
239
269
|
# We use a lower-level method here b/c we're working with the
|
240
270
|
# full key and not just the identifier.
|
241
271
|
does_exist = redis.exists(objkey).positive?
|
242
272
|
|
243
|
-
Familia.ld "[.
|
244
|
-
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?
|
245
275
|
|
246
276
|
# This is the reason for calling exists first. We want to definitively
|
247
277
|
# and without any ambiguity know if the object exists in Redis. If it
|
@@ -251,7 +281,7 @@ module Familia
|
|
251
281
|
return unless does_exist
|
252
282
|
|
253
283
|
obj = redis.hgetall(objkey) # horreum objects are persisted as redis hashes
|
254
|
-
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?
|
255
285
|
|
256
286
|
new(**obj)
|
257
287
|
end
|
@@ -265,7 +295,7 @@ module Familia
|
|
265
295
|
# @return [Object, nil] An instance of the class if found, nil otherwise.
|
266
296
|
#
|
267
297
|
# This method constructs the full Redis key using the provided identifier
|
268
|
-
# and suffix, then delegates to `
|
298
|
+
# and suffix, then delegates to `from_rediskey` for the actual retrieval and
|
269
299
|
# instantiation.
|
270
300
|
#
|
271
301
|
# It's a higher-level method that abstracts away the key construction,
|
@@ -273,59 +303,95 @@ module Familia
|
|
273
303
|
# identifier.
|
274
304
|
#
|
275
305
|
# @example
|
276
|
-
# User.
|
306
|
+
# User.from_identifier(123) # Equivalent to User.from_rediskey("user:123:object")
|
277
307
|
#
|
278
|
-
def
|
308
|
+
def from_identifier(identifier, suffix = nil)
|
309
|
+
suffix ||= self.suffix
|
279
310
|
return nil if identifier.to_s.empty?
|
280
311
|
|
281
312
|
objkey = rediskey(identifier, suffix)
|
282
|
-
|
283
|
-
Familia.
|
284
|
-
|
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
|
285
317
|
end
|
318
|
+
alias load from_identifier
|
286
319
|
|
287
|
-
|
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
|
288
334
|
return false if identifier.to_s.empty?
|
289
335
|
|
290
336
|
objkey = rediskey identifier, suffix
|
291
337
|
|
292
338
|
ret = redis.exists objkey
|
293
|
-
Familia.trace :EXISTS, redis, "#{objkey} #{ret.inspect}", caller if Familia.debug?
|
294
|
-
|
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.
|
295
342
|
end
|
296
343
|
|
297
|
-
|
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
|
298
358
|
return false if identifier.to_s.empty?
|
299
359
|
|
300
360
|
objkey = rediskey identifier, suffix
|
301
361
|
|
302
362
|
ret = redis.del objkey
|
303
|
-
if Familia.debug?
|
304
|
-
Familia.trace :DELETED, redis, "#{objkey}: #{ret.inspect}",
|
305
|
-
caller
|
306
|
-
end
|
363
|
+
Familia.trace :DELETED, redis, "#{objkey}: #{ret.inspect}", caller(1..1) if Familia.debug?
|
307
364
|
ret.positive?
|
308
365
|
end
|
309
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
|
+
#
|
310
379
|
def find(suffix = '*')
|
311
380
|
redis.keys(rediskey('*', suffix)) || []
|
312
381
|
end
|
313
382
|
|
314
|
-
def qstamp(quantum = nil, pattern = nil, now = Familia.now)
|
315
|
-
quantum ||= ttl || 10.minutes
|
316
|
-
pattern ||= '%H%M'
|
317
|
-
rounded = now - (now % quantum)
|
318
|
-
Time.at(rounded).utc.strftime(pattern)
|
319
|
-
end
|
320
|
-
|
321
383
|
# +identifier+ can be a value or an Array of values used to create the index.
|
322
384
|
# We don't enforce a default suffix; that's left up to the instance.
|
323
385
|
# The suffix is used to differentiate between different types of objects.
|
324
386
|
#
|
325
|
-
#
|
326
|
-
#
|
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.
|
327
393
|
def rediskey(identifier, suffix = self.suffix)
|
328
|
-
Familia.ld "[.rediskey] #{identifier} for #{self} (suffix:#{suffix})"
|
394
|
+
# Familia.ld "[.rediskey] #{identifier} for #{self} (suffix:#{suffix})"
|
329
395
|
raise NoIdentifier, self if identifier.to_s.empty?
|
330
396
|
|
331
397
|
identifier &&= identifier.to_s
|
@@ -19,8 +19,8 @@ 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
26
|
# Sets a timeout on key. After the timeout has expired, the key will automatically be deleted.
|
@@ -28,10 +28,12 @@ module Familia
|
|
28
28
|
#
|
29
29
|
def expire(ttl = nil)
|
30
30
|
ttl ||= self.class.ttl
|
31
|
+
Familia.trace :EXPIRE, redis, ttl, caller(1..1) if Familia.debug?
|
31
32
|
redis.expire rediskey, ttl.to_i
|
32
33
|
end
|
33
34
|
|
34
35
|
def realttl
|
36
|
+
Familia.trace :REALTTL, redis, redisuri, caller(1..1) if Familia.debug?
|
35
37
|
redis.ttl rediskey
|
36
38
|
end
|
37
39
|
|
@@ -41,15 +43,18 @@ module Familia
|
|
41
43
|
# @return [Integer] The number of fields that were removed from the hash (0 or 1).
|
42
44
|
# @note This method is destructive, as indicated by the bang (!).
|
43
45
|
def hdel!(field)
|
46
|
+
Familia.trace :HDEL, redis, field, caller(1..1) if Familia.debug?
|
44
47
|
redis.hdel rediskey, field
|
45
48
|
end
|
46
49
|
|
47
50
|
def redistype
|
51
|
+
Familia.trace :REDISTYPE, redis, redisuri, caller(1..1) if Familia.debug?
|
48
52
|
redis.type rediskey(suffix)
|
49
53
|
end
|
50
54
|
|
51
55
|
# Parity with RedisType#rename
|
52
56
|
def rename(newkey)
|
57
|
+
Familia.trace :RENAME, redis, "#{rediskey} -> #{newkey}", caller(1..1) if Familia.debug?
|
53
58
|
redis.rename rediskey, newkey
|
54
59
|
end
|
55
60
|
|
@@ -61,13 +66,14 @@ module Familia
|
|
61
66
|
alias all hgetall
|
62
67
|
|
63
68
|
def hget(field)
|
69
|
+
Familia.trace :HGET, redis, field, caller(1..1) if Familia.debug?
|
64
70
|
redis.hget rediskey(suffix), field
|
65
71
|
end
|
66
72
|
|
67
73
|
# @return The number of fields that were added to the hash. If the
|
68
74
|
# field already exists, this will return 0.
|
69
75
|
def hset(field, value)
|
70
|
-
Familia.trace :HSET, redis,
|
76
|
+
Familia.trace :HSET, redis, field, caller(1..1) if Familia.debug?
|
71
77
|
redis.hset rediskey, field, value
|
72
78
|
end
|
73
79
|
|
@@ -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
|
@@ -113,19 +113,23 @@ module Familia
|
|
113
113
|
Familia.ld "[save] #{self.class} #{rediskey} #{ret}"
|
114
114
|
|
115
115
|
# Did Redis accept our offering?
|
116
|
-
ret.uniq.all? { |value|
|
116
|
+
ret.uniq.all? { |value| ["OK", true].include?(value) }
|
117
117
|
end
|
118
118
|
|
119
119
|
# Apply a smattering of fields to this object like fairy dust.
|
120
120
|
#
|
121
|
-
# @param fields [Hash] A magical bag of named attributes to sprinkle onto
|
122
|
-
# Each key-value pair is like a tiny spell, ready to
|
121
|
+
# @param fields [Hash] A magical bag of named attributes to sprinkle onto
|
122
|
+
# this instance. Each key-value pair is like a tiny spell, ready to
|
123
|
+
# enchant our object's properties.
|
123
124
|
#
|
124
|
-
# @return [self] Returns the newly bejeweled instance, now sparkling with
|
125
|
+
# @return [self] Returns the newly bejeweled instance, now sparkling with
|
126
|
+
# fresh attributes.
|
125
127
|
#
|
126
128
|
# @example Giving your object a makeover
|
127
|
-
# dragon.apply_fields(name: "Puff", breathes: "fire", loves: "little boys
|
128
|
-
#
|
129
|
+
# dragon.apply_fields(name: "Puff", breathes: "fire", loves: "little boys
|
130
|
+
# named Jackie")
|
131
|
+
# # => #<Dragon:0x007f8a1c8b0a28 @name="Puff", @breathes="fire",
|
132
|
+
# @loves="little boys named Jackie">
|
129
133
|
#
|
130
134
|
def apply_fields(**fields)
|
131
135
|
fields.each do |field, value|
|
@@ -140,10 +144,12 @@ module Familia
|
|
140
144
|
# This method performs a sacred ritual, sending our cherished attributes
|
141
145
|
# on a journey through the ethernet to find their resting place in Redis.
|
142
146
|
#
|
143
|
-
# @return [Array<String>] A mystical array of strings, cryptic messages
|
147
|
+
# @return [Array<String>] A mystical array of strings, cryptic messages
|
148
|
+
# from the Redis gods.
|
144
149
|
#
|
145
|
-
# @note Be warned, young programmer! This method dabbles in the arcane
|
146
|
-
# Side effects may include data persistence and a
|
150
|
+
# @note Be warned, young programmer! This method dabbles in the arcane
|
151
|
+
# art of transactions. Side effects may include data persistence and a
|
152
|
+
# slight tingling sensation.
|
147
153
|
#
|
148
154
|
# @example Offering your changes to the Redis deities
|
149
155
|
# unicorn.name = "Charlie"
|
@@ -155,6 +161,10 @@ module Familia
|
|
155
161
|
Familia.ld "[commit_fields] #{self.class} #{rediskey} #{to_h}"
|
156
162
|
transaction do |conn|
|
157
163
|
hmset
|
164
|
+
|
165
|
+
# Only classes that have the expiration ferature enabled will
|
166
|
+
# actually set an expiration time on their keys. Otherwise
|
167
|
+
# this will be a no-op.
|
158
168
|
update_expiration
|
159
169
|
end
|
160
170
|
end
|
@@ -183,7 +193,6 @@ module Familia
|
|
183
193
|
delete!
|
184
194
|
end
|
185
195
|
|
186
|
-
|
187
196
|
# Refreshes the object's state by querying Redis and overwriting the
|
188
197
|
# current field values. This method performs a destructive update on the
|
189
198
|
# object, regardless of unsaved changes.
|
@@ -217,7 +226,8 @@ module Familia
|
|
217
226
|
# into a more plebeian hash. But fear not, for in this form, it can slip through
|
218
227
|
# the cracks of the universe (or at least, into Redis) with ease.
|
219
228
|
#
|
220
|
-
# @return [Hash] A glittering hash, each key a field name, each value a
|
229
|
+
# @return [Hash] A glittering hash, each key a field name, each value a
|
230
|
+
# Redis-ready treasure.
|
221
231
|
#
|
222
232
|
# @example Turning your dragon into a hash
|
223
233
|
# dragon.to_h
|
@@ -247,7 +257,8 @@ module Familia
|
|
247
257
|
# unicorn.to_a
|
248
258
|
# # => ["Charlie", "magnificent", 5]
|
249
259
|
#
|
250
|
-
# @note Each value is carefully disguised in its Redis costume
|
260
|
+
# @note Each value is carefully disguised in its Redis costume
|
261
|
+
# before joining the parade.
|
251
262
|
#
|
252
263
|
def to_a
|
253
264
|
self.class.fields.map do |field|
|
@@ -299,36 +310,6 @@ module Familia
|
|
299
310
|
prepared
|
300
311
|
end
|
301
312
|
|
302
|
-
# Set an expiration date for our data, like a "best before" sticker for Redis!
|
303
|
-
#
|
304
|
-
# This method gives our data a lifespan in Redis. It's like telling Redis,
|
305
|
-
# "Hey, this data is fresh now, but it might get stale after a while!"
|
306
|
-
#
|
307
|
-
# @param ttl [Integer, nil] The Time To Live in seconds. If nil, we'll check
|
308
|
-
# our options for a default expiration time.
|
309
|
-
#
|
310
|
-
# @return [Boolean] true if the expiration was set successfully, false otherwise.
|
311
|
-
# It's like asking Redis, "Did you stick that expiration label on properly?"
|
312
|
-
#
|
313
|
-
# @example Making your pet rock data mortal
|
314
|
-
# rocky.update_expiration(86400) # Dwayne will live in Redis for one day
|
315
|
-
#
|
316
|
-
# @note If the TTL is zero, we assume our data wants to live forever.
|
317
|
-
# Immortality in Redis! Who wouldn't want that?
|
318
|
-
#
|
319
|
-
def update_expiration(ttl = nil)
|
320
|
-
ttl ||= opts[:ttl]
|
321
|
-
ttl = ttl.to_i
|
322
|
-
|
323
|
-
return if ttl.zero?
|
324
|
-
|
325
|
-
Familia.ld "Setting expiration for #{rediskey} to #{ttl} seconds"
|
326
|
-
|
327
|
-
# EXPIRE command returns 1 if the timeout was set, 0 if key does not
|
328
|
-
# exist or the timeout could not be set.
|
329
|
-
expire(ttl).positive?
|
330
|
-
end
|
331
|
-
|
332
313
|
end
|
333
314
|
# End of Serialization module
|
334
315
|
|
@@ -28,7 +28,6 @@ module Familia
|
|
28
28
|
# from the `identifier` method).
|
29
29
|
#
|
30
30
|
def rediskey(suffix = nil, ignored = nil)
|
31
|
-
Familia.ld "[#rediskey] #{identifier} for #{self.class}"
|
32
31
|
raise Familia::NoIdentifier, "No identifier for #{self.class}" if identifier.to_s.empty?
|
33
32
|
suffix ||= self.suffix # use the instance method to get the default suffix
|
34
33
|
self.class.rediskey identifier, suffix
|
data/lib/familia/horreum.rb
CHANGED
@@ -56,7 +56,7 @@ module Familia
|
|
56
56
|
class << self
|
57
57
|
# Extends ClassMethods to subclasses and tracks Familia members
|
58
58
|
def inherited(member)
|
59
|
-
Familia.trace :
|
59
|
+
Familia.trace :HORREUM, nil, "Welcome #{member} to the family", caller(1..1) if Familia.debug?
|
60
60
|
member.extend(ClassMethods)
|
61
61
|
member.extend(Features)
|
62
62
|
|
data/lib/familia/logging.rb
CHANGED
@@ -126,26 +126,48 @@ module Familia
|
|
126
126
|
#
|
127
127
|
# @param label [Symbol] A label for the trace message (e.g., :EXPAND,
|
128
128
|
# :FROMREDIS, :LOAD, :EXISTS).
|
129
|
-
# @param redis_instance [
|
129
|
+
# @param redis_instance [Redis, Redis::Future, nil] The Redis instance or
|
130
|
+
# Future being used.
|
130
131
|
# @param ident [String] An identifier or key related to the operation being
|
131
132
|
# traced.
|
132
133
|
# @param context [Array<String>, String, nil] The calling context, typically
|
133
134
|
# obtained from `caller` or `caller.first`. Default is nil.
|
134
135
|
#
|
135
136
|
# @example
|
136
|
-
# Familia.trace :LOAD, Familia.redis(uri), objkey, caller if
|
137
|
-
#
|
137
|
+
# Familia.trace :LOAD, Familia.redis(uri), objkey, caller(1..1) if
|
138
|
+
# Familia.debug?
|
138
139
|
#
|
139
140
|
# @return [nil]
|
140
141
|
#
|
142
|
+
# @note This method only executes if LoggerTraceRefinement::ENABLED is true.
|
143
|
+
# @note The redis_instance can be a Redis object, Redis::Future (used in
|
144
|
+
# pipelined and multi blocks), or nil (when the redis connection isn't
|
145
|
+
# relevant).
|
146
|
+
#
|
141
147
|
def trace(label, redis_instance, ident, context = nil)
|
142
148
|
return unless LoggerTraceRefinement::ENABLED
|
143
|
-
|
149
|
+
|
150
|
+
# Usually redis_instance is a Redis object, but it could be
|
151
|
+
# a Redis::Future which is what is used inside of pipelined
|
152
|
+
# and multi blocks. In some contexts it's nil where the
|
153
|
+
# redis connection isn't relevant.
|
154
|
+
instance_id = if redis_instance
|
155
|
+
case redis_instance
|
156
|
+
when Redis
|
157
|
+
redis_instance.id.respond_to?(:to_s) ? redis_instance.id.to_s : redis_instance.class.name
|
158
|
+
when Redis::Future
|
159
|
+
"Redis::Future"
|
160
|
+
else
|
161
|
+
redis_instance.class.name
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
144
165
|
codeline = if context
|
145
166
|
context = [context].flatten
|
146
167
|
context.reject! { |line| line =~ %r{lib/familia} }
|
147
168
|
context.first
|
148
169
|
end
|
170
|
+
|
149
171
|
@logger.trace format('[%s] %s -> %s <- at %s', label, instance_id, ident, codeline)
|
150
172
|
end
|
151
173
|
|