familia 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt CHANGED
@@ -1,5 +1,20 @@
1
1
  FAMILIA, CHANGES
2
2
 
3
+ #### 0.6.2 (2010-12-29) ###############################
4
+
5
+ * FIXED: Familia::ClassMethods.ttl=
6
+ * FIXED: Familia.class_lists (etc) methods.
7
+ * FIXED: Redis objects check if parent is a Module
8
+ * CHANGE: redis_objects, class_redis_objects maintain order they're defined in. (See Bluth::Queue)
9
+ * CHANGE: Depends on Storable 0.8.4
10
+ * CHANGE: FamiliaObject.instances is now a sorted set
11
+ * ADDED: InstanceMethods#initialize calls init if available
12
+ * ADDED: Can set index values via InstanceMethods#index=
13
+ * ADDED: empty? method for List, Set, et al
14
+ * ADDED: :extend option for RedisObject instances
15
+ * ADDED: :reference option for storing the index value instead of the marshaled value
16
+
17
+
3
18
  #### 0.6.1 (2010-12-18) ###############################
4
19
 
5
20
  * CHANGE: Default initialize method calls initialize_redis_objects before super
data/Rakefile CHANGED
@@ -26,9 +26,9 @@ begin
26
26
  gem.homepage = "http://github.com/delano/familia"
27
27
  gem.authors = ["Delano Mandelbaum"]
28
28
  gem.add_dependency("redis", ">= 2.1.0")
29
- gem.add_dependency("uri-redis", ">= 0.4.1")
30
- gem.add_dependency("gibbler", ">= 0.8.4")
31
- gem.add_dependency("storable", ">= 0.8.3")
29
+ gem.add_dependency("uri-redis", ">= 0.4.2")
30
+ gem.add_dependency("gibbler", ">= 0.8.6")
31
+ gem.add_dependency("storable", ">= 0.8.5")
32
32
 
33
33
  #gem.add_development_dependency("rspec", ">= 1.2.9")
34
34
  #gem.add_development_dependency("mocha", ">= 0.9.8")
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :MAJOR: 0
3
3
  :MINOR: 6
4
- :PATCH: 1
4
+ :PATCH: 2
data/familia.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{familia}
8
- s.version = "0.6.1"
8
+ s.version = "0.6.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Delano Mandelbaum"]
12
- s.date = %q{2010-12-18}
12
+ s.date = %q{2010-12-30}
13
13
  s.description = %q{Organize and store ruby objects in Redis}
14
14
  s.email = %q{delano@solutious.com}
15
15
  s.extra_rdoc_files = [
@@ -53,20 +53,20 @@ Gem::Specification.new do |s|
53
53
 
54
54
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
55
  s.add_runtime_dependency(%q<redis>, [">= 2.1.0"])
56
- s.add_runtime_dependency(%q<uri-redis>, [">= 0.4.1"])
57
- s.add_runtime_dependency(%q<gibbler>, [">= 0.8.4"])
58
- s.add_runtime_dependency(%q<storable>, [">= 0.8.3"])
56
+ s.add_runtime_dependency(%q<uri-redis>, [">= 0.4.2"])
57
+ s.add_runtime_dependency(%q<gibbler>, [">= 0.8.6"])
58
+ s.add_runtime_dependency(%q<storable>, [">= 0.8.5"])
59
59
  else
60
60
  s.add_dependency(%q<redis>, [">= 2.1.0"])
61
- s.add_dependency(%q<uri-redis>, [">= 0.4.1"])
62
- s.add_dependency(%q<gibbler>, [">= 0.8.4"])
63
- s.add_dependency(%q<storable>, [">= 0.8.3"])
61
+ s.add_dependency(%q<uri-redis>, [">= 0.4.2"])
62
+ s.add_dependency(%q<gibbler>, [">= 0.8.6"])
63
+ s.add_dependency(%q<storable>, [">= 0.8.5"])
64
64
  end
65
65
  else
66
66
  s.add_dependency(%q<redis>, [">= 2.1.0"])
67
- s.add_dependency(%q<uri-redis>, [">= 0.4.1"])
68
- s.add_dependency(%q<gibbler>, [">= 0.8.4"])
69
- s.add_dependency(%q<storable>, [">= 0.8.3"])
67
+ s.add_dependency(%q<uri-redis>, [">= 0.4.2"])
68
+ s.add_dependency(%q<gibbler>, [">= 0.8.6"])
69
+ s.add_dependency(%q<storable>, [">= 0.8.5"])
70
70
  end
71
71
  end
72
72
 
@@ -17,102 +17,103 @@ class String
17
17
  end
18
18
  end
19
19
 
20
- class Time
21
- module Units
22
- PER_MICROSECOND = 0.000001.freeze
23
- PER_MILLISECOND = 0.001.freeze
24
- PER_MINUTE = 60.0.freeze
25
- PER_HOUR = 3600.0.freeze
26
- PER_DAY = 86400.0.freeze
20
+ unless defined?(Time::Units)
21
+ class Time
22
+ module Units
23
+ PER_MICROSECOND = 0.000001.freeze
24
+ PER_MILLISECOND = 0.001.freeze
25
+ PER_MINUTE = 60.0.freeze
26
+ PER_HOUR = 3600.0.freeze
27
+ PER_DAY = 86400.0.freeze
27
28
 
28
- def microseconds() seconds * PER_MICROSECOND end
29
- def milliseconds() seconds * PER_MILLISECOND end
30
- def seconds() self end
31
- def minutes() seconds * PER_MINUTE end
32
- def hours() seconds * PER_HOUR end
33
- def days() seconds * PER_DAY end
34
- def weeks() seconds * PER_DAY * 7 end
35
- def years() seconds * PER_DAY * 365 end
29
+ def microseconds() seconds * PER_MICROSECOND end
30
+ def milliseconds() seconds * PER_MILLISECOND end
31
+ def seconds() self end
32
+ def minutes() seconds * PER_MINUTE end
33
+ def hours() seconds * PER_HOUR end
34
+ def days() seconds * PER_DAY end
35
+ def weeks() seconds * PER_DAY * 7 end
36
+ def years() seconds * PER_DAY * 365 end
36
37
 
37
- def in_years() seconds / PER_DAY / 365 end
38
- def in_weeks() seconds / PER_DAY / 7 end
39
- def in_days() seconds / PER_DAY end
40
- def in_hours() seconds / PER_HOUR end
41
- def in_minutes() seconds / PER_MINUTE end
42
- def in_milliseconds() seconds / PER_MILLISECOND end
43
- def in_microseconds() seconds / PER_MICROSECOND end
38
+ def in_years() seconds / PER_DAY / 365 end
39
+ def in_weeks() seconds / PER_DAY / 7 end
40
+ def in_days() seconds / PER_DAY end
41
+ def in_hours() seconds / PER_HOUR end
42
+ def in_minutes() seconds / PER_MINUTE end
43
+ def in_milliseconds() seconds / PER_MILLISECOND end
44
+ def in_microseconds() seconds / PER_MICROSECOND end
44
45
 
45
- def in_time
46
- Time.at(self).utc
47
- end
46
+ def in_time
47
+ Time.at(self).utc
48
+ end
48
49
 
49
- def in_seconds(u=nil)
50
- case u.to_s
51
- when /\A(y)|(years?)\z/
52
- years
53
- when /\A(w)|(weeks?)\z/
54
- weeks
55
- when /\A(d)|(days?)\z/
56
- days
57
- when /\A(h)|(hours?)\z/
58
- hours
59
- when /\A(m)|(minutes?)\z/
60
- minutes
61
- when /\A(ms)|(milliseconds?)\z/
62
- milliseconds
63
- when /\A(us)|(microseconds?)|(μs)\z/
64
- microseconds
65
- else
66
- self
50
+ def in_seconds(u=nil)
51
+ case u.to_s
52
+ when /\A(y)|(years?)\z/
53
+ years
54
+ when /\A(w)|(weeks?)\z/
55
+ weeks
56
+ when /\A(d)|(days?)\z/
57
+ days
58
+ when /\A(h)|(hours?)\z/
59
+ hours
60
+ when /\A(m)|(minutes?)\z/
61
+ minutes
62
+ when /\A(ms)|(milliseconds?)\z/
63
+ milliseconds
64
+ when /\A(us)|(microseconds?)|(μs)\z/
65
+ microseconds
66
+ else
67
+ self
68
+ end
67
69
  end
68
- end
69
70
 
70
- ## JRuby doesn't like using instance_methods.select here.
71
- ## It could be a bug or something quirky with Attic
72
- ## (although it works in 1.8 and 1.9). The error:
73
- ##
74
- ## lib/attic.rb:32:in `select': yield called out of block (LocalJumpError)
75
- ## lib/stella/mixins/numeric.rb:24
76
- ##
77
- ## Create singular methods, like hour and day.
78
- # instance_methods.select.each do |plural|
79
- # singular = plural.to_s.chop
80
- # alias_method singular, plural
81
- # end
71
+ ## JRuby doesn't like using instance_methods.select here.
72
+ ## It could be a bug or something quirky with Attic
73
+ ## (although it works in 1.8 and 1.9). The error:
74
+ ##
75
+ ## lib/attic.rb:32:in `select': yield called out of block (LocalJumpError)
76
+ ## lib/stella/mixins/numeric.rb:24
77
+ ##
78
+ ## Create singular methods, like hour and day.
79
+ # instance_methods.select.each do |plural|
80
+ # singular = plural.to_s.chop
81
+ # alias_method singular, plural
82
+ # end
82
83
 
83
- alias_method :ms, :milliseconds
84
- alias_method :'μs', :microseconds
85
- alias_method :second, :seconds
86
- alias_method :minute, :minutes
87
- alias_method :hour, :hours
88
- alias_method :day, :days
89
- alias_method :week, :weeks
90
- alias_method :year, :years
84
+ alias_method :ms, :milliseconds
85
+ alias_method :'μs', :microseconds
86
+ alias_method :second, :seconds
87
+ alias_method :minute, :minutes
88
+ alias_method :hour, :hours
89
+ alias_method :day, :days
90
+ alias_method :week, :weeks
91
+ alias_method :year, :years
91
92
 
93
+ end
92
94
  end
93
- end
94
95
 
95
- class Numeric
96
- include Time::Units
96
+ class Numeric
97
+ include Time::Units
97
98
 
98
- def to_ms
99
- (self*1000.to_f)
100
- end
99
+ def to_ms
100
+ (self*1000.to_f)
101
+ end
101
102
 
102
- # TODO: Use 1024?
103
- def to_bytes
104
- args = case self.abs.to_i
105
- when (1000)..(1000**2)
106
- '%3.2f%s' % [(self / 1000.to_f).to_s, 'KB']
107
- when (1000**2)..(1000**3)
108
- '%3.2f%s' % [(self / (1000**2).to_f).to_s, 'MB']
109
- when (1000**3)..(1000**4)
110
- '%3.2f%s' % [(self / (1000**3).to_f).to_s, 'GB']
111
- when (1000**4)..(1000**6)
112
- '%3.2f%s' % [(self / (1000**4).to_f).to_s, 'TB']
113
- else
114
- [self.to_i, 'B'].join
103
+ # TODO: Use 1024?
104
+ def to_bytes
105
+ args = case self.abs.to_i
106
+ when (1000)..(1000**2)
107
+ '%3.2f%s' % [(self / 1000.to_f).to_s, 'KB']
108
+ when (1000**2)..(1000**3)
109
+ '%3.2f%s' % [(self / (1000**2).to_f).to_s, 'MB']
110
+ when (1000**3)..(1000**4)
111
+ '%3.2f%s' % [(self / (1000**3).to_f).to_s, 'GB']
112
+ when (1000**4)..(1000**6)
113
+ '%3.2f%s' % [(self / (1000**4).to_f).to_s, 'TB']
114
+ else
115
+ [self.to_i, 'B'].join
116
+ end
115
117
  end
116
118
  end
117
119
  end
118
-
@@ -23,7 +23,7 @@ module Familia
23
23
  !obj.nil? && klass == obj.klass
24
24
  end
25
25
  define_method :"#{kind}s" do
26
- names = redis_objects.keys.select { |name| send(:"#{kind}?", name) }
26
+ names = redis_objects_order.select { |name| send(:"#{kind}?", name) }
27
27
  names.collect! { |name| redis_objects[name] }
28
28
  names
29
29
  end
@@ -42,7 +42,12 @@ module Familia
42
42
  !obj.nil? && klass == obj.klass
43
43
  end
44
44
  define_method :"class_#{kind}s" do
45
- names = class_redis_objects.keys.select { |name| send(:"#{kind}?", name) }
45
+ names = class_redis_objects_order.select { |name| ret = send(:"class_#{kind}?", name) }
46
+ # TODO: This returns instances of the RedisObject class which
47
+ # also contain the options. This is different from the instance
48
+ # RedisObjects defined above which returns the OpenStruct of name, klass, and opts.
49
+ #names.collect! { |name| self.send name }
50
+ # OR NOT:
46
51
  names.collect! { |name| class_redis_objects[name] }
47
52
  names
48
53
  end
@@ -50,10 +55,15 @@ module Familia
50
55
 
51
56
  def inherited(obj)
52
57
  obj.db = self.db
53
- obj.ttl = self.ttl
54
58
  obj.uri = self.uri
59
+ obj.ttl = self.ttl
55
60
  obj.parent = self
56
- obj.class_set :instances
61
+ obj.class_zset :instances, :class => obj, :reference => true
62
+ # :object is a special redis object because its reserved
63
+ # for storing the marshaled instance data (e.g. to_json).
64
+ # When it isn't defined explicitly we define it here b/c
65
+ # it's assumed to exist in other places (see #save).
66
+ obj.string :object, :class => obj unless obj.redis_object? :object
57
67
  Familia.classes << obj
58
68
  super(obj)
59
69
  end
@@ -62,15 +72,17 @@ module Familia
62
72
  obj.ttl = self.ttl
63
73
  obj.uri = self.uri
64
74
  obj.parent = self
65
- obj.class_set :instances
75
+ obj.class_zset :instances, :class => obj, :reference => true
66
76
  Familia.classes << obj
67
77
  end
68
78
 
69
79
  # Creates an instance method called +name+ that
70
80
  # returns an instance of the RedisObject +klass+
71
81
  def install_redis_object name, klass, opts
82
+ raise ArgumentError, "Name is blank" if name.to_s.empty?
72
83
  name = name.to_s.to_sym
73
84
  opts ||= {}
85
+ redis_objects_order << name
74
86
  redis_objects[name] = OpenStruct.new
75
87
  redis_objects[name].name = name
76
88
  redis_objects[name].klass = klass
@@ -79,17 +91,22 @@ module Familia
79
91
  define_method "#{name}=" do |v|
80
92
  self.send(name).replace v
81
93
  end
94
+ define_method "#{name}?" do
95
+ !self.send(name).empty?
96
+ end
82
97
  redis_objects[name]
83
98
  end
84
99
 
85
100
  # Creates a class method called +name+ that
86
101
  # returns an instance of the RedisObject +klass+
87
102
  def install_class_redis_object name, klass, opts
103
+ raise ArgumentError, "Name is blank" if name.to_s.empty?
88
104
  name = name.to_s.to_sym
89
105
  opts ||= {}
90
106
  opts[:suffix] ||= nil
91
107
  opts[:parent] ||= self
92
- # TODO: investigate metaclass.redis_objects
108
+ # TODO: investigate using metaclass.redis_objects
109
+ class_redis_objects_order << name
93
110
  class_redis_objects[name] = OpenStruct.new
94
111
  class_redis_objects[name].name = name
95
112
  class_redis_objects[name].klass = klass
@@ -100,6 +117,9 @@ module Familia
100
117
  metaclass.send :define_method, "#{name}=" do |v|
101
118
  send(name).replace v
102
119
  end
120
+ metaclass.send :define_method, "#{name}?" do
121
+ !send(name).empty?
122
+ end
103
123
  redis_object = klass.new name, opts
104
124
  redis_object.freeze
105
125
  self.instance_variable_set("@#{name}", redis_object)
@@ -114,6 +134,7 @@ module Familia
114
134
  @ttl = v unless v.nil?
115
135
  @ttl || (parent ? parent.ttl : nil)
116
136
  end
137
+ def ttl=(v) @ttl = v end
117
138
  def db v=nil
118
139
  @db = v unless v.nil?
119
140
  @db || (parent ? parent.db : nil)
@@ -128,15 +149,13 @@ module Familia
128
149
  @uri = uri
129
150
  end
130
151
  def uri(uri=nil)
131
- self.uri = uri unless uri.to_s.empty?
132
- return @uri if @uri
133
- @uri = URI.parse Familia.uri.to_s
134
- @uri.db = @db if @db
135
- Familia.connect @uri #unless Familia.connected?(@uri)
136
- @uri || (parent ? parent.uri : Familia.uri)
152
+ self.uri = uri if !uri.to_s.empty?
153
+ @uri ||= (parent ? parent.uri : Familia.uri)
154
+ @uri.db = @db if @db && @uri.db.to_s != @db.to_s
155
+ @uri
137
156
  end
138
157
  def redis
139
- Familia.redis(self.uri)
158
+ Familia.redis uri
140
159
  end
141
160
  def flushdb
142
161
  Familia.info "flushing #{uri}"
@@ -145,7 +164,7 @@ module Familia
145
164
  def keys(suffix=nil)
146
165
  self.redis.keys(rediskey('*',suffix)) || []
147
166
  end
148
- def all(suffix=nil)
167
+ def all(suffix=:object)
149
168
  # objects that could not be parsed will be nil
150
169
  keys(suffix).collect { |k| from_key(k) }.compact
151
170
  end
@@ -170,6 +189,10 @@ module Familia
170
189
  def suffixes
171
190
  redis_objects.keys.uniq
172
191
  end
192
+ def class_redis_objects_order
193
+ @class_redis_objects_order ||= []
194
+ @class_redis_objects_order
195
+ end
173
196
  def class_redis_objects
174
197
  @class_redis_objects ||= {}
175
198
  @class_redis_objects
@@ -180,26 +203,20 @@ module Familia
180
203
  def redis_object? name
181
204
  redis_objects.has_key? name.to_s.to_sym
182
205
  end
206
+ def redis_objects_order
207
+ @redis_objects_order ||= []
208
+ @redis_objects_order
209
+ end
183
210
  def redis_objects
184
211
  @redis_objects ||= {}
185
212
  @redis_objects
186
213
  end
187
- def create(*args)
188
- me = new(*args)
189
- raise "#{self} exists: #{me.to_json}" if me.exists?
214
+ def create *args
215
+ me = from_array *args
216
+ raise "#{self} exists: #{me.rediskey}" if me.exists?
190
217
  me.save
191
218
  me
192
219
  end
193
- def load_or_create(id)
194
- if exists?(id)
195
- from_redis(id)
196
- else
197
- me = new id
198
- me.save
199
- me
200
- end
201
- end
202
-
203
220
  def multiget(*ids)
204
221
  ids = rawmultiget(*ids)
205
222
  ids.compact.collect { |json| self.from_json(json) }.compact
@@ -211,49 +228,56 @@ module Familia
211
228
  ids = self.redis.mget *ids
212
229
  end
213
230
 
214
- def from_key(akey)
215
- raise ArgumentError, "Null key" if akey.nil? || akey.empty?
216
- Familia.trace :LOAD, redis, "#{self.uri}/#{akey}", caller if Familia.debug?
217
- return unless redis.exists akey
218
- v = redis.get akey
219
- begin
220
- if v.to_s.empty?
221
- Familia.info "No content @ #{akey}"
222
- nil
223
- else
224
- self.send Familia.load_method, v
225
- end
226
- rescue => ex
227
- Familia.info v
228
- Familia.info "Non-fatal error parsing JSON for #{akey}: #{ex.message}"
229
- Familia.info ex.backtrace
230
- nil
231
- end
232
- end
233
- def from_redis(objid)
234
- objid &&= objid.to_s
235
- return nil if objid.nil? || objid.empty?
236
- this_key = rediskey(objid, self.suffix)
237
- me = from_key(this_key)
231
+ # Returns an instance based on +idx+ otherwise it
232
+ # creates and saves a new instance base on +idx+.
233
+ # See from_index
234
+ def load_or_create idx
235
+ return from_redis(idx) if exists?(idx)
236
+ obj = from_index idx
237
+ obj.save
238
+ obj
239
+ end
240
+ # Note +idx+ needs to be an appropriate index for
241
+ # the given class. If the index is multi-value it
242
+ # must be passed as an Array in the proper order.
243
+ # Does not call save.
244
+ def from_index idx
245
+ obj = new
246
+ obj.index = idx
247
+ obj
248
+ end
249
+ def from_key objkey
250
+ raise ArgumentError, "Null key" if objkey.to_s.empty?
251
+ Familia.trace :LOAD, redis, "#{self.uri}/#{objkey}", caller if Familia.debug?
252
+ obj = Familia::String.new objkey, :class => self
253
+ obj.exists? ? obj.value : nil
254
+ end
255
+ def from_redis idx, suffix=:object
256
+ return nil if idx.to_s.empty?
257
+ objkey = rediskey idx, suffix
258
+ Familia.trace :FROMREDIS, Familia.redis(self.uri), objkey, caller.first
259
+ me = from_key objkey
238
260
  me
239
261
  end
240
- def exists?(objid, suffix=nil)
241
- objid &&= objid.to_s
242
- return false if objid.nil? || objid.empty?
243
- ret = Familia.redis(self.uri).exists rediskey(objid, suffix)
244
- Familia.trace :EXISTS, Familia.redis(self.uri), "#{rediskey(objid)} #{ret}", caller.first
262
+ def exists? idx, suffix=:object
263
+ return false if idx.to_s.empty?
264
+ objkey = rediskey idx, suffix
265
+ ret = Familia.redis(self.uri).exists objkey
266
+ Familia.trace :EXISTS, Familia.redis(self.uri), "#{rediskey(obj.index, suffix)} #{ret}", caller.first
245
267
  ret
246
268
  end
247
- def destroy!(idx, suffix=nil) # TODO: remove suffix arg
248
- ret = Familia.redis(self.uri).del rediskey(runid, suffix)
249
- Familia.trace :DELETED, Familia.redis(self.uri), "#{rediskey(runid)}: #{ret}", caller.first
269
+ def destroy! idx, suffix=:object
270
+ ret = Familia.redis(self.uri).del rediskey(idx, suffix)
271
+ Familia.trace :DELETED, Familia.redis(self.uri), "#{rediskey(idx, suffix)}: #{ret}", caller.first
250
272
  ret
251
273
  end
252
- def find(suffix='*')
274
+ def find suffix='*'
253
275
  list = Familia.redis(self.uri).keys(rediskey('*', suffix)) || []
254
276
  end
255
- def rediskey(idx, suffix=nil)
277
+ # idx can be a value or an Array of values used to create the index.
278
+ def rediskey idx, suffix=nil
256
279
  raise RuntimeError, "No index for #{self}" if idx.to_s.empty?
280
+ idx = Familia.join *idx if Array === idx
257
281
  idx &&= idx.to_s
258
282
  Familia.rediskey(prefix, idx, suffix)
259
283
  end
@@ -271,12 +295,6 @@ module Familia
271
295
  raise Familia::NonUniqueKey, "Short key returned more than 1 match"
272
296
  end
273
297
  end
274
- ## TODO: Investigate
275
- ##def float
276
- ## Proc.new do |v|
277
- ## v.nil? ? 0 : v.to_f
278
- ## end
279
- ##end
280
298
  end
281
299
 
282
300
 
@@ -288,23 +306,17 @@ module Familia
288
306
  # must call initialize_redis_objects.
289
307
  def initialize *args
290
308
  initialize_redis_objects
291
- super # call Storable#initialize or equivalent
309
+ from_array *args if respond_to? :from_array
310
+ init *args if respond_to? :init
292
311
  end
293
312
 
294
313
  # This needs to be called in the initialize method of
295
314
  # any class that includes Familia.
296
315
  def initialize_redis_objects
297
- # :object is a special redis object because its reserved
298
- # for storing the marshaled instance data (e.g. to_json).
299
- # When it isn't defined explicitly we define it here b/c
300
- # it's assumed to exist in other places (see #save).
301
- unless self.class.redis_object? :object
302
- self.class.string :object, :class => self.class
303
- end
304
-
305
316
  # Generate instances of each RedisObject. These need to be
306
317
  # unique for each instance of this class so they can refer
307
- # to the index of this specific instance.
318
+ # to the index of this specific instance.
319
+ #
308
320
  # i.e.
309
321
  # familia_object.rediskey == v1:bone:INDEXVALUE:object
310
322
  # familia_object.redis_object.rediskey == v1:bone:INDEXVALUE:name
@@ -326,7 +338,8 @@ module Familia
326
338
 
327
339
  def redisinfo
328
340
  info = {
329
- :db => self.class.db || 0,
341
+ :uri => self.class.uri,
342
+ :db => self.class.db,
330
343
  :key => rediskey,
331
344
  :type => redistype,
332
345
  :ttl => realttl
@@ -363,9 +376,11 @@ module Familia
363
376
  #Familia.trace :SAVE, Familia.redis(self.class.uri), redisuri, caller.first
364
377
  preprocess if respond_to?(:preprocess)
365
378
  self.update_time if self.respond_to?(:update_time)
379
+ # TODO: Check here (run checkup)
366
380
  ret = self.object.set self # object is a name reserved by Familia
367
381
  unless ret.nil?
368
- self.class.instances.add index # use this set instead of Klass.keys
382
+ now = Time.now.utc.to_i
383
+ self.class.instances.add now, self # use this set instead of Klass.keys
369
384
  self.object.update_expiration self.ttl # does nothing unless if not specified
370
385
  end
371
386
  true
@@ -375,7 +390,7 @@ module Familia
375
390
  if Familia.debug?
376
391
  Familia.trace :DELETED, Familia.redis(self.class.uri), "#{rediskey}: #{ret}", caller.first
377
392
  end
378
- self.class.instances.rem index if ret > 0
393
+ self.class.instances.rem self if ret > 0
379
394
  ret
380
395
  end
381
396
  def index
@@ -397,14 +412,39 @@ module Familia
397
412
  unless self.respond_to? self.class.index
398
413
  raise NoIndex, "No such method: `#{self.class.index}' for #{self.class}"
399
414
  end
400
- self.send self.class.index
415
+ self.send( self.class.index)
401
416
  end
402
417
  else
403
418
  raise Familia::NoIndex, self
404
419
  end
405
420
  end
406
- def index=(i)
407
- @index = i
421
+ def index=(v)
422
+ case self.class.index
423
+ when Proc
424
+ raise ArgumentError, "Cannot set a Proc index"
425
+ when Array
426
+ unless Array === v && v.size == self.class.index.size
427
+ raise ArgumentError, "Index mismatch (#{v.size} for #{self.class.index.size})"
428
+ end
429
+ parts = self.class.index.each_with_index { |meth,idx|
430
+ unless self.respond_to? "#{meth}="
431
+ raise NoIndex, "No such method: `#{meth}=' for #{self.class}"
432
+ end
433
+ self.send("#{meth}=", v[idx])
434
+ }
435
+ when Symbol, String
436
+ if self.class.redis_object?(self.class.index.to_sym)
437
+ raise Familia::NoIndex, "Cannot use a RedisObject as an index"
438
+ else
439
+ unless self.respond_to? "#{self.class.index}="
440
+ raise NoIndex, "No such method: `#{self.class.index}=' for #{self.class}"
441
+ end
442
+ self.send("#{self.class.index}=", v)
443
+ end
444
+ else
445
+ raise Familia::NoIndex, self
446
+ end
447
+
408
448
  end
409
449
  def expire(ttl=nil)
410
450
  ttl ||= self.class.ttl