Skip to content

Commit

Permalink
make SimplyStored compatible with latest CouchPotato - ActiveModel Ca…
Browse files Browse the repository at this point in the history
…llbacks and Dirty are fun... NOT!
  • Loading branch information
jweiss committed Apr 3, 2011
1 parent 9c27a91 commit c9ed8ef
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 65 deletions.
6 changes: 3 additions & 3 deletions Gemfile
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ gem "uuidtools"
gem "mattmatt-validatable", '1.8.4' gem "mattmatt-validatable", '1.8.4'
gem 'rest-client', '1.6.1', :require => 'restclient' gem 'rest-client', '1.6.1', :require => 'restclient'
gem 'couchrest', '1.0.1' gem 'couchrest', '1.0.1'
gem 'couch_potato', '0.2.31.2' gem 'couch_potato', '0.4.0'
gem 'shoulda' gem 'shoulda'
gem 'shoulda-addons' gem 'shoulda-addons'
gem 'mocha' gem 'mocha'
gem 'activesupport', '2.3.11', :require => 'active_support' gem 'activesupport', '3.0.0', :require => 'active_support'
gem 'right_aws' gem 'right_aws'
22 changes: 12 additions & 10 deletions lib/simply_stored.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,12 +1,14 @@
$:<<(File.expand_path(File.dirname(__FILE__) + "/lib")) unless defined?(SimplyStored)
require File.expand_path(File.dirname(__FILE__) + '/simply_stored/instance_methods') $:<<(File.expand_path(File.dirname(__FILE__) + "/lib"))
require File.expand_path(File.dirname(__FILE__) + '/simply_stored/storage') require File.expand_path(File.dirname(__FILE__) + '/simply_stored/instance_methods')
require File.expand_path(File.dirname(__FILE__) + '/simply_stored/class_methods_base') require File.expand_path(File.dirname(__FILE__) + '/simply_stored/storage')
require File.expand_path(File.dirname(__FILE__) + '/simply_stored/class_methods_base')


module SimplyStored module SimplyStored
VERSION = '0.5.3' VERSION = '0.5.3'
class Error < RuntimeError; end class Error < RuntimeError; end
class RecordNotFound < RuntimeError; end class RecordNotFound < RuntimeError; end
end end


require File.expand_path(File.dirname(__FILE__) + '/simply_stored/couch') require File.expand_path(File.dirname(__FILE__) + '/simply_stored/couch')
end
32 changes: 2 additions & 30 deletions lib/simply_stored/couch.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
require 'simply_stored/couch/ext/couch_potato' require 'simply_stored/couch/ext/couch_potato'
require 'simply_stored/couch/views' require 'simply_stored/couch/views'


CouchPotato::Config.validation_framework = :validatable

module SimplyStored module SimplyStored
module Couch module Couch
def self.included(clazz) def self.included(clazz)
Expand All @@ -30,8 +32,6 @@ def self.included(clazz)


clazz.instance_eval do clazz.instance_eval do
attr_accessor :_accessible_attributes, :_protected_attributes attr_accessor :_accessible_attributes, :_protected_attributes
alias :simpledb_array :simpledb_string
alias :simpledb_integer :simpledb_string


view :all_documents, :key => :created_at view :all_documents, :key => :created_at
end end
Expand Down Expand Up @@ -84,34 +84,6 @@ def auto_conflict_resolution_on_save=(val)
@auto_conflict_resolution_on_save = val @auto_conflict_resolution_on_save = val
end end


def simpledb_string(*names)
names.each do |name|
property name
end
end

def simpledb_timestamp(*names)
names.each do |name|
property name, :type => Time
end
end

def require_attributes(*names)
names.each do |name|
validates_presence_of name
end
end

def require_inclusion_of(name, valid_set, options = {})
options.update(:in => valid_set)
validates_inclusion_of(name, options)
end

def require_format_of(attr, valid_regex, options = {})
options.update(:with => valid_regex)
validates_format_of(attr, options)
end

def method_missing(name, *args) def method_missing(name, *args)
if name.to_s =~ /^find_by/ if name.to_s =~ /^find_by/
_define_find_by(name, *args) _define_find_by(name, *args)
Expand Down
15 changes: 7 additions & 8 deletions lib/simply_stored/couch/belongs_to.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ def initialize(owner_clazz, name, options = {})
@options.assert_valid_keys(:class_name) @options.assert_valid_keys(:class_name)


owner_clazz.class_eval do owner_clazz.class_eval do
attr_reader "#{name}_id"
attr_accessor "#{name}_id_was" attr_accessor "#{name}_id_was"
property "#{name}_id" property "#{name}_id"


Expand All @@ -74,17 +73,17 @@ def initialize(owner_clazz, name, options = {})
klass = self.class.get_class_from_name(self.class._find_property(name).options[:class_name]) klass = self.class.get_class_from_name(self.class._find_property(name).options[:class_name])
raise ArgumentError, "expected #{klass} got #{value.class}" unless value.nil? || value.is_a?(klass) raise ArgumentError, "expected #{klass} got #{value.class}" unless value.nil? || value.is_a?(klass)


new_foreign_id = value.nil? ? nil : value.id

send("#{name}_id_will_change!") if send("#{name}_id_was") != new_foreign_id
send("#{name}_id=", new_foreign_id)
instance_variable_set("@#{name}", value) instance_variable_set("@#{name}", value)
if value.nil?
instance_variable_set("@#{name}_id", nil)
else
instance_variable_set("@#{name}_id", value.id)
end
end end


define_method "#{name}_id=" do |id| define_method "#{name}_id=" do |new_foreign_id|
remove_instance_variable("@#{name}") if instance_variable_defined?("@#{name}") remove_instance_variable("@#{name}") if instance_variable_defined?("@#{name}")
instance_variable_set("@#{name}_id", id) send("#{name}_id_will_change!") if send("#{name}_id_was") != new_foreign_id
instance_variable_set("@#{name}_id", new_foreign_id)
end end


define_method "#{name}_changed?" do define_method "#{name}_changed?" do
Expand Down
26 changes: 26 additions & 0 deletions lib/simply_stored/couch/ext/couch_potato.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ module DirtyAttributes
private private


def reset_dirty_attributes def reset_dirty_attributes
# active mode reset
@previously_changed = changes
@changed_attributes.clear
@forced_dirty = nil

# belongs_to properties reset
self.class.properties.each do |property| self.class.properties.each do |property|
if !property.respond_to?(:supports_dirty?) || property.supports_dirty? if !property.respond_to?(:supports_dirty?) || property.supports_dirty?
property.respond_to?(:reset_dirty_attribute) ? property.reset_dirty_attribute(self) : property.respond_to?(:reset_dirty_attribute) ? property.reset_dirty_attribute(self) :
Expand All @@ -13,4 +19,24 @@ def reset_dirty_attributes
end end
end end
end end
end

module CouchPotato
class Database

# the original implementation does not delete if callbacks are skipped
def destroy_document(document, run_callbacks = true)
if run_callbacks
document.run_callbacks :destroy do
document._deleted = true
database.delete_doc document.to_hash
end
else
document._deleted = true
database.delete_doc document.to_hash
end
document._id = nil
document._rev = nil
end
end
end end
2 changes: 1 addition & 1 deletion lib/simply_stored/couch/has_many.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def define_has_many_setter_remove(name, options)
elsif options[:dependent] == :ignore elsif options[:dependent] == :ignore
# skip # skip
else # nullify else # nullify
value.send("#{self.class.foreign_key}=", nil) value.send("#{self.class.foreign_key}=", nil)
value.save(false) value.save(false)
end end


Expand Down
22 changes: 14 additions & 8 deletions lib/simply_stored/instance_methods.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -29,12 +29,18 @@ def save!
def destroy(override_soft_delete=false) def destroy(override_soft_delete=false)
check_and_destroy_dependents check_and_destroy_dependents
if self.class.soft_deleting_enabled? && !override_soft_delete if self.class.soft_deleting_enabled? && !override_soft_delete
# soft-delete
_mark_as_deleted _mark_as_deleted
else else
self.skip_callbacks = true if self.class.soft_deleting_enabled? && deleted? if self.class.soft_deleting_enabled? && deleted?
CouchPotato.database.destroy_document(self) # really deleting a previously soft-deleted object - skipping callbacks
CouchPotato.database.destroy_document(self, false)
else # deleting a normal object or a soft-deletable object that was not soft-deleted before
CouchPotato.database.destroy_document(self, true)
end
freeze freeze
end end
self
end end
alias :delete :destroy alias :delete :destroy


Expand Down Expand Up @@ -65,8 +71,8 @@ def deleted?
false false
end end
end end

protected protected


def retry_on_conflict(max_retries = 2, &blk) def retry_on_conflict(max_retries = 2, &blk)
retry_count = 0 retry_count = 0
Expand Down Expand Up @@ -255,10 +261,10 @@ def count_associated_via_join_view(from, to, options = {})
end end


def _mark_as_deleted def _mark_as_deleted
run_callbacks(:before_destroy) _run_destroy_callbacks do
send("#{self.class.soft_delete_attribute}=", Time.now) send("#{self.class.soft_delete_attribute}=", Time.now)
save(false) save(false)
run_callbacks(:after_destroy) end
end end


end end
Expand Down
13 changes: 12 additions & 1 deletion test/belongs_to_test.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -30,7 +30,18 @@ class ::DoubleBelongsToUser
post = Post.find(post.id) post = Post.find(post.id)
assert_equal user.id, post.user_id assert_equal user.id, post.user_id
end end

should "create a property for the foreign key attribute" do
assert Post.properties.any?{|p| p.is_a?(CouchPotato::Persistence::SimpleProperty) && p.name == 'user_id'}
end


should "notice a change to the foreign key attribute in dirty checks" do
user = User.create!(:title => 'Prof')
post = Post.create!
post.user = user
assert post.user_id_changed?
end

should "set also the foreign key id to nil if setting the referencing object to nil" do should "set also the foreign key id to nil if setting the referencing object to nil" do
user = User.create(:title => "Mr.") user = User.create(:title => "Mr.")
post = Post.create(:user => user) post = Post.create(:user => user)
Expand Down Expand Up @@ -63,7 +74,7 @@ class ::DoubleBelongsToUser
post.save post.save


post = Post.find(post.id) post = Post.find(post.id)
assert_equal user2, post.user assert_equal user2.id, post.user.id
end end


should "check the class and raise an error if not matching in belongs_to setter" do should "check the class and raise an error if not matching in belongs_to setter" do
Expand Down
2 changes: 1 addition & 1 deletion test/instance_lifecycle_test.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class InstanceLifecycleTest < Test::Unit::TestCase
user2 = User.find(user.id) user2 = User.find(user.id)
user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress") user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress")
user.reload user.reload
assert !user.dirty? assert !user.dirty?, user.changes.inspect
end end


should "ensure that association caches for has_many are cleared" do should "ensure that association caches for has_many are cleared" do
Expand Down
12 changes: 9 additions & 3 deletions test/soft_deletable_test.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -65,9 +65,15 @@ class SoftDeletableTest < Test::Unit::TestCase
CouchPotato.database.expects(:destroy_document).never CouchPotato.database.expects(:destroy_document).never
@hemorrhoid.destroy @hemorrhoid.destroy
end end

should "really delete with callbacks" do
CouchPotato.database.expects(:destroy_document).with(@hemorrhoid, true)
@hemorrhoid.destroy!
end


should "really delete if asked to" do should "really delete without callbacks if the object was soft-deleted before" do
CouchPotato.database.expects(:destroy_document).with(@hemorrhoid) CouchPotato.database.expects(:destroy_document).with(@hemorrhoid, false)
@hemorrhoid.destroy
@hemorrhoid.destroy! @hemorrhoid.destroy!
end end


Expand Down Expand Up @@ -110,7 +116,7 @@ def @hemorrhoid.deleted?
end end
end end


should "not fire the callbacks on the real destroy if the object is not deleted" do should "fire the callbacks on the real destroy if the object is not deleted" do
@hemorrhoid = Hemorrhoid.create @hemorrhoid = Hemorrhoid.create
$before = nil $before = nil
$after = nil $after = nil
Expand Down

0 comments on commit c9ed8ef

Please sign in to comment.