Navigation Menu

Skip to content

Commit

Permalink
destroying and restoring records no longer triggers saving/updating c…
Browse files Browse the repository at this point in the history
…allbacks
  • Loading branch information
Jeffrey Chupp committed Apr 22, 2009
1 parent 507bbde commit 1c913e2
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
@@ -1,5 +1,8 @@
This will only document major changes. Please see the commit log for minor changes.

-2009-04-22
* destroying and restoring records no longer triggers saving/updating callbacks

-2009-03-28
* removing syntax for calculation require (all find and ActiveRecord calculations are done on-the-fly now via method_missing)
* adding ability to specify alternate fields and values for destroyed objects
Expand Down
25 changes: 20 additions & 5 deletions lib/is_paranoid.rb
Expand Up @@ -21,7 +21,8 @@ module ClassAndInstanceMethods
def self.included(base)
base.class_eval do
# This is the real magic. All calls made to this model will append
# the conditions deleted_at => nil. Exceptions require using
# the conditions deleted_at => nil (or whatever your destroyed_field
# and field_not_destroyed are). All exceptions require using
# exclusive_scope (see self.delete_all, self.count_with_destroyed,
# and self.find_with_destroyed )
default_scope :conditions => {destroyed_field => field_not_destroyed}
Expand All @@ -36,7 +37,10 @@ def self.delete_all conditions = nil

# Mark the model deleted_at as now.
def destroy_without_callbacks
self.update_attribute(destroyed_field, ( field_destroyed.respond_to?(:call) ? field_destroyed.call : field_destroyed))
self.class.update_all(
"#{destroyed_field} = #{self.class.connection.quote(( field_destroyed.respond_to?(:call) ? field_destroyed.call : field_destroyed))}",
"id = #{self.id}"
)
end

# Override the default destroy to allow us to flag deleted_at.
Expand All @@ -51,10 +55,21 @@ def destroy
result
end

# Set deleted_at flag on a model to nil, effectively undoing the
# soft-deletion.
# Use update_all with an exclusive scope to restore undo the soft-delete.
# This bypasses update-related callbacks
def self.restore(id)
with_exclusive_scope do
update_all(
"#{destroyed_field} = #{connection.quote(field_not_destroyed)}",
"id = #{id}"
)
end
end

# Set deleted_at flag on a model to field_not_destroyed, effectively
# undoing the soft-deletion.
def restore
self.update_attribute(destroyed_field, field_not_destroyed)
self.class.restore(id)
end

# find_with_destroyed and other blah_with_destroyed and
Expand Down
10 changes: 8 additions & 2 deletions spec/is_paranoid_spec.rb
Expand Up @@ -7,7 +7,13 @@ class Person < ActiveRecord::Base

class Android < ActiveRecord::Base
validates_uniqueness_of :name

is_paranoid

before_update :raise_hell
def raise_hell
raise "hell"
end
end

class AndroidWithScopedUniqueness < ActiveRecord::Base
Expand Down Expand Up @@ -57,7 +63,7 @@ class DeadPirate < ActiveRecord::Base
Android.count_with_destroyed.should == 2
end

it "should handle Model.destroy(id) properly" do
it "should handle Model.destroy(id) properly without hitting update/save related callbacks" do
lambda{
Android.destroy(@r2d2.id)
}.should change(Android, :count).from(2).to(1)
Expand Down Expand Up @@ -99,7 +105,7 @@ class DeadPirate < ActiveRecord::Base
Android.count_with_destroyed.should == 2
end

it "should allow restoring" do
it "should allow restoring without hitting update/save related callbacks" do
@r2d2.destroy
lambda{
@r2d2.restore
Expand Down

0 comments on commit 1c913e2

Please sign in to comment.