familia 1.0.0.pre.rc7 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.pre-commit-config.yaml +1 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +3 -1
- data/README.md +5 -5
- data/VERSION.yml +1 -2
- data/familia.gemspec +1 -2
- data/lib/familia/base.rb +11 -11
- data/lib/familia/connection.rb +18 -2
- data/lib/familia/errors.rb +14 -0
- data/lib/familia/features/expiration.rb +13 -2
- data/lib/familia/features/quantization.rb +1 -1
- data/lib/familia/horreum/class_methods.rb +31 -21
- data/lib/familia/horreum/commands.rb +58 -15
- data/lib/familia/horreum/relations_management.rb +9 -2
- data/lib/familia/horreum/serialization.rb +130 -29
- data/lib/familia/horreum.rb +69 -19
- data/lib/familia/redistype/commands.rb +5 -2
- data/lib/familia/redistype/serialization.rb +17 -16
- data/lib/familia/redistype/types/hashkey.rb +167 -0
- data/lib/familia/{types → redistype/types}/list.rb +19 -14
- data/lib/familia/{types → redistype/types}/sorted_set.rb +22 -19
- data/lib/familia/{types → redistype/types}/string.rb +8 -6
- data/lib/familia/{types → redistype/types}/unsorted_set.rb +16 -12
- data/lib/familia/redistype.rb +19 -9
- data/lib/familia.rb +6 -1
- data/try/10_familia_try.rb +1 -1
- data/try/20_redis_type_try.rb +1 -1
- data/try/21_redis_type_zset_try.rb +1 -1
- data/try/22_redis_type_set_try.rb +1 -1
- data/try/23_redis_type_list_try.rb +2 -2
- data/try/24_redis_type_string_try.rb +3 -3
- data/try/25_redis_type_hash_try.rb +1 -1
- data/try/26_redis_bool_try.rb +2 -2
- data/try/27_redis_horreum_try.rb +2 -2
- data/try/28_redis_horreum_serialization_try.rb +159 -0
- data/try/29_redis_horreum_initialization_try.rb +113 -0
- data/try/30_familia_object_try.rb +8 -5
- data/try/40_customer_try.rb +6 -6
- data/try/91_json_bug_try.rb +86 -0
- data/try/92_symbolize_try.rb +96 -0
- data/try/93_string_coercion_try.rb +154 -0
- metadata +20 -15
- data/lib/familia/types/hashkey.rb +0 -108
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a664f87a493fffb0e362752c8f988c8de27d83008f3a796b66737e3d15e771d
|
4
|
+
data.tar.gz: cb22d85f774e40a7b656713760f2f1e31b154171620038c282ce2dbe5e88492b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3942bab094053731bd0d45c47a89c7bde62d26b99339cd2fad36e5481b28f91cd3eecb17c8ef6f77e3e486858151f6d5821e973f9fb7c068431abf11634806db
|
7
|
+
data.tar.gz: 32933d4adbb991b3cb790f4a47134f7e428fa617b3f33d3068d98a2e795d2ee58b2654ac22fb913abdc46dd0f3459bcdfd50d71273a137d2f337c4da521e475d
|
data/.gitignore
CHANGED
data/.pre-commit-config.yaml
CHANGED
data/Gemfile
CHANGED
@@ -5,7 +5,9 @@ source 'https://rubygems.org'
|
|
5
5
|
gemspec
|
6
6
|
|
7
7
|
group :development, :test do
|
8
|
-
|
8
|
+
# byebug only works with MRI
|
9
|
+
gem 'byebug', '~> 11.0', require: false if RUBY_ENGINE == 'ruby'
|
10
|
+
gem 'pry-byebug', '~> 3.10.1', require: false if RUBY_ENGINE == 'ruby'
|
9
11
|
gem 'rubocop', require: false
|
10
12
|
gem 'rubocop-performance', require: false
|
11
13
|
gem 'rubocop-thread_safety', require: false
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
familia (1.
|
4
|
+
familia (1.1.0.pre.rc1)
|
5
5
|
redis (>= 4.8.1, < 6.0)
|
6
|
+
stringio (~> 3.1.1)
|
6
7
|
uri-redis (~> 1.3)
|
7
8
|
|
8
9
|
GEM
|
@@ -55,6 +56,7 @@ GEM
|
|
55
56
|
rubocop (>= 0.90.0)
|
56
57
|
ruby-progressbar (1.13.0)
|
57
58
|
storable (0.10.0)
|
59
|
+
stringio (3.1.1)
|
58
60
|
strscan (3.1.0)
|
59
61
|
sysinfo (0.10.0)
|
60
62
|
drydock (< 1.0)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Familia - 1.
|
1
|
+
# Familia - 1.1.0-rc1 (November 2024)
|
2
2
|
|
3
3
|
**Organize and store Ruby objects in Redis. A powerful Ruby ORM (of sorts) for Redis.**
|
4
4
|
|
@@ -9,7 +9,7 @@ Familia provides a flexible and feature-rich way to interact with Redis using Ru
|
|
9
9
|
|
10
10
|
Get it in one of the following ways:
|
11
11
|
|
12
|
-
* In your Gemfile: `gem 'familia', '>= 1.
|
12
|
+
* In your Gemfile: `gem 'familia', '>= 1.1.0-rc1'`
|
13
13
|
* Install it by hand: `gem install familia --pre`
|
14
14
|
* Or for development: `git clone git@github.com:delano/familia.git`
|
15
15
|
|
@@ -152,11 +152,11 @@ end
|
|
152
152
|
|
153
153
|
```ruby
|
154
154
|
class ComplexObject < Familia::Horreum
|
155
|
-
def
|
155
|
+
def serialize_value
|
156
156
|
custom_serialization_method
|
157
157
|
end
|
158
158
|
|
159
|
-
def self.
|
159
|
+
def self.deserialize_value(data)
|
160
160
|
custom_deserialization_method(data)
|
161
161
|
end
|
162
162
|
end
|
@@ -188,7 +188,7 @@ flower.save
|
|
188
188
|
### Retrieving and Updating Objects
|
189
189
|
|
190
190
|
```ruby
|
191
|
-
rose = Flower.
|
191
|
+
rose = Flower.find_by_id("rrose")
|
192
192
|
rose.name = "Pink Rose"
|
193
193
|
rose.save
|
194
194
|
```
|
data/VERSION.yml
CHANGED
data/familia.gemspec
CHANGED
@@ -20,9 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.7.8')
|
21
21
|
|
22
22
|
spec.add_dependency 'redis', '>= 4.8.1', '< 6.0'
|
23
|
+
spec.add_dependency 'stringio', '~> 3.1.1'
|
23
24
|
spec.add_dependency 'uri-redis', '~> 1.3'
|
24
25
|
|
25
|
-
# byebug only works with MRI
|
26
|
-
spec.add_development_dependency 'byebug', '~> 11.0' if RUBY_ENGINE == 'ruby'
|
27
26
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
28
27
|
end
|
data/lib/familia/base.rb
CHANGED
@@ -30,21 +30,21 @@ module Familia
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
#
|
34
|
-
#
|
33
|
+
# Base implementation of update_expiration that maintains API compatibility
|
34
|
+
# with the :expiration feature's implementation.
|
35
35
|
#
|
36
|
-
#
|
37
|
-
#
|
36
|
+
# This is a no-op implementation that gets overridden by features like
|
37
|
+
# :expiration. It accepts an optional ttl parameter to maintain interface
|
38
|
+
# compatibility with the overriding implementations.
|
38
39
|
#
|
39
|
-
# @
|
40
|
+
# @param ttl [Integer, nil] Time To Live in seconds (ignored in base implementation)
|
41
|
+
# @return [nil] Always returns nil
|
40
42
|
#
|
41
|
-
# @
|
42
|
-
#
|
43
|
+
# @note This is a no-op implementation. Classes that need expiration
|
44
|
+
# functionality should include the :expiration feature.
|
43
45
|
#
|
44
|
-
|
45
|
-
|
46
|
-
def update_expiration(*)
|
47
|
-
Familia.info "[update_expiration] Skipped for #{rediskey}. #{self.class} data is immortal!"
|
46
|
+
def update_expiration(ttl: nil)
|
47
|
+
Familia.ld "[update_expiration] Feature not enabled for #{self.class}. Key: #{rediskey} (caller: #{caller(1..1)})"
|
48
48
|
nil
|
49
49
|
end
|
50
50
|
|
data/lib/familia/connection.rb
CHANGED
@@ -6,6 +6,7 @@ require_relative '../../lib/redis_middleware'
|
|
6
6
|
module Familia
|
7
7
|
@uri = URI.parse 'redis://127.0.0.1'
|
8
8
|
@redis_clients = {}
|
9
|
+
@redis_uri_by_class = {}
|
9
10
|
|
10
11
|
# The Connection module provides Redis connection management for Familia.
|
11
12
|
# It allows easy setup and access to Redis clients across different URIs.
|
@@ -25,17 +26,20 @@ module Familia
|
|
25
26
|
# Establishes a connection to a Redis server.
|
26
27
|
#
|
27
28
|
# @param uri [String, URI, nil] The URI of the Redis server to connect to.
|
28
|
-
# If nil, uses the default URI.
|
29
|
-
# @return [Redis] The connected Redis client
|
29
|
+
# If nil, uses the default URI from `@redis_uri_by_class` or `Familia.uri`.
|
30
|
+
# @return [Redis] The connected Redis client.
|
31
|
+
# @raise [ArgumentError] If no URI is specified.
|
30
32
|
# @example
|
31
33
|
# Familia.connect('redis://localhost:6379')
|
32
34
|
def connect(uri = nil)
|
33
35
|
uri = URI.parse(uri) if uri.is_a?(String)
|
36
|
+
uri ||= @redis_uri_by_class[self]
|
34
37
|
uri ||= Familia.uri
|
35
38
|
|
36
39
|
raise ArgumentError, 'No URI specified' unless uri
|
37
40
|
|
38
41
|
conf = uri.conf
|
42
|
+
@redis_uri_by_class[self] = uri.serverid
|
39
43
|
|
40
44
|
if Familia.enable_redis_logging
|
41
45
|
RedisLogger.logger = Familia.logger
|
@@ -49,6 +53,9 @@ module Familia
|
|
49
53
|
end
|
50
54
|
|
51
55
|
redis = Redis.new(conf)
|
56
|
+
|
57
|
+
# Close the existing connection if it exists
|
58
|
+
@redis_clients[uri.serverid].close if @redis_clients[uri.serverid]
|
52
59
|
@redis_clients[uri.serverid] = redis
|
53
60
|
end
|
54
61
|
|
@@ -72,6 +79,15 @@ module Familia
|
|
72
79
|
@redis_clients[uri.serverid]
|
73
80
|
end
|
74
81
|
|
82
|
+
# Retrieves the Redis client associated with the given class.
|
83
|
+
#
|
84
|
+
# @param klass [Class] The class for which to retrieve the Redis client.
|
85
|
+
# @return [Redis] The Redis client associated with the given class.
|
86
|
+
def redis_uri_by_class(klass)
|
87
|
+
uri = @redis_uri_by_class[klass]
|
88
|
+
connect(uri)
|
89
|
+
end
|
90
|
+
|
75
91
|
# Sets the default URI for Redis connections.
|
76
92
|
#
|
77
93
|
# @param v [String, URI] The new default URI
|
data/lib/familia/errors.rb
CHANGED
@@ -30,4 +30,18 @@ module Familia
|
|
30
30
|
"No client for #{uri.serverid}"
|
31
31
|
end
|
32
32
|
end
|
33
|
+
|
34
|
+
# Raised when attempting to refresh an object whose key doesn't exist in Redis
|
35
|
+
class KeyNotFoundError < Problem
|
36
|
+
attr_reader :key
|
37
|
+
|
38
|
+
def initialize(key)
|
39
|
+
@key = key
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
43
|
+
def message
|
44
|
+
"Key not found in Redis: #{key}"
|
45
|
+
end
|
46
|
+
end
|
33
47
|
end
|
@@ -49,7 +49,7 @@ module Familia::Features
|
|
49
49
|
# false otherwise.
|
50
50
|
#
|
51
51
|
# @example Setting an expiration of one day
|
52
|
-
# object.update_expiration(86400)
|
52
|
+
# object.update_expiration(ttl: 86400)
|
53
53
|
#
|
54
54
|
# @note If TTL is set to zero, the expiration will be removed, making the
|
55
55
|
# data persist indefinitely.
|
@@ -57,8 +57,19 @@ module Familia::Features
|
|
57
57
|
# @raise [Familia::Problem] Raises an error if the TTL is not a non-negative
|
58
58
|
# integer.
|
59
59
|
#
|
60
|
-
|
60
|
+
def update_expiration(ttl: nil)
|
61
61
|
ttl ||= self.ttl
|
62
|
+
|
63
|
+
if self.class.has_relations?
|
64
|
+
Familia.ld "[update_expiration] #{self.class} has relations: #{self.class.redis_types.keys}"
|
65
|
+
self.class.redis_types.each do |name, definition|
|
66
|
+
next if definition.opts[:ttl].nil?
|
67
|
+
obj = send(name)
|
68
|
+
Familia.ld "[update_expiration] Updating expiration for #{name} (#{obj.rediskey}) to #{ttl}"
|
69
|
+
obj.update_expiration(ttl: ttl)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
62
73
|
# It's important to raise exceptions here and not just log warnings. We
|
63
74
|
# don't want to silently fail at setting expirations and cause data
|
64
75
|
# retention issues (e.g. not removed in a timely fashion).
|
@@ -46,7 +46,7 @@ module Familia::Features
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def qstamp(quantum = nil, pattern: nil, time: nil)
|
49
|
-
self.class.qstamp(quantum || ttl, pattern: pattern, time: time)
|
49
|
+
self.class.qstamp(quantum || self.class.ttl, pattern: pattern, time: time)
|
50
50
|
end
|
51
51
|
|
52
52
|
extend ClassMethods
|
@@ -33,9 +33,6 @@ module Familia
|
|
33
33
|
include Familia::Settings
|
34
34
|
include Familia::Horreum::RelationsManagement
|
35
35
|
|
36
|
-
attr_accessor :parent
|
37
|
-
attr_writer :redis, :dump_method, :load_method
|
38
|
-
|
39
36
|
# Returns the Redis connection for the class.
|
40
37
|
#
|
41
38
|
# This method retrieves the Redis connection instance for the class. If no
|
@@ -156,7 +153,7 @@ module Familia
|
|
156
153
|
Familia.trace :FAST_WRITER, redis, "#{name}: #{val.inspect}", caller(1..1) if Familia.debug?
|
157
154
|
|
158
155
|
# Convert the provided value to a format suitable for Redis storage.
|
159
|
-
prepared =
|
156
|
+
prepared = serialize_value(val)
|
160
157
|
Familia.ld "[.fast_attribute!] #{name} val: #{val.class} prepared: #{prepared.class}"
|
161
158
|
|
162
159
|
# Use the existing accessor method to set the attribute value.
|
@@ -196,6 +193,10 @@ module Familia
|
|
196
193
|
@redis_types
|
197
194
|
end
|
198
195
|
|
196
|
+
def has_relations?
|
197
|
+
@has_relations ||= false
|
198
|
+
end
|
199
|
+
|
199
200
|
def db(v = nil)
|
200
201
|
@db = v unless v.nil?
|
201
202
|
@db || parent&.db
|
@@ -209,16 +210,20 @@ module Familia
|
|
209
210
|
def all(suffix = nil)
|
210
211
|
suffix ||= self.suffix
|
211
212
|
# objects that could not be parsed will be nil
|
212
|
-
keys(suffix).filter_map { |k|
|
213
|
+
keys(suffix).filter_map { |k| find_by_key(k) }
|
213
214
|
end
|
214
215
|
|
215
216
|
def any?(filter = '*')
|
216
|
-
|
217
|
+
matching_keys_count(filter) > 0
|
217
218
|
end
|
218
219
|
|
219
|
-
|
220
|
+
# Returns the number of Redis keys matching the given filter pattern
|
221
|
+
# @param filter [String] Redis key pattern to match (default: '*')
|
222
|
+
# @return [Integer] Number of matching keys
|
223
|
+
def matching_keys_count(filter = '*')
|
220
224
|
redis.keys(rediskey(filter)).compact.size
|
221
225
|
end
|
226
|
+
alias size matching_keys_count # For backwards compatibility
|
222
227
|
|
223
228
|
def suffix(a = nil, &blk)
|
224
229
|
@suffix = a || blk if a || !blk.nil?
|
@@ -312,17 +317,17 @@ module Familia
|
|
312
317
|
# debugging.
|
313
318
|
#
|
314
319
|
# @example
|
315
|
-
# User.
|
320
|
+
# User.find_by_key("user:123") # Returns a User instance if it exists,
|
316
321
|
# nil otherwise
|
317
322
|
#
|
318
|
-
def
|
323
|
+
def find_by_key(objkey)
|
319
324
|
raise ArgumentError, 'Empty key' if objkey.to_s.empty?
|
320
325
|
|
321
326
|
# We use a lower-level method here b/c we're working with the
|
322
327
|
# full key and not just the identifier.
|
323
328
|
does_exist = redis.exists(objkey).positive?
|
324
329
|
|
325
|
-
Familia.ld "[.
|
330
|
+
Familia.ld "[.find_by_key] #{self} from key #{objkey} (exists: #{does_exist})"
|
326
331
|
Familia.trace :FROM_KEY, redis, objkey, caller(1..1) if Familia.debug?
|
327
332
|
|
328
333
|
# This is the reason for calling exists first. We want to definitively
|
@@ -337,6 +342,7 @@ module Familia
|
|
337
342
|
|
338
343
|
new(**obj)
|
339
344
|
end
|
345
|
+
alias from_rediskey find_by_key # deprecated
|
340
346
|
|
341
347
|
# Retrieves and instantiates an object from Redis using its identifier.
|
342
348
|
#
|
@@ -347,7 +353,7 @@ module Familia
|
|
347
353
|
# @return [Object, nil] An instance of the class if found, nil otherwise.
|
348
354
|
#
|
349
355
|
# This method constructs the full Redis key using the provided identifier
|
350
|
-
# and suffix, then delegates to `
|
356
|
+
# and suffix, then delegates to `find_by_key` for the actual retrieval and
|
351
357
|
# instantiation.
|
352
358
|
#
|
353
359
|
# It's a higher-level method that abstracts away the key construction,
|
@@ -355,19 +361,21 @@ module Familia
|
|
355
361
|
# identifier.
|
356
362
|
#
|
357
363
|
# @example
|
358
|
-
# User.
|
364
|
+
# User.find_by_id(123) # Equivalent to User.find_by_key("user:123:object")
|
359
365
|
#
|
360
|
-
def
|
366
|
+
def find_by_id(identifier, suffix = nil)
|
361
367
|
suffix ||= self.suffix
|
362
368
|
return nil if identifier.to_s.empty?
|
363
369
|
|
364
370
|
objkey = rediskey(identifier, suffix)
|
365
371
|
|
366
|
-
Familia.ld "[.
|
367
|
-
Familia.trace :
|
368
|
-
|
372
|
+
Familia.ld "[.find_by_id] #{self} from key #{objkey})"
|
373
|
+
Familia.trace :FIND_BY_ID, Familia.redis(uri), objkey, caller(1..1).first if Familia.debug?
|
374
|
+
find_by_key objkey
|
369
375
|
end
|
370
|
-
alias
|
376
|
+
alias find find_by_id
|
377
|
+
alias load find_by_id # deprecated
|
378
|
+
alias from_identifier find_by_id # deprecated
|
371
379
|
|
372
380
|
# Checks if an object with the given identifier exists in Redis.
|
373
381
|
#
|
@@ -399,8 +407,10 @@ module Familia
|
|
399
407
|
# @param suffix [Symbol, nil] The suffix to use in the Redis key (default: class suffix).
|
400
408
|
# @return [Boolean] true if the object was successfully destroyed, false otherwise.
|
401
409
|
#
|
402
|
-
# This method
|
403
|
-
#
|
410
|
+
# This method is part of Familia's high-level object lifecycle management. While `delete!`
|
411
|
+
# operates directly on Redis keys, `destroy!` operates at the object level and is used for
|
412
|
+
# ORM-style operations. Use `destroy!` when removing complete objects from the system, and
|
413
|
+
# `delete!` when working directly with Redis keys.
|
404
414
|
#
|
405
415
|
# @example
|
406
416
|
# User.destroy!(123) # Removes user:123:object from Redis
|
@@ -412,7 +422,7 @@ module Familia
|
|
412
422
|
objkey = rediskey identifier, suffix
|
413
423
|
|
414
424
|
ret = redis.del objkey
|
415
|
-
Familia.trace :
|
425
|
+
Familia.trace :DESTROY!, redis, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
|
416
426
|
ret.positive?
|
417
427
|
end
|
418
428
|
|
@@ -428,7 +438,7 @@ module Familia
|
|
428
438
|
# User.find # Returns all keys matching user:*:object
|
429
439
|
# User.find('active') # Returns all keys matching user:*:active
|
430
440
|
#
|
431
|
-
def
|
441
|
+
def find_keys(suffix = '*')
|
432
442
|
redis.keys(rediskey('*', suffix)) || []
|
433
443
|
end
|
434
444
|
|
@@ -18,11 +18,39 @@ module Familia
|
|
18
18
|
#
|
19
19
|
module Commands
|
20
20
|
|
21
|
-
def
|
22
|
-
|
23
|
-
self.class.exists? identifier, suffix
|
21
|
+
def move(db)
|
22
|
+
redis.move rediskey, db
|
24
23
|
end
|
25
24
|
|
25
|
+
# Checks if the calling object's key exists in Redis.
|
26
|
+
#
|
27
|
+
# @param check_size [Boolean] When true (default), also verifies the hash has a non-zero size.
|
28
|
+
# When false, only checks key existence regardless of content.
|
29
|
+
# @return [Boolean] Returns `true` if the key exists in Redis. When `check_size` is true,
|
30
|
+
# also requires the hash to have at least one field.
|
31
|
+
#
|
32
|
+
# @example Check existence with size validation (default behavior)
|
33
|
+
# some_object.exists? # => false for empty hashes
|
34
|
+
# some_object.exists?(check_size: true) # => false for empty hashes
|
35
|
+
#
|
36
|
+
# @example Check existence only
|
37
|
+
# some_object.exists?(check_size: false) # => true for empty hashes
|
38
|
+
#
|
39
|
+
# @note The default behavior maintains backward compatibility by treating empty hashes
|
40
|
+
# as non-existent. Use `check_size: false` for pure key existence checking.
|
41
|
+
def exists?(check_size: true)
|
42
|
+
key_exists = self.class.redis.exists?(rediskey)
|
43
|
+
return key_exists unless check_size
|
44
|
+
key_exists && !size.zero?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the number of fields in the main object hash
|
48
|
+
# @return [Integer] number of fields
|
49
|
+
def field_count
|
50
|
+
redis.hlen rediskey
|
51
|
+
end
|
52
|
+
alias size field_count
|
53
|
+
|
26
54
|
# Sets a timeout on key. After the timeout has expired, the key will
|
27
55
|
# automatically be deleted. Returns 1 if the timeout was set, 0 if key
|
28
56
|
# does not exist or the timeout could not be set.
|
@@ -33,20 +61,27 @@ module Familia
|
|
33
61
|
redis.expire rediskey, ttl.to_i
|
34
62
|
end
|
35
63
|
|
64
|
+
# Retrieves the remaining time to live (TTL) for the object's Redis key.
|
65
|
+
#
|
66
|
+
# This method accesses the ovjects Redis client to obtain the TTL of `rediskey`.
|
67
|
+
# If debugging is enabled, it logs the TTL retrieval operation using `Familia.trace`.
|
68
|
+
#
|
69
|
+
# @return [Integer] The TTL of the key in seconds. Returns -1 if the key does not exist
|
70
|
+
# or has no associated expire time.
|
36
71
|
def realttl
|
37
72
|
Familia.trace :REALTTL, redis, redisuri, caller(1..1) if Familia.debug?
|
38
73
|
redis.ttl rediskey
|
39
74
|
end
|
40
75
|
|
41
|
-
#
|
76
|
+
# Removes a field from the hash stored at the Redis key.
|
42
77
|
#
|
43
|
-
# @param field [String] The field to
|
78
|
+
# @param field [String] The field to remove from the hash.
|
44
79
|
# @return [Integer] The number of fields that were removed from the hash (0 or 1).
|
45
|
-
|
46
|
-
def hdel!(field)
|
80
|
+
def remove_field(field)
|
47
81
|
Familia.trace :HDEL, redis, field, caller(1..1) if Familia.debug?
|
48
82
|
redis.hdel rediskey, field
|
49
83
|
end
|
84
|
+
alias remove remove_field # deprecated
|
50
85
|
|
51
86
|
def redistype
|
52
87
|
Familia.trace :REDISTYPE, redis, redisuri, caller(1..1) if Familia.debug?
|
@@ -59,6 +94,15 @@ module Familia
|
|
59
94
|
redis.rename rediskey, newkey
|
60
95
|
end
|
61
96
|
|
97
|
+
# Retrieves the prefix for the current instance by delegating to its class.
|
98
|
+
#
|
99
|
+
# @return [String] The prefix associated with the class of the current instance.
|
100
|
+
# @example
|
101
|
+
# instance.prefix
|
102
|
+
def prefix
|
103
|
+
self.class.prefix
|
104
|
+
end
|
105
|
+
|
62
106
|
# For parity with RedisType#hgetall
|
63
107
|
def hgetall
|
64
108
|
Familia.trace :HGETALL, redis, redisuri, caller(1..1) if Familia.debug?
|
@@ -78,8 +122,10 @@ module Familia
|
|
78
122
|
redis.hset rediskey, field, value
|
79
123
|
end
|
80
124
|
|
81
|
-
def hmset
|
82
|
-
|
125
|
+
def hmset(hsh={})
|
126
|
+
hsh ||= self.to_h
|
127
|
+
Familia.trace :HMSET, redis, hsh, caller(1..1) if Familia.debug?
|
128
|
+
redis.hmset rediskey(suffix), hsh
|
83
129
|
end
|
84
130
|
|
85
131
|
def hkeys
|
@@ -116,11 +162,6 @@ module Familia
|
|
116
162
|
end
|
117
163
|
alias decrement decr
|
118
164
|
|
119
|
-
def hlen
|
120
|
-
redis.hlen rediskey(suffix)
|
121
|
-
end
|
122
|
-
alias hlength hlen
|
123
|
-
|
124
165
|
def hstrlen(field)
|
125
166
|
redis.hstrlen rediskey(suffix), field
|
126
167
|
end
|
@@ -131,12 +172,14 @@ module Familia
|
|
131
172
|
end
|
132
173
|
alias has_key? key?
|
133
174
|
|
175
|
+
# Deletes the entire Redis key
|
176
|
+
# @return [Boolean] true if the key was deleted, false otherwise
|
134
177
|
def delete!
|
135
178
|
Familia.trace :DELETE!, redis, redisuri, caller(1..1) if Familia.debug?
|
136
179
|
ret = redis.del rediskey
|
137
180
|
ret.positive?
|
138
181
|
end
|
139
|
-
|
182
|
+
alias clear delete!
|
140
183
|
|
141
184
|
end
|
142
185
|
|
@@ -16,6 +16,10 @@ module Familia
|
|
16
16
|
# Call setup_relations_accessors to initialize the feature
|
17
17
|
#
|
18
18
|
module RelationsManagement
|
19
|
+
# A practical flag to indicate that a Horreum member has relations,
|
20
|
+
# not just theoretically but actually at least one list/haskey/etc.
|
21
|
+
@has_relations = nil
|
22
|
+
|
19
23
|
def self.included(base)
|
20
24
|
base.extend(ClassMethods)
|
21
25
|
base.setup_relations_accessors
|
@@ -31,14 +35,17 @@ module Familia
|
|
31
35
|
|
32
36
|
# Dynamically define instance-level relation methods
|
33
37
|
#
|
34
|
-
# Once defined, these methods can be used at the
|
38
|
+
# Once defined, these methods can be used at the instance-level of a
|
35
39
|
# Familia member to define *instance-level* relations to any of the
|
36
40
|
# RedisType types (e.g. set, list, hash, etc).
|
37
41
|
#
|
38
42
|
define_method :"#{kind}" do |*args|
|
39
43
|
name, opts = *args
|
44
|
+
|
45
|
+
# As log as we have at least one relation, we can set this flag.
|
46
|
+
@has_relations = true
|
47
|
+
|
40
48
|
attach_instance_redis_object_relation name, klass, opts
|
41
|
-
redis_types[name.to_s.to_sym]
|
42
49
|
end
|
43
50
|
define_method :"#{kind}?" do |name|
|
44
51
|
obj = redis_types[name.to_s.to_sym]
|