lib/ohm.rb in ohm-0.0.2 vs lib/ohm.rb in ohm-0.0.3
- old
+ new
@@ -1,50 +1,16 @@
require "rubygems"
require "redis"
+require File.join(File.dirname(__FILE__), "ohm", "validations")
module Ohm
- module Validations
- def valid?
- errors.clear
- validate
- errors.empty?
- end
-
- def validate
- end
-
- def errors
- @errors ||= []
- end
-
- private
-
- def assert_format(att, format)
- if assert_present(att)
- assert attribute(att).match(format), [att, :format]
- end
- end
-
- def assert_present(att)
- if assert_not_nil(att)
- assert attribute(att).any?, [att, :empty]
- end
- end
-
- def assert_not_nil(att)
- assert attribute(att), [att, :nil]
- end
-
- def assert(value, error)
- value or errors.push(error) && false
- end
-
- def attribute(att)
- instance_variable_get("@#{att}")
- end
+ def key(*args)
+ args.join(":")
end
+ module_function :key
+
module Attributes
class Collection < Array
attr_accessor :key, :db
def initialize(db, key)
@@ -82,22 +48,38 @@
end
end
end
class Model
+ module Validations
+ include Ohm::Validations
+
+ def assert_unique(attrs)
+ index_key = index_key_for(attrs, read_locals(attrs))
+ assert(db.set_count(index_key).zero? || db.set_member?(index_key, id), [attrs, :not_unique])
+ end
+ end
+
include Validations
ModelIsNew = Class.new(StandardError)
@@attributes = Hash.new { |hash, key| hash[key] = [] }
@@collections = Hash.new { |hash, key| hash[key] = [] }
+ @@indices = Hash.new { |hash, key| hash[key] = [] }
attr_accessor :id
def self.attribute(name)
- attr_writer(name)
- attr_value_reader(name)
+ define_method(name) do
+ read_local(name)
+ end
+
+ define_method(:"#{name}=") do |value|
+ write_local(name, value)
+ end
+
attributes << name
end
def self.list(name)
attr_list_reader(name)
@@ -107,17 +89,12 @@
def self.set(name)
attr_set_reader(name)
collections << name
end
- # TODO Encapsulate access to db?
- def self.attr_value_reader(name)
- class_eval <<-EOS
- def #{name}
- @#{name} ||= db[key("#{name}")]
- end
- EOS
+ def self.index(attrs)
+ indices << attrs
end
def self.attr_list_reader(name)
class_eval <<-EOS
def #{name}
@@ -137,46 +114,60 @@
def self.[](id)
new(:id => id) if exists?(id)
end
def self.all
- filter(:all).map do |id|
- new(:id => id)
- end
+ filter(:all)
end
def self.attributes
@@attributes[self]
end
def self.collections
@@collections[self]
end
+ def self.indices
+ @@indices[self]
+ end
+
def self.create(*args)
new(*args).create
end
+ # TODO Add a method that receives several arguments and returns a
+ # string with the values separated by colons.
+ def self.find(attribute, value)
+ # filter("#{attribute}:#{value}")
+ filter(Ohm.key(attribute, value))
+ end
+
def initialize(attrs = {})
+ @_attributes = Hash.new {|hash,key| hash[key] = read_remote(key) }
+
attrs.each do |key, value|
send(:"#{key}=", value)
end
end
def create
return unless valid?
initialize_id
create_model_membership
+ add_to_indices
save!
end
def save
return unless valid?
+ update_indices
save!
end
def delete
+ delete_from_indices
delete_attributes(collections)
delete_attributes(attributes)
delete_model_membership
self
end
@@ -187,22 +178,41 @@
def collections
self.class.collections
end
+ def indices
+ self.class.indices
+ end
+
+ def ==(other)
+ other.key == key
+ rescue ModelIsNew
+ false
+ end
+
+ protected
+
+ def key(*args)
+ raise ModelIsNew unless id
+ self.class.key(id, *args)
+ end
+
private
def self.db
$redis
end
def self.key(*args)
- args.unshift(self).join(":")
+ Ohm.key(*args.unshift(self))
end
def self.filter(name)
- db.set_members(key(name))
+ db.set_members(key(name)).map do |id|
+ new(:id => id)
+ end
end
def self.exists?(id)
db.set_member?(key(:all), id)
end
@@ -213,15 +223,10 @@
def db
self.class.db
end
- def key(*args)
- raise ModelIsNew unless id
- self.class.key(id, *args)
- end
-
def delete_attributes(atts)
atts.each do |att|
db.delete(key(att))
end
end
@@ -233,10 +238,59 @@
def delete_model_membership
db.set_delete(self.class.key(:all), id)
end
def save!
- attributes.each { |att| db[key(att)] = send(att) }
+ attributes.each { |att| write_remote(att, send(att)) }
self
+ end
+
+ def update_indices
+ delete_from_indices
+ add_to_indices
+ end
+
+ def add_to_indices
+ indices.each do |attrs|
+ db.set_add(index_key_for(attrs, read_locals(attrs)), id)
+ end
+ end
+
+ def delete_from_indices
+ indices.each do |attrs|
+ db.set_delete(index_key_for(attrs, read_remotes(attrs)), id)
+ end
+ end
+
+ def read_local(att)
+ @_attributes[att]
+ end
+
+ def write_local(att, value)
+ @_attributes[att] = value
+ end
+
+ def read_remote(att)
+ id && db[key(att)]
+ end
+
+ def write_remote(att, value)
+ db[key(att)] = value
+ end
+
+ def read_locals(attrs)
+ attrs.map do |att|
+ read_local(att)
+ end
+ end
+
+ def read_remotes(attrs)
+ attrs.map do |att|
+ read_remote(att)
+ end
+ end
+
+ def index_key_for(attrs, values)
+ self.class.key *(attrs + values)
end
end
end