lib/ohm.rb in ohm-1.0.2 vs lib/ohm.rb in ohm-1.1.0.rc1
- old
+ new
@@ -3,10 +3,11 @@
require "nest"
require "redis"
require "securerandom"
require "scrivener"
require "ohm/transaction"
+require "ohm/command"
module Ohm
# All of the known errors in Ohm can be traced back to one of these
# exceptions.
@@ -64,10 +65,20 @@
case name
when Symbol then context.const_get(name)
else name
end
end
+
+ if Redis::VERSION >= "3.0.0"
+ def self.dict(dict)
+ dict
+ end
+ else
+ def self.dict(arr)
+ Hash[*arr]
+ end
+ end
end
class Connection
attr_accessor :context
attr_accessor :options
@@ -140,11 +151,11 @@
res = []
return res if arr.nil?
arr.each_with_index do |atts, idx|
- res << model.new(Hash[*atts].update(:id => ids[idx]))
+ res << model.new(Utils.dict(atts).update(:id => ids[idx]))
end
res
end
end
@@ -415,13 +426,13 @@
#
# set = User.find(:name => "John")
# set.find(:age => 30)
#
def find(dict)
- filters = model.filters(dict).push(key)
-
- MultiSet.new(namespace, model).append(:sinterstore, filters)
+ MultiSet.new(
+ namespace, model, Command[:sinterstore, key, *model.filters(dict)]
+ )
end
# Reduce the set using any number of filters.
#
# Example:
@@ -431,11 +442,11 @@
#
# # You can also do it in one line.
# User.find(:name => "John").except(:country => "US")
#
def except(dict)
- MultiSet.new(namespace, model).append(:sinterstore, key).except(dict)
+ MultiSet.new(namespace, model, key).except(dict)
end
# Do a union to the existing set using any number of filters.
#
# Example:
@@ -445,11 +456,11 @@
#
# # You can also do it in one line.
# User.find(:name => "John").union(:name => "Jane")
#
def union(dict)
- MultiSet.new(namespace, model).append(:sinterstore, key).union(dict)
+ MultiSet.new(namespace, model, key).union(dict)
end
private
def execute
yield key
@@ -527,30 +538,24 @@
# # => true
#
# User.find(:name => "John", :age => 30).kind_of?(Ohm::MultiSet)
# # => true
#
- class MultiSet < Struct.new(:namespace, :model)
+ class MultiSet < Struct.new(:namespace, :model, :command)
include Collection
- def append(operation, list)
- filters.push([operation, list])
-
- return self
- end
-
# Chain new fiters on an existing set.
#
# Example:
#
# set = User.find(:name => "John", :age => 30)
# set.find(:status => 'pending')
#
def find(dict)
- filters.push([:sinterstore, model.filters(dict)])
-
- return self
+ MultiSet.new(
+ namespace, model, Command[:sinterstore, command, intersected(dict)]
+ )
end
# Reduce the set using any number of filters.
#
# Example:
@@ -560,13 +565,13 @@
#
# # You can also do it in one line.
# User.find(:name => "John").except(:country => "US")
#
def except(dict)
- filters.push([:sdiffstore, model.filters(dict)])
-
- return self
+ MultiSet.new(
+ namespace, model, Command[:sdiffstore, command, intersected(dict)]
+ )
end
# Do a union to the existing set using any number of filters.
#
# Example:
@@ -576,84 +581,41 @@
#
# # You can also do it in one line.
# User.find(:name => "John").union(:name => "Jane")
#
def union(dict)
- filters.push([:sunionstore, model.filters(dict)])
-
- return self
+ MultiSet.new(
+ namespace, model, Command[:sunionstore, command, intersected(dict)]
+ )
end
private
def db
model.db
end
- def filters
- @filters ||= []
+ def intersected(dict)
+ Command[:sinterstore, *model.filters(dict)]
end
- def temp_keys
- @temp_keys ||= []
- end
-
- def clean_temp_keys
- db.del(*temp_keys)
- temp_keys.clear
- end
-
- def generate_temp_key
- key = namespace[:temp][SecureRandom.hex(32)]
- temp_keys << key
- key
- end
-
def execute
+ # namespace[:tmp] is where all the temp keys should be stored in.
+ # db will be where all the commands are executed against.
+ res = command.call(namespace[:tmp], db)
- # Hold the final result key for this MultiSet.
- main = nil
-
- filters.each do |operation, list|
-
- # Operation can be sinterstore, sdiffstore, or sunionstore.
- # each operation we do, i.e. `.union(...)`, will be considered
- # one intersected set, hence we need to `sinterstore` all
- # the filters in a temporary set.
- temp = generate_temp_key
- db.sinterstore(temp, *list)
-
- # If this is the first set, we simply assign the generated
- # set to main, which could possibly be the return value
- # for simple filters like one `.find(...)`.
- if main.nil?
- main = temp
- else
-
- # Append the generated temporary set using the operation.
- # i.e. if we have (mood=happy & book=1) and we have an
- # `sunionstore`, we do (mood=happy & book=1) | (mood=sad & book=1)
- #
- # Here we dynamically call the stored command, e.g.
- #
- # SUNIONSTORE main main temp
- #
- db.send(operation, main, main, temp)
- end
- end
-
begin
# At this point, we have the final aggregated set, which we yield
# to the caller. the caller can do all the normal set operations,
# i.e. SCARD, SMEMBERS, etc.
- yield main
+ yield res
ensure
# We have to make sure we clean up the temporary keys to avoid
# memory leaks and the unintended explosion of memory usage.
- clean_temp_keys
+ command.clean
end
end
end
# The base class for all your models. In order to better understand
@@ -766,11 +728,11 @@
# ids.map(&User)
#
# Note: The use of this should be a last resort for your actual
# application runtime, or for simply debugging in your console. If
# you care about performance, you should pipeline your reads. For
- # more information checkout the implementation of Ohm::Set#fetch.
+ # more information checkout the implementation of Ohm::List#fetch.
#
def self.to_proc
lambda { |id| self[id] }
end
@@ -838,11 +800,11 @@
keys = filters(dict)
if keys.size == 1
Ohm::Set.new(keys.first, key, self)
else
- Ohm::MultiSet.new(key, self).append(:sinterstore, keys)
+ Ohm::MultiSet.new(key, self, Command.new(:sinterstore, *keys))
end
end
# Index any method on your model. Once you index a method, you can
# use it in `find` statements.
@@ -1518,48 +1480,8 @@
model.toindices(att, val).each do |index|
db.sadd(index, id)
db.sadd(key[:_indices], index)
end
end
- end
- end
-
- class Lua
- attr :dir
- attr :redis
- attr :files
- attr :scripts
-
- def initialize(dir, redis)
- @dir = dir
- @redis = redis
- @files = Hash.new { |h, cmd| h[cmd] = read(cmd) }
- @scripts = {}
- end
-
- def run_file(file, options)
- run(files[file], options)
- end
-
- def run(script, options)
- keys = options[:keys]
- argv = options[:argv]
-
- params = keys + argv
-
- begin
- redis.evalsha(sha(script), keys.size, *params)
- rescue RuntimeError
- redis.eval(script, keys.size, *params)
- end
- end
-
- private
- def read(file)
- File.read("%s/%s.lua" % [dir, file])
- end
-
- def sha(script)
- Digest::SHA1.hexdigest(script)
end
end
end