From b41975082dd9ed2f1e23d053c4abde25f0cda2d8 Mon Sep 17 00:00:00 2001 From: Diego Algorta Date: Sat, 30 Aug 2008 00:28:00 -0300 Subject: [PATCH] Add only_deleted scope and associated methods. This patch makes it easy to get only the deleted records from a model class. This facilitates the creation of deleted records listings for "undelete"-like functionalities in your apps. Added methods: find_only_deleted, count_only_deleted and exists_only_deleted? Passing tests included (tested with activerecord 2.1). --- lib/caboose/acts/paranoid.rb | 35 ++++++++++++++++++++++++++++++++++- test/paranoid_test.rb | 13 +++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/caboose/acts/paranoid.rb b/lib/caboose/acts/paranoid.rb index eb191466..aa1606b4 100644 --- a/lib/caboose/acts/paranoid.rb +++ b/lib/caboose/acts/paranoid.rb @@ -19,6 +19,9 @@ module Acts #:nodoc: # Widget.find(:all, :with_deleted => true) # # SELECT * FROM widgets # + # Widget.find_only_deleted(:all) + # # SELECT * FROM widgets WHERE widgets.deleted_at IS NOT NULL + # # Widget.find_with_deleted(1).deleted? # # Returns true if the record was previously destroyed, false if not # @@ -31,6 +34,9 @@ module Acts #:nodoc: # Widget.count_with_deleted # # SELECT COUNT(*) FROM widgets # + # Widget.count_only_deleted + # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NOT NULL + # # Widget.delete_all # # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36' # @@ -87,14 +93,35 @@ def find_with_deleted(*args) end end + def find_only_deleted(*args) + options = args.extract_options! + validate_find_options(options) + set_readonly_option!(options) + options[:only_deleted] = true # yuck! + + case args.first + when :first then find_initial(options) + when :all then find_every(options) + else find_from_ids(args, options) + end + end + def exists?(*args) with_deleted_scope { exists_with_deleted?(*args) } end + def exists_only_deleted?(*args) + with_only_deleted_scope { exists_with_deleted?(*args) } + end + def count_with_deleted(*args) calculate_with_deleted(:count, *construct_count_options_from_args(*args)) end + def count_only_deleted(*args) + with_only_deleted_scope { count_with_deleted(*args) } + end + def count(*args) with_deleted_scope { count_with_deleted(*args) } end @@ -116,12 +143,18 @@ def with_deleted_scope(&block) with_scope({:find => { :conditions => ["#{table_name}.#{deleted_attribute} IS NULL OR #{table_name}.#{deleted_attribute} > ?", current_time] } }, :merge, &block) end + def with_only_deleted_scope(&block) + with_scope({:find => { :conditions => ["#{table_name}.#{deleted_attribute} IS NOT NULL AND #{table_name}.#{deleted_attribute} <= ?", current_time] } }, :merge, &block) + end + private # all find calls lead here def find_every(options) options.delete(:with_deleted) ? find_every_with_deleted(options) : - with_deleted_scope { find_every_with_deleted(options) } + options.delete(:only_deleted) ? + with_only_deleted_scope { find_every_with_deleted(options) } : + with_deleted_scope { find_every_with_deleted(options) } end end diff --git a/test/paranoid_test.rb b/test/paranoid_test.rb index 719c1e37..bda049b4 100644 --- a/test/paranoid_test.rb +++ b/test/paranoid_test.rb @@ -47,9 +47,15 @@ def test_should_exists_with_deleted assert !Widget.exists?(2) end + def test_should_exists_only_deleted + assert Widget.exists_only_deleted?(2) + assert !Widget.exists_only_deleted?(1) + end + def test_should_count_with_deleted assert_equal 1, Widget.count assert_equal 2, Widget.count_with_deleted + assert_equal 1, Widget.count_only_deleted assert_equal 2, Widget.calculate_with_deleted(:count, :all) end @@ -69,6 +75,7 @@ def test_should_destroy widgets(:widget_1).destroy! assert_equal 0, Widget.count assert_equal 0, Category.count + assert_equal 1, Widget.count_only_deleted assert_equal 1, Widget.calculate_with_deleted(:count, :all) # Category doesn't get destroyed because the dependent before_destroy callback uses #destroy assert_equal 4, Category.calculate_with_deleted(:count, :all) @@ -113,6 +120,12 @@ def test_should_not_count_deleted assert_equal 1, Widget.count assert_equal 1, Widget.count(:all, :conditions => ['title=?', 'widget 1']) assert_equal 2, Widget.calculate_with_deleted(:count, :all) + assert_equal 1, Widget.count_only_deleted + end + + def test_should_find_only_deleted + assert_equal [2], Widget.find_only_deleted(:all).collect { |w| w.id } + assert_equal [1, 2], Widget.find_with_deleted(:all, :order => 'id').collect { |w| w.id } end def test_should_not_find_deleted