acts_as_paranoid 0.4.2 → 0.4.3.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/zzak/acts_as_paranoid.png?branch=master)](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
|