familia 0.6.1 → 0.6.2

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.
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