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