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