Permalink
Browse files

stop using `with_exclusive_scope`, ensuring other scopes stay in effect

  • Loading branch information...
1 parent daeb386 commit 5ea623ff58b01b15eecacdd6c11de9f42c310221 @chewi chewi committed with Apr 21, 2010
Showing with 54 additions and 16 deletions.
  1. +0 −1 README.markdown
  2. +41 −15 lib/is_paranoid.rb
  3. +13 −0 spec/is_paranoid_spec.rb
View
@@ -62,4 +62,3 @@ Pitfalls
* `validates_uniqueness_of` does not ignore items marked with a "deleted_at" flag
* various eager-loading and associations-related issues (see ["Killing is_paranoid"](http://blog.semanticart.com/killing_is_paranoid/))
-* `find/count_with_destroyed` uses `with_exclusive_scope` and thus doesn't play nicely with named scopes
View
@@ -15,35 +15,32 @@ module SafetyNet
# end
def is_paranoid
class_eval do
- # This is the real magic. All calls made to this model will append
- # the conditions deleted_at => nil. Exceptions require using
- # exclusive_scope (see self.delete_all, self.count_with_destroyed,
- # and self.find_with_destroyed )
+ # This is the real magic. All calls made to this model will
+ # append the conditions deleted_at => nil. Exceptions require
+ # using with_destroyed_scope (see self.delete_all,
+ # self.count_with_destroyed, and self.find_with_destroyed )
default_scope :conditions => {:deleted_at => nil}
# Actually delete the model, bypassing the safety net. Because
# this method is called internally by Model.delete(id) and on the
# delete method in each instance, we don't need to specify those
# methods separately
- ## FIXME: this is dangerous. find a better solution
- # def self.delete_all conditions = nil
- # self.with_exclusive_scope do
- # super conditions
- # end
- # end
+ def self.delete_all conditions = nil
+ self.with_destroyed_scope { super conditions }
+ end
# Return a count that includes the soft-deleted models.
def self.count_with_destroyed *args
- self.with_exclusive_scope { count(*args) }
+ self.with_destroyed_scope { count(*args) }
end
# Return instances of all models matching the query regardless
# of whether or not they have been soft-deleted.
def self.find_with_destroyed *args
- self.with_exclusive_scope { find(*args) }
+ self.with_destroyed_scope { find(*args) }
end
- # Perofm a find only on destroyed instances
+ # Perform a find only on destroyed instances.
def self.find_only_destroyed *args
self.with_only_destroyed_scope { find(*args) }
end
@@ -77,8 +74,37 @@ def destroy_without_callbacks
end
def self.with_only_destroyed_scope(&block)
- with_exclusive_scope do
- with_scope({:find => { :conditions => ["deleted_at IS NOT NULL"] }}, &block)
+ with_destroyed_scope do
+ table = connection.quote_table_name(table_name)
+ attr = connection.quote_column_name(:deleted_at)
+ with_scope(:find => { :conditions => "#{table}.#{attr} IS NOT NULL" }, &block)
+ end
+ end
+
+ def self.with_destroyed_scope
+ find = current_scoped_methods[:find]
+
+ if find[:conditions]
+ original = find[:conditions].dup
+
+ begin
+ case find[:conditions]
+ when Hash:
+ if find[:conditions][:deleted_at].nil?
+ find[:conditions].delete(:deleted_at)
+ end
+ when String:
+ conditions = sanitize_conditions(:deleted_at => nil)
+ find[:conditions].gsub!(conditions, '1=1')
+ end
+
+ result = yield
+ ensure
+ find[:conditions] = original
+ return result if result
+ end
+ else
+ yield
end
end
end
View
@@ -7,6 +7,9 @@ class Person < ActiveRecord::Base
class Android < ActiveRecord::Base
validates_uniqueness_of :name
is_paranoid
+ named_scope :ordered, :order => 'name DESC'
+ named_scope :r2d2, :conditions => { :name => 'R2D2' }
+ named_scope :c3p0, :conditions => { :name => 'C3P0' }
end
describe Android do
@@ -88,4 +91,14 @@ class Android < ActiveRecord::Base
@r2d2.destroy
Android.find_only_destroyed(:all).should == [@r2d2]
end
+
+ it "should honor named scopes" do
+ @r2d2.destroy
+ @c3p0.destroy
+ Android.r2d2.find_only_destroyed(:all).should == [@r2d2]
+ Android.c3p0.ordered.find_only_destroyed(:all).should == [@c3p0]
+ Android.ordered.find_only_destroyed(:all).should == [@r2d2,@c3p0]
+ Android.r2d2.c3p0.find_only_destroyed(:all).should == []
+ Android.find_only_destroyed(:all).should == [@r2d2,@c3p0]
+ end
end

0 comments on commit 5ea623f

Please sign in to comment.