acts_as_paranoid 0.4.2 → 0.4.3.pre
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.
- checksums.yaml +7 -0
- data/LICENSE +1 -1
- data/{README.markdown → README.md} +15 -11
- data/lib/acts_as_paranoid.rb +8 -15
- data/lib/acts_as_paranoid/core.rb +14 -6
- data/lib/acts_as_paranoid/relation.rb +2 -2
- data/lib/acts_as_paranoid/version.rb +3 -0
- data/test/test_associations.rb +222 -0
- data/test/test_core.rb +387 -0
- data/test/test_default_scopes.rb +52 -0
- data/test/test_helper.rb +442 -0
- data/test/test_inheritance.rb +14 -0
- data/test/test_observers.rb +16 -0
- data/test/test_relations.rb +117 -0
- data/test/test_validations.rb +42 -0
- metadata +92 -24
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4045fd67f66b6cbbaae31fce07b7e6f5cadafd77
|
4
|
+
data.tar.gz: e97fdbb51832ea05c1704627a4ce43cce1d2a824
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 01ff43ed7501da46e4c2dcd0f595ac689f26a578a8e11fdaa250f393befb9ac51ec07117ff9b7970ef67ef050c9212d504702be63bb7b9558c320fe3fcb942ca
|
7
|
+
data.tar.gz: 6075e2f197eb0c8545c3eb3e772079976f4485b34c1b3e1f65ae496da85dde0c5b275c0b2c3812918e59e2a58b0cfad3d0759675164852165a81b113eb9d8d1c
|
data/LICENSE
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
# ActsAsParanoid
|
2
2
|
|
3
|
+
[](https://travis-ci.org/zzak/acts_as_paranoid)
|
4
|
+
|
3
5
|
A simple plugin which hides records instead of deleting them, being able to recover them.
|
4
6
|
|
5
7
|
**This branch targets Rails 3.2.** If you're working with another version, switch to the corresponding branch.
|
6
8
|
|
7
|
-
## Credits
|
8
|
-
|
9
|
-
This plugin was inspired by [acts_as_paranoid](http://github.com/technoweenie/acts_as_paranoid) and [acts_as_active](http://github.com/fernandoluizao/acts_as_active).
|
10
|
-
|
11
|
-
While porting it to Rails 3, I decided to apply the ideas behind those plugins to an unified solution while removing a **lot** of the complexity found in them. I eventually ended up writing a new plugin from scratch.
|
12
|
-
|
13
9
|
## Usage
|
14
10
|
|
15
11
|
You can enable ActsAsParanoid like this:
|
@@ -222,20 +218,21 @@ This gem supports the most recent versions of Rails and Ruby.
|
|
222
218
|
|
223
219
|
For Rails 3.2 check the README at the [rails3.2](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.2) branch and add this to your Gemfile:
|
224
220
|
|
225
|
-
|
221
|
+
gem "acts_as_paranoid", "~> 0.4.0"
|
226
222
|
|
227
223
|
For Rails 3.1 check the README at the [rails3.1](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.1) branch and add this to your Gemfile:
|
228
224
|
|
229
|
-
|
225
|
+
gem "rails3_acts_as_paranoid", "~>0.1.4"
|
230
226
|
|
231
227
|
For Rails 3.0 check the README at the [rails3.0](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.0) branch and add this to your Gemfile:
|
232
228
|
|
233
|
-
|
229
|
+
gem "rails3_acts_as_paranoid", "~>0.0.9"
|
234
230
|
|
235
231
|
|
236
232
|
## Ruby
|
237
233
|
|
238
|
-
This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode).
|
234
|
+
This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode).
|
235
|
+
|
239
236
|
|
240
237
|
# Acknowledgements
|
241
238
|
|
@@ -247,4 +244,11 @@ This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode). It *might
|
|
247
244
|
* To [Craig Walker](https://github.com/softcraft-development) for Rails 3.1 support and fixing various pending issues
|
248
245
|
* To [Charles G.](https://github.com/chuckg) for Rails 3.2 support and for making a desperately needed global code refactoring
|
249
246
|
|
250
|
-
|
247
|
+
## Credits
|
248
|
+
|
249
|
+
This plugin was inspired by [acts_as_paranoid](http://github.com/technoweenie/acts_as_paranoid) and [acts_as_active](http://github.com/fernandoluizao/acts_as_active).
|
250
|
+
|
251
|
+
While porting it to Rails 3, I decided to apply the ideas behind those plugins to an unified solution while removing a **lot** of the complexity found in them. I eventually ended up writing a new plugin from scratch.
|
252
|
+
|
253
|
+
|
254
|
+
Copyright © 2014 Zachary Scott, Gonçalo Silva, Rick Olson, released under the MIT license
|
data/lib/acts_as_paranoid.rb
CHANGED
@@ -1,27 +1,23 @@
|
|
1
|
-
require 'active_record/base'
|
2
|
-
require 'active_record/relation'
|
3
|
-
require 'active_record/callbacks'
|
4
1
|
require 'acts_as_paranoid/core'
|
5
2
|
require 'acts_as_paranoid/associations'
|
6
3
|
require 'acts_as_paranoid/validations'
|
7
4
|
require 'acts_as_paranoid/relation'
|
8
5
|
|
9
|
-
|
10
6
|
module ActsAsParanoid
|
11
|
-
|
7
|
+
|
12
8
|
def paranoid?
|
13
9
|
self.included_modules.include?(ActsAsParanoid::Core)
|
14
10
|
end
|
15
|
-
|
11
|
+
|
16
12
|
def validates_as_paranoid
|
17
13
|
include ActsAsParanoid::Validations
|
18
14
|
end
|
19
|
-
|
15
|
+
|
20
16
|
def acts_as_paranoid(options = {})
|
21
17
|
raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
|
22
|
-
|
18
|
+
|
23
19
|
class_attribute :paranoid_configuration, :paranoid_column_reference
|
24
|
-
|
20
|
+
|
25
21
|
self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes }
|
26
22
|
self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
|
27
23
|
self.paranoid_configuration.merge!(options) # user options
|
@@ -29,17 +25,14 @@ module ActsAsParanoid
|
|
29
25
|
raise ArgumentError, "'time', 'boolean' or 'string' expected for :column_type option, got #{paranoid_configuration[:column_type]}" unless ['time', 'boolean', 'string'].include? paranoid_configuration[:column_type]
|
30
26
|
|
31
27
|
self.paranoid_column_reference = "#{self.table_name}.#{paranoid_configuration[:column]}"
|
32
|
-
|
28
|
+
|
33
29
|
return if paranoid?
|
34
|
-
|
30
|
+
|
35
31
|
include ActsAsParanoid::Core
|
36
|
-
|
32
|
+
|
37
33
|
# Magic!
|
38
34
|
default_scope { where(paranoid_default_scope_sql) }
|
39
35
|
|
40
|
-
# The paranoid column should not be mass-assignable
|
41
|
-
attr_protected paranoid_configuration[:column]
|
42
|
-
|
43
36
|
if paranoid_configuration[:column_type] == 'time'
|
44
37
|
scope :deleted_inside_time_window, lambda {|time, window|
|
45
38
|
deleted_after_time((time - window)).deleted_before_time((time + window))
|
@@ -134,17 +134,17 @@ module ActsAsParanoid
|
|
134
134
|
|
135
135
|
def recover_dependent_associations(window, options)
|
136
136
|
self.class.dependent_associations.each do |reflection|
|
137
|
-
next unless reflection.
|
137
|
+
next unless (klass = get_reflection_class(reflection)).paranoid?
|
138
138
|
|
139
|
-
scope =
|
139
|
+
scope = klass.only_deleted
|
140
140
|
|
141
141
|
# Merge in the association's scope
|
142
142
|
scope = scope.merge(association(reflection.name).association_scope)
|
143
143
|
|
144
144
|
# We can only recover by window if both parent and dependant have a
|
145
145
|
# paranoid column type of :time.
|
146
|
-
if self.class.paranoid_column_type == :time &&
|
147
|
-
scope = scope.merge(
|
146
|
+
if self.class.paranoid_column_type == :time && klass.paranoid_column_type == :time
|
147
|
+
scope = scope.merge(klass.deleted_inside_time_window(paranoid_value, window))
|
148
148
|
end
|
149
149
|
|
150
150
|
scope.each do |object|
|
@@ -155,9 +155,9 @@ module ActsAsParanoid
|
|
155
155
|
|
156
156
|
def destroy_dependent_associations!
|
157
157
|
self.class.dependent_associations.each do |reflection|
|
158
|
-
next unless reflection.
|
158
|
+
next unless (klass = get_reflection_class(reflection)).paranoid?
|
159
159
|
|
160
|
-
scope =
|
160
|
+
scope = klass.only_deleted
|
161
161
|
|
162
162
|
# Merge in the association's scope
|
163
163
|
scope = scope.merge(association(reflection.name).association_scope)
|
@@ -177,6 +177,14 @@ module ActsAsParanoid
|
|
177
177
|
|
178
178
|
private
|
179
179
|
|
180
|
+
def get_reflection_class(reflection)
|
181
|
+
if reflection.macro == :belongs_to && reflection.options.include?(:polymorphic)
|
182
|
+
self.send(reflection.foreign_type).constantize
|
183
|
+
else
|
184
|
+
reflection.klass
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
180
188
|
def paranoid_value=(value)
|
181
189
|
self.send("#{self.class.paranoid_column}=", value)
|
182
190
|
end
|
@@ -5,7 +5,7 @@ module ActsAsParanoid
|
|
5
5
|
def paranoid?
|
6
6
|
klass.try(:paranoid?) ? true : false
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def paranoid_deletion_attributes
|
10
10
|
{ klass.paranoid_column => klass.delete_now_value }
|
11
11
|
end
|
@@ -18,7 +18,7 @@ module ActsAsParanoid
|
|
18
18
|
orig_delete_all
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def delete_all(conditions = nil)
|
23
23
|
if paranoid?
|
24
24
|
update_all(paranoid_deletion_attributes, conditions)
|
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AssociationsTest < ParanoidBaseTest
|
4
|
+
def test_removal_with_associations
|
5
|
+
# This test shows that the current implementation doesn't handle
|
6
|
+
# assciation deletion correctly (when hard deleting via parent-object)
|
7
|
+
paranoid_company_1 = ParanoidDestroyCompany.create! :name => "ParanoidDestroyCompany #1"
|
8
|
+
paranoid_company_2 = ParanoidDeleteCompany.create! :name => "ParanoidDestroyCompany #1"
|
9
|
+
paranoid_company_1.paranoid_products.create! :name => "ParanoidProduct #1"
|
10
|
+
paranoid_company_2.paranoid_products.create! :name => "ParanoidProduct #2"
|
11
|
+
|
12
|
+
assert_equal 1, ParanoidDestroyCompany.count
|
13
|
+
assert_equal 1, ParanoidDeleteCompany.count
|
14
|
+
assert_equal 2, ParanoidProduct.count
|
15
|
+
|
16
|
+
ParanoidDestroyCompany.first.destroy
|
17
|
+
assert_equal 0, ParanoidDestroyCompany.count
|
18
|
+
assert_equal 1, ParanoidProduct.count
|
19
|
+
assert_equal 1, ParanoidDestroyCompany.with_deleted.count
|
20
|
+
assert_equal 2, ParanoidProduct.with_deleted.count
|
21
|
+
|
22
|
+
ParanoidDestroyCompany.with_deleted.first.destroy!
|
23
|
+
assert_equal 0, ParanoidDestroyCompany.count
|
24
|
+
assert_equal 1, ParanoidProduct.count
|
25
|
+
assert_equal 0, ParanoidDestroyCompany.with_deleted.count
|
26
|
+
assert_equal 1, ParanoidProduct.with_deleted.count
|
27
|
+
|
28
|
+
ParanoidDeleteCompany.with_deleted.first.destroy!
|
29
|
+
assert_equal 0, ParanoidDeleteCompany.count
|
30
|
+
assert_equal 0, ParanoidProduct.count
|
31
|
+
assert_equal 0, ParanoidDeleteCompany.with_deleted.count
|
32
|
+
assert_equal 0, ParanoidProduct.with_deleted.count
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_belongs_to_with_deleted
|
36
|
+
paranoid_time = ParanoidTime.first
|
37
|
+
paranoid_has_many_dependant = paranoid_time.paranoid_has_many_dependants.create(:name => 'dependant!')
|
38
|
+
|
39
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time
|
40
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted
|
41
|
+
|
42
|
+
paranoid_time.destroy
|
43
|
+
|
44
|
+
assert_nil paranoid_has_many_dependant.paranoid_time(true)
|
45
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted(true)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_belongs_to_polymorphic_with_deleted
|
49
|
+
paranoid_time = ParanoidTime.first
|
50
|
+
paranoid_has_many_dependant = ParanoidHasManyDependant.create!(:name => 'dependant!', :paranoid_time_polymorphic_with_deleted => paranoid_time)
|
51
|
+
|
52
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time
|
53
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted
|
54
|
+
|
55
|
+
paranoid_time.destroy
|
56
|
+
|
57
|
+
assert_nil paranoid_has_many_dependant.paranoid_time(true)
|
58
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted(true)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_belongs_to_nil_polymorphic_with_deleted
|
62
|
+
paranoid_time = ParanoidTime.first
|
63
|
+
paranoid_has_many_dependant = ParanoidHasManyDependant.create!(:name => 'dependant!', :paranoid_time_polymorphic_with_deleted => nil)
|
64
|
+
|
65
|
+
assert_nil paranoid_has_many_dependant.paranoid_time
|
66
|
+
assert_nil paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted
|
67
|
+
|
68
|
+
paranoid_time.destroy
|
69
|
+
|
70
|
+
assert_nil paranoid_has_many_dependant.paranoid_time(true)
|
71
|
+
assert_nil paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_belongs_to_options
|
75
|
+
paranoid_time = ParanoidHasManyDependant.reflections[:paranoid_time]
|
76
|
+
assert_equal :belongs_to, paranoid_time.macro
|
77
|
+
assert_nil paranoid_time.options[:with_deleted]
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_belongs_to_with_deleted_options
|
81
|
+
paranoid_time_with_deleted = ParanoidHasManyDependant.reflections[:paranoid_time_with_deleted]
|
82
|
+
assert_equal :belongs_to, paranoid_time_with_deleted.macro
|
83
|
+
assert paranoid_time_with_deleted.options[:with_deleted]
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_belongs_to_polymorphic_with_deleted_options
|
87
|
+
paranoid_time_polymorphic_with_deleted = ParanoidHasManyDependant.reflections[:paranoid_time_polymorphic_with_deleted]
|
88
|
+
assert_equal :belongs_to, paranoid_time_polymorphic_with_deleted.macro
|
89
|
+
assert paranoid_time_polymorphic_with_deleted.options[:with_deleted]
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_only_find_associated_records_when_finding_with_paranoid_deleted
|
93
|
+
parent = ParanoidBelongsDependant.create
|
94
|
+
child = ParanoidHasManyDependant.create
|
95
|
+
parent.paranoid_has_many_dependants << child
|
96
|
+
|
97
|
+
unrelated_parent = ParanoidBelongsDependant.create
|
98
|
+
unrelated_child = ParanoidHasManyDependant.create
|
99
|
+
unrelated_parent.paranoid_has_many_dependants << unrelated_child
|
100
|
+
|
101
|
+
child.destroy
|
102
|
+
assert_paranoid_deletion(child)
|
103
|
+
|
104
|
+
assert_equal [child], parent.paranoid_has_many_dependants.with_deleted.to_a
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_cannot_find_a_paranoid_deleted_many_many_association
|
108
|
+
left = ParanoidManyManyParentLeft.create
|
109
|
+
right = ParanoidManyManyParentRight.create
|
110
|
+
left.paranoid_many_many_parent_rights << right
|
111
|
+
|
112
|
+
left.paranoid_many_many_parent_rights.delete(right)
|
113
|
+
|
114
|
+
left.reload
|
115
|
+
|
116
|
+
assert_equal [], left.paranoid_many_many_children, "Linking objects not deleted"
|
117
|
+
assert_equal [], left.paranoid_many_many_parent_rights, "Associated objects not unlinked"
|
118
|
+
assert_equal right, ParanoidManyManyParentRight.find(right.id), "Associated object deleted"
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_cannot_find_a_paranoid_destroyed_many_many_association
|
122
|
+
left = ParanoidManyManyParentLeft.create
|
123
|
+
right = ParanoidManyManyParentRight.create
|
124
|
+
left.paranoid_many_many_parent_rights << right
|
125
|
+
|
126
|
+
left.paranoid_many_many_parent_rights.destroy(right)
|
127
|
+
|
128
|
+
left.reload
|
129
|
+
|
130
|
+
assert_equal [], left.paranoid_many_many_children, "Linking objects not deleted"
|
131
|
+
assert_equal [], left.paranoid_many_many_parent_rights, "Associated objects not unlinked"
|
132
|
+
assert_equal right, ParanoidManyManyParentRight.find(right.id), "Associated object deleted"
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_cannot_find_a_has_many_through_object_when_its_linking_object_is_paranoid_destroyed
|
136
|
+
left = ParanoidManyManyParentLeft.create
|
137
|
+
right = ParanoidManyManyParentRight.create
|
138
|
+
left.paranoid_many_many_parent_rights << right
|
139
|
+
|
140
|
+
child = left.paranoid_many_many_children.first
|
141
|
+
|
142
|
+
child.destroy
|
143
|
+
|
144
|
+
left.reload
|
145
|
+
|
146
|
+
assert_equal [], left.paranoid_many_many_parent_rights, "Associated objects not deleted"
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_cannot_find_a_paranoid_deleted_model
|
150
|
+
model = ParanoidBelongsDependant.create
|
151
|
+
model.destroy
|
152
|
+
|
153
|
+
assert_raises ActiveRecord::RecordNotFound do
|
154
|
+
ParanoidBelongsDependant.find(model.id)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_bidirectional_has_many_through_association_clear_is_paranoid
|
159
|
+
left = ParanoidManyManyParentLeft.create
|
160
|
+
right = ParanoidManyManyParentRight.create
|
161
|
+
left.paranoid_many_many_parent_rights << right
|
162
|
+
|
163
|
+
child = left.paranoid_many_many_children.first
|
164
|
+
assert_equal left, child.paranoid_many_many_parent_left, "Child's left parent is incorrect"
|
165
|
+
assert_equal right, child.paranoid_many_many_parent_right, "Child's right parent is incorrect"
|
166
|
+
|
167
|
+
left.paranoid_many_many_parent_rights.clear
|
168
|
+
|
169
|
+
assert_paranoid_deletion(child)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_bidirectional_has_many_through_association_destroy_is_paranoid
|
173
|
+
left = ParanoidManyManyParentLeft.create
|
174
|
+
right = ParanoidManyManyParentRight.create
|
175
|
+
left.paranoid_many_many_parent_rights << right
|
176
|
+
|
177
|
+
child = left.paranoid_many_many_children.first
|
178
|
+
assert_equal left, child.paranoid_many_many_parent_left, "Child's left parent is incorrect"
|
179
|
+
assert_equal right, child.paranoid_many_many_parent_right, "Child's right parent is incorrect"
|
180
|
+
|
181
|
+
left.paranoid_many_many_parent_rights.destroy(right)
|
182
|
+
|
183
|
+
assert_paranoid_deletion(child)
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_bidirectional_has_many_through_association_delete_is_paranoid
|
187
|
+
left = ParanoidManyManyParentLeft.create
|
188
|
+
right = ParanoidManyManyParentRight.create
|
189
|
+
left.paranoid_many_many_parent_rights << right
|
190
|
+
|
191
|
+
child = left.paranoid_many_many_children.first
|
192
|
+
assert_equal left, child.paranoid_many_many_parent_left, "Child's left parent is incorrect"
|
193
|
+
assert_equal right, child.paranoid_many_many_parent_right, "Child's right parent is incorrect"
|
194
|
+
|
195
|
+
left.paranoid_many_many_parent_rights.delete(right)
|
196
|
+
|
197
|
+
assert_paranoid_deletion(child)
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_belongs_to_on_normal_model_is_paranoid
|
201
|
+
not_paranoid = HasOneNotParanoid.create
|
202
|
+
not_paranoid.paranoid_time = ParanoidTime.create
|
203
|
+
|
204
|
+
assert not_paranoid.save
|
205
|
+
assert_not_nil not_paranoid.paranoid_time
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_double_belongs_to_with_deleted
|
209
|
+
not_paranoid = DoubleHasOneNotParanoid.create
|
210
|
+
not_paranoid.paranoid_time = ParanoidTime.create
|
211
|
+
|
212
|
+
assert not_paranoid.save
|
213
|
+
assert_not_nil not_paranoid.paranoid_time
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_mass_assignment_of_paranoid_column_enabled
|
217
|
+
now = Time.now
|
218
|
+
record = ParanoidTime.create! :name => 'Foo', :deleted_at => now
|
219
|
+
assert_equal 'Foo', record.name
|
220
|
+
assert_equal now, record.deleted_at
|
221
|
+
end
|
222
|
+
end
|
data/test/test_core.rb
ADDED
@@ -0,0 +1,387 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ParanoidTest < ParanoidBaseTest
|
4
|
+
def test_paranoid?
|
5
|
+
assert !NotParanoid.paranoid?
|
6
|
+
assert_raise(NoMethodError) { NotParanoid.delete_all! }
|
7
|
+
assert_raise(NoMethodError) { NotParanoid.first.destroy! }
|
8
|
+
assert_raise(NoMethodError) { NotParanoid.with_deleted }
|
9
|
+
assert_raise(NoMethodError) { NotParanoid.only_deleted }
|
10
|
+
|
11
|
+
assert ParanoidTime.paranoid?
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_scope_inclusion_with_time_column_type
|
15
|
+
assert ParanoidTime.respond_to?(:deleted_inside_time_window)
|
16
|
+
assert ParanoidTime.respond_to?(:deleted_before_time)
|
17
|
+
assert ParanoidTime.respond_to?(:deleted_after_time)
|
18
|
+
|
19
|
+
assert !ParanoidBoolean.respond_to?(:deleted_inside_time_window)
|
20
|
+
assert !ParanoidBoolean.respond_to?(:deleted_before_time)
|
21
|
+
assert !ParanoidBoolean.respond_to?(:deleted_after_time)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_fake_removal
|
25
|
+
assert_equal 3, ParanoidTime.count
|
26
|
+
assert_equal 3, ParanoidBoolean.count
|
27
|
+
assert_equal 1, ParanoidString.count
|
28
|
+
|
29
|
+
ParanoidTime.first.destroy
|
30
|
+
ParanoidBoolean.delete_all("name = 'paranoid' OR name = 'really paranoid'")
|
31
|
+
ParanoidString.first.destroy
|
32
|
+
assert_equal 2, ParanoidTime.count
|
33
|
+
assert_equal 1, ParanoidBoolean.count
|
34
|
+
assert_equal 0, ParanoidString.count
|
35
|
+
assert_equal 1, ParanoidTime.only_deleted.count
|
36
|
+
assert_equal 2, ParanoidBoolean.only_deleted.count
|
37
|
+
assert_equal 1, ParanoidString.only_deleted.count
|
38
|
+
assert_equal 3, ParanoidTime.with_deleted.count
|
39
|
+
assert_equal 3, ParanoidBoolean.with_deleted.count
|
40
|
+
assert_equal 1, ParanoidString.with_deleted.count
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_real_removal
|
44
|
+
ParanoidTime.first.destroy!
|
45
|
+
ParanoidBoolean.delete_all!("name = 'extremely paranoid' OR name = 'really paranoid'")
|
46
|
+
ParanoidString.first.destroy!
|
47
|
+
assert_equal 2, ParanoidTime.count
|
48
|
+
assert_equal 1, ParanoidBoolean.count
|
49
|
+
assert_equal 0, ParanoidString.count
|
50
|
+
assert_equal 2, ParanoidTime.with_deleted.count
|
51
|
+
assert_equal 1, ParanoidBoolean.with_deleted.count
|
52
|
+
assert_equal 0, ParanoidString.with_deleted.count
|
53
|
+
assert_equal 0, ParanoidTime.only_deleted.count
|
54
|
+
assert_equal 0, ParanoidBoolean.only_deleted.count
|
55
|
+
assert_equal 0, ParanoidString.only_deleted.count
|
56
|
+
|
57
|
+
ParanoidTime.first.destroy
|
58
|
+
ParanoidTime.only_deleted.first.destroy
|
59
|
+
assert_equal 0, ParanoidTime.only_deleted.count
|
60
|
+
|
61
|
+
ParanoidTime.delete_all!
|
62
|
+
assert_empty ParanoidTime.all
|
63
|
+
assert_empty ParanoidTime.with_deleted.all
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_non_persisted_destroy
|
67
|
+
pt = ParanoidTime.new
|
68
|
+
assert_nil pt.paranoid_value
|
69
|
+
pt.destroy
|
70
|
+
assert_not_nil pt.paranoid_value
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_non_persisted_destroy!
|
74
|
+
pt = ParanoidTime.new
|
75
|
+
assert_nil pt.paranoid_value
|
76
|
+
pt.destroy!
|
77
|
+
assert_not_nil pt.paranoid_value
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_recovery
|
81
|
+
assert_equal 3, ParanoidBoolean.count
|
82
|
+
ParanoidBoolean.first.destroy
|
83
|
+
assert_equal 2, ParanoidBoolean.count
|
84
|
+
ParanoidBoolean.only_deleted.first.recover
|
85
|
+
assert_equal 3, ParanoidBoolean.count
|
86
|
+
|
87
|
+
assert_equal 1, ParanoidString.count
|
88
|
+
ParanoidString.first.destroy
|
89
|
+
assert_equal 0, ParanoidString.count
|
90
|
+
ParanoidString.with_deleted.first.recover
|
91
|
+
assert_equal 1, ParanoidString.count
|
92
|
+
end
|
93
|
+
|
94
|
+
def setup_recursive_tests
|
95
|
+
@paranoid_time_object = ParanoidTime.first
|
96
|
+
|
97
|
+
# Create one extra ParanoidHasManyDependant record so that we can validate
|
98
|
+
# the correct dependants are recovered.
|
99
|
+
ParanoidTime.where('id <> ?', @paranoid_time_object.id).first.paranoid_has_many_dependants.create(:name => "should not be recovered").destroy
|
100
|
+
|
101
|
+
@paranoid_boolean_count = ParanoidBoolean.count
|
102
|
+
|
103
|
+
assert_equal 0, ParanoidHasManyDependant.count
|
104
|
+
assert_equal 0, ParanoidBelongsDependant.count
|
105
|
+
assert_equal 1, NotParanoid.count
|
106
|
+
|
107
|
+
(1..3).each do |i|
|
108
|
+
has_many_object = @paranoid_time_object.paranoid_has_many_dependants.create(:name => "has_many_#{i}")
|
109
|
+
has_many_object.create_paranoid_belongs_dependant(:name => "belongs_to_#{i}")
|
110
|
+
has_many_object.save
|
111
|
+
|
112
|
+
paranoid_boolean = @paranoid_time_object.paranoid_booleans.create(:name => "boolean_#{i}")
|
113
|
+
paranoid_boolean.create_paranoid_has_one_dependant(:name => "has_one_#{i}")
|
114
|
+
paranoid_boolean.save
|
115
|
+
|
116
|
+
@paranoid_time_object.not_paranoids.create(:name => "not_paranoid_a#{i}")
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
@paranoid_time_object.create_not_paranoid(:name => "not_paranoid_belongs_to")
|
121
|
+
@paranoid_time_object.create_has_one_not_paranoid(:name => "has_one_not_paranoid")
|
122
|
+
|
123
|
+
assert_equal 3, ParanoidTime.count
|
124
|
+
assert_equal 3, ParanoidHasManyDependant.count
|
125
|
+
assert_equal 3, ParanoidBelongsDependant.count
|
126
|
+
assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.count
|
127
|
+
assert_equal 3, ParanoidHasOneDependant.count
|
128
|
+
assert_equal 5, NotParanoid.count
|
129
|
+
assert_equal 1, HasOneNotParanoid.count
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_recursive_fake_removal
|
133
|
+
setup_recursive_tests
|
134
|
+
|
135
|
+
@paranoid_time_object.destroy
|
136
|
+
|
137
|
+
assert_equal 2, ParanoidTime.count
|
138
|
+
assert_equal 0, ParanoidHasManyDependant.count
|
139
|
+
assert_equal 0, ParanoidBelongsDependant.count
|
140
|
+
assert_equal @paranoid_boolean_count, ParanoidBoolean.count
|
141
|
+
assert_equal 0, ParanoidHasOneDependant.count
|
142
|
+
assert_equal 1, NotParanoid.count
|
143
|
+
assert_equal 0, HasOneNotParanoid.count
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_recursive_real_removal
|
147
|
+
setup_recursive_tests
|
148
|
+
|
149
|
+
@paranoid_time_object.destroy!
|
150
|
+
|
151
|
+
assert_equal 0, ParanoidTime.only_deleted.count
|
152
|
+
assert_equal 1, ParanoidHasManyDependant.only_deleted.count
|
153
|
+
assert_equal 0, ParanoidBelongsDependant.only_deleted.count
|
154
|
+
assert_equal 0, ParanoidBoolean.only_deleted.count
|
155
|
+
assert_equal 0, ParanoidHasOneDependant.only_deleted.count
|
156
|
+
assert_equal 1, NotParanoid.count
|
157
|
+
assert_equal 0, HasOneNotParanoid.count
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_recursive_recovery
|
161
|
+
setup_recursive_tests
|
162
|
+
|
163
|
+
@paranoid_time_object.destroy
|
164
|
+
@paranoid_time_object.reload
|
165
|
+
|
166
|
+
@paranoid_time_object.recover(:recursive => true)
|
167
|
+
|
168
|
+
assert_equal 3, ParanoidTime.count
|
169
|
+
assert_equal 3, ParanoidHasManyDependant.count
|
170
|
+
assert_equal 3, ParanoidBelongsDependant.count
|
171
|
+
assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.count
|
172
|
+
assert_equal 3, ParanoidHasOneDependant.count
|
173
|
+
assert_equal 1, NotParanoid.count
|
174
|
+
assert_equal 0, HasOneNotParanoid.count
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_recursive_recovery_dependant_window
|
178
|
+
setup_recursive_tests
|
179
|
+
|
180
|
+
@paranoid_time_object.destroy
|
181
|
+
@paranoid_time_object.reload
|
182
|
+
|
183
|
+
# Stop the following from recovering:
|
184
|
+
# - ParanoidHasManyDependant and its ParanoidBelongsDependant
|
185
|
+
# - A single ParanoidBelongsDependant, but not its parent
|
186
|
+
dependants = @paranoid_time_object.paranoid_has_many_dependants.with_deleted
|
187
|
+
dependants.first.update_attribute(:deleted_at, 2.days.ago)
|
188
|
+
ParanoidBelongsDependant.with_deleted.where(:id => dependants.last.paranoid_belongs_dependant_id).first.update_attribute(:deleted_at, 1.hour.ago)
|
189
|
+
|
190
|
+
@paranoid_time_object.recover(:recursive => true)
|
191
|
+
|
192
|
+
assert_equal 3, ParanoidTime.count
|
193
|
+
assert_equal 2, ParanoidHasManyDependant.count
|
194
|
+
assert_equal 1, ParanoidBelongsDependant.count
|
195
|
+
assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.count
|
196
|
+
assert_equal 3, ParanoidHasOneDependant.count
|
197
|
+
assert_equal 1, NotParanoid.count
|
198
|
+
assert_equal 0, HasOneNotParanoid.count
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_recursive_recovery_for_belongs_to_polymorphic
|
202
|
+
child_1 = ParanoidAndroid.create
|
203
|
+
section_1 = ParanoidSection.create(:paranoid_thing => child_1)
|
204
|
+
|
205
|
+
child_2 = ParanoidHuman.create(:gender => 'male')
|
206
|
+
section_2 = ParanoidSection.create(:paranoid_thing => child_2)
|
207
|
+
|
208
|
+
assert_equal section_1.paranoid_thing, child_1
|
209
|
+
assert_equal section_1.paranoid_thing.class, ParanoidAndroid
|
210
|
+
assert_equal section_2.paranoid_thing, child_2
|
211
|
+
assert_equal section_2.paranoid_thing.class, ParanoidHuman
|
212
|
+
|
213
|
+
parent = ParanoidTime.create(:name => "paranoid_parent")
|
214
|
+
parent.paranoid_sections << section_1
|
215
|
+
parent.paranoid_sections << section_2
|
216
|
+
|
217
|
+
assert_equal 4, ParanoidTime.count
|
218
|
+
assert_equal 2, ParanoidSection.count
|
219
|
+
assert_equal 1, ParanoidAndroid.count
|
220
|
+
assert_equal 1, ParanoidHuman.count
|
221
|
+
|
222
|
+
parent.destroy
|
223
|
+
|
224
|
+
assert_equal 3, ParanoidTime.count
|
225
|
+
assert_equal 0, ParanoidSection.count
|
226
|
+
assert_equal 0, ParanoidAndroid.count
|
227
|
+
assert_equal 0, ParanoidHuman.count
|
228
|
+
|
229
|
+
parent.reload
|
230
|
+
parent.recover
|
231
|
+
|
232
|
+
assert_equal 4, ParanoidTime.count
|
233
|
+
assert_equal 2, ParanoidSection.count
|
234
|
+
assert_equal 1, ParanoidAndroid.count
|
235
|
+
assert_equal 1, ParanoidHuman.count
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_non_recursive_recovery
|
239
|
+
setup_recursive_tests
|
240
|
+
|
241
|
+
@paranoid_time_object.destroy
|
242
|
+
@paranoid_time_object.reload
|
243
|
+
|
244
|
+
@paranoid_time_object.recover(:recursive => false)
|
245
|
+
|
246
|
+
assert_equal 3, ParanoidTime.count
|
247
|
+
assert_equal 0, ParanoidHasManyDependant.count
|
248
|
+
assert_equal 0, ParanoidBelongsDependant.count
|
249
|
+
assert_equal @paranoid_boolean_count, ParanoidBoolean.count
|
250
|
+
assert_equal 0, ParanoidHasOneDependant.count
|
251
|
+
assert_equal 1, NotParanoid.count
|
252
|
+
assert_equal 0, HasOneNotParanoid.count
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_deleted?
|
256
|
+
ParanoidTime.first.destroy
|
257
|
+
assert ParanoidTime.with_deleted.first.deleted?
|
258
|
+
|
259
|
+
ParanoidString.first.destroy
|
260
|
+
assert ParanoidString.with_deleted.first.deleted?
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_paranoid_destroy_callbacks
|
264
|
+
@paranoid_with_callback = ParanoidWithCallback.first
|
265
|
+
ParanoidWithCallback.transaction do
|
266
|
+
@paranoid_with_callback.destroy
|
267
|
+
end
|
268
|
+
|
269
|
+
assert @paranoid_with_callback.called_before_destroy
|
270
|
+
assert @paranoid_with_callback.called_after_destroy
|
271
|
+
assert @paranoid_with_callback.called_after_commit_on_destroy
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_hard_destroy_callbacks
|
275
|
+
@paranoid_with_callback = ParanoidWithCallback.first
|
276
|
+
|
277
|
+
ParanoidWithCallback.transaction do
|
278
|
+
@paranoid_with_callback.destroy!
|
279
|
+
end
|
280
|
+
|
281
|
+
assert @paranoid_with_callback.called_before_destroy
|
282
|
+
assert @paranoid_with_callback.called_after_destroy
|
283
|
+
assert @paranoid_with_callback.called_after_commit_on_destroy
|
284
|
+
end
|
285
|
+
|
286
|
+
def test_recovery_callbacks
|
287
|
+
@paranoid_with_callback = ParanoidWithCallback.first
|
288
|
+
|
289
|
+
ParanoidWithCallback.transaction do
|
290
|
+
@paranoid_with_callback.destroy
|
291
|
+
|
292
|
+
assert_nil @paranoid_with_callback.called_before_recover
|
293
|
+
assert_nil @paranoid_with_callback.called_after_recover
|
294
|
+
|
295
|
+
@paranoid_with_callback.recover
|
296
|
+
end
|
297
|
+
|
298
|
+
assert @paranoid_with_callback.called_before_recover
|
299
|
+
assert @paranoid_with_callback.called_after_recover
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_delete_by_multiple_id_is_paranoid
|
303
|
+
model_a = ParanoidBelongsDependant.create
|
304
|
+
model_b = ParanoidBelongsDependant.create
|
305
|
+
ParanoidBelongsDependant.delete([model_a.id, model_b.id])
|
306
|
+
|
307
|
+
assert_paranoid_deletion(model_a)
|
308
|
+
assert_paranoid_deletion(model_b)
|
309
|
+
end
|
310
|
+
|
311
|
+
def test_destroy_by_multiple_id_is_paranoid
|
312
|
+
model_a = ParanoidBelongsDependant.create
|
313
|
+
model_b = ParanoidBelongsDependant.create
|
314
|
+
ParanoidBelongsDependant.destroy([model_a.id, model_b.id])
|
315
|
+
|
316
|
+
assert_paranoid_deletion(model_a)
|
317
|
+
assert_paranoid_deletion(model_b)
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_delete_by_single_id_is_paranoid
|
321
|
+
model = ParanoidBelongsDependant.create
|
322
|
+
ParanoidBelongsDependant.delete(model.id)
|
323
|
+
|
324
|
+
assert_paranoid_deletion(model)
|
325
|
+
end
|
326
|
+
|
327
|
+
def test_destroy_by_single_id_is_paranoid
|
328
|
+
model = ParanoidBelongsDependant.create
|
329
|
+
ParanoidBelongsDependant.destroy(model.id)
|
330
|
+
|
331
|
+
assert_paranoid_deletion(model)
|
332
|
+
end
|
333
|
+
|
334
|
+
def test_instance_delete_is_paranoid
|
335
|
+
model = ParanoidBelongsDependant.create
|
336
|
+
model.delete
|
337
|
+
|
338
|
+
assert_paranoid_deletion(model)
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_instance_destroy_is_paranoid
|
342
|
+
model = ParanoidBelongsDependant.create
|
343
|
+
model.destroy
|
344
|
+
|
345
|
+
assert_paranoid_deletion(model)
|
346
|
+
end
|
347
|
+
|
348
|
+
# Test string type columns that don't have a nil value when not deleted (Y/N for example)
|
349
|
+
def test_string_type_with_no_nil_value_before_destroy
|
350
|
+
ps = ParanoidString.create!(:deleted => 'not dead')
|
351
|
+
assert_equal 1, ParanoidString.where(:id => ps).count
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_string_type_with_no_nil_value_after_destroy
|
355
|
+
ps = ParanoidString.create!(:deleted => 'not dead')
|
356
|
+
ps.destroy
|
357
|
+
assert_equal 0, ParanoidString.where(:id => ps).count
|
358
|
+
end
|
359
|
+
|
360
|
+
def test_string_type_with_no_nil_value_before_destroy_with_deleted
|
361
|
+
ps = ParanoidString.create!(:deleted => 'not dead')
|
362
|
+
assert_equal 1, ParanoidString.with_deleted.where(:id => ps).count
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_string_type_with_no_nil_value_after_destroy_with_deleted
|
366
|
+
ps = ParanoidString.create!(:deleted => 'not dead')
|
367
|
+
ps.destroy
|
368
|
+
assert_equal 1, ParanoidString.with_deleted.where(:id => ps).count
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_string_type_with_no_nil_value_before_destroy_only_deleted
|
372
|
+
ps = ParanoidString.create!(:deleted => 'not dead')
|
373
|
+
assert_equal 0, ParanoidString.only_deleted.where(:id => ps).count
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_string_type_with_no_nil_value_after_destroy_only_deleted
|
377
|
+
ps = ParanoidString.create!(:deleted => 'not dead')
|
378
|
+
ps.destroy
|
379
|
+
assert_equal 1, ParanoidString.only_deleted.where(:id => ps).count
|
380
|
+
end
|
381
|
+
|
382
|
+
def test_string_type_with_no_nil_value_after_destroyed_twice
|
383
|
+
ps = ParanoidString.create!(:deleted => 'not dead')
|
384
|
+
2.times { ps.destroy }
|
385
|
+
assert_equal 0, ParanoidString.with_deleted.where(:id => ps).count
|
386
|
+
end
|
387
|
+
end
|