familia 1.0.0.pre.rc6 → 1.1.0.pre.rc1
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/Gemfile +3 -1
- data/Gemfile.lock +3 -1
- data/README.md +5 -5
- data/VERSION.yml +2 -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 +99 -41
- data/lib/familia/horreum/commands.rb +53 -14
- data/lib/familia/horreum/relations_management.rb +9 -2
- data/lib/familia/horreum/serialization.rb +32 -23
- data/lib/familia/horreum.rb +8 -2
- data/lib/familia/redistype/commands.rb +5 -2
- data/lib/familia/redistype/serialization.rb +17 -16
- data/lib/familia/redistype/types/hashkey.rb +166 -0
- data/lib/familia/{types → redistype/types}/list.rb +19 -14
- data/lib/familia/{types → redistype/types}/sorted_set.rb +23 -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 +5 -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 +4 -4
- data/try/30_familia_object_try.rb +8 -5
- data/try/40_customer_try.rb +6 -6
- metadata +15 -15
- data/lib/familia/types/hashkey.rb +0 -108
@@ -97,15 +97,8 @@ module Familia
|
|
97
97
|
# connection. Don't worry, it puts everything back where it found it when it's done.
|
98
98
|
#
|
99
99
|
def transaction
|
100
|
-
|
101
|
-
|
102
|
-
begin
|
103
|
-
redis.multi do |conn|
|
104
|
-
self.instance_variable_set(:@redis, conn)
|
105
|
-
yield(conn)
|
106
|
-
end
|
107
|
-
ensure
|
108
|
-
self.redis = original_redis
|
100
|
+
redis.multi do |conn|
|
101
|
+
yield(conn)
|
109
102
|
end
|
110
103
|
end
|
111
104
|
|
@@ -130,15 +123,15 @@ module Familia
|
|
130
123
|
|
131
124
|
# Update our object's life story
|
132
125
|
self.key ||= self.identifier
|
133
|
-
self.
|
134
|
-
self.
|
126
|
+
self.created ||= Familia.now.to_i if respond_to?(:created)
|
127
|
+
self.updated = Familia.now.to_i if respond_to?(:updated)
|
135
128
|
|
136
129
|
# Commit our tale to the Redis chronicles
|
137
130
|
#
|
138
131
|
# e.g. `ret` # => MultiResult.new(true, ["OK", "OK"])
|
139
132
|
ret = commit_fields(update_expiration: update_expiration)
|
140
133
|
|
141
|
-
Familia.ld "[save] #{self.class} #{rediskey} #{ret}"
|
134
|
+
Familia.ld "[save] #{self.class} #{rediskey} #{ret} (update_expiration: #{update_expiration})"
|
142
135
|
|
143
136
|
# Did Redis accept our offering?
|
144
137
|
ret.successful?
|
@@ -174,6 +167,10 @@ module Familia
|
|
174
167
|
# It executes a transaction that includes setting field values and,
|
175
168
|
# if applicable, updating the expiration time.
|
176
169
|
#
|
170
|
+
# @param update_expiration [Boolean] Whether to update the expiration time
|
171
|
+
# of the Redis key. This is true by default, but can be disabled if you
|
172
|
+
# don't want to mess with the cosmic balance of your key's lifespan.
|
173
|
+
#
|
177
174
|
# @return [MultiResult] A mystical object containing:
|
178
175
|
# - success: A boolean indicating if all Redis commands succeeded
|
179
176
|
# - results: An array of strings, cryptic messages from the Redis gods
|
@@ -213,13 +210,12 @@ module Familia
|
|
213
210
|
def commit_fields update_expiration: true
|
214
211
|
Familia.ld "[commit_fields1] #{self.class} #{rediskey} #{to_h} (update_expiration: #{update_expiration})"
|
215
212
|
command_return_values = transaction do |conn|
|
216
|
-
hmset
|
217
|
-
|
218
|
-
# Only classes that have the expiration ferature enabled will
|
219
|
-
# actually set an expiration time on their keys. Otherwise
|
220
|
-
# this will be a no-op that simply logs the attempt.
|
221
|
-
self.update_expiration if update_expiration
|
213
|
+
conn.hmset rediskey(suffix), self.to_h # using the prepared connection
|
222
214
|
end
|
215
|
+
# Only classes that have the expiration ferature enabled will
|
216
|
+
# actually set an expiration time on their keys. Otherwise
|
217
|
+
# this will be a no-op that simply logs the attempt.
|
218
|
+
self.update_expiration(ttl: nil) if update_expiration
|
223
219
|
|
224
220
|
# The acceptable redis command return values are defined in the
|
225
221
|
# Horreum class. This is to ensure that all commands return values
|
@@ -256,6 +252,11 @@ module Familia
|
|
256
252
|
# rocky.destroy!
|
257
253
|
# # => *poof* Rocky is no more. A moment of silence, please.
|
258
254
|
#
|
255
|
+
# This method is part of Familia's high-level object lifecycle management. While `delete!`
|
256
|
+
# operates directly on Redis keys, `destroy!` operates at the object level and is used for
|
257
|
+
# ORM-style operations. Use `destroy!` when removing complete objects from the system, and
|
258
|
+
# `delete!` when working directly with Redis keys.
|
259
|
+
#
|
259
260
|
# @note If debugging is enabled, this method will leave a trace of its
|
260
261
|
# destructive path, like breadcrumbs for future data archaeologists.
|
261
262
|
#
|
@@ -276,14 +277,19 @@ module Familia
|
|
276
277
|
# Gone quicker than cake at a hobbit's birthday party. Unsaved spells
|
277
278
|
# will definitely be forgotten.
|
278
279
|
#
|
279
|
-
# @return What do you get for this daring act of digital amnesia? A shiny
|
280
|
+
# @return [void] What do you get for this daring act of digital amnesia? A shiny
|
280
281
|
# list of all the brain bits that got a makeover!
|
281
282
|
#
|
282
283
|
# Remember: In the game of Redis-Refresh, you win or you... well, you
|
283
284
|
# always win, but sometimes you forget why you played in the first place.
|
284
285
|
#
|
286
|
+
# @raise [Familia::KeyNotFoundError] If the Redis key does not exist.
|
287
|
+
#
|
288
|
+
# @example
|
289
|
+
# object.refresh!
|
285
290
|
def refresh!
|
286
291
|
Familia.trace :REFRESH, redis, redisuri, caller(1..1) if Familia.debug?
|
292
|
+
raise Familia::KeyNotFoundError, rediskey unless redis.exists(rediskey)
|
287
293
|
fields = hgetall
|
288
294
|
Familia.ld "[refresh!] #{self.class} #{rediskey} #{fields.keys}"
|
289
295
|
optimistic_refresh(**fields)
|
@@ -303,6 +309,8 @@ module Familia
|
|
303
309
|
# @return [self] Your object, freshly bathed in Redis waters, ready
|
304
310
|
# to dance with more methods in a conga line of Ruby joy!
|
305
311
|
#
|
312
|
+
# @raise [Familia::KeyNotFoundError] If the Redis key does not exist.
|
313
|
+
#
|
306
314
|
def refresh
|
307
315
|
refresh!
|
308
316
|
self
|
@@ -326,7 +334,7 @@ module Familia
|
|
326
334
|
def to_h
|
327
335
|
self.class.fields.inject({}) do |hsh, field|
|
328
336
|
val = send(field)
|
329
|
-
prepared =
|
337
|
+
prepared = serialize_value(val)
|
330
338
|
Familia.ld " [to_h] field: #{field} val: #{val.class} prepared: #{prepared.class}"
|
331
339
|
hsh[field] = prepared
|
332
340
|
hsh
|
@@ -351,7 +359,7 @@ module Familia
|
|
351
359
|
def to_a
|
352
360
|
self.class.fields.map do |field|
|
353
361
|
val = send(field)
|
354
|
-
prepared =
|
362
|
+
prepared = serialize_value(val)
|
355
363
|
Familia.ld " [to_a] field: #{field} val: #{val.class} prepared: #{prepared.class}"
|
356
364
|
prepared
|
357
365
|
end
|
@@ -392,7 +400,7 @@ module Familia
|
|
392
400
|
#
|
393
401
|
# @return [String] The transformed, Redis-ready value.
|
394
402
|
#
|
395
|
-
def
|
403
|
+
def serialize_value(val)
|
396
404
|
prepared = Familia.distinguisher(val, strict_values: false)
|
397
405
|
|
398
406
|
if prepared.nil? && val.respond_to?(dump_method)
|
@@ -400,11 +408,12 @@ module Familia
|
|
400
408
|
end
|
401
409
|
|
402
410
|
if prepared.nil?
|
403
|
-
Familia.ld "[#{self.class}#
|
411
|
+
Familia.ld "[#{self.class}#serialize_value] nil returned for #{self.class}##{name}"
|
404
412
|
end
|
405
413
|
|
406
414
|
prepared
|
407
415
|
end
|
416
|
+
alias to_redis serialize_value
|
408
417
|
|
409
418
|
end
|
410
419
|
# End of Serialization module
|
data/lib/familia/horreum.rb
CHANGED
@@ -54,6 +54,10 @@ module Familia
|
|
54
54
|
# but not existing instances of the class.
|
55
55
|
#
|
56
56
|
class << self
|
57
|
+
attr_accessor :parent
|
58
|
+
attr_writer :redis, :dump_method, :load_method
|
59
|
+
attr_reader :has_relations
|
60
|
+
|
57
61
|
# Extends ClassMethods to subclasses and tracks Familia members
|
58
62
|
def inherited(member)
|
59
63
|
Familia.trace :HORREUM, nil, "Welcome #{member} to the family", caller(1..1) if Familia.debug?
|
@@ -82,7 +86,7 @@ module Familia
|
|
82
86
|
# Define the 'key' field for this class
|
83
87
|
# This approach allows flexibility in how identifiers are generated
|
84
88
|
# while ensuring each object has a consistent way to be referenced
|
85
|
-
self.class.field :key
|
89
|
+
self.class.field :key
|
86
90
|
end
|
87
91
|
|
88
92
|
# If there are positional arguments, they should be the field
|
@@ -142,9 +146,11 @@ module Familia
|
|
142
146
|
#
|
143
147
|
opts[:parent] = self # unless opts.key(:parent)
|
144
148
|
|
149
|
+
suffix_override = opts.fetch(:suffix, name)
|
150
|
+
|
145
151
|
# Instantiate the RedisType object and below we store it in
|
146
152
|
# an instance variable.
|
147
|
-
redis_type = klass.new
|
153
|
+
redis_type = klass.new suffix_override, opts
|
148
154
|
|
149
155
|
# Freezes the redis_type, making it immutable.
|
150
156
|
# This ensures the object's state remains consistent and prevents any modifications,
|
@@ -22,11 +22,14 @@ class Familia::RedisType
|
|
22
22
|
redis.type rediskey
|
23
23
|
end
|
24
24
|
|
25
|
+
# Deletes the entire Redis key
|
26
|
+
# @return [Boolean] true if the key was deleted, false otherwise
|
25
27
|
def delete!
|
26
|
-
redis.
|
28
|
+
Familia.trace :DELETE!, redis, redisuri, caller(1..1) if Familia.debug?
|
29
|
+
ret = redis.del rediskey
|
30
|
+
ret.positive?
|
27
31
|
end
|
28
32
|
alias clear delete!
|
29
|
-
alias del delete!
|
30
33
|
|
31
34
|
def exists?
|
32
35
|
redis.exists(rediskey) && !size.zero?
|
@@ -17,16 +17,16 @@ class Familia::RedisType
|
|
17
17
|
# serialization.
|
18
18
|
#
|
19
19
|
# @example With a class option
|
20
|
-
#
|
20
|
+
# serialize_value(User.new(name: "Cloe"), strict_values: false) #=> '{"name":"Cloe"}'
|
21
21
|
#
|
22
22
|
# @example Without a class option
|
23
|
-
#
|
24
|
-
#
|
23
|
+
# serialize_value(123) #=> "123"
|
24
|
+
# serialize_value("hello") #=> "hello"
|
25
25
|
#
|
26
26
|
# @raise [Familia::HighRiskFactor] If serialization fails under strict
|
27
27
|
# mode.
|
28
28
|
#
|
29
|
-
def
|
29
|
+
def serialize_value(val, strict_values: true)
|
30
30
|
prepared = nil
|
31
31
|
|
32
32
|
Familia.trace :TOREDIS, redis, "#{val}<#{val.class}|#{opts[:class]}>", caller(1..1) if Familia.debug?
|
@@ -44,24 +44,26 @@ class Familia::RedisType
|
|
44
44
|
|
45
45
|
Familia.trace :TOREDIS, redis, "#{val}<#{val.class}|#{opts[:class]}> => #{prepared}<#{prepared.class}>", caller(1..1) if Familia.debug?
|
46
46
|
|
47
|
-
Familia.warn "[#{self.class}\#
|
47
|
+
Familia.warn "[#{self.class}\#serialize_value] nil returned for #{opts[:class]}\##{name}" if prepared.nil?
|
48
48
|
prepared
|
49
49
|
end
|
50
|
+
alias to_redis serialize_value
|
50
51
|
|
51
52
|
# Deserializes multiple values from Redis, removing nil values.
|
52
53
|
#
|
53
54
|
# @param values [Array<String>] The values to deserialize.
|
54
55
|
# @return [Array<Object>] Deserialized objects, with nil values removed.
|
55
56
|
#
|
56
|
-
# @see #
|
57
|
+
# @see #deserialize_values_with_nil
|
57
58
|
#
|
58
|
-
def
|
59
|
+
def deserialize_values(*values)
|
59
60
|
# Avoid using compact! here. Using compact! as the last expression in the
|
60
61
|
# method can unintentionally return nil if no changes are made, which is
|
61
62
|
# not desirable. Instead, use compact to ensure the method returns the
|
62
63
|
# expected value.
|
63
|
-
|
64
|
+
deserialize_values_with_nil(*values).compact
|
64
65
|
end
|
66
|
+
alias from_redis deserialize_values
|
65
67
|
|
66
68
|
# Deserializes multiple values from Redis, preserving nil values.
|
67
69
|
#
|
@@ -75,11 +77,8 @@ class Familia::RedisType
|
|
75
77
|
# class's load method. If deserialization fails for a value, it's
|
76
78
|
# replaced with nil.
|
77
79
|
#
|
78
|
-
|
79
|
-
|
80
|
-
#
|
81
|
-
def multi_from_redis_with_nil(*values)
|
82
|
-
Familia.ld "multi_from_redis: (#{@opts}) #{values}"
|
80
|
+
def deserialize_values_with_nil(*values)
|
81
|
+
Familia.ld "deserialize_values: (#{@opts}) #{values}"
|
83
82
|
return [] if values.empty?
|
84
83
|
return values.flatten unless @opts[:class]
|
85
84
|
|
@@ -92,7 +91,7 @@ class Familia::RedisType
|
|
92
91
|
|
93
92
|
val = @opts[:class].send load_method, obj
|
94
93
|
if val.nil?
|
95
|
-
Familia.ld "[#{self.class}\#
|
94
|
+
Familia.ld "[#{self.class}\#deserialize_values] nil returned for #{@opts[:class]}\##{name}"
|
96
95
|
end
|
97
96
|
|
98
97
|
val
|
@@ -105,6 +104,7 @@ class Familia::RedisType
|
|
105
104
|
|
106
105
|
values
|
107
106
|
end
|
107
|
+
alias from_redis_with_nil deserialize_values_with_nil
|
108
108
|
|
109
109
|
# Deserializes a single value from Redis.
|
110
110
|
#
|
@@ -120,13 +120,14 @@ class Familia::RedisType
|
|
120
120
|
# deserialization options that RedisType supports. It uses to_redis
|
121
121
|
# for serialization since everything becomes a string in Redis.
|
122
122
|
#
|
123
|
-
def
|
123
|
+
def deserialize_value(val)
|
124
124
|
return @opts[:default] if val.nil?
|
125
125
|
return val unless @opts[:class]
|
126
126
|
|
127
|
-
ret =
|
127
|
+
ret = deserialize_values val
|
128
128
|
ret&.first # return the object or nil
|
129
129
|
end
|
130
|
+
alias from_redis deserialize_value
|
130
131
|
end
|
131
132
|
|
132
133
|
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Familia
|
4
|
+
class HashKey < RedisType
|
5
|
+
# Returns the number of fields in the hash
|
6
|
+
# @return [Integer] number of fields
|
7
|
+
def field_count
|
8
|
+
redis.hlen rediskey
|
9
|
+
end
|
10
|
+
alias size field_count
|
11
|
+
|
12
|
+
def empty?
|
13
|
+
field_count.zero?
|
14
|
+
end
|
15
|
+
|
16
|
+
# +return+ [Integer] Returns 1 if the field is new and added, 0 if the
|
17
|
+
# field already existed and the value was updated.
|
18
|
+
def []=(field, val)
|
19
|
+
ret = redis.hset rediskey, field, serialize_value(val)
|
20
|
+
update_expiration
|
21
|
+
ret
|
22
|
+
rescue TypeError => e
|
23
|
+
Familia.le "[hset]= #{e.message}"
|
24
|
+
Familia.ld "[hset]= #{rediskey} #{field}=#{val}" if Familia.debug
|
25
|
+
echo :hset, caller(1..1).first if Familia.debug # logs via echo to redis and back
|
26
|
+
klass = val.class
|
27
|
+
msg = "Cannot store #{field} => #{val.inspect} (#{klass}) in #{rediskey}"
|
28
|
+
raise e.class, msg
|
29
|
+
end
|
30
|
+
alias put []=
|
31
|
+
alias store []=
|
32
|
+
|
33
|
+
def [](field)
|
34
|
+
deserialize_value redis.hget(rediskey, field)
|
35
|
+
end
|
36
|
+
alias get []
|
37
|
+
|
38
|
+
def fetch(field, default = nil)
|
39
|
+
ret = self[field]
|
40
|
+
if ret.nil?
|
41
|
+
raise IndexError, "No such index for: #{field}" if default.nil?
|
42
|
+
|
43
|
+
default
|
44
|
+
else
|
45
|
+
ret
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def keys
|
50
|
+
redis.hkeys rediskey
|
51
|
+
end
|
52
|
+
|
53
|
+
def values
|
54
|
+
redis.hvals(rediskey).map { |v| deserialize_value v }
|
55
|
+
end
|
56
|
+
|
57
|
+
def hgetall
|
58
|
+
redis.hgetall(rediskey).each_with_object({}) do |(k,v), ret|
|
59
|
+
ret[k] = deserialize_value v
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias all hgetall
|
63
|
+
|
64
|
+
def key?(field)
|
65
|
+
redis.hexists rediskey, field
|
66
|
+
end
|
67
|
+
alias has_key? key?
|
68
|
+
alias include? key?
|
69
|
+
alias member? key?
|
70
|
+
|
71
|
+
# Removes a field from the hash
|
72
|
+
# @param field [String] The field to remove
|
73
|
+
# @return [Integer] The number of fields that were removed (0 or 1)
|
74
|
+
def remove_field(field)
|
75
|
+
redis.hdel rediskey, field
|
76
|
+
end
|
77
|
+
alias remove remove_field # deprecated
|
78
|
+
|
79
|
+
def increment(field, by = 1)
|
80
|
+
redis.hincrby(rediskey, field, by).to_i
|
81
|
+
end
|
82
|
+
alias incr increment
|
83
|
+
alias incrby increment
|
84
|
+
|
85
|
+
def decrement(field, by = 1)
|
86
|
+
increment field, -by
|
87
|
+
end
|
88
|
+
alias decr decrement
|
89
|
+
alias decrby decrement
|
90
|
+
|
91
|
+
def update(hsh = {})
|
92
|
+
raise ArgumentError, 'Argument to bulk_set must be a hash' unless hsh.is_a?(Hash)
|
93
|
+
|
94
|
+
data = hsh.inject([]) { |ret, pair| ret << [pair[0], serialize_value(pair[1])] }.flatten
|
95
|
+
|
96
|
+
ret = redis.hmset(rediskey, *data)
|
97
|
+
update_expiration
|
98
|
+
ret
|
99
|
+
end
|
100
|
+
alias merge! update
|
101
|
+
|
102
|
+
def values_at *fields
|
103
|
+
elements = redis.hmget(rediskey, *fields.flatten.compact)
|
104
|
+
deserialize_values(*elements)
|
105
|
+
end
|
106
|
+
|
107
|
+
# The Great Redis Refresh-o-matic 3000 for HashKey!
|
108
|
+
#
|
109
|
+
# This method performs a complete refresh of the hash's state from Redis.
|
110
|
+
# It's like giving your hash a memory transfusion - out with the old state,
|
111
|
+
# in with the fresh data straight from Redis!
|
112
|
+
#
|
113
|
+
# @note This operation is atomic - it either succeeds completely or fails
|
114
|
+
# safely. Any unsaved changes to the hash will be overwritten.
|
115
|
+
#
|
116
|
+
# @return [void] Returns nothing, but your hash will be sparkling clean
|
117
|
+
# with all its fields synchronized with Redis.
|
118
|
+
#
|
119
|
+
# @raise [Familia::KeyNotFoundError] If the Redis key for this hash no
|
120
|
+
# longer exists. Time travelers beware!
|
121
|
+
#
|
122
|
+
# @example Basic usage
|
123
|
+
# my_hash.refresh! # ZAP! Fresh data loaded
|
124
|
+
#
|
125
|
+
# @example With error handling
|
126
|
+
# begin
|
127
|
+
# my_hash.refresh!
|
128
|
+
# rescue Familia::KeyNotFoundError
|
129
|
+
# puts "Oops! Our hash seems to have vanished into the Redis void!"
|
130
|
+
# end
|
131
|
+
def refresh!
|
132
|
+
Familia.trace :REFRESH, redis, redisuri, caller(1..1) if Familia.debug?
|
133
|
+
raise Familia::KeyNotFoundError, rediskey unless redis.exists(rediskey)
|
134
|
+
|
135
|
+
fields = hgetall
|
136
|
+
Familia.ld "[refresh!] #{self.class} #{rediskey} #{fields.keys}"
|
137
|
+
|
138
|
+
# For HashKey, we update by merging the fresh data
|
139
|
+
update(fields)
|
140
|
+
end
|
141
|
+
|
142
|
+
# The friendly neighborhood refresh method!
|
143
|
+
#
|
144
|
+
# This method is like refresh! but with better manners - it returns self
|
145
|
+
# so you can chain it with other methods. It's perfect for when you want
|
146
|
+
# to refresh your hash and immediately do something with it.
|
147
|
+
#
|
148
|
+
# @return [self] Returns the refreshed hash, ready for more adventures!
|
149
|
+
#
|
150
|
+
# @raise [Familia::KeyNotFoundError] If the Redis key does not exist.
|
151
|
+
# The hash must exist in Redis-land for this to work!
|
152
|
+
#
|
153
|
+
# @example Refresh and chain
|
154
|
+
# my_hash.refresh.keys # Refresh and get all keys
|
155
|
+
# my_hash.refresh['field'] # Refresh and get a specific field
|
156
|
+
#
|
157
|
+
# @see #refresh! For the heavy lifting behind the scenes
|
158
|
+
def refresh
|
159
|
+
refresh!
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
Familia::RedisType.register self, :hash # legacy, deprecated
|
164
|
+
Familia::RedisType.register self, :hashkey
|
165
|
+
end
|
166
|
+
end
|
@@ -2,18 +2,21 @@
|
|
2
2
|
|
3
3
|
module Familia
|
4
4
|
class List < RedisType
|
5
|
-
|
5
|
+
|
6
|
+
# Returns the number of elements in the list
|
7
|
+
# @return [Integer] number of elements
|
8
|
+
def element_count
|
6
9
|
redis.llen rediskey
|
7
10
|
end
|
8
|
-
alias
|
11
|
+
alias size element_count
|
9
12
|
|
10
13
|
def empty?
|
11
|
-
|
14
|
+
element_count.zero?
|
12
15
|
end
|
13
16
|
|
14
17
|
def push *values
|
15
18
|
echo :push, caller(1..1).first if Familia.debug
|
16
|
-
values.flatten.compact.each { |v| redis.rpush rediskey,
|
19
|
+
values.flatten.compact.each { |v| redis.rpush rediskey, serialize_value(v) }
|
17
20
|
redis.ltrim rediskey, -@opts[:maxlength], -1 if @opts[:maxlength]
|
18
21
|
update_expiration
|
19
22
|
self
|
@@ -26,7 +29,7 @@ module Familia
|
|
26
29
|
alias add <<
|
27
30
|
|
28
31
|
def unshift *values
|
29
|
-
values.flatten.compact.each { |v| redis.lpush rediskey,
|
32
|
+
values.flatten.compact.each { |v| redis.lpush rediskey, serialize_value(v) }
|
30
33
|
# TODO: test maxlength
|
31
34
|
redis.ltrim rediskey, 0, @opts[:maxlength] - 1 if @opts[:maxlength]
|
32
35
|
update_expiration
|
@@ -35,11 +38,11 @@ module Familia
|
|
35
38
|
alias prepend unshift
|
36
39
|
|
37
40
|
def pop
|
38
|
-
|
41
|
+
deserialize_value redis.rpop(rediskey)
|
39
42
|
end
|
40
43
|
|
41
44
|
def shift
|
42
|
-
|
45
|
+
deserialize_value redis.lpop(rediskey)
|
43
46
|
end
|
44
47
|
|
45
48
|
def [](idx, count = nil)
|
@@ -57,16 +60,18 @@ module Familia
|
|
57
60
|
end
|
58
61
|
alias slice []
|
59
62
|
|
60
|
-
|
61
|
-
|
63
|
+
# Removes elements equal to value from the list
|
64
|
+
# @param value The value to remove
|
65
|
+
# @param count [Integer] Number of elements to remove (0 means all)
|
66
|
+
# @return [Integer] The number of removed elements
|
67
|
+
def remove_element(value, count = 0)
|
68
|
+
redis.lrem rediskey, count, serialize_value(value)
|
62
69
|
end
|
63
|
-
alias remove
|
64
|
-
alias rem delete
|
65
|
-
alias del delete
|
70
|
+
alias remove remove_element
|
66
71
|
|
67
72
|
def range(sidx = 0, eidx = -1)
|
68
73
|
elements = rangeraw sidx, eidx
|
69
|
-
|
74
|
+
deserialize_values(*elements)
|
70
75
|
end
|
71
76
|
|
72
77
|
def rangeraw(sidx = 0, eidx = -1)
|
@@ -119,7 +124,7 @@ module Familia
|
|
119
124
|
end
|
120
125
|
|
121
126
|
def at(idx)
|
122
|
-
|
127
|
+
deserialize_value redis.lindex(rediskey, idx)
|
123
128
|
end
|
124
129
|
|
125
130
|
def first
|
@@ -2,13 +2,16 @@
|
|
2
2
|
|
3
3
|
module Familia
|
4
4
|
class SortedSet < RedisType
|
5
|
-
|
5
|
+
|
6
|
+
# Returns the number of elements in the sorted set
|
7
|
+
# @return [Integer] number of elements
|
8
|
+
def element_count
|
6
9
|
redis.zcard rediskey
|
7
10
|
end
|
8
|
-
alias
|
11
|
+
alias size element_count
|
9
12
|
|
10
13
|
def empty?
|
11
|
-
|
14
|
+
element_count.zero?
|
12
15
|
end
|
13
16
|
|
14
17
|
# Adds a new element to the sorted set with the current timestamp as the
|
@@ -44,13 +47,13 @@ module Familia
|
|
44
47
|
end
|
45
48
|
|
46
49
|
def add(score, val)
|
47
|
-
ret = redis.zadd rediskey, score,
|
50
|
+
ret = redis.zadd rediskey, score, serialize_value(val)
|
48
51
|
update_expiration
|
49
52
|
ret
|
50
53
|
end
|
51
54
|
|
52
55
|
def score(val)
|
53
|
-
ret = redis.zscore rediskey,
|
56
|
+
ret = redis.zscore rediskey, serialize_value(val, strict_values: false)
|
54
57
|
ret&.to_f
|
55
58
|
end
|
56
59
|
alias [] score
|
@@ -63,20 +66,20 @@ module Familia
|
|
63
66
|
|
64
67
|
# rank of member +v+ when ordered lowest to highest (starts at 0)
|
65
68
|
def rank(v)
|
66
|
-
ret = redis.zrank rediskey,
|
69
|
+
ret = redis.zrank rediskey, serialize_value(v, strict_values: false)
|
67
70
|
ret&.to_i
|
68
71
|
end
|
69
72
|
|
70
73
|
# rank of member +v+ when ordered highest to lowest (starts at 0)
|
71
74
|
def revrank(v)
|
72
|
-
ret = redis.zrevrank rediskey,
|
75
|
+
ret = redis.zrevrank rediskey, serialize_value(v, strict_values: false)
|
73
76
|
ret&.to_i
|
74
77
|
end
|
75
78
|
|
76
79
|
def members(count = -1, opts = {})
|
77
80
|
count -= 1 if count.positive?
|
78
81
|
elements = membersraw count, opts
|
79
|
-
|
82
|
+
deserialize_values(*elements)
|
80
83
|
end
|
81
84
|
alias to_a members
|
82
85
|
alias all members
|
@@ -89,7 +92,7 @@ module Familia
|
|
89
92
|
def revmembers(count = -1, opts = {})
|
90
93
|
count -= 1 if count.positive?
|
91
94
|
elements = revmembersraw count, opts
|
92
|
-
|
95
|
+
deserialize_values(*elements)
|
93
96
|
end
|
94
97
|
|
95
98
|
def revmembersraw(count = -1, opts = {})
|
@@ -132,7 +135,7 @@ module Familia
|
|
132
135
|
def range(sidx, eidx, opts = {})
|
133
136
|
echo :range, caller(1..1).first if Familia.debug
|
134
137
|
elements = rangeraw(sidx, eidx, opts)
|
135
|
-
|
138
|
+
deserialize_values(*elements)
|
136
139
|
end
|
137
140
|
|
138
141
|
def rangeraw(sidx, eidx, opts = {})
|
@@ -149,7 +152,7 @@ module Familia
|
|
149
152
|
def revrange(sidx, eidx, opts = {})
|
150
153
|
echo :revrange, caller(1..1).first if Familia.debug
|
151
154
|
elements = revrangeraw(sidx, eidx, opts)
|
152
|
-
|
155
|
+
deserialize_values(*elements)
|
153
156
|
end
|
154
157
|
|
155
158
|
def revrangeraw(sidx, eidx, opts = {})
|
@@ -160,7 +163,7 @@ module Familia
|
|
160
163
|
def rangebyscore(sscore, escore, opts = {})
|
161
164
|
echo :rangebyscore, caller(1..1).first if Familia.debug
|
162
165
|
elements = rangebyscoreraw(sscore, escore, opts)
|
163
|
-
|
166
|
+
deserialize_values(*elements)
|
164
167
|
end
|
165
168
|
|
166
169
|
def rangebyscoreraw(sscore, escore, opts = {})
|
@@ -172,7 +175,7 @@ module Familia
|
|
172
175
|
def revrangebyscore(sscore, escore, opts = {})
|
173
176
|
echo :revrangebyscore, caller(1..1).first if Familia.debug
|
174
177
|
elements = revrangebyscoreraw(sscore, escore, opts)
|
175
|
-
|
178
|
+
deserialize_values(*elements)
|
176
179
|
end
|
177
180
|
|
178
181
|
def revrangebyscoreraw(sscore, escore, opts = {})
|
@@ -201,18 +204,19 @@ module Familia
|
|
201
204
|
alias decr decrement
|
202
205
|
alias decrby decrement
|
203
206
|
|
204
|
-
|
205
|
-
|
207
|
+
# Removes a member from the sorted set
|
208
|
+
# @param value The value to remove from the sorted set
|
209
|
+
# @return [Integer] The number of members that were removed (0 or 1)
|
210
|
+
def remove_element(value)
|
211
|
+
Familia.trace :REMOVE_ELEMENT, redis, "#{value}<#{value.class}>", caller(1..1) if Familia.debug?
|
206
212
|
# We use `strict_values: false` here to allow for the deletion of values
|
207
213
|
# that are in the sorted set. If it's a horreum object, the value is
|
208
214
|
# the identifier and not a serialized version of the object. So either
|
209
215
|
# the value exists in the sorted set or it doesn't -- we don't need to
|
210
216
|
# raise an error if it's not found.
|
211
|
-
redis.zrem rediskey,
|
217
|
+
redis.zrem rediskey, serialize_value(value, strict_values: false)
|
212
218
|
end
|
213
|
-
alias remove
|
214
|
-
alias rem delete
|
215
|
-
alias del delete
|
219
|
+
alias remove remove_element # deprecated
|
216
220
|
|
217
221
|
def at(idx)
|
218
222
|
range(idx, idx).first
|