delete_paranoid 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/delete_paranoid.rb +5 -2
- data/lib/delete_paranoid/version.rb +1 -1
- data/test/database_setup.rb +25 -0
- data/test/helper.rb +4 -122
- data/test/matchers/callback_matcher.rb +90 -0
- data/test/matchers/destroy_matcher.rb +80 -0
- data/test/test_delete_paranoid.rb +62 -68
- metadata +7 -3
data/lib/delete_paranoid.rb
CHANGED
@@ -5,8 +5,11 @@ module ActiveRecord
|
|
5
5
|
class Relation
|
6
6
|
alias_method :delete_all!, :delete_all
|
7
7
|
def delete_all(conditions = nil)
|
8
|
-
|
9
|
-
|
8
|
+
if @klass.paranoid?
|
9
|
+
update_all({:deleted_at => Time.now.utc}, conditions)
|
10
|
+
else
|
11
|
+
delete_all!(conditions)
|
12
|
+
end
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
data/test/database_setup.rb
CHANGED
@@ -14,5 +14,30 @@ ActiveRecord::Schema.define(:version => 2) do
|
|
14
14
|
t.column :text, :string
|
15
15
|
t.column :deleted_at, :timestamp
|
16
16
|
end
|
17
|
+
create_table :links, :force => true do |t|
|
18
|
+
t.column :blog_id, :integer
|
19
|
+
t.column :name, :string
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Blog < ActiveRecord::Base
|
24
|
+
has_many :comments, :dependent => :destroy
|
25
|
+
has_many :links, :dependent => :destroy
|
26
|
+
acts_as_paranoid
|
27
|
+
attr_accessible :title
|
28
|
+
include CallbackMatcher::ActiveRecordHooks
|
29
|
+
end
|
30
|
+
|
31
|
+
class Comment < ActiveRecord::Base
|
32
|
+
acts_as_paranoid
|
33
|
+
attr_accessible :text
|
34
|
+
belongs_to :blog
|
35
|
+
include CallbackMatcher::ActiveRecordHooks
|
36
|
+
end
|
37
|
+
|
38
|
+
class Link < ActiveRecord::Base
|
39
|
+
belongs_to :blog
|
40
|
+
attr_accessible :name
|
41
|
+
include CallbackMatcher::ActiveRecordHooks
|
17
42
|
end
|
18
43
|
|
data/test/helper.rb
CHANGED
@@ -16,130 +16,12 @@ require 'timecop'
|
|
16
16
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
17
17
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
18
18
|
require 'delete_paranoid'
|
19
|
+
require 'matchers/destroy_matcher'
|
20
|
+
require 'matchers/callback_matcher'
|
19
21
|
require 'database_setup'
|
20
22
|
|
21
23
|
class Test::Unit::TestCase
|
22
|
-
|
23
|
-
|
24
|
-
should "set #{subject} to destroyed" do
|
25
|
-
assert instance_variable_get(:"@#{subject}").destroyed?
|
26
|
-
end
|
27
|
-
should "freeze #{subject}" do
|
28
|
-
assert instance_variable_get(:"@#{subject}").frozen?
|
29
|
-
end
|
30
|
-
should "not find #{subject} normally" do
|
31
|
-
destroyed_subject = instance_variable_get(:"@#{subject}")
|
32
|
-
assert_raises ActiveRecord::RecordNotFound do
|
33
|
-
destroyed_subject.class.find destroyed_subject.id
|
34
|
-
end
|
35
|
-
end
|
36
|
-
should "find #{subject} when in with_deleted block" do
|
37
|
-
destroyed_subject = instance_variable_get(:"@#{subject}")
|
38
|
-
destroyed_subject.class.with_deleted do
|
39
|
-
assert_nothing_raised ActiveRecord::RecordNotFound do
|
40
|
-
assert_not_nil destroyed_subject.class.find destroyed_subject.id
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.should_hard_destroy(subject)
|
47
|
-
should "not find #{subject} normally" do
|
48
|
-
destroyed_subject = instance_variable_get(:"@#{subject}")
|
49
|
-
assert_raises ActiveRecord::RecordNotFound do
|
50
|
-
destroyed_subject.class.find destroyed_subject.id
|
51
|
-
end
|
52
|
-
end
|
53
|
-
should "not find #{subject} in with_deleted block" do
|
54
|
-
destroyed_subject = instance_variable_get(:"@#{subject}")
|
55
|
-
destroyed_subject.class.with_deleted do
|
56
|
-
assert_raises ActiveRecord::RecordNotFound do
|
57
|
-
destroyed_subject.class.find destroyed_subject.id
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.should_trigger_destroy_callbacks(subject)
|
64
|
-
should "call before_destroy callbacks on #{subject}" do
|
65
|
-
assert instance_variable_get(:"@#{subject}").called_before_destroy
|
66
|
-
end
|
67
|
-
should "call after_destroy callbacks on #{subject}" do
|
68
|
-
assert instance_variable_get(:"@#{subject}").called_after_destroy
|
69
|
-
end
|
70
|
-
should "call after_commit_on_destroy callbacks on #{subject}" do
|
71
|
-
assert instance_variable_get(:"@#{subject}").called_after_commit_on_destroy
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.should_not_trigger_update_callbacks(subject)
|
76
|
-
should "not call before_update callbacks on #{subject}" do
|
77
|
-
assert !instance_variable_get(:"@#{subject}").called_before_update
|
78
|
-
end
|
79
|
-
should "not call after_update callbacks on #{subject}" do
|
80
|
-
assert !instance_variable_get(:"@#{subject}").called_after_update
|
81
|
-
end
|
82
|
-
should "not call after_commit_on_update callbacks on #{subject}" do
|
83
|
-
assert !instance_variable_get(:"@#{subject}").called_after_commit_on_update
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
24
|
+
extend DestroyMatcher::MatcherMethods
|
25
|
+
extend CallbackMatcher::MatcherMethods
|
87
26
|
end
|
88
27
|
|
89
|
-
|
90
|
-
module CallbackTester
|
91
|
-
ATTRS = %w{ called_before_destroy called_after_destroy called_after_commit_on_destroy called_before_update called_after_update called_after_commit_on_update }
|
92
|
-
|
93
|
-
def self.included(base)
|
94
|
-
base.class_eval do
|
95
|
-
attr_accessor *ATTRS
|
96
|
-
|
97
|
-
before_update :call_me_before_update
|
98
|
-
after_update :call_me_after_update
|
99
|
-
|
100
|
-
before_destroy :call_me_before_destroy
|
101
|
-
after_destroy :call_me_after_destroy
|
102
|
-
|
103
|
-
after_commit :call_me_after_commit_on_destroy, :on => :destroy
|
104
|
-
after_commit :call_me_after_commit_on_update, :on => :update
|
105
|
-
|
106
|
-
alias_method_chain :initialize, :callback_init
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def initialize_with_callback_init(*args)
|
111
|
-
reset_callback_flags!
|
112
|
-
initialize_without_callback_init(*args)
|
113
|
-
end
|
114
|
-
|
115
|
-
def reset_callback_flags!
|
116
|
-
ATTRS.each do |attr|
|
117
|
-
send("#{attr}=", false)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def call_me_before_destroy
|
122
|
-
@called_before_destroy = true
|
123
|
-
end
|
124
|
-
|
125
|
-
def call_me_after_destroy
|
126
|
-
@called_after_destroy = true
|
127
|
-
end
|
128
|
-
|
129
|
-
def call_me_after_commit_on_destroy
|
130
|
-
@called_after_commit_on_destroy = true
|
131
|
-
end
|
132
|
-
|
133
|
-
def call_me_before_update
|
134
|
-
@called_before_update = true
|
135
|
-
end
|
136
|
-
|
137
|
-
def call_me_after_update
|
138
|
-
@called_after_update = true
|
139
|
-
end
|
140
|
-
|
141
|
-
def call_me_after_commit_on_update
|
142
|
-
@called_after_commit_on_update = true
|
143
|
-
end
|
144
|
-
|
145
|
-
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
class CallbackMatcher
|
3
|
+
CALLBACK_EVENTS = [:before, :after, :after_commit_on]
|
4
|
+
CALLBACK_TYPES = [:create, :update, :destroy, :save]
|
5
|
+
|
6
|
+
module MatcherMethods
|
7
|
+
|
8
|
+
def trigger_callbacks_for(callback_types)
|
9
|
+
CallbackMatcher.new Array.wrap(callback_types)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
module ActiveRecordHooks
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.class_eval do
|
18
|
+
class << self
|
19
|
+
attr_accessor :callback_tester_attrs
|
20
|
+
end
|
21
|
+
@callback_tester_attrs = []
|
22
|
+
CALLBACK_EVENTS.each do |ce|
|
23
|
+
CALLBACK_TYPES.each do |ct|
|
24
|
+
callback_name = :"#{ce}_#{ct}"
|
25
|
+
callback_attr = :"called_#{callback_name}"
|
26
|
+
callback_method, has_on_option = (ce.to_s =~ /_on/ ? [ce.to_s.gsub('_on',''), true] : [callback_name, false])
|
27
|
+
@callback_tester_attrs << callback_attr
|
28
|
+
attr_accessor callback_attr
|
29
|
+
send( callback_method, (has_on_option ? {:on => ct} : {})) {
|
30
|
+
instance_variable_set(:"@#{callback_attr}", true)
|
31
|
+
}
|
32
|
+
|
33
|
+
define_method :"#{callback_attr}?" do
|
34
|
+
instance_variable_get(:"@#{callback_attr}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias_method_chain :initialize, :callback_init
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize_with_callback_init(*args)
|
43
|
+
reset_callback_flags!
|
44
|
+
initialize_without_callback_init(*args)
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset_callback_flags!
|
48
|
+
self.class.callback_tester_attrs.each do |attr|
|
49
|
+
send("#{attr}=", false)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(callback_types)
|
56
|
+
@callback_types = callback_types
|
57
|
+
end
|
58
|
+
|
59
|
+
def failure_message
|
60
|
+
"Expected #{@subject} #{expectation}:"
|
61
|
+
end
|
62
|
+
|
63
|
+
def negative_failure_message
|
64
|
+
"Did not expect #{@subject} #{expectation}:"
|
65
|
+
end
|
66
|
+
|
67
|
+
def description
|
68
|
+
"check that #{@callback_types.join(', ')} callbacks were called"
|
69
|
+
end
|
70
|
+
|
71
|
+
def expectation
|
72
|
+
@expectations.join("\n")
|
73
|
+
end
|
74
|
+
|
75
|
+
def matches?(subject)
|
76
|
+
@subject = subject
|
77
|
+
@expectations = []
|
78
|
+
result = true
|
79
|
+
@callback_types.each do |ct|
|
80
|
+
CALLBACK_EVENTS.each do |ce|
|
81
|
+
called = @subject.send(:"called_#{ce}_#{ct}?")
|
82
|
+
result &&= called
|
83
|
+
@expectations << "#{ce}_#{ct} callbacks to be triggered"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module DeleteParanoid
|
2
|
+
class DestroyMatcher
|
3
|
+
def softly
|
4
|
+
@softly = true
|
5
|
+
self
|
6
|
+
end
|
7
|
+
def and_freeze
|
8
|
+
@frozen = true
|
9
|
+
self
|
10
|
+
end
|
11
|
+
def and_mark_as_destroyed
|
12
|
+
@destroyed = true
|
13
|
+
self
|
14
|
+
end
|
15
|
+
def description
|
16
|
+
"destroy the subject"
|
17
|
+
end
|
18
|
+
def matches?(subject)
|
19
|
+
@subject = subject
|
20
|
+
errors.empty?
|
21
|
+
end
|
22
|
+
def failure_message
|
23
|
+
"Expected #{@subject.inspect} to be destroyed: #{errors.join("\n")}"
|
24
|
+
end
|
25
|
+
def errors
|
26
|
+
return @errors if @errors
|
27
|
+
@errors = []
|
28
|
+
@errors << "was found in database" if subject_found?
|
29
|
+
@errors << "was not found with_deleted in database" if softly? && !subject_found_with_deleted?
|
30
|
+
@errors << "did not populate deleted_at timestamp" if softly? && !subject_deleted_at?
|
31
|
+
@errors << "did not freeze instance" if frozen? && !@subject.frozen?
|
32
|
+
@errors << "did not destroy instance" if destroyed? && !@subject.destroyed?
|
33
|
+
@errors
|
34
|
+
end
|
35
|
+
def softly?
|
36
|
+
!!@softly
|
37
|
+
end
|
38
|
+
def frozen?
|
39
|
+
!!@frozen
|
40
|
+
end
|
41
|
+
def destroyed?
|
42
|
+
!!@destroyed
|
43
|
+
end
|
44
|
+
def subject_found?
|
45
|
+
!!@subject.class.find(@subject.id)
|
46
|
+
rescue ActiveRecord::RecordNotFound
|
47
|
+
false
|
48
|
+
end
|
49
|
+
def subject_found_with_deleted?
|
50
|
+
@subject.class.with_deleted do
|
51
|
+
!!@subject.class.find(@subject.id)
|
52
|
+
end
|
53
|
+
rescue ActiveRecord::RecordNotFound
|
54
|
+
false
|
55
|
+
end
|
56
|
+
def subject_deleted_at?
|
57
|
+
@subject.class.with_deleted do
|
58
|
+
@subject.class.find(@subject.id).deleted_at
|
59
|
+
end
|
60
|
+
rescue ActiveRecord::RecordNotFound
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class DestroyMatcher
|
68
|
+
module MatcherMethods
|
69
|
+
def soft_destroy
|
70
|
+
DestroyMatcher.new :soft
|
71
|
+
end
|
72
|
+
def hard_destroy
|
73
|
+
DestroyMatcher.new :hard
|
74
|
+
end
|
75
|
+
def destroy_subject
|
76
|
+
DeleteParanoid::DestroyMatcher.new
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -1,125 +1,119 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'helper')
|
2
2
|
|
3
3
|
class TestDeleteParanoid < Test::Unit::TestCase
|
4
|
-
class Blog < ActiveRecord::Base
|
5
|
-
has_many :comments, :dependent => :destroy
|
6
|
-
acts_as_paranoid
|
7
|
-
attr_accessible :title
|
8
|
-
include CallbackTester
|
9
|
-
end
|
10
|
-
|
11
|
-
class Comment < ActiveRecord::Base
|
12
|
-
acts_as_paranoid
|
13
|
-
attr_accessible :text
|
14
|
-
belongs_to :blog
|
15
|
-
include CallbackTester
|
16
|
-
end
|
17
|
-
|
18
|
-
class User < ActiveRecord::Base
|
19
|
-
end
|
20
4
|
context 'with non-paranoid activerecord class' do
|
21
5
|
should 'not be paranoid' do
|
22
|
-
assert !
|
6
|
+
assert !Link.paranoid?
|
23
7
|
end
|
24
8
|
end
|
9
|
+
|
25
10
|
context 'with paranoid activerecord class' do
|
26
11
|
should 'be paranoid' do
|
27
12
|
assert Blog.paranoid?
|
28
13
|
end
|
29
14
|
end
|
15
|
+
|
30
16
|
context 'with instance of paranoid class' do
|
31
17
|
setup do
|
32
18
|
@blog = Blog.create! :title => 'foo'
|
33
19
|
end
|
34
20
|
context 'when destroying instance with instance.destroy' do
|
35
|
-
|
36
|
-
@
|
37
|
-
|
38
|
-
@blog.destroy
|
39
|
-
end
|
21
|
+
subject do
|
22
|
+
@blog.destroy
|
23
|
+
@blog
|
40
24
|
end
|
41
25
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
should 'save deleted_at timestamp on database record' do
|
46
|
-
blog = Blog.find_by_sql(['SELECT deleted_at FROM blogs WHERE id = ?', @blog.id]).first
|
47
|
-
assert_not_nil blog
|
48
|
-
assert_not_nil blog.deleted_at
|
49
|
-
assert_equal @now.to_i, blog.deleted_at.to_i
|
50
|
-
end
|
26
|
+
should destroy_subject.softly.and_freeze.and_mark_as_destroyed
|
27
|
+
should trigger_callbacks_for :destroy
|
28
|
+
should_not trigger_callbacks_for :update
|
51
29
|
end
|
52
30
|
context 'when destroying instance with Class.destroy_all' do
|
53
|
-
|
31
|
+
subject do
|
54
32
|
Blog.destroy_all :id => @blog.id
|
33
|
+
@blog
|
55
34
|
end
|
56
|
-
should
|
57
|
-
assert_raises ActiveRecord::RecordNotFound do
|
58
|
-
Blog.find @blog.id
|
59
|
-
end
|
60
|
-
end
|
61
|
-
should "find instance when in with_deleted block" do
|
62
|
-
Blog.with_deleted do
|
63
|
-
assert_nothing_raised ActiveRecord::RecordNotFound do
|
64
|
-
Blog.find @blog.id
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
35
|
+
should destroy_subject.softly
|
68
36
|
end
|
69
37
|
context "when destroying instance with Class.delete_all!" do
|
70
|
-
|
38
|
+
subject do
|
71
39
|
Blog.where({:id => @blog.id}).delete_all!
|
40
|
+
@blog
|
72
41
|
end
|
73
|
-
|
42
|
+
should destroy_subject
|
74
43
|
end
|
75
44
|
context "when destroying instance with Class.delete!" do
|
76
|
-
|
45
|
+
subject do
|
77
46
|
Blog.delete! @blog.id
|
47
|
+
@blog
|
78
48
|
end
|
79
|
-
|
49
|
+
should destroy_subject
|
80
50
|
end
|
81
51
|
context 'when destroying instance with instance.destroy!' do
|
82
|
-
|
52
|
+
subject do
|
83
53
|
@blog.destroy!
|
54
|
+
@blog
|
84
55
|
end
|
85
|
-
|
86
|
-
|
56
|
+
should destroy_subject
|
57
|
+
should trigger_callbacks_for :destroy
|
58
|
+
should_not trigger_callbacks_for :update
|
87
59
|
end
|
88
60
|
context 'when destroying instance with instance.delete!' do
|
89
|
-
|
61
|
+
subject do
|
90
62
|
@blog.delete!
|
63
|
+
@blog
|
91
64
|
end
|
92
|
-
|
65
|
+
should destroy_subject
|
93
66
|
end
|
94
67
|
end
|
95
68
|
|
96
|
-
context 'with paranoid instance that has
|
69
|
+
context 'with paranoid instance that has belongs to paranoid instance' do
|
97
70
|
setup do
|
98
71
|
@blog = Blog.create!(:title => 'foo')
|
99
72
|
@comment = @blog.comments.create! :text => 'bar'
|
100
73
|
end
|
101
|
-
context 'when destroying paranoid instance' do
|
102
|
-
|
74
|
+
context 'when destroying parent paranoid instance with destroy' do
|
75
|
+
subject do
|
103
76
|
@blog.destroy
|
77
|
+
@comment
|
104
78
|
end
|
105
79
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
should_soft_destroy :comment
|
111
|
-
should_trigger_destroy_callbacks :comment
|
80
|
+
should destroy_subject.softly.and_freeze.and_mark_as_destroyed
|
81
|
+
should trigger_callbacks_for :destroy
|
82
|
+
#should_not trigger_callbacks_for :update
|
112
83
|
end
|
113
|
-
context 'when destroying paranoid instance with delete_all!' do
|
114
|
-
|
84
|
+
context 'when destroying parent paranoid instance with delete_all!' do
|
85
|
+
subject do
|
115
86
|
Blog.where({:id => @blog.id}).delete_all!
|
87
|
+
@comment
|
116
88
|
end
|
117
89
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
90
|
+
should_not destroy_subject
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'with non-paranoid instance that has belongs to paranoid instance' do
|
95
|
+
setup do
|
96
|
+
@blog = Blog.create!(:title => 'foo')
|
97
|
+
@link = @blog.links.create! :name => 'bar'
|
98
|
+
end
|
99
|
+
context 'when destroying parent paranoid instance with destroy' do
|
100
|
+
subject do
|
101
|
+
@blog.destroy
|
102
|
+
@link
|
103
|
+
end
|
104
|
+
|
105
|
+
should destroy_subject.and_freeze.and_mark_as_destroyed
|
106
|
+
should trigger_callbacks_for :destroy
|
107
|
+
#should_not trigger_callbacks_for :update
|
108
|
+
end
|
109
|
+
context 'when destroying parent paranoid instance with delete_all!' do
|
110
|
+
subject do
|
111
|
+
Blog.where({:id => @blog.id}).delete_all!
|
112
|
+
@link
|
122
113
|
end
|
114
|
+
|
115
|
+
should_not destroy_subject
|
123
116
|
end
|
124
117
|
end
|
125
118
|
end
|
119
|
+
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delete_paranoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 2
|
10
|
+
version: 1.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Sonnek
|
@@ -141,6 +141,8 @@ files:
|
|
141
141
|
- test/database.yml
|
142
142
|
- test/database_setup.rb
|
143
143
|
- test/helper.rb
|
144
|
+
- test/matchers/callback_matcher.rb
|
145
|
+
- test/matchers/destroy_matcher.rb
|
144
146
|
- test/test_delete_paranoid.rb
|
145
147
|
has_rdoc: true
|
146
148
|
homepage: http://github.com/wireframe/delete_paranoid
|
@@ -180,4 +182,6 @@ test_files:
|
|
180
182
|
- test/database.yml
|
181
183
|
- test/database_setup.rb
|
182
184
|
- test/helper.rb
|
185
|
+
- test/matchers/callback_matcher.rb
|
186
|
+
- test/matchers/destroy_matcher.rb
|
183
187
|
- test/test_delete_paranoid.rb
|