acts_as_paranoid 0.6.0 → 0.6.3
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 +4 -4
- data/CHANGELOG.md +64 -0
- data/README.md +40 -4
- data/lib/acts_as_paranoid.rb +5 -3
- data/lib/acts_as_paranoid/associations.rb +1 -1
- data/lib/acts_as_paranoid/core.rb +87 -18
- data/lib/acts_as_paranoid/validations.rb +10 -5
- data/lib/acts_as_paranoid/version.rb +1 -1
- data/test/test_associations.rb +46 -3
- data/test/test_core.rb +169 -0
- data/test/test_default_scopes.rb +0 -1
- data/test/test_helper.rb +50 -3
- metadata +30 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e2b91eb0df1ed0d8cfb306f7272860c3f03e1da75259b202d222c1fece04927
|
4
|
+
data.tar.gz: 6dc0038fd41642f40e774e32eebe18ee5da6eae6750077781902aed17975ad1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1587bb00c56acbb5daf5929ef4588f89622eb83141258c2f686ce5ed55719849fa17064bc011cf6cd3720d05f7900f1121442ba6abb71cdcc977f637800b00dc
|
7
|
+
data.tar.gz: 026be4a7b7eec225d6a1113af33aa610438bc4f895d8539e1bf6159f8bfe07bda3add7733ecdec982ac85a4b28f1e3abc9a4cd85ab56f07bc6f9fcb4bd20211b
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
Notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
## 0.6.3
|
6
|
+
|
7
|
+
* Update Travis CI configuration ([#137], by [Matijs van Zuijlen][mvz]).
|
8
|
+
* Add predicate to check if record was soft deleted or hard deleted ([#136],
|
9
|
+
by [Aymeric Le Dorze][aymeric-ledorze]).
|
10
|
+
* Add support for recover! method ([#75], by [vinoth][avinoth]).
|
11
|
+
* Fix a record being dirty after destroying it ([#135], by
|
12
|
+
[Aymeric Le Dorze][aymeric-ledorze]).
|
13
|
+
|
14
|
+
## 0.6.2
|
15
|
+
|
16
|
+
* Prevent recovery of non-deleted records
|
17
|
+
([#133], by [Mary Beliveau][marycodes2] and [Valerie Woolard][valeriecodes])
|
18
|
+
* Allow model to set `table_name` after `acts_as_paranoid` macro
|
19
|
+
([#131], by [Alex Wheeler][AlexWheeler]).
|
20
|
+
* Make counter cache work with a custom column name and with optional
|
21
|
+
associations ([#123], by [Ned Campion][nedcampion]).
|
22
|
+
|
23
|
+
## 0.6.1
|
24
|
+
|
25
|
+
* Add support for Rails 6 ([#124], by [Daniel Rice][danielricecodes],
|
26
|
+
[Josh Bryant][jbryant92], and [Romain Alexandre][RomainAlexandre])
|
27
|
+
* Add support for incrementing and decrementing counter cache columns on
|
28
|
+
associated objects ([#119], by [Dimitar Lukanov][shadydealer])
|
29
|
+
* Add `:double_tap_destroys_fully` option, with default `true` ([#116],
|
30
|
+
by [Michael Riviera][ri4a]).
|
31
|
+
* Officially support Ruby 2.6 ([#114], by [Matijs van Zuijlen][mvz]).
|
32
|
+
|
33
|
+
## 0.6.0 and earlier
|
34
|
+
|
35
|
+
(To be added)
|
36
|
+
|
37
|
+
<!-- Contributors -->
|
38
|
+
|
39
|
+
[ri4a]: https://github.com/ri4a
|
40
|
+
[mvz]: https://github.com/mvz
|
41
|
+
[shadydealer]: https://github.com/shadydealer
|
42
|
+
[danielricecodes]: https://github.com/danielricecodes
|
43
|
+
[jbryant92]: https://github.com/jbryant92
|
44
|
+
[nedcampion]: https://github.com/nedcampion
|
45
|
+
[RomainAlexandre]: https://github.com/RomainAlexandre
|
46
|
+
[AlexWheeler]: https://github.com/AlexWheeler
|
47
|
+
[marycodes2]: https://github.com/marycodes2
|
48
|
+
[valeriecodes]: https://github.com/valeriecodes
|
49
|
+
[aymeric-ledorze]: https://github.com/aymeric-ledorze
|
50
|
+
[avinoth]: https://github.com/avinoth
|
51
|
+
|
52
|
+
<!-- issues & pull requests -->
|
53
|
+
|
54
|
+
[#137]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/137
|
55
|
+
[#136]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/136
|
56
|
+
[#135]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/135
|
57
|
+
[#133]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/133
|
58
|
+
[#131]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/131
|
59
|
+
[#124]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/124
|
60
|
+
[#123]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/123
|
61
|
+
[#119]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/119
|
62
|
+
[#116]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/116
|
63
|
+
[#114]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/114
|
64
|
+
[#75]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/75
|
data/README.md
CHANGED
@@ -9,12 +9,12 @@ recoverable later.
|
|
9
9
|
|
10
10
|
## Support
|
11
11
|
|
12
|
-
**This branch targets Rails 4.2, 5.0 and 5.1, with experimental support for 5.2
|
12
|
+
**This branch targets Rails 4.2, 5.0 and 5.1, with experimental support for 5.2+**
|
13
13
|
|
14
|
-
If you're working with
|
14
|
+
If you're working with Rails 4.1-, switch to the corresponding branch, or
|
15
15
|
require an older version of the `acts_as_paranoid` gem.
|
16
16
|
|
17
|
-
### Known issues with Rails 5.2
|
17
|
+
### Known issues with Rails 5.2+
|
18
18
|
|
19
19
|
* Using acts_as_paranoid and ActiveStorage on the same model
|
20
20
|
[leads to a SystemStackError](https://github.com/ActsAsParanoid/acts_as_paranoid/issues/103).
|
@@ -114,6 +114,10 @@ p.destroy # does NOT delete the first record, just hides it
|
|
114
114
|
Paranoiac.only_deleted.where(:id => p.id).first.destroy # deletes the first record from the database
|
115
115
|
```
|
116
116
|
|
117
|
+
This behaviour can be disabled by setting the configuration option. In a future version, `false` will be the default setting.
|
118
|
+
|
119
|
+
- `:double_tap_destroys_fully => false`
|
120
|
+
|
117
121
|
### Recovery
|
118
122
|
|
119
123
|
Recovery is easy. Just invoke `recover` on it, like this:
|
@@ -167,6 +171,22 @@ or in the recover statement
|
|
167
171
|
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:recovery_window => 30.seconds)
|
168
172
|
```
|
169
173
|
|
174
|
+
### recover!
|
175
|
+
|
176
|
+
You can invoke `recover!` if you wish to raise an error if the recovery fails. The error generally stems from ActiveRecord.
|
177
|
+
|
178
|
+
```
|
179
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover!
|
180
|
+
|
181
|
+
### => ActiveRecord::RecordInvalid: Validation failed: Name already exists
|
182
|
+
```
|
183
|
+
|
184
|
+
Optionally, you may also raise the error by passing `:raise_error => true` to the `recover` method. This behaves the same as `recover!`.
|
185
|
+
|
186
|
+
```
|
187
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:raise_error => true)
|
188
|
+
```
|
189
|
+
|
170
190
|
### Validation
|
171
191
|
ActiveRecord's built-in uniqueness validation does not account for records deleted by ActsAsParanoid. If you want to check for uniqueness among non-deleted records only, use the macro `validates_as_paranoid` in your model. Then, instead of using `validates_uniqueness_of`, use `validates_uniqueness_of_without_deleted`. This will keep deleted records from counting against the uniqueness check.
|
172
192
|
|
@@ -259,11 +279,26 @@ child.parent #=> nil
|
|
259
279
|
child.parent_including_deleted #=> Parent (it works!)
|
260
280
|
```
|
261
281
|
|
282
|
+
### Callbacks
|
283
|
+
|
284
|
+
There are couple of callbacks that you may use when dealing with deletion and recovery of objects. There is `before_recover` and `after_recover` which will be
|
285
|
+
triggered before and after the recovery of an object respectively.
|
286
|
+
|
287
|
+
Default ActiveRecord callbaks such as `before_destroy` and `after_destroy` will be triggered around `.destroy!` and `.destroy_fully!`.
|
288
|
+
|
289
|
+
```
|
290
|
+
class Paranoiac < ActiveRecord::Base
|
291
|
+
acts_as_paranoid
|
292
|
+
|
293
|
+
before_recover :set_counts
|
294
|
+
after_recover :update_logs
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
262
298
|
## Caveats
|
263
299
|
|
264
300
|
Watch out for these caveats:
|
265
301
|
|
266
|
-
|
267
302
|
- You cannot use scopes named `with_deleted` and `only_deleted`
|
268
303
|
- You cannot use scopes named `deleted_inside_time_window`, `deleted_before_time`, `deleted_after_time` **if** your paranoid column's type is `time`
|
269
304
|
- You cannot name association `*_with_deleted`
|
@@ -283,5 +318,6 @@ Watch out for these caveats:
|
|
283
318
|
* To [Jean Boussier](https://github.com/byroot) for initial Rails 4.0.0 support
|
284
319
|
* To [Matijs van Zuijlen](https://github.com/mvz) for Rails 4.1 and 4.2 support
|
285
320
|
* To [Andrey Ponomarenko](https://github.com/sjke) for Rails 5 support
|
321
|
+
* To [Daniel Rice](https://github.com/danielricecodes), [Josh Bryant](https://github.com/jbryant92), and [Romain Alexandre](https://github.com/RomainAlexandre) for Rails 6.0 support.
|
286
322
|
|
287
323
|
See `LICENSE`.
|
data/lib/acts_as_paranoid.rb
CHANGED
@@ -17,16 +17,18 @@ module ActsAsParanoid
|
|
17
17
|
def acts_as_paranoid(options = {})
|
18
18
|
raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
|
19
19
|
|
20
|
-
class_attribute :paranoid_configuration
|
20
|
+
class_attribute :paranoid_configuration
|
21
21
|
|
22
|
-
self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes, :recovery_value => nil }
|
22
|
+
self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes, :recovery_value => nil, double_tap_destroys_fully: true }
|
23
23
|
self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
|
24
24
|
self.paranoid_configuration.merge!({ :allow_nulls => true }) if options[:column_type] == "boolean"
|
25
25
|
self.paranoid_configuration.merge!(options) # user options
|
26
26
|
|
27
27
|
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]
|
28
28
|
|
29
|
-
self.paranoid_column_reference
|
29
|
+
def self.paranoid_column_reference
|
30
|
+
"#{self.table_name}.#{paranoid_configuration[:column]}"
|
31
|
+
end
|
30
32
|
|
31
33
|
return if paranoid?
|
32
34
|
|
@@ -31,7 +31,7 @@ module ActsAsParanoid
|
|
31
31
|
association = association(:#{target})
|
32
32
|
return nil if association.options[:polymorphic] && association.klass.nil?
|
33
33
|
return #{target}_without_unscoped(*args) unless association.klass.paranoid?
|
34
|
-
association.klass.with_deleted.scoping { #{target}_without_unscoped(*args) }
|
34
|
+
association.klass.with_deleted.scoping { association.klass.unscoped { #{target}_without_unscoped(*args) } }
|
35
35
|
end
|
36
36
|
alias_method :#{target}_without_unscoped, :#{target}
|
37
37
|
alias_method :#{target}, :#{target}_with_unscoped
|
@@ -108,7 +108,7 @@ module ActsAsParanoid
|
|
108
108
|
# Straight from ActiveRecord 5.1!
|
109
109
|
def delete
|
110
110
|
self.class.delete(id) if persisted?
|
111
|
-
|
111
|
+
stale_paranoid_value
|
112
112
|
freeze
|
113
113
|
end
|
114
114
|
|
@@ -116,9 +116,16 @@ module ActsAsParanoid
|
|
116
116
|
with_transaction_returning_status do
|
117
117
|
run_callbacks :destroy do
|
118
118
|
destroy_dependent_associations!
|
119
|
-
|
120
|
-
|
121
|
-
|
119
|
+
|
120
|
+
if persisted?
|
121
|
+
# Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
|
122
|
+
self.class.delete_all!(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
|
123
|
+
|
124
|
+
decrement_counters_on_associations
|
125
|
+
end
|
126
|
+
|
127
|
+
stale_paranoid_value
|
128
|
+
@destroyed = true
|
122
129
|
freeze
|
123
130
|
end
|
124
131
|
end
|
@@ -128,40 +135,57 @@ module ActsAsParanoid
|
|
128
135
|
if !deleted?
|
129
136
|
with_transaction_returning_status do
|
130
137
|
run_callbacks :destroy do
|
131
|
-
# Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
|
132
|
-
@_trigger_destroy_callback = if persisted?
|
133
|
-
self.class.delete_all(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
|
134
|
-
else
|
135
|
-
true
|
136
|
-
end
|
137
138
|
|
138
|
-
|
139
|
+
if persisted?
|
140
|
+
# Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
|
141
|
+
self.class.delete_all(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
|
142
|
+
|
143
|
+
decrement_counters_on_associations
|
144
|
+
end
|
145
|
+
|
146
|
+
@_trigger_destroy_callback = true
|
147
|
+
|
148
|
+
stale_paranoid_value
|
139
149
|
self
|
140
150
|
end
|
141
151
|
end
|
142
152
|
else
|
143
|
-
|
153
|
+
if paranoid_configuration[:double_tap_destroys_fully]
|
154
|
+
destroy_fully!
|
155
|
+
end
|
144
156
|
end
|
145
157
|
end
|
146
158
|
|
147
159
|
alias_method :destroy, :destroy!
|
148
160
|
|
149
161
|
def recover(options={})
|
162
|
+
return if !self.deleted?
|
150
163
|
options = {
|
151
164
|
:recursive => self.class.paranoid_configuration[:recover_dependent_associations],
|
152
|
-
:recovery_window => self.class.paranoid_configuration[:dependent_recovery_window]
|
165
|
+
:recovery_window => self.class.paranoid_configuration[:dependent_recovery_window],
|
166
|
+
:raise_error => false
|
153
167
|
}.merge(options)
|
154
168
|
|
155
169
|
self.class.transaction do
|
156
170
|
run_callbacks :recover do
|
157
171
|
recover_dependent_associations(options[:recovery_window], options) if options[:recursive]
|
158
|
-
|
172
|
+
increment_counters_on_associations
|
159
173
|
self.paranoid_value = self.class.paranoid_configuration[:recovery_value]
|
160
|
-
|
174
|
+
if options[:raise_error]
|
175
|
+
self.save!
|
176
|
+
else
|
177
|
+
self.save
|
178
|
+
end
|
161
179
|
end
|
162
180
|
end
|
163
181
|
end
|
164
182
|
|
183
|
+
def recover!(options={})
|
184
|
+
options[:raise_error] = true
|
185
|
+
|
186
|
+
recover(options)
|
187
|
+
end
|
188
|
+
|
165
189
|
def recover_dependent_associations(window, options)
|
166
190
|
self.class.dependent_associations.each do |reflection|
|
167
191
|
next unless (klass = get_reflection_class(reflection)).paranoid?
|
@@ -169,7 +193,11 @@ module ActsAsParanoid
|
|
169
193
|
scope = klass.only_deleted
|
170
194
|
|
171
195
|
# Merge in the association's scope
|
172
|
-
scope =
|
196
|
+
scope = if ActiveRecord::VERSION::MAJOR >= 6
|
197
|
+
scope.merge(ActiveRecord::Associations::AssociationScope.scope(association(reflection.name)))
|
198
|
+
else
|
199
|
+
scope.merge(association(reflection.name).association_scope)
|
200
|
+
end
|
173
201
|
|
174
202
|
# We can only recover by window if both parent and dependant have a
|
175
203
|
# paranoid column type of :time.
|
@@ -190,7 +218,11 @@ module ActsAsParanoid
|
|
190
218
|
scope = klass.only_deleted
|
191
219
|
|
192
220
|
# Merge in the association's scope
|
193
|
-
scope =
|
221
|
+
scope = if ActiveRecord::VERSION::MAJOR >= 6
|
222
|
+
scope.merge(ActiveRecord::Associations::AssociationScope.scope(association(reflection.name)))
|
223
|
+
else
|
224
|
+
scope.merge(association(reflection.name).association_scope)
|
225
|
+
end
|
194
226
|
|
195
227
|
scope.each do |object|
|
196
228
|
object.destroy!
|
@@ -199,7 +231,7 @@ module ActsAsParanoid
|
|
199
231
|
end
|
200
232
|
|
201
233
|
def deleted?
|
202
|
-
!if self.class.string_type_with_deleted_value?
|
234
|
+
@destroyed || !if self.class.string_type_with_deleted_value?
|
203
235
|
paranoid_value != self.class.delete_now_value || paranoid_value.nil?
|
204
236
|
elsif self.class.boolean_type_not_nullable?
|
205
237
|
paranoid_value == false
|
@@ -210,6 +242,12 @@ module ActsAsParanoid
|
|
210
242
|
|
211
243
|
alias_method :destroyed?, :deleted?
|
212
244
|
|
245
|
+
def deleted_fully?
|
246
|
+
@destroyed
|
247
|
+
end
|
248
|
+
|
249
|
+
alias_method :destroyed_fully?, :deleted_fully?
|
250
|
+
|
213
251
|
private
|
214
252
|
|
215
253
|
def get_reflection_class(reflection)
|
@@ -223,5 +261,36 @@ module ActsAsParanoid
|
|
223
261
|
def paranoid_value=(value)
|
224
262
|
self.send("#{self.class.paranoid_column}=", value)
|
225
263
|
end
|
264
|
+
|
265
|
+
def update_counters_on_associations method_sym
|
266
|
+
|
267
|
+
return unless [:decrement_counter, :increment_counter].include? method_sym
|
268
|
+
|
269
|
+
each_counter_cached_association_reflection do |assoc_reflection|
|
270
|
+
if associated_object = send(assoc_reflection.name)
|
271
|
+
counter_cache_column = assoc_reflection.counter_cache_column
|
272
|
+
associated_object.class.send(method_sym, counter_cache_column, associated_object.id)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def each_counter_cached_association_reflection
|
278
|
+
_reflections.each do |name, reflection|
|
279
|
+
yield reflection if reflection.belongs_to? && reflection.counter_cache_column
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def increment_counters_on_associations
|
284
|
+
update_counters_on_associations :increment_counter
|
285
|
+
end
|
286
|
+
|
287
|
+
def decrement_counters_on_associations
|
288
|
+
update_counters_on_associations :decrement_counter
|
289
|
+
end
|
290
|
+
|
291
|
+
def stale_paranoid_value
|
292
|
+
self.paranoid_value = self.class.delete_now_value
|
293
|
+
clear_attribute_changes([self.class.paranoid_column])
|
294
|
+
end
|
226
295
|
end
|
227
296
|
end
|
@@ -8,10 +8,9 @@ module ActsAsParanoid
|
|
8
8
|
|
9
9
|
class UniquenessWithoutDeletedValidator
|
10
10
|
def self.[](version)
|
11
|
-
|
12
|
-
name = "V#{version.tr('.', '_')}"
|
11
|
+
name = "V#{version.to_s.tr('.', '_')}"
|
13
12
|
unless constants.include? name.to_sym
|
14
|
-
raise "Unknown validator version #{
|
13
|
+
raise "Unknown validator version #{name.inspect}; expected one of #{constants.sort.join(', ')}"
|
15
14
|
end
|
16
15
|
const_get name
|
17
16
|
end
|
@@ -38,8 +37,11 @@ module ActsAsParanoid
|
|
38
37
|
protected
|
39
38
|
|
40
39
|
def build_relation(klass, attribute, value)
|
41
|
-
|
42
|
-
|
40
|
+
if ActiveRecord::VERSION::MINOR == 0 && ActiveRecord::VERSION::MAJOR == 5
|
41
|
+
return super(klass, klass.arel_table, attribute, value)
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
@@ -75,6 +77,9 @@ module ActsAsParanoid
|
|
75
77
|
end
|
76
78
|
end
|
77
79
|
end
|
80
|
+
|
81
|
+
class V6 < V5
|
82
|
+
end
|
78
83
|
end
|
79
84
|
|
80
85
|
module ClassMethods
|
data/test/test_associations.rb
CHANGED
@@ -41,6 +41,48 @@ class AssociationsTest < ParanoidBaseTest
|
|
41
41
|
includes_values = ParanoidTime.includes(:not_paranoid).includes_values
|
42
42
|
|
43
43
|
assert_equal includes_values, paranoid_has_many_dependant.association(:paranoid_time_with_scope).scope.includes_values
|
44
|
+
|
45
|
+
paranoid_time = ParanoidTime.create(name: 'not-hello')
|
46
|
+
paranoid_has_many_dependant.paranoid_time = paranoid_time
|
47
|
+
paranoid_has_many_dependant.save!
|
48
|
+
|
49
|
+
assert_nil paranoid_has_many_dependant.paranoid_time_with_scope
|
50
|
+
|
51
|
+
paranoid_time.update(name: 'hello')
|
52
|
+
|
53
|
+
paranoid_has_many_dependant.reload
|
54
|
+
|
55
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_scope
|
56
|
+
|
57
|
+
paranoid_time.destroy
|
58
|
+
|
59
|
+
paranoid_has_many_dependant.reload
|
60
|
+
|
61
|
+
assert_nil paranoid_has_many_dependant.paranoid_time_with_scope
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_belongs_to_with_scope_and_deleted_option
|
65
|
+
paranoid_has_many_dependant = ParanoidHasManyDependant.new
|
66
|
+
includes_values = ParanoidTime.includes(:not_paranoid).includes_values
|
67
|
+
|
68
|
+
assert_equal includes_values, paranoid_has_many_dependant
|
69
|
+
.association(:paranoid_time_with_scope_with_deleted).scope.includes_values
|
70
|
+
|
71
|
+
paranoid_time = ParanoidTime.create(name: 'not-hello')
|
72
|
+
paranoid_has_many_dependant.paranoid_time = paranoid_time
|
73
|
+
paranoid_has_many_dependant.save!
|
74
|
+
|
75
|
+
assert_nil paranoid_has_many_dependant.paranoid_time_with_scope_with_deleted
|
76
|
+
|
77
|
+
paranoid_time.update(name: 'hello')
|
78
|
+
paranoid_has_many_dependant.reload
|
79
|
+
|
80
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_scope_with_deleted
|
81
|
+
|
82
|
+
paranoid_time.destroy
|
83
|
+
paranoid_has_many_dependant.reload
|
84
|
+
|
85
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_scope_with_deleted
|
44
86
|
end
|
45
87
|
|
46
88
|
def test_belongs_to_with_deleted
|
@@ -51,9 +93,10 @@ class AssociationsTest < ParanoidBaseTest
|
|
51
93
|
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted
|
52
94
|
|
53
95
|
paranoid_time.destroy
|
96
|
+
paranoid_has_many_dependant.reload
|
54
97
|
|
55
|
-
assert_nil paranoid_has_many_dependant.
|
56
|
-
assert_equal paranoid_time, paranoid_has_many_dependant.
|
98
|
+
assert_nil paranoid_has_many_dependant.paranoid_time
|
99
|
+
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted
|
57
100
|
end
|
58
101
|
|
59
102
|
def test_belongs_to_polymorphic_with_deleted
|
@@ -228,7 +271,7 @@ class AssociationsTest < ParanoidBaseTest
|
|
228
271
|
end
|
229
272
|
|
230
273
|
def test_mass_assignment_of_paranoid_column_enabled
|
231
|
-
if
|
274
|
+
if Gem.loaded_specs['activerecord'].version >= Gem::Version.new('5.2.0')
|
232
275
|
skip 'Creation as deleted is not supported with Rails >= 5.2'
|
233
276
|
end
|
234
277
|
now = Time.now
|
data/test/test_core.rb
CHANGED
@@ -69,6 +69,13 @@ class ParanoidTest < ParanoidBaseTest
|
|
69
69
|
assert_not_nil pt.paranoid_value
|
70
70
|
end
|
71
71
|
|
72
|
+
def test_non_persisted_delete
|
73
|
+
pt = ParanoidTime.new
|
74
|
+
assert_nil pt.paranoid_value
|
75
|
+
pt.delete
|
76
|
+
assert_not_nil pt.paranoid_value
|
77
|
+
end
|
78
|
+
|
72
79
|
def test_non_persisted_destroy!
|
73
80
|
pt = ParanoidTime.new
|
74
81
|
assert_nil pt.paranoid_value
|
@@ -94,6 +101,15 @@ class ParanoidTest < ParanoidBaseTest
|
|
94
101
|
assert_equal 1, ParanoidString.count
|
95
102
|
end
|
96
103
|
|
104
|
+
def test_recovery!
|
105
|
+
ParanoidBoolean.first.destroy
|
106
|
+
ParanoidBoolean.create(:name => 'paranoid')
|
107
|
+
|
108
|
+
assert_raise do
|
109
|
+
ParanoidBoolean.only_deleted.first.recover!
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
97
113
|
def setup_recursive_tests
|
98
114
|
@paranoid_time_object = ParanoidTime.first
|
99
115
|
|
@@ -265,6 +281,24 @@ class ParanoidTest < ParanoidBaseTest
|
|
265
281
|
assert_equal 0, HasOneNotParanoid.count
|
266
282
|
end
|
267
283
|
|
284
|
+
def test_dirty
|
285
|
+
pt = ParanoidTime.create
|
286
|
+
pt.destroy
|
287
|
+
assert_not pt.changed?
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_delete_dirty
|
291
|
+
pt = ParanoidTime.create
|
292
|
+
pt.delete
|
293
|
+
assert_not pt.changed?
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_destroy_fully_dirty
|
297
|
+
pt = ParanoidTime.create
|
298
|
+
pt.destroy_fully!
|
299
|
+
assert_not pt.changed?
|
300
|
+
end
|
301
|
+
|
268
302
|
def test_deleted?
|
269
303
|
ParanoidTime.first.destroy
|
270
304
|
assert ParanoidTime.with_deleted.first.deleted?
|
@@ -273,6 +307,43 @@ class ParanoidTest < ParanoidBaseTest
|
|
273
307
|
assert ParanoidString.with_deleted.first.deleted?
|
274
308
|
end
|
275
309
|
|
310
|
+
def test_delete_deleted?
|
311
|
+
ParanoidTime.first.delete
|
312
|
+
assert ParanoidTime.with_deleted.first.deleted?
|
313
|
+
|
314
|
+
ParanoidString.first.delete
|
315
|
+
assert ParanoidString.with_deleted.first.deleted?
|
316
|
+
end
|
317
|
+
|
318
|
+
def test_destroy_fully_deleted?
|
319
|
+
object = ParanoidTime.first
|
320
|
+
object.destroy_fully!
|
321
|
+
assert object.deleted?
|
322
|
+
|
323
|
+
object = ParanoidString.first
|
324
|
+
object.destroy_fully!
|
325
|
+
assert object.deleted?
|
326
|
+
end
|
327
|
+
|
328
|
+
def test_deleted_fully?
|
329
|
+
ParanoidTime.first.destroy
|
330
|
+
assert_not ParanoidTime.with_deleted.first.deleted_fully?
|
331
|
+
|
332
|
+
ParanoidString.first.destroy
|
333
|
+
assert ParanoidString.with_deleted.first.deleted?
|
334
|
+
end
|
335
|
+
|
336
|
+
def test_delete_deleted_fully?
|
337
|
+
ParanoidTime.first.delete
|
338
|
+
assert_not ParanoidTime.with_deleted.first.deleted_fully?
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_destroy_fully_deleted_fully?
|
342
|
+
object = ParanoidTime.first
|
343
|
+
object.destroy_fully!
|
344
|
+
assert object.deleted_fully?
|
345
|
+
end
|
346
|
+
|
276
347
|
def test_paranoid_destroy_callbacks
|
277
348
|
@paranoid_with_callback = ParanoidWithCallback.first
|
278
349
|
ParanoidWithCallback.transaction do
|
@@ -312,6 +383,14 @@ class ParanoidTest < ParanoidBaseTest
|
|
312
383
|
assert @paranoid_with_callback.called_after_recover
|
313
384
|
end
|
314
385
|
|
386
|
+
def test_recovery_callbacks_without_destroy
|
387
|
+
@paranoid_with_callback = ParanoidWithCallback.first
|
388
|
+
@paranoid_with_callback.recover
|
389
|
+
|
390
|
+
assert_nil @paranoid_with_callback.called_before_recover
|
391
|
+
assert_nil @paranoid_with_callback.called_after_recover
|
392
|
+
end
|
393
|
+
|
315
394
|
def test_delete_by_multiple_id_is_paranoid
|
316
395
|
model_a = ParanoidBelongsDependant.create
|
317
396
|
model_b = ParanoidBelongsDependant.create
|
@@ -437,4 +516,94 @@ class ParanoidTest < ParanoidBaseTest
|
|
437
516
|
2.times { ps.destroy }
|
438
517
|
assert_equal 0, ParanoidBooleanNotNullable.with_deleted.where(:id => ps).count
|
439
518
|
end
|
519
|
+
|
520
|
+
def test_no_double_tap_destroys_fully
|
521
|
+
ps = ParanoidNoDoubleTapDestroysFully.create!()
|
522
|
+
2.times { ps.destroy }
|
523
|
+
assert_equal 1, ParanoidNoDoubleTapDestroysFully.with_deleted.where(:id => ps).count
|
524
|
+
end
|
525
|
+
|
526
|
+
def test_decrement_counters
|
527
|
+
paranoid_boolean = ParanoidBoolean.create!()
|
528
|
+
paranoid_with_counter_cache = ParanoidWithCounterCache.create!(paranoid_boolean: paranoid_boolean)
|
529
|
+
|
530
|
+
assert_equal 1, paranoid_boolean.paranoid_with_counter_caches_count
|
531
|
+
|
532
|
+
paranoid_with_counter_cache.destroy
|
533
|
+
|
534
|
+
assert_equal 0, paranoid_boolean.reload.paranoid_with_counter_caches_count
|
535
|
+
end
|
536
|
+
|
537
|
+
def test_decrement_custom_counters
|
538
|
+
paranoid_boolean = ParanoidBoolean.create!()
|
539
|
+
paranoid_with_custom_counter_cache = ParanoidWithCustomCounterCache.create!(paranoid_boolean: paranoid_boolean)
|
540
|
+
|
541
|
+
assert_equal 1, paranoid_boolean.custom_counter_cache
|
542
|
+
|
543
|
+
paranoid_with_custom_counter_cache.destroy
|
544
|
+
|
545
|
+
assert_equal 0, paranoid_boolean.reload.custom_counter_cache
|
546
|
+
end
|
547
|
+
|
548
|
+
def test_destroy_with_optional_belongs_to_and_counter_cache
|
549
|
+
ps = ParanoidWithCounterCacheOnOptionalBelognsTo.create!()
|
550
|
+
ps.destroy
|
551
|
+
assert_equal 1, ParanoidWithCounterCacheOnOptionalBelognsTo.only_deleted.where(:id => ps).count
|
552
|
+
end
|
553
|
+
|
554
|
+
def test_hard_destroy_decrement_counters
|
555
|
+
paranoid_boolean = ParanoidBoolean.create!()
|
556
|
+
paranoid_with_counter_cache = ParanoidWithCounterCache.create!(paranoid_boolean: paranoid_boolean)
|
557
|
+
|
558
|
+
assert_equal 1, paranoid_boolean.paranoid_with_counter_caches_count
|
559
|
+
|
560
|
+
paranoid_with_counter_cache.destroy_fully!
|
561
|
+
|
562
|
+
assert_equal 0, paranoid_boolean.reload.paranoid_with_counter_caches_count
|
563
|
+
end
|
564
|
+
|
565
|
+
def test_hard_destroy_decrement_custom_counters
|
566
|
+
paranoid_boolean = ParanoidBoolean.create!()
|
567
|
+
paranoid_with_custom_counter_cache = ParanoidWithCustomCounterCache.create!(paranoid_boolean: paranoid_boolean)
|
568
|
+
|
569
|
+
assert_equal 1, paranoid_boolean.custom_counter_cache
|
570
|
+
|
571
|
+
paranoid_with_custom_counter_cache.destroy_fully!
|
572
|
+
|
573
|
+
assert_equal 0, paranoid_boolean.reload.custom_counter_cache
|
574
|
+
end
|
575
|
+
|
576
|
+
def test_increment_counters
|
577
|
+
paranoid_boolean = ParanoidBoolean.create!()
|
578
|
+
paranoid_with_counter_cache = ParanoidWithCounterCache.create!(paranoid_boolean: paranoid_boolean)
|
579
|
+
|
580
|
+
assert_equal 1, paranoid_boolean.paranoid_with_counter_caches_count
|
581
|
+
|
582
|
+
paranoid_with_counter_cache.destroy
|
583
|
+
|
584
|
+
assert_equal 0, paranoid_boolean.reload.paranoid_with_counter_caches_count
|
585
|
+
|
586
|
+
paranoid_with_counter_cache.recover
|
587
|
+
|
588
|
+
assert_equal 1, paranoid_boolean.reload.paranoid_with_counter_caches_count
|
589
|
+
end
|
590
|
+
|
591
|
+
def test_increment_custom_counters
|
592
|
+
paranoid_boolean = ParanoidBoolean.create!()
|
593
|
+
paranoid_with_custom_counter_cache = ParanoidWithCustomCounterCache.create!(paranoid_boolean: paranoid_boolean)
|
594
|
+
|
595
|
+
assert_equal 1, paranoid_boolean.custom_counter_cache
|
596
|
+
|
597
|
+
paranoid_with_custom_counter_cache.destroy
|
598
|
+
|
599
|
+
assert_equal 0, paranoid_boolean.reload.custom_counter_cache
|
600
|
+
|
601
|
+
paranoid_with_custom_counter_cache.recover
|
602
|
+
|
603
|
+
assert_equal 1, paranoid_boolean.reload.custom_counter_cache
|
604
|
+
end
|
605
|
+
|
606
|
+
def test_explicitly_setting_table_name_after_acts_as_paranoid_macro
|
607
|
+
assert_equal "explicit_table.deleted_at", ParanoidWithExplicitTableNameAfterMacro.paranoid_column_reference
|
608
|
+
end
|
440
609
|
end
|
data/test/test_default_scopes.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -31,7 +31,8 @@ def setup_db
|
|
31
31
|
t.string :name
|
32
32
|
t.boolean :is_deleted
|
33
33
|
t.integer :paranoid_time_id
|
34
|
-
|
34
|
+
t.integer :paranoid_with_counter_caches_count
|
35
|
+
t.integer :custom_counter_cache
|
35
36
|
timestamps t
|
36
37
|
end
|
37
38
|
|
@@ -148,7 +149,7 @@ def setup_db
|
|
148
149
|
timestamps t
|
149
150
|
end
|
150
151
|
|
151
|
-
|
152
|
+
create_table :paranoid_forests do |t|
|
152
153
|
t.string :name
|
153
154
|
t.boolean :rainforest
|
154
155
|
t.datetime :deleted_at
|
@@ -208,6 +209,18 @@ def setup_db
|
|
208
209
|
|
209
210
|
timestamps t
|
210
211
|
end
|
212
|
+
|
213
|
+
create_table :paranoid_no_double_tap_destroys_fullies do |t|
|
214
|
+
t.datetime :deleted_at
|
215
|
+
end
|
216
|
+
|
217
|
+
create_table :paranoid_with_counter_caches do |t|
|
218
|
+
t.string :name
|
219
|
+
t.datetime :deleted_at
|
220
|
+
t.integer :paranoid_boolean_id
|
221
|
+
|
222
|
+
timestamps t
|
223
|
+
end
|
211
224
|
end
|
212
225
|
end
|
213
226
|
|
@@ -247,6 +260,8 @@ class ParanoidBoolean < ActiveRecord::Base
|
|
247
260
|
|
248
261
|
belongs_to :paranoid_time
|
249
262
|
has_one :paranoid_has_one_dependant, :dependent => :destroy
|
263
|
+
has_many :paranoid_with_counter_cache, :dependent => :destroy
|
264
|
+
has_many :paranoid_with_custom_counter_cache, :dependent => :destroy
|
250
265
|
end
|
251
266
|
|
252
267
|
class ParanoidString < ActiveRecord::Base
|
@@ -256,6 +271,10 @@ end
|
|
256
271
|
class NotParanoid < ActiveRecord::Base
|
257
272
|
end
|
258
273
|
|
274
|
+
class ParanoidNoDoubleTapDestroysFully < ActiveRecord::Base
|
275
|
+
acts_as_paranoid :double_tap_destroys_fully => false
|
276
|
+
end
|
277
|
+
|
259
278
|
class HasOneNotParanoid < ActiveRecord::Base
|
260
279
|
belongs_to :paranoid_time, :with_deleted => true
|
261
280
|
end
|
@@ -265,11 +284,35 @@ class DoubleHasOneNotParanoid < HasOneNotParanoid
|
|
265
284
|
belongs_to :paranoid_time, :with_deleted => true
|
266
285
|
end
|
267
286
|
|
287
|
+
class ParanoidWithCounterCache < ActiveRecord::Base
|
288
|
+
acts_as_paranoid
|
289
|
+
belongs_to :paranoid_boolean, :counter_cache => true
|
290
|
+
end
|
291
|
+
|
292
|
+
class ParanoidWithCustomCounterCache < ActiveRecord::Base
|
293
|
+
self.table_name = "paranoid_with_counter_caches"
|
294
|
+
|
295
|
+
acts_as_paranoid
|
296
|
+
belongs_to :paranoid_boolean, counter_cache: :custom_counter_cache
|
297
|
+
end
|
298
|
+
|
299
|
+
class ParanoidWithCounterCacheOnOptionalBelognsTo < ActiveRecord::Base
|
300
|
+
self.table_name = "paranoid_with_counter_caches"
|
301
|
+
|
302
|
+
acts_as_paranoid
|
303
|
+
if ActiveRecord::VERSION::MAJOR < 5
|
304
|
+
belongs_to :paranoid_boolean, counter_cache: true, required: false
|
305
|
+
else
|
306
|
+
belongs_to :paranoid_boolean, counter_cache: true, optional: true
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
268
310
|
class ParanoidHasManyDependant < ActiveRecord::Base
|
269
311
|
acts_as_paranoid
|
270
312
|
belongs_to :paranoid_time
|
271
|
-
belongs_to :paranoid_time_with_scope, -> { includes(:not_paranoid) }, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id
|
313
|
+
belongs_to :paranoid_time_with_scope, -> { where(name: 'hello').includes(:not_paranoid) }, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id
|
272
314
|
belongs_to :paranoid_time_with_deleted, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :with_deleted => true
|
315
|
+
belongs_to :paranoid_time_with_scope_with_deleted, -> { where(name: 'hello').includes(:not_paranoid) }, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :with_deleted => true
|
273
316
|
belongs_to :paranoid_time_polymorphic_with_deleted, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :polymorphic => true, :with_deleted => true
|
274
317
|
|
275
318
|
belongs_to :paranoid_belongs_dependant, :dependent => :destroy
|
@@ -470,3 +513,7 @@ class ParanoidBooleanNotNullable < ActiveRecord::Base
|
|
470
513
|
acts_as_paranoid column: 'deleted', column_type: 'boolean', allow_nulls: false
|
471
514
|
end
|
472
515
|
|
516
|
+
class ParanoidWithExplicitTableNameAfterMacro < ActiveRecord::Base
|
517
|
+
acts_as_paranoid
|
518
|
+
self.table_name = "explicit_table"
|
519
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_paranoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zachary Scott
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-01-25 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '4.2'
|
22
22
|
- - "<"
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: '
|
24
|
+
version: '7.0'
|
25
25
|
type: :runtime
|
26
26
|
prerelease: false
|
27
27
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
version: '4.2'
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '7.0'
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: activesupport
|
37
37
|
requirement: !ruby/object:Gem::Requirement
|
@@ -41,7 +41,7 @@ dependencies:
|
|
41
41
|
version: '4.2'
|
42
42
|
- - "<"
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: '
|
44
|
+
version: '7.0'
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
47
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -51,21 +51,27 @@ dependencies:
|
|
51
51
|
version: '4.2'
|
52
52
|
- - "<"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '7.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '1.5'
|
62
|
+
- - "<"
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '3.0'
|
62
65
|
type: :development
|
63
66
|
prerelease: false
|
64
67
|
version_requirements: !ruby/object:Gem::Requirement
|
65
68
|
requirements:
|
66
|
-
- - "
|
69
|
+
- - ">="
|
67
70
|
- !ruby/object:Gem::Version
|
68
71
|
version: '1.5'
|
72
|
+
- - "<"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '3.0'
|
69
75
|
- !ruby/object:Gem::Dependency
|
70
76
|
name: rake
|
71
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,6 +120,20 @@ dependencies:
|
|
114
120
|
- - "<="
|
115
121
|
- !ruby/object:Gem::Version
|
116
122
|
version: '6.0'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: pry
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
117
137
|
description: Check the home page for more in-depth information.
|
118
138
|
email:
|
119
139
|
- e@zzak.io
|
@@ -121,6 +141,7 @@ executables: []
|
|
121
141
|
extensions: []
|
122
142
|
extra_rdoc_files: []
|
123
143
|
files:
|
144
|
+
- CHANGELOG.md
|
124
145
|
- LICENSE
|
125
146
|
- README.md
|
126
147
|
- lib/acts_as_paranoid.rb
|
@@ -157,8 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
178
|
- !ruby/object:Gem::Version
|
158
179
|
version: '0'
|
159
180
|
requirements: []
|
160
|
-
|
161
|
-
rubygems_version: 2.7.6
|
181
|
+
rubygems_version: 3.1.2
|
162
182
|
signing_key:
|
163
183
|
specification_version: 4
|
164
184
|
summary: Active Record plugin which allows you to hide and restore records without
|