familia 1.0.0.pre.rc3 → 1.0.0.pre.rc5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +198 -48
  4. data/VERSION.yml +1 -1
  5. data/lib/familia/base.rb +29 -1
  6. data/lib/familia/features/expiration.rb +91 -0
  7. data/lib/familia/features/quantization.rb +56 -0
  8. data/lib/familia/features/safe_dump.rb +2 -33
  9. data/lib/familia/features.rb +5 -4
  10. data/lib/familia/horreum/class_methods.rb +112 -46
  11. data/lib/familia/horreum/commands.rb +9 -3
  12. data/lib/familia/horreum/relations_management.rb +2 -2
  13. data/lib/familia/horreum/serialization.rb +150 -41
  14. data/lib/familia/horreum/settings.rb +0 -8
  15. data/lib/familia/horreum/utils.rb +0 -1
  16. data/lib/familia/horreum.rb +2 -2
  17. data/lib/familia/logging.rb +26 -4
  18. data/lib/familia/redistype/serialization.rb +60 -38
  19. data/lib/familia/redistype.rb +50 -17
  20. data/lib/familia/settings.rb +11 -1
  21. data/lib/familia/tools.rb +68 -0
  22. data/lib/familia/types/hashkey.rb +5 -5
  23. data/lib/familia/types/list.rb +2 -2
  24. data/lib/familia/types/sorted_set.rb +12 -12
  25. data/lib/familia/types/string.rb +1 -1
  26. data/lib/familia/types/unsorted_set.rb +2 -2
  27. data/lib/familia/utils.rb +106 -51
  28. data/lib/familia/version.rb +2 -2
  29. data/try/10_familia_try.rb +4 -4
  30. data/try/20_redis_type_try.rb +9 -6
  31. data/try/26_redis_bool_try.rb +1 -1
  32. data/try/27_redis_horreum_try.rb +1 -1
  33. data/try/30_familia_object_try.rb +3 -2
  34. data/try/40_customer_try.rb +3 -3
  35. data/try/test_helpers.rb +9 -2
  36. metadata +5 -5
  37. data/lib/familia/features/api_version.rb +0 -19
  38. data/lib/familia/features/atomic_saves.rb +0 -8
  39. data/lib/familia/features/quantizer.rb +0 -35
@@ -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 [Object] The Redis instance being used.
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 Familia.debug?
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
- instance_id = redis_instance&.id
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
 
@@ -4,63 +4,80 @@ class Familia::RedisType
4
4
 
5
5
  module Serialization
6
6
 
7
- # Serializes an individual value for storage in Redis.
8
- #
9
- # This method prepares a value for storage in Redis by converting it to a string representation.
10
- # If a class option is specified, it uses that class's serialization method.
11
- # Otherwise, it relies on the value's own `to_s` method for serialization.
7
+ # Serializes a value for storage in Redis.
12
8
  #
13
9
  # @param val [Object] The value to be serialized.
14
- # @param strict_values [Boolean] Whether to enforce strict value serialization (default: true). Only applies when no class option is specified because the class option is assumed to handle its own serialization.
15
- # @return [String] The serialized representation of the value.
10
+ # @param strict_values [Boolean] Whether to enforce strict value
11
+ # serialization (default: true).
12
+ # @return [String, nil] The serialized representation of the value, or nil
13
+ # if serialization fails.
16
14
  #
17
- # @note When no class option is specified, this method attempts to serialize the value directly.
18
- # If the serialization fails, it falls back to the value's own string representation.
15
+ # @note When a class option is specified, it uses that class's
16
+ # serialization method. Otherwise, it relies on Familia.distinguisher for
17
+ # serialization.
19
18
  #
20
19
  # @example With a class option
21
- # to_redis(User.new(name: "John"), strict_values: false) #=> '{"name":"John"}'
22
- # to_redis(nil, strict_values: false) #=> "" (empty string)
23
- # to_redis(true, strict_values: false) #=> "true"
20
+ # to_redis(User.new(name: "Cloe"), strict_values: false) #=> '{"name":"Cloe"}'
24
21
  #
25
- # @example Without a class option and strict values
26
- # to_redis(123) #=> "123" (which becomes "123" in Redis)
22
+ # @example Without a class option
23
+ # to_redis(123) #=> "123"
27
24
  # to_redis("hello") #=> "hello"
28
- # to_redis(nil) # raises an exception
29
- # to_redis(true) # raises an exception
30
25
  #
31
- # @raise [Familia::HighRiskFactor]
26
+ # @raise [Familia::HighRiskFactor] If serialization fails under strict
27
+ # mode.
32
28
  #
33
29
  def to_redis(val, strict_values = true)
34
- ret = nil
30
+ prepared = nil
35
31
 
36
32
  Familia.trace :TOREDIS, redis, "#{val}<#{val.class}|#{opts[:class]}>", caller(1..1) if Familia.debug?
37
33
 
38
34
  if opts[:class]
39
- ret = Familia.distinguisher(opts[:class], strict_values)
40
- Familia.ld " from opts[class] <#{opts[:class]}>: #{ret||'<nil>'}"
35
+ prepared = Familia.distinguisher(opts[:class], strict_values)
36
+ Familia.ld " from opts[class] <#{opts[:class]}>: #{prepared||'<nil>'}"
41
37
  end
42
38
 
43
- if ret.nil?
39
+ if prepared.nil?
44
40
  # Enforce strict values when no class option is specified
45
- ret = Familia.distinguisher(val, true)
46
- Familia.ld " from value #{val}<#{val.class}>: #{ret}<#{ret.class}>"
41
+ prepared = Familia.distinguisher(val, true)
42
+ Familia.ld " from <#{val.class}> => <#{prepared.class}>"
47
43
  end
48
44
 
49
- Familia.trace :TOREDIS, redis, "#{val}<#{val.class}|#{opts[:class]}> => #{ret}<#{ret.class}>", caller(1..1) if Familia.debug?
45
+ Familia.trace :TOREDIS, redis, "#{val}<#{val.class}|#{opts[:class]}> => #{prepared}<#{prepared.class}>", caller(1..1) if Familia.debug?
50
46
 
51
- Familia.warn "[#{self.class}\#to_redis] nil returned for #{opts[:class]}\##{name}" if ret.nil?
52
- ret
47
+ Familia.warn "[#{self.class}\#to_redis] nil returned for #{opts[:class]}\##{name}" if prepared.nil?
48
+ prepared
53
49
  end
54
50
 
51
+ # Deserializes multiple values from Redis, removing nil values.
52
+ #
53
+ # @param values [Array<String>] The values to deserialize.
54
+ # @return [Array<Object>] Deserialized objects, with nil values removed.
55
+ #
56
+ # @see #multi_from_redis_with_nil
57
+ #
55
58
  def multi_from_redis(*values)
56
- # Avoid using compact! here. Using compact! as the last expression in the method
57
- # can unintentionally return nil if no changes are made, which is not desirable.
58
- # Instead, use compact to ensure the method returns the expected value.
59
+ # Avoid using compact! here. Using compact! as the last expression in the
60
+ # method can unintentionally return nil if no changes are made, which is
61
+ # not desirable. Instead, use compact to ensure the method returns the
62
+ # expected value.
59
63
  multi_from_redis_with_nil(*values).compact
60
64
  end
61
65
 
66
+ # Deserializes multiple values from Redis, preserving nil values.
67
+ #
68
+ # @param values [Array<String>] The values to deserialize.
69
+ # @return [Array<Object, nil>] Deserialized objects, including nil values.
70
+ #
71
+ # @raise [Familia::Problem] If the specified class doesn't respond to the
72
+ # load method.
73
+ #
74
+ # @note This method attempts to deserialize each value using the specified
75
+ # class's load method. If deserialization fails for a value, it's
76
+ # replaced with nil.
77
+ #
62
78
  # NOTE: `multi` in this method name refers to multiple values from
63
79
  # redis and not the Redis server MULTI command.
80
+ #
64
81
  def multi_from_redis_with_nil(*values)
65
82
  Familia.ld "multi_from_redis: (#{@opts}) #{values}"
66
83
  return [] if values.empty?
@@ -89,6 +106,20 @@ class Familia::RedisType
89
106
  values
90
107
  end
91
108
 
109
+ # Deserializes a single value from Redis.
110
+ #
111
+ # @param val [String, nil] The value to deserialize.
112
+ # @return [Object, nil] The deserialized object, the default value if
113
+ # val is nil, or nil if deserialization fails.
114
+ #
115
+ # @note If no class option is specified, the original value is
116
+ # returned unchanged.
117
+ #
118
+ # NOTE: Currently only the RedisType class uses this method. Horreum
119
+ # fields are a newer addition and don't support the full range of
120
+ # deserialization options that RedisType supports. It uses to_redis
121
+ # for serialization since everything becomes a string in Redis.
122
+ #
92
123
  def from_redis(val)
93
124
  return @opts[:default] if val.nil?
94
125
  return val unless @opts[:class]
@@ -96,15 +127,6 @@ class Familia::RedisType
96
127
  ret = multi_from_redis val
97
128
  ret&.first # return the object or nil
98
129
  end
99
-
100
- def update_expiration(ttl = nil)
101
- ttl ||= opts[:ttl]
102
- return if ttl.to_i.zero? # nil will be zero
103
-
104
- Familia.ld "#{rediskey} to #{ttl}"
105
- expire ttl.to_i
106
- end
107
-
108
130
  end
109
131
 
110
132
  end
@@ -13,31 +13,29 @@ module Familia
13
13
  # @abstract Subclass and implement Redis data type specific methods
14
14
  class RedisType
15
15
  include Familia::Base
16
+ extend Familia::Features
16
17
 
17
18
  @registered_types = {}
18
19
  @valid_options = %i[class parent ttl default db key redis]
19
20
  @db = nil
20
- @ttl = nil
21
+
22
+ feature :expiration
23
+ feature :quantization
21
24
 
22
25
  class << self
23
26
  attr_reader :registered_types, :valid_options
24
27
  attr_accessor :parent
25
- attr_writer :ttl, :db, :uri
28
+ attr_writer :db, :uri
26
29
 
27
30
  # To be called inside every class that inherits RedisType
28
31
  # +methname+ is the term used for the class and instance methods
29
32
  # that are created for the given +klass+ (e.g. set, list, etc)
30
33
  def register(klass, methname)
31
- Familia.ld "[#{self}] Registering #{klass} as #{methname}"
34
+ Familia.ld "[#{self}] Registering #{klass} as #{methname.inspect}"
32
35
 
33
36
  @registered_types[methname] = klass
34
37
  end
35
38
 
36
- def ttl(val = nil)
37
- @ttl = val unless val.nil?
38
- @ttl || parent&.ttl
39
- end
40
-
41
39
  def db(val = nil)
42
40
  @db = val unless val.nil?
43
41
  @db || parent&.db
@@ -49,8 +47,9 @@ module Familia
49
47
  end
50
48
 
51
49
  def inherited(obj)
50
+ Familia.trace :REDISTYPE, nil, "#{obj} is my kinda type", caller(1..1) if Familia.debug?
52
51
  obj.db = db
53
- obj.ttl = ttl
52
+ obj.ttl = ttl # method added via Features::Expiration
54
53
  obj.uri = uri
55
54
  obj.parent = self
56
55
  super(obj)
@@ -101,6 +100,17 @@ module Familia
101
100
  @opts = opts || {}
102
101
  @opts = RedisType.valid_keys_only(@opts)
103
102
 
103
+ # Apply the options to instance method setters of the same name
104
+ @opts.each do |k, v|
105
+ # Bewarde logging :parent instance here implicitly calls #to_s which for
106
+ # some classes could include the identifier which could still be nil at
107
+ # this point. This would result in a Familia::Problem being raised. So
108
+ # to be on the safe-side here until we have a better understanding of
109
+ # the issue, we'll just log the class name for each key-value pair.
110
+ Familia.ld " [setting] #{k} #{v.class}"
111
+ send(:"#{k}=", v) if respond_to? :"#{k}="
112
+ end
113
+
104
114
  init if respond_to? :init
105
115
  end
106
116
 
@@ -110,10 +120,37 @@ module Familia
110
120
  parent? ? parent.redis : Familia.redis(opts[:db])
111
121
  end
112
122
 
113
- # Produces the full redis key for this object.
123
+ # Produces the full Redis key for this object.
124
+ #
125
+ # @return [String] The full Redis key.
126
+ #
127
+ # This method determines the appropriate Redis key based on the context of the RedisType object:
128
+ #
129
+ # 1. If a hardcoded key is set in the options, it returns that key.
130
+ # 2. For instance-level RedisType objects, it uses the parent instance's rediskey method.
131
+ # 3. For class-level RedisType objects, it uses the parent class's rediskey method.
132
+ # 4. For standalone RedisType objects, it uses the keystring as the full Redis key.
133
+ #
134
+ # For class-level RedisType objects (parent_class? == true):
135
+ # - The suffix is optional and used to differentiate between different types of objects.
136
+ # - If no suffix is provided, the class's default suffix is used (via the self.suffix method).
137
+ # - If a nil suffix is explicitly passed, it won't appear in the resulting Redis key.
138
+ # - Passing nil as the suffix is how class-level RedisType objects are created without
139
+ # the global default 'object' suffix.
140
+ #
141
+ # @example Instance-level RedisType
142
+ # user_instance.some_redistype.rediskey # => "user:123:some_redistype"
143
+ #
144
+ # @example Class-level RedisType
145
+ # User.some_redistype.rediskey # => "user:some_redistype"
146
+ #
147
+ # @example Standalone RedisType
148
+ # RedisType.new("mykey").rediskey # => "mykey"
149
+ #
150
+ # @example Class-level RedisType with explicit nil suffix
151
+ # User.rediskey("123", nil) # => "user:123"
152
+ #
114
153
  def rediskey
115
- Familia.ld "[rediskey] #{keystring} for #{self.class} (#{opts})"
116
-
117
154
  # Return the hardcoded key if it's set. This is useful for
118
155
  # support legacy keys that aren't derived in the same way.
119
156
  return opts[:key] if opts[:key]
@@ -128,7 +165,7 @@ module Familia
128
165
  parent.rediskey(keystring, nil)
129
166
  else
130
167
  # This is a standalone RedisType object where it's keystring
131
- # is the full key.
168
+ # is the full redis key.
132
169
  keystring
133
170
  end
134
171
  end
@@ -153,10 +190,6 @@ module Familia
153
190
  @opts[:parent]
154
191
  end
155
192
 
156
- def ttl
157
- @opts[:ttl] || self.class.ttl
158
- end
159
-
160
193
  def db
161
194
  @opts[:db] || self.class.db
162
195
  end
@@ -5,7 +5,7 @@ module Familia
5
5
  @delim = ':'
6
6
  @prefix = nil
7
7
  @suffix = :object
8
- @ttl = nil
8
+ @ttl = 0 # see update_expiration. Zero is skip. nil is an exception.
9
9
  @db = nil
10
10
 
11
11
  module Settings
@@ -27,6 +27,16 @@ module Familia
27
27
  @suffix
28
28
  end
29
29
 
30
+ def ttl(v = nil)
31
+ @ttl = v unless v.nil?
32
+ @ttl
33
+ end
34
+
35
+ def db(v = nil)
36
+ @db = v unless v.nil?
37
+ @db
38
+ end
39
+
30
40
  # We define this do-nothing method because it reads better
31
41
  # than simply Familia.suffix in some contexts.
32
42
  def default_suffix
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Familia
4
+ module Tools
5
+ extend self
6
+ def move_keys(filter, source_uri, target_uri, &each_key)
7
+ raise "Source and target are the same (#{target_uri})" if target_uri == source_uri
8
+
9
+ Familia.connect target_uri
10
+ source_keys = Familia.redis(source_uri).keys(filter)
11
+ puts "Moving #{source_keys.size} keys from #{source_uri} to #{target_uri} (filter: #{filter})"
12
+ source_keys.each_with_index do |key, idx|
13
+ type = Familia.redis(source_uri).type key
14
+ ttl = Familia.redis(source_uri).ttl key
15
+ if source_uri.host == target_uri.host && source_uri.port == target_uri.port
16
+ Familia.redis(source_uri).move key, target_uri.db
17
+ else
18
+ case type
19
+ when 'string'
20
+ Familia.redis(source_uri).get key
21
+ when 'list'
22
+ Familia.redis(source_uri).lrange key, 0, -1
23
+ when 'set'
24
+ Familia.redis(source_uri).smembers key
25
+ else
26
+ raise Familia::Problem, "unknown key type: #{type}"
27
+ end
28
+ raise 'Not implemented'
29
+ end
30
+ yield(idx, type, key, ttl) unless each_key.nil?
31
+ end
32
+ end
33
+
34
+ # Use the return value from each_key as the new key name
35
+ def rename(filter, source_uri, target_uri = nil, &each_key)
36
+ target_uri ||= source_uri
37
+ move_keys filter, source_uri, target_uri if source_uri != target_uri
38
+ source_keys = Familia.redis(source_uri).keys(filter)
39
+ puts "Renaming #{source_keys.size} keys from #{source_uri} (filter: #{filter})"
40
+ source_keys.each_with_index do |key, idx|
41
+ Familia.trace :RENAME1, Familia.redis(source_uri), "#{key}", ''
42
+ type = Familia.redis(source_uri).type key
43
+ ttl = Familia.redis(source_uri).ttl key
44
+ newkey = yield(idx, type, key, ttl) unless each_key.nil?
45
+ Familia.trace :RENAME2, Familia.redis(source_uri), "#{key} -> #{newkey}", caller(1..1).first
46
+ Familia.redis(source_uri).renamenx key, newkey
47
+ end
48
+ end
49
+
50
+ def get_any(keyname, uri = nil)
51
+ type = Familia.redis(uri).type keyname
52
+ case type
53
+ when 'string'
54
+ Familia.redis(uri).get keyname
55
+ when 'list'
56
+ Familia.redis(uri).lrange(keyname, 0, -1) || []
57
+ when 'set'
58
+ Familia.redis(uri).smembers(keyname) || []
59
+ when 'zset'
60
+ Familia.redis(uri).zrange(keyname, 0, -1) || []
61
+ when 'hash'
62
+ Familia.redis(uri).hgetall(keyname) || {}
63
+ else
64
+ nil
65
+ end
66
+ end
67
+ end
68
+ end
@@ -49,12 +49,12 @@ module Familia
49
49
  end
50
50
 
51
51
  def values
52
- el = redis.hvals(rediskey)
53
- multi_from_redis(*el)
52
+ elements = redis.hvals(rediskey)
53
+ multi_from_redis(*elements)
54
54
  end
55
55
 
56
56
  def hgetall
57
- # TODO: Use from_redis. Also name `all` is confusing with
57
+ # TODO: Use from_redis. Also alias `all` is confusing with
58
58
  # Onetime::Customer.all which returns all customers.
59
59
  redis.hgetall rediskey
60
60
  end
@@ -98,8 +98,8 @@ module Familia
98
98
  alias merge! update
99
99
 
100
100
  def values_at *fields
101
- el = redis.hmget(rediskey, *fields.flatten.compact)
102
- multi_from_redis(*el)
101
+ elements = redis.hmget(rediskey, *fields.flatten.compact)
102
+ multi_from_redis(*elements)
103
103
  end
104
104
 
105
105
  Familia::RedisType.register self, :hash # legacy, deprecated
@@ -65,8 +65,8 @@ module Familia
65
65
  alias del delete
66
66
 
67
67
  def range(sidx = 0, eidx = -1)
68
- el = rangeraw sidx, eidx
69
- multi_from_redis(*el)
68
+ elements = rangeraw sidx, eidx
69
+ multi_from_redis(*elements)
70
70
  end
71
71
 
72
72
  def rangeraw(sidx = 0, eidx = -1)
@@ -75,8 +75,8 @@ module Familia
75
75
 
76
76
  def members(count = -1, opts = {})
77
77
  count -= 1 if count.positive?
78
- el = membersraw count, opts
79
- multi_from_redis(*el)
78
+ elements = membersraw count, opts
79
+ multi_from_redis(*elements)
80
80
  end
81
81
  alias to_a members
82
82
  alias all members
@@ -88,8 +88,8 @@ module Familia
88
88
 
89
89
  def revmembers(count = -1, opts = {})
90
90
  count -= 1 if count.positive?
91
- el = revmembersraw count, opts
92
- multi_from_redis(*el)
91
+ elements = revmembersraw count, opts
92
+ multi_from_redis(*elements)
93
93
  end
94
94
 
95
95
  def revmembersraw(count = -1, opts = {})
@@ -131,8 +131,8 @@ module Familia
131
131
 
132
132
  def range(sidx, eidx, opts = {})
133
133
  echo :range, caller(1..1).first if Familia.debug
134
- el = rangeraw(sidx, eidx, opts)
135
- multi_from_redis(*el)
134
+ elements = rangeraw(sidx, eidx, opts)
135
+ multi_from_redis(*elements)
136
136
  end
137
137
 
138
138
  def rangeraw(sidx, eidx, opts = {})
@@ -148,8 +148,8 @@ module Familia
148
148
 
149
149
  def revrange(sidx, eidx, opts = {})
150
150
  echo :revrange, caller(1..1).first if Familia.debug
151
- el = revrangeraw(sidx, eidx, opts)
152
- multi_from_redis(*el)
151
+ elements = revrangeraw(sidx, eidx, opts)
152
+ multi_from_redis(*elements)
153
153
  end
154
154
 
155
155
  def revrangeraw(sidx, eidx, opts = {})
@@ -159,8 +159,8 @@ module Familia
159
159
  # e.g. obj.metrics.rangebyscore (now-12.hours), now, :limit => [0, 10]
160
160
  def rangebyscore(sscore, escore, opts = {})
161
161
  echo :rangebyscore, caller(1..1).first if Familia.debug
162
- el = rangebyscoreraw(sscore, escore, opts)
163
- multi_from_redis(*el)
162
+ elements = rangebyscoreraw(sscore, escore, opts)
163
+ multi_from_redis(*elements)
164
164
  end
165
165
 
166
166
  def rangebyscoreraw(sscore, escore, opts = {})
@@ -171,8 +171,8 @@ module Familia
171
171
  # e.g. obj.metrics.revrangebyscore (now-12.hours), now, :limit => [0, 10]
172
172
  def revrangebyscore(sscore, escore, opts = {})
173
173
  echo :revrangebyscore, caller(1..1).first if Familia.debug
174
- el = revrangebyscoreraw(sscore, escore, opts)
175
- multi_from_redis(*el)
174
+ elements = revrangebyscoreraw(sscore, escore, opts)
175
+ multi_from_redis(*elements)
176
176
  end
177
177
 
178
178
  def revrangebyscoreraw(sscore, escore, opts = {})
@@ -14,7 +14,7 @@ module Familia
14
14
  end
15
15
 
16
16
  def value
17
- echo :value, caller[0..0] if Familia.debug
17
+ echo :value, caller(0..0) if Familia.debug
18
18
  redis.setnx rediskey, @opts[:default] if @opts[:default]
19
19
  from_redis redis.get(rediskey)
20
20
  end
@@ -23,8 +23,8 @@ module Familia
23
23
 
24
24
  def members
25
25
  echo :members, caller(1..1).first if Familia.debug
26
- el = membersraw
27
- multi_from_redis(*el)
26
+ elements = membersraw
27
+ multi_from_redis(*elements)
28
28
  end
29
29
  alias all members
30
30
  alias to_a members