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