lib/ohm.rb in ohm-0.0.34 vs lib/ohm.rb in ohm-0.0.35
- old
+ new
@@ -1,16 +1,17 @@
# encoding: UTF-8
require "base64"
-require File.join(File.dirname(__FILE__), "ohm", "redis")
+require "redis"
+
require File.join(File.dirname(__FILE__), "ohm", "validations")
require File.join(File.dirname(__FILE__), "ohm", "compat-1.8.6")
require File.join(File.dirname(__FILE__), "ohm", "key")
require File.join(File.dirname(__FILE__), "ohm", "collection")
module Ohm
- VERSION = "0.0.34"
+ VERSION = "0.0.35"
# Provides access to the Redis database. This is shared accross all models and instances.
def redis
threaded[:redis] ||= connection(*options)
end
@@ -37,13 +38,13 @@
@options = options
end
# Return a connection to Redis.
#
- # This is a wapper around Ohm::Redis.new(options)
+ # This is a wapper around Redis.new(options)
def connection(*options)
- Ohm::Redis.new(*options)
+ Redis.new(*options)
end
def options
@options || []
end
@@ -51,29 +52,60 @@
# Clear the database.
def flush
redis.flushdb
end
- # Join the parameters with ":" to create a key.
def key(*args)
Key[*args]
end
module_function :key, :connect, :connection, :flush, :redis, :redis=, :options, :threaded
Error = Class.new(StandardError)
class Model
+
+ # Wraps a model name for lazy evaluation.
+ class Wrapper < BasicObject
+ def initialize(name, &block)
+ @name = name
+ @caller = ::Kernel.caller[2]
+ @block = block
+
+ class << self
+ def method_missing(method_id, *args)
+ ::Kernel.raise ::NoMethodError, "You tried to call #{@name}##{method_id}, but #{@name} is not defined on #{@caller}"
+ end
+ end
+ end
+
+ def self.wrap(object)
+ object.class == self ? object : new(object.inspect) { object }
+ end
+
+ def unwrap
+ @block.call
+ end
+
+ def class
+ Wrapper
+ end
+
+ def inspect
+ "<Wrapper for #{@name} (in #{@caller})>"
+ end
+ end
+
class Collection
include Enumerable
attr :raw
attr :model
- def initialize(key, model, db = model.db)
- @raw = self.class::Raw.new(key, db)
- @model = model
+ def initialize(key, model, db = nil)
+ @model = model.unwrap
+ @raw = self.class::Raw.new(key, db || @model.db)
end
def <<(model)
raw << model.id
end
@@ -195,11 +227,11 @@
# Apply a redis operation on a collection of sets.
def apply(operation, hash, glue)
target = key.volatile.group(glue).append(*keys(hash))
model.db.send(operation, target, *target.sub_keys)
- Set.new(target, model)
+ Set.new(target, Wrapper.wrap(model))
end
# Transform a hash of attribute/values into an array of keys.
def keys(hash)
[].tap do |keys|
@@ -238,11 +270,11 @@
end
class Index < Set
def apply(operation, hash, glue)
if hash.keys.size == 1
- return Set.new(keys(hash).first, model)
+ return Set.new(keys(hash).first, Wrapper.wrap(model))
else
super
end
end
end
@@ -388,18 +420,20 @@
# @comment.post
# # => nil
#
# @see Ohm::Model::collection
def self.reference(name, model)
+ model = Wrapper.wrap(model)
+
reader = :"#{name}_id"
writer = :"#{name}_id="
attribute reader
index reader
define_memoized_method(name) do
- model[send(reader)]
+ model.unwrap[send(reader)]
end
define_method(:"#{name}=") do |value|
instance_variable_set("@#{name}", nil)
send(writer, value ? value.id : nil)
@@ -449,19 +483,21 @@
# @see Ohm::Model::reference
# @param name [Symbol] Name of the collection.
# @param model [Constant] Model where the reference is defined.
# @param reference [Symbol] Reference as defined in the associated model.
def self.collection(name, model, reference = to_reference)
- define_method(name) { model.find(:"#{reference}_id" => send(:id)) }
+ model = Wrapper.wrap(model)
+ define_method(name) { model.unwrap.find(:"#{reference}_id" => send(:id)) }
end
def self.to_reference
name.to_s.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.to_sym
end
def self.attr_collection_reader(name, type, model)
if model
+ model = Wrapper.wrap(model)
define_memoized_method(name) { Ohm::Model::const_get(type).new(key(name), model, db) }
else
define_memoized_method(name) { Ohm::const_get(type).new(key(name), db) }
end
end
@@ -480,11 +516,11 @@
def self.to_proc
Proc.new { |id| self[id] }
end
def self.all
- @all ||= Ohm::Model::Index.new(key(:all), self)
+ @all ||= Ohm::Model::Index.new(key(:all), Wrapper.wrap(self))
end
def self.attributes
@@attributes[self]
end
@@ -657,39 +693,24 @@
def key(*args)
self.class.key(id, *args)
end
- # Use MSET if possible, SET otherwise.
- def write
- db.support_mset? ?
- write_with_mset :
- write_with_set
- end
-
- # Write attributes using SET
- # This method will be removed once MSET becomes standard.
- def write_with_set
- attributes.each do |att|
- value = send(att)
- value.to_s.empty? ?
- db.set(key(att), value) :
- db.del(key(att))
- end
- end
-
# Write attributes using MSET
- # This is the preferred method, and will be the only option
- # available once MSET becomes standard.
- def write_with_mset
+ def write
unless attributes.empty?
rems, adds = attributes.map { |a| [key(a), send(a)] }.partition { |t| t.last.to_s.empty? }
+
db.del(*rems.flatten.compact) unless rems.empty?
- db.mset(adds.flatten) unless adds.empty?
+ db.mapped_mset(adds.flatten) unless adds.empty?
end
end
+ def self.const_missing(name)
+ Wrapper.new(name) { const_get(name) }
+ end
+
private
# Provides access to the Redis database. This is shared accross all models and instances.
def self.db
Ohm.threaded[self] || Ohm.redis
@@ -757,10 +778,11 @@
end
def delete_from_indices
db.smembers(key(:_indices)).each do |index|
db.srem(index, id)
+ db.srem(key(:_indices), index)
end
end
def read_local(att)
@_attributes[att]
@@ -769,10 +791,15 @@
def write_local(att, value)
@_attributes[att] = value
end
def read_remote(att)
- db.get(key(att)) unless new?
+ unless new?
+ value = db.get(key(att))
+ value.respond_to?(:force_encoding) ?
+ value.force_encoding("UTF-8") :
+ value
+ end
end
def read_locals(attrs)
attrs.map do |att|
send(att)