acts_as_paranoid 0.6.0 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|