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
#