lib/ohm.rb in ohm-2.0.0.rc1 vs lib/ohm.rb in ohm-2.0.0.rc2

- old
+ new

@@ -2,11 +2,11 @@ require "msgpack" require "nido" require "redic" require "securerandom" -require "ohm/command" +require_relative "ohm/command" module Ohm LUA_CACHE = Hash.new { |h, k| h[k] = Hash.new } LUA_SAVE = File.expand_path("../ohm/lua/save.lua", __FILE__) LUA_DELETE = File.expand_path("../ohm/lua/delete.lua", __FILE__) @@ -132,16 +132,20 @@ size == 0 end # Wraps the whole pipelining functionality. def fetch(ids) - ids.each do |id| - redis.queue("HGETALL", namespace[id]) - end + data = nil - data = redis.commit + model.synchronize do + ids.each do |id| + redis.queue("HGETALL", namespace[id]) + end + data = redis.commit + end + return [] if data.nil? [].tap do |result| data.each_with_index do |atts, idx| result << model.new(Utils.dict(atts).update(:id => ids[idx])) @@ -176,19 +180,47 @@ # Returns the last element of the list using LINDEX. def last model[redis.call("LINDEX", key, -1)] end + # Returns an array of elements from the list using LRANGE. + # #range receives 2 integers, start and stop + # + # Example: + # + # class Comment < Ohm::Model + # end + # + # class Post < Ohm::Model + # list :comments, :Comment + # end + # + # c1 = Comment.create + # c2 = Comment.create + # c3 = Comment.create + # + # post = Post.create + # + # post.comments.push(c1) + # post.comments.push(c2) + # post.comments.push(c3) + # + # [c1, c2] == post.comments.range(0, 1) + # # => true + def range(start, stop) + fetch(redis.call("LRANGE", key, start, stop)) + end + # Checks if the model is part of this List. # # An important thing to note is that this method loads all of the # elements of the List since there is no command in Redis that # allows you to actually check the list contents efficiently. # # You may want to avoid doing this if your list has say, 10K entries. def include?(model) - ids.include?(model.id.to_s) + ids.include?(model.id) end # Replace all the existing elements of a list with a different # collection of models. This happens atomically in a MULTI-EXEC # block. @@ -204,17 +236,19 @@ # # user.posts.include?(p1) # # => false # def replace(models) - ids = models.map { |model| model.id } + ids = models.map(&:id) - redis.queue("MULTI") - redis.queue("DEL", key) - ids.each { |id| redis.queue("RPUSH", key, id) } - redis.queue("EXEC") - redis.commit + model.synchronize do + redis.queue("MULTI") + redis.queue("DEL", key) + ids.each { |id| redis.queue("RPUSH", key, id) } + redis.queue("EXEC") + redis.commit + end end # Pushes the model to the _end_ of the list using RPUSH. def push(model) redis.call("RPUSH", key, model.id) @@ -234,11 +268,11 @@ # # class Comment < Ohm::Model # end # # class Post < Ohm::Model - # list :comments, Comment + # list :comments, :Comment # end # # p = Post.create # c = Comment.create # @@ -254,28 +288,48 @@ # LREM key 0 <id> means remove all elements matching <id> # @see http://redis.io/commands/lrem redis.call("LREM", key, 0, model.id) end - private + # Returns an array with all the ID's of the list. + # + # class Comment < Ohm::Model + # end + # + # class Post < Ohm::Model + # list :comments, :Comment + # end + # + # post = Post.create + # post.comments.push(Comment.create) + # post.comments.push(Comment.create) + # post.comments.push(Comment.create) + # + # post.comments.map(&:id) + # # => ["1", "2", "3"] + # + # post.comments.ids + # # => ["1", "2", "3"] + # def ids redis.call("LRANGE", key, 0, -1) end + private + def redis model.redis end end # Defines most of the methods used by `Set` and `MultiSet`. class BasicSet include Collection - # Allows you to sort by any field in your model. + # Allows you to sort by any attribute in the hash, this doesn't include + # the +id+. If you want to sort by ID, use #sort. # - # Example: - # # class User < Ohm::Model # attribute :name # end # # User.all.sort_by(:name, :order => "ALPHA") @@ -363,11 +417,31 @@ else sort(opts).first end end - # Grab all the elements of this set using SMEMBERS. + # Returns an array with all the ID's of the set. + # + # class Post < Ohm::Model + # end + # + # class User < Ohm::Model + # attribute :name + # index :name + # + # set :posts, :Post + # end + # + # User.create(name: "John") + # User.create(name: "Jane") + # + # User.all.ids + # # => ["1", "2"] + # + # User.find(name: "John").union(name: "Jane").ids + # # => ["1", "2"] + # def ids execute { |key| redis.call("SMEMBERS", key) } end # Retrieve a specific element using an ID from this set. @@ -522,17 +596,19 @@ # # user.posts.include?(p1) # # => false # def replace(models) - ids = models.map { |model| model.id } + ids = models.map(&:id) - redis.queue("MULTI") - redis.queue("DEL", key) - ids.each { |id| redis.queue("SADD", key, id) } - redis.queue("EXEC") - redis.commit + model.synchronize do + redis.queue("MULTI") + redis.queue("DEL", key) + ids.each { |id| redis.queue("SADD", key, id) } + redis.queue("EXEC") + redis.commit + end end end # Anytime you filter a set with more than one requirement, you # internally use a `MultiSet`. `MutiSet` is a bit slower than just @@ -693,17 +769,26 @@ def self.redis=(redis) @redis = redis end def self.redis - @redis ||= Redic.new(Ohm.redis.url) + defined?(@redis) ? @redis : Ohm.redis end + def self.mutex + @mutex ||= Mutex.new + end + + def self.synchronize(&block) + mutex.synchronize(&block) + end + # Returns the namespace for all the keys generated using this model. # # Example: # # class User < Ohm::Model + # end # # User.key == "User" # User.key.kind_of?(String) # # => true #