lib/redis_object/collection.rb in redis_object-1.0 vs lib/redis_object/collection.rb in redis_object-1.1

- old
+ new

@@ -1,90 +1,55 @@ module Seabright module Collections - def dump - require "utf8_utils" - out = ["puts \"Creating: #{id}\""] - s_id = id.gsub(/\W/,"_") - out << "a#{s_id} = #{self.class.cname}.new(#{actual.to_s.tidy_bytes})" - collections.each do |col| - col.each do |sobj| - out << sobj.dump(self) - end - end - out << "a#{s_id}.save" - out.join("\n") - end - def hkey_col(ident = nil) "#{hkey}:collections" end def load(o_id) super(o_id) store.smembers(hkey_col).each do |name| collections[name] = Seabright::Collection.load(name,self) + define_access(name) do + get_collection(name) + end + define_access(name.to_s.singularize) do + get_collection(name).latest + end end - # TODO: Here's an example of dynamic method creation - should do this instead of method_missing + module intercepts... - # %w[report alert error summary].each do |kind| - # class_eval <<-END - # if "#{kind}" == "summary" - # def summaries - # data_for_server[:summaries] - # end - # else - # def #{kind}s - # data_for_server[:#{kind}s] - # end - # end - # - # if "#{kind}" == "report" - # def report(new_entry) - # reports << new_entry - # end - # elsif "#{kind}" == "summary" - # def summary(new_entry) - # summaries << new_entry - # end - # else - # def #{kind}(*fields) - # #{kind}s << ( fields.first.is_a?(Hash) ? - # fields.first : - # {:subject => fields.first, :body => fields.last} ) - # end - # end - # alias_method :add_#{kind}, :#{kind} - # END - # end true end def delete_child(obj) - if col = collections[obj.collection_name] - col.delete obj.hkey + if col = get_collection(obj.collection_name) + col.delete obj end end def collection_name - self.class.plname.underscore.to_sym + self.class.collection_name end def ref_key(ident = nil) "#{hkey}:backreferences" end def reference(obj) - name = obj.collection_name - store.sadd hkey_col, name - collections[name.to_s] ||= Seabright::Collection.load(name,self) - collections[name.to_s] << obj.hkey + raise "Not an object." unless obj.is_a?(RedisObject) + get_collection(obj.collection_name) << obj.hkey obj.referenced_by self end - alias_method :<<, :reference - alias_method :push, :reference + def <<(obj) + reference obj + end + + def push(obj) + reference obj + end + def remove_collection!(name) store.srem hkey_col, name end def referenced_by(obj) @@ -122,66 +87,135 @@ super(k) end end def has_collection?(name) - store.sismember(hkey_col,name.to_s) + collection_names.include?(name.to_s) end def get_collection(name) - collections[name.to_s] ||= Collection.load(name,self) + if has_collection?(name) + collections[name.to_s] ||= Collection.load(name,self) + else + store.sadd hkey_col, name + @collection_names << name.to_s + collections[name.to_s] ||= Collection.load(name,self) + define_access(name.to_s.pluralize) do + get_collection(name) + end + define_access(name.to_s.singularize) do + get_collection(name).latest + end + end collections[name.to_s] end def collections @collections ||= {} end + def collection_names + @collection_names ||= store.smembers(hkey_col) + end + def mset(dat) dat.select! {|k,v| !collections[k.to_s] } super(dat) end def set(k,v) - @data ? super(k,v) : collections[k.to_s] ? get_collection(k.to_s).replace(v) : super(k,v) + @data ? super(k,v) : has_collection?(k) ? get_collection(k.to_s).replace(v) : super(k,v) v end module ClassMethods - def hkey_col(ident = id) + def hkey_col(ident = nil) "#{hkey(ident)}:collections" end + def delete_child(obj) + if col = get_collection(obj.collection_name) + col.delete obj + end + end + + def collection_name + self.name.split('::').last.pluralize.underscore.to_sym + end + + def reference(obj) + name = obj.collection_name + store.sadd hkey_col, name + get_collection(name) << obj.hkey + end + + def <<(obj) + reference obj + end + + def push(obj) + reference obj + end + + def remove_collection!(name) + store.srem hkey_col, name + end + + def get(k) + if has_collection?(k) + get_collection(k) + elsif has_collection?(pk = k.to_s.pluralize) + get_collection(pk).first + else + super(k) + end + end + + def has_collection?(name) + store.sismember(hkey_col,name.to_s) + end + + def get_collection(name) + collections[name.to_s] ||= Collection.load(name,self) + collections[name.to_s] + end + + def collections + @collections ||= {} + end + end def self.included(base) base.extend(ClassMethods) end end class Collection < Array - def initialize(name,parent) + include Seabright::CachedScripts + + def initialize(name,owner) @name = name.to_s - @parent = parent + @owner = owner end def remove! - @parent.remove_collection! @name + @owner.remove_collection! @name end def latest - indexed(:created_at,5,true).first + indexed(:created_at,5,true).first || first end def indexed(idx,num=-1,reverse=false) keys = keys_by_index(idx,num,reverse) out = Enumerator.new do |y| keys.each do |member| - if a = RedisObject.find_by_key(member) + if a = class_const.find_by_key(member) y << a end end end if block_given? @@ -192,22 +226,20 @@ out end end def temp_key - "zintersect_temp" + "#{key}::zintersect_temp::#{RedisObject.new_id(4)}" end + RedisObject::ScriptSources::FwdScript = "redis.call('ZINTERSTORE', KEYS[1], 2, KEYS[2], KEYS[3], 'WEIGHTS', 1, 0)\nlocal keys = redis.call('ZRANGE', KEYS[1], 0, KEYS[4])\nredis.call('DEL', KEYS[1])\nreturn keys".freeze + RedisObject::ScriptSources::RevScript = "redis.call('ZINTERSTORE', KEYS[1], 2, KEYS[2], KEYS[3], 'WEIGHTS', 1, 0)\nlocal keys = redis.call('ZREVRANGE', KEYS[1], 0, KEYS[4])\nredis.call('DEL', KEYS[1])\nreturn keys".freeze + def keys_by_index(idx,num=-1,reverse=false) - keys = nil - store.multi do - store.zinterstore(temp_key, [index_key(idx), key], {:weights => ["1","0"]}) - keys = store.send(reverse ? :zrevrange : :zrange, temp_key, 0, num) - store.del temp_key - end + keys = run_script(reverse ? :RevScript : :FwdScript, [temp_key, index_key(idx), key, num]) Enumerator.new do |y| - keys.value.each do |member| + keys.each do |member| y << member end end end @@ -222,17 +254,20 @@ def find(k) if k.is_a? String return real_at(item_key(k)) elsif k.is_a? Hash return match(k) - elsif k.is_a? Number + elsif k.is_a? Integer return real_at(at(k)) end return nil end - alias_method :[], :find + def [](k) + find k + end + def match(pkt) Enumerator.new do |y| each do |i| if pkt.map {|hk,va| i.get(hk)==va }.all? y << i @@ -240,33 +275,29 @@ end end end def real_at(key) - RedisObject.find_by_key(key) + class_const.find_by_key(key) end - # def [](idx) - # class_const.find_by_key(at(idx)) - # end - def objects each.to_a end def first - RedisObject.find_by_key(super) + class_const.find_by_key(super) end def last - RedisObject.find_by_key(super) + class_const.find_by_key(super) end def each out = Enumerator.new do |y| each_index do |key| - if a = RedisObject.find_by_key(at(key)) + if a = class_const.find_by_key(at(key)) y << a end end end if block_given? @@ -278,11 +309,11 @@ end end def cleanup! each_index do |key| - unless a = RedisObject.find_by_key(at(key)) + unless a = class_const.find_by_key(at(key)) puts "Deleting #{key} because not #{a.inspect}" if DEBUG delete at(key) end end if size < 1 @@ -297,11 +328,11 @@ def select(&block) return nil unless block_given? Enumerator.new do |y| each_index do |key| - if (a = RedisObject.find_by_key(at(key))) && block.call(a) + if (a = class_const.find_by_key(at(key))) && block.call(a) y << a end end end end @@ -319,39 +350,38 @@ def <<(obj) k = obj.class == String ? obj : obj.hkey store.zadd(key,store.zcount(key,"-inf", "+inf"),k) super(k) end - alias_method :push, :<< + def push(obj) + self << obj + end + def class_const - Object.const_get(@name.to_s.classify.to_sym) + self.class.class_const_for(@name) end + def store + class_const.store + end + def key - "#{@parent ? "#{@parent.key}:" : ""}COLLECTION:#{@name}" + "#{@owner ? "#{@owner.key}:" : ""}COLLECTION:#{@name}" end class << self - def load(name,parent) - out = new(name,parent) - out.replace store.zrange(out.key,0,-1) + def load(name,owner) + out = new(name,owner) + out.replace class_const_for(name).store.zrange(out.key,0,-1) out end - private - - def store - @@store ||= RedisObject.store + def class_const_for(name) + Object.const_get(name.to_s.classify.to_sym) rescue RedisObject end - - end - - private - - def store - @@store ||= RedisObject.store + end end end