class Familia::RedisObject
Attributes
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.
Public Class Methods
# File lib/familia/redisobject.rb, line 19 def RedisObject.classes @classes end
# File lib/familia/redisobject.rb, line 31 def db v=nil @db = v unless v.nil? @db || (parent ? parent.db : nil) end
# 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
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.
:quantize => append a quantized timestamp to the rediskey. Takes one of the following:
Boolean: include the default stamp (now % 10 minutes) Integer: the number of seconds to quantize to (e.g. 1.hour) Array: All arguments for qstamp (quantum, pattern, Time.now)
:default => the default value (String-only)
:dump_method => the instance method to call to serialize the object before sending it to Redis (default: Familia.dump_method).
:load_method => the class method to call to deserialize the object after it's read from Redis (default: Familia.load_method).
: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).
# File lib/familia/redisobject.rb, line 104 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
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)
# File lib/familia/redisobject.rb, line 11 def RedisObject.register klass, meth registration[meth] = klass end
# File lib/familia/redisobject.rb, line 15 def RedisObject.registration @registration end
# File lib/familia/redisobject.rb, line 27 def ttl v=nil @ttl = v unless v.nil? @ttl || (parent ? parent.ttl : nil) end
# File lib/familia/redisobject.rb, line 35 def uri v=nil @uri = v unless v.nil? @uri || (parent ? parent.uri : Familia.uri) end
Public Instance Methods
# File lib/familia/redisobject.rb, line 183 def class? !@opts[:class].to_s.empty? && @opts[:class].kind_of?(Familia) end
# File lib/familia/redisobject.rb, line 117 def clear_cache @cache.clear end
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.
# File lib/familia/redisobject.rb, line 138 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
# File lib/familia/redisobject.rb, line 221 def delete redis.del rediskey end
# File lib/familia/redisobject.rb, line 252 def dump_method @opts[:dump_method] || Familia.dump_method end
# File lib/familia/redisobject.rb, line 121 def echo meth, trace redis.echo "[#{self.class}\##{meth}] #{trace} (#{@opts[:class]}\#)" end
def destroy!
clear # TODO: delete redis objects for this instance
end
# File lib/familia/redisobject.rb, line 232 def exists? redis.exists(rediskey) && !size.zero? end
# File lib/familia/redisobject.rb, line 240 def expire sec redis.expire rediskey, sec.to_i end
# File lib/familia/redisobject.rb, line 244 def expireat unixtime redis.expireat rediskey, unixtime end
# File lib/familia/redisobject.rb, line 332 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
# File lib/familia/redisobject.rb, line 256 def load_method @opts[:load_method] || Familia.load_method end
# File lib/familia/redisobject.rb, line 205 def move db redis.move rediskey, db end
# File lib/familia/redisobject.rb, line 291 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
# File lib/familia/redisobject.rb, line 187 def parent? Class === parent || Module === parent || parent.kind_of?(Familia) end
# File lib/familia/redisobject.rb, line 248 def persist redis.persist rediskey end
# File lib/familia/redisobject.rb, line 191 def qstamp quantum=nil, pattern=nil, now=Familia.now quantum ||= ttl || 10.minutes pattern ||= '%H%M' rounded = now - (now % quantum) Time.at(rounded).utc.strftime(pattern) end
# File lib/familia/redisobject.rb, line 236 def realttl redis.ttl rediskey end
# File lib/familia/redisobject.rb, line 125 def redis return @redis if @redis parent? ? parent.redis : Familia.redis(db) end
returns a redis key based on the parent object so it will include the proper index.
# File lib/familia/redisobject.rb, line 160 def rediskey if parent? # We need to check if the parent has a specific suffix # for the case where we have specified one other than :object. suffix = parent.kind_of?(Familia) && parent.class.suffix != :object ? parent.class.suffix : name k = parent.rediskey(name, nil) else k = [name].flatten.compact.join(Familia.delim) end if @opts[:quantize] args = case @opts[:quantize] when Numeric [@opts[:quantize]] # :quantize => 1.minute when Array @opts[:quantize] # :quantize => [1.day, '%m%D'] else [] # :quantize => true end k = [k, qstamp(*args)].join(Familia.delim) end k end
# File lib/familia/redisobject.rb, line 209 def rename newkey redis.rename rediskey, newkey end
# File lib/familia/redisobject.rb, line 213 def renamenx newkey redis.renamenx rediskey, newkey end
# File lib/familia/redisobject.rb, line 260 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
# File lib/familia/redisobject.rb, line 151 def ttl @ttl || (parent.ttl if parent?) || (@opts[:class].ttl if class?) || (self.class.ttl if self.class.respond_to?(:ttl)) end
# File lib/familia/redisobject.rb, line 217 def type redis.type rediskey end
# File lib/familia/redisobject.rb, line 198 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