Class: Familia::RedisObject

Inherits:
Object
  • Object
show all
Defined in:
lib/familia/redisobject.rb

Direct Known Subclasses

HashKey, List, Set, SortedSet, String

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, opts = {}) ⇒ RedisObject

name: If parent is set, this will be used as the suffix for rediskey. Otherwise this becomes the value of the key. If this is an Array, the elements will be joined.

Options:

:class => A class that responds to Familia.load_method and Familia.dump_method. These will be used when loading and saving data from/to redis to unmarshal/marshal the class.

:reference => When true the index of the given value will be stored rather than the marshaled value. This assumes that the marshaled object is stored at a separate key. When read, from_redis looks for that separate key and returns the unmarshaled object. :class must be specified. Default: false.

:extend => Extend this instance with the functionality in an other module. Literally: “self.extend opts”.

:parent => The Familia object that this redis object belongs to. This can be a class that includes Familia or an instance.

:ttl => the time to live in seconds. When not nil, this will set the redis expire for this key whenever #save is called. You can also call it explicitly via #update_expiration.

:default => the default value (String-only)

:db => the redis database to use (ignored if :redis is used).

:redis => an instance of Redis.

Uses the redis connection of the parent or the value of opts or Familia.redis (in that order).



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/familia/redisobject.rb', line 92

def initialize name, opts={}
  @name, @opts = name, opts
  @name = @name.join(Familia.delim) if Array === @name
  #Familia.ld [name, opts, caller[0]].inspect
  self.extend @opts[:extend] if Module === @opts[:extend]
  @db = @opts.delete(:db)
  @parent = @opts.delete(:parent)
  @ttl ||= @opts.delete(:ttl) 
  @redis ||= @opts.delete(:redis)
  @cache = {}
  init if respond_to? :init
end

Class Attribute Details

.classesObject



19
20
21
# File 'lib/familia/redisobject.rb', line 19

def RedisObject.classes
  @classes
end

.db(v = nil) ⇒ Object



31
32
33
34
# File 'lib/familia/redisobject.rb', line 31

def db v=nil
  @db = v unless v.nil?
  @db || (parent ? parent.db : nil)
end

.parentObject

Returns the value of attribute parent.



25
26
27
# File 'lib/familia/redisobject.rb', line 25

def parent
  @parent
end

.ttl(v = nil) ⇒ Object



27
28
29
30
# File 'lib/familia/redisobject.rb', line 27

def ttl v=nil
  @ttl = v unless v.nil?
  @ttl || (parent ? parent.ttl : nil)
end

.uri(v = nil) ⇒ Object



35
36
37
38
# File 'lib/familia/redisobject.rb', line 35

def uri v=nil
  @uri = v unless v.nil?
  @uri || (parent ? parent.uri : Familia.uri)
end

Instance Attribute Details

#cacheObject (readonly)

RedisObject instances are frozen. ‘cache` is a hash for you to store values retreived from Redis. This is not used anywhere by default, but you’re encouraged to use it in your specific scenarios.



56
57
58
# File 'lib/familia/redisobject.rb', line 56

def cache
  @cache
end

#nameObject (readonly)

Returns the value of attribute name.



49
50
51
# File 'lib/familia/redisobject.rb', line 49

def name
  @name
end

#parentObject (readonly)

Returns the value of attribute parent.



49
50
51
# File 'lib/familia/redisobject.rb', line 49

def parent
  @parent
end

#redisObject



113
114
115
116
# File 'lib/familia/redisobject.rb', line 113

def redis
  return @redis if @redis
  parent? ? parent.redis : Familia.redis(db)
end

Class Method Details

.inherited(obj) ⇒ Object



39
40
41
42
43
44
45
46
# File 'lib/familia/redisobject.rb', line 39

def inherited(obj)
  obj.db = self.db
  obj.ttl = self.ttl
  obj.uri = self.uri
  obj.parent = self
  RedisObject.classes << obj
  super(obj)
end

.register(klass, meth) ⇒ Object

To be called inside every class that inherits RedisObject meth becomes the base for the class and instances methods that are created for the given klass (e.g. Obj.list)



11
12
13
# File 'lib/familia/redisobject.rb', line 11

def RedisObject.register klass, meth
  registration[meth] = klass
end

.registrationObject



15
16
17
# File 'lib/familia/redisobject.rb', line 15

def RedisObject.registration
  @registration
end

Instance Method Details

#class?Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/familia/redisobject.rb', line 152

def class?
  !@opts[:class].to_s.empty? && @opts[:class].kind_of?(Familia)
end

#clear_cacheObject



105
106
107
# File 'lib/familia/redisobject.rb', line 105

def clear_cache
  @cache.clear
end

#dbObject

Returns the most likely value for db, checking (in this order):

* the value from :class if it's a Familia object
* the value from :parent
* the value self.class.db
* assumes the db is 0

After this is called once, this method will always return the same value.



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/familia/redisobject.rb', line 126

def db 
  # Note it's important that we select this value at the last
  # possible moment rather than in initialize b/c the value 
  # could be modified after that but before this is called. 
  if @opts[:class] && @opts[:class].ancestors.member?(Familia)
    @opts[:class].db 
  elsif parent?
    parent.db
  else
    self.class.db || @db || 0
  end
end

#deleteObject Also known as: clear, del



183
184
185
# File 'lib/familia/redisobject.rb', line 183

def delete 
  redis.del rediskey
end

#dump_methodObject



214
215
216
# File 'lib/familia/redisobject.rb', line 214

def dump_method
  @opts[:dump_method] || Familia.dump_method
end

#echo(meth, trace) ⇒ Object



109
110
111
# File 'lib/familia/redisobject.rb', line 109

def echo meth, trace
  redis.echo "[#{self.class}\##{meth}] #{trace} (#{@opts[:class]}\#)"
end

#exists?Boolean

def destroy!

clear
# TODO: delete redis objects for this instance

end

Returns:

  • (Boolean)


194
195
196
# File 'lib/familia/redisobject.rb', line 194

def exists?
  redis.exists(rediskey) && !size.zero?
end

#expire(sec) ⇒ Object



202
203
204
# File 'lib/familia/redisobject.rb', line 202

def expire sec
  redis.expire rediskey, sec.to_i
end

#expireat(unixtime) ⇒ Object



206
207
208
# File 'lib/familia/redisobject.rb', line 206

def expireat unixtime
  redis.expireat rediskey, unixtime
end

#from_redis(v) ⇒ Object



294
295
296
297
298
299
# File 'lib/familia/redisobject.rb', line 294

def from_redis v
  return @opts[:default] if v.nil?
  return v unless @opts[:class]
  ret = multi_from_redis v
  ret.first unless ret.nil? # return the object or nil
end

#load_methodObject



218
219
220
# File 'lib/familia/redisobject.rb', line 218

def load_method
  @opts[:load_method] || Familia.load_method
end

#move(db) ⇒ Object



167
168
169
# File 'lib/familia/redisobject.rb', line 167

def move db
  redis.move rediskey, db
end

#multi_from_redis(*values) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/familia/redisobject.rb', line 253

def multi_from_redis *values
  Familia.ld "multi_from_redis: (#{@opts}) #{values}"
  return [] if values.empty?
  return values.flatten unless @opts[:class]
  ret = case @opts[:class]
  when ::String
    v.to_s
  when ::Symbol
    v.to_s.to_sym
  when ::Fixnum, ::Float
    @opts[:class].induced_from v
  else
    objs = values
    
    if @opts[:reference] == true
      objs = @opts[:class].rawmultiget *values
    end
    objs.compact!
    if @opts[:class].respond_to? load_method
      objs.collect! { |obj| 
        begin
          v = @opts[:class].send load_method, obj
          if v.nil?
            Familia.ld "[#{self.class}\#multi_from_redis] nil returned for #{@opts[:class]}\##{name}" 
          end
          v
        rescue => ex
          Familia.info v
          Familia.info "Parse error for #{rediskey} (#{load_method}): #{ex.message}"
          Familia.info ex.backtrace
          nil
        end
      }
    else
      raise Familia::Problem, "No such method: #{@opts[:class]}##{load_method}"
    end
    objs.compact # don't use compact! b/c the return value appears in ret
  end
  ret
end

#parent?Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/familia/redisobject.rb', line 156

def parent?
  Class === parent || Module === parent || parent.kind_of?(Familia)
end

#persistObject



210
211
212
# File 'lib/familia/redisobject.rb', line 210

def persist
  redis.persist rediskey
end

#realttlObject



198
199
200
# File 'lib/familia/redisobject.rb', line 198

def realttl
  redis.ttl rediskey
end

#rediskeyObject

returns a redis key based on the parent object so it will include the proper index.



148
149
150
# File 'lib/familia/redisobject.rb', line 148

def rediskey
  parent? ? parent.rediskey(name, nil) : [name].flatten.compact.join(Familia.delim)
end

#rename(newkey) ⇒ Object



171
172
173
# File 'lib/familia/redisobject.rb', line 171

def rename newkey
  redis.rename rediskey, newkey
end

#renamenx(newkey) ⇒ Object



175
176
177
# File 'lib/familia/redisobject.rb', line 175

def renamenx newkey
  redis.renamenx rediskey, newkey
end

#to_redis(v) ⇒ Object



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/familia/redisobject.rb', line 222

def to_redis v
  return v unless @opts[:class]
  ret = case @opts[:class]
  when ::Symbol, ::String, ::Fixnum, ::Float, Gibbler::Digest
    v
  else
    if ::String === v
      v
      
    elsif @opts[:reference] == true
      unless v.respond_to? :index
        raise Familia::Problem, "#{v.class} does not have an index method"
      end
      unless v.kind_of?(Familia)
        raise Familia::Problem, "#{v.class} is not Familia (#{name})"
      end
      v.index

    elsif v.respond_to? dump_method
      v.send dump_method
      
    else
      raise Familia::Problem, "No such method: #{v.class}.#{dump_method}"
    end
  end
  if ret.nil?
    Familia.ld "[#{self.class}\#to_redis] nil returned for #{@opts[:class]}\##{name}" 
  end
  ret
end

#ttlObject



139
140
141
142
143
144
# File 'lib/familia/redisobject.rb', line 139

def ttl
  @ttl || 
  (parent.ttl if parent?) || 
  (@opts[:class].ttl if class?) || 
  (self.class.ttl if self.class.respond_to?(:ttl))
end

#typeObject



179
180
181
# File 'lib/familia/redisobject.rb', line 179

def type 
  redis.type rediskey
end

#update_expiration(ttl = nil) ⇒ Object



160
161
162
163
164
165
# File 'lib/familia/redisobject.rb', line 160

def update_expiration(ttl=nil)
  ttl ||= self.ttl
  return if ttl.to_i.zero?  # nil will be zero
  Familia.ld "#{rediskey} to #{ttl}"
  expire ttl.to_i
end