paranoia 2.4.3 → 3.0.1
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/.github/workflows/build.yml +40 -0
- data/CHANGELOG.md +93 -0
- data/Gemfile +9 -7
- data/README.md +41 -2
- data/lib/paranoia/rspec.rb +20 -17
- data/lib/paranoia/version.rb +1 -1
- data/lib/paranoia.rb +145 -46
- data/paranoia.gemspec +9 -4
- metadata +10 -14
- data/.travis.yml +0 -45
- data/test/paranoia_test.rb +0 -1388
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e7ad759d079b1e2bea13d47f092005677c98dee88423a12666fc77db15e8249
|
4
|
+
data.tar.gz: f95c4af13b076d6d235d3b6d0b7057595742a8dcd177e71b0ada70cb11674f91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b42a61dde0a3e2c417f2d7a0c71c23ad4b1c6980fe4a0a232313625017fe5c5e24065405a28e202bb3751d34e379753ae70a908d552f6704c7b1e78fb18862a
|
7
|
+
data.tar.gz: 45fc5b4fe5c4705f29e9bd208dd18d1fcec907c79649167467dc76ae883e002da9b2db93ac41148ab7a47872a39aa3c7545c754e7627dd8b5348e519654806ee
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: build
|
9
|
+
|
10
|
+
on: [push, pull_request]
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
test:
|
14
|
+
runs-on: ubuntu-20.04
|
15
|
+
strategy:
|
16
|
+
fail-fast: false
|
17
|
+
matrix:
|
18
|
+
rails: ["~> 7.2.0", "~> 7.1.0", "~> 7.0.0", "~> 6.1.0"]
|
19
|
+
ruby: ["3.3","3.2", "3.1", "3.0", "2.7"]
|
20
|
+
exclude:
|
21
|
+
- rails: "~> 7.2.0"
|
22
|
+
ruby: "3.0"
|
23
|
+
- rails: "~> 7.2.0"
|
24
|
+
ruby: "2.7"
|
25
|
+
- rails: "edge"
|
26
|
+
ruby: "3.0"
|
27
|
+
- rails: "edge"
|
28
|
+
ruby: "2.7"
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
env:
|
33
|
+
RAILS: ${{ matrix.rails }}
|
34
|
+
steps:
|
35
|
+
- uses: actions/checkout@v4
|
36
|
+
- uses: ruby/setup-ruby@v1
|
37
|
+
with:
|
38
|
+
ruby-version: ${{ matrix.ruby }}
|
39
|
+
bundler-cache: true
|
40
|
+
- run: bundle exec rake
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,98 @@
|
|
1
1
|
# paranoia Changelog
|
2
2
|
|
3
|
+
## 3.0.1 - January 19, 2025
|
4
|
+
|
5
|
+
- [#566](https://github.com/rubysherpas/paranoia/pull/566) Handle #delete_all
|
6
|
+
- [#559](https://github.com/rubysherpas/paranoia/pull/559) Trigger an after_commit callback when restoring a record
|
7
|
+
- [#567](https://github.com/rubysherpas/paranoia/pull/567) Fix typo in newly added readme
|
8
|
+
|
9
|
+
## 3.0.0 - August 13, 2024
|
10
|
+
|
11
|
+
_Tagged as 3.0 as Ruby + Rails version constraints have been modernised._
|
12
|
+
|
13
|
+
- [#564](https://github.com/rubysherpas/paranoia/pull/564) Support Rails edge
|
14
|
+
- [#563](https://github.com/rubysherpas/paranoia/pull/563) Support Rails 7.2
|
15
|
+
|
16
|
+
## 2.6.4 - July 20, 2024
|
17
|
+
|
18
|
+
* [#554](https://github.com/rubysherpas/paranoia/pull/554) Support prebuilt counter cache association list (#554)
|
19
|
+
[Joé Dupuis](https://github.com/JoeDupuis)
|
20
|
+
* [#551](https://github.com/rubysherpas/paranoia/pull/551) Fix: restore has_one with scope (#551)
|
21
|
+
[Paweł Charyło](https://github.com/zygzagZ)
|
22
|
+
* [#555](https://github.com/rubysherpas/paranoia/pull/555) 📝 Add Yard documentation for Paranoia::Query (#555)
|
23
|
+
[Clément Prod'homme](https://github.com/cprodhomme)
|
24
|
+
|
25
|
+
## 2.6.3 - Oct 12, 2023
|
26
|
+
|
27
|
+
* [#548](https://github.com/rubysherpas/paranoia/pull/548) Add support for [Rails 7.1](https://github.com/rails/rails/releases/tag/v7.1.0) (#548)
|
28
|
+
[Indyarocks](https://github.com/indyarocks)
|
29
|
+
|
30
|
+
## 2.6.2 - Jun 6, 2023
|
31
|
+
|
32
|
+
* [#441](https://github.com/rubysherpas/paranoia/pull/441) Recursive restore with has_many/one through assocs (#441)
|
33
|
+
[Emil Ong](https://github.com/emilong)
|
34
|
+
|
35
|
+
## 2.6.1 - Nov 16, 2022
|
36
|
+
|
37
|
+
* [#535](https://github.com/rubysherpas/paranoia/pull/535) Allow to skip updating paranoia_destroy_attributes for records while really_destroy!
|
38
|
+
[Anton Bogdanov](https://github.com/kortirso)
|
39
|
+
|
40
|
+
## 2.6.0 - Mar 23, 2022
|
41
|
+
|
42
|
+
* [#512](https://github.com/rubysherpas/paranoia/pull/512) Quote table names; Mysql 8 has keywords that might match table names which cause an exception.
|
43
|
+
* [#476](https://github.com/rubysherpas/paranoia/pull/476) Fix syntax error in documentation.
|
44
|
+
* [#485](https://github.com/rubysherpas/paranoia/pull/485) Rollback transaction if destroy aborted.
|
45
|
+
* [#522](https://github.com/rubysherpas/paranoia/pull/522) Add failing tests for association with abort on destroy.
|
46
|
+
* [#513](https://github.com/rubysherpas/paranoia/pull/513) Fix create callback called on destroy.
|
47
|
+
|
48
|
+
## 2.5.3
|
49
|
+
|
50
|
+
* [#532](https://github.com/rubysherpas/paranoia/pull/532) Fix: correct bug when sentinel_value is not a timestamp
|
51
|
+
[Hassanin Ahmed](https://github.com/sas1ni69)
|
52
|
+
* [#531](https://github.com/rubysherpas/paranoia/pull/531) Added test case to reproduce bug introduce in v2.5.1
|
53
|
+
[Sherif Elkassaby](https://github.com/sherif-nedap)
|
54
|
+
* [#529](https://github.com/rubysherpas/paranoia/pull/529) Fix: Do not define a RSpec matcher when RSpec isn't present
|
55
|
+
[Sebastian Welther](https://github.com/swelther)
|
56
|
+
|
57
|
+
## 2.5.2
|
58
|
+
|
59
|
+
* [#526](https://github.com/rubysherpas/paranoia/pull/526) Do not include tests files in packaged gem
|
60
|
+
|
61
|
+
[Jason Fleetwood-Boldt](https://github.com/jasonfb)
|
62
|
+
* [#492](https://github.com/rubysherpas/paranoia/pull/492) Warn if acts_as_paranoid is called more than once on the same model
|
63
|
+
|
64
|
+
[Ignatius Reza](https://github.com/ignatiusreza)
|
65
|
+
|
66
|
+
## 2.5.1
|
67
|
+
|
68
|
+
* [#481](https://github.com/rubysherpas/paranoia/pull/481) Replaces hard coded `deleted_at` with `paranoia_column`.
|
69
|
+
|
70
|
+
[Hassanin Ahmed](https://github.com/sas1ni69)
|
71
|
+
|
72
|
+
## 2.5.0
|
73
|
+
|
74
|
+
* [#516](https://github.com/rubysherpas/paranoia/pull/516) Add support for ActiveRecord 7.0, drop support for EOL Ruby < 2.5 and Rails < 5.1
|
75
|
+
adding support for Rails 7
|
76
|
+
|
77
|
+
[Mathieu Jobin](https://github.com/mathieujobin)
|
78
|
+
* [#515](https://github.com/rubysherpas/paranoia/pull/515) Switch from Travis CI to GitHub Actions
|
79
|
+
|
80
|
+
[Shinichi Maeshima](https://github.com/willnet)
|
81
|
+
|
82
|
+
## 2.4.3
|
83
|
+
|
84
|
+
* [#503](https://github.com/rubysherpas/paranoia/pull/503) Bump activerecord dependency for Rails 6.1
|
85
|
+
|
86
|
+
[Jörg Schiller](https://github.com/joergschiller)
|
87
|
+
|
88
|
+
* [#483](https://github.com/rubysherpas/paranoia/pull/483) Update JRuby version to 9.2.8.0 + remove EOL Ruby 2.2
|
89
|
+
|
90
|
+
[Uwe Kubosch](https://github.com/donv)
|
91
|
+
|
92
|
+
* [#482](https://github.com/rubysherpas/paranoia/pull/482) Fix after_commit for Rails 6
|
93
|
+
|
94
|
+
[Ashwin Hegde](https://github.com/hashwin)
|
95
|
+
|
3
96
|
## 2.4.2
|
4
97
|
|
5
98
|
* [#470](https://github.com/rubysherpas/paranoia/pull/470) Add support for ActiveRecord 6.0
|
data/Gemfile
CHANGED
@@ -5,22 +5,24 @@ sqlite = ENV['SQLITE_VERSION']
|
|
5
5
|
if sqlite
|
6
6
|
gem 'sqlite3', sqlite, platforms: [:ruby]
|
7
7
|
else
|
8
|
-
gem 'sqlite3', platforms: [:ruby]
|
8
|
+
gem 'sqlite3', '~> 1.4', platforms: [:ruby]
|
9
9
|
end
|
10
10
|
|
11
11
|
platforms :jruby do
|
12
12
|
gem 'activerecord-jdbcsqlite3-adapter'
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
if RUBY_ENGINE == 'rbx'
|
16
|
+
platforms :rbx do
|
17
|
+
gem 'rubinius-developer_tools'
|
18
|
+
gem 'rubysl', '~> 2.0'
|
19
|
+
gem 'rubysl-test-unit'
|
20
|
+
end
|
19
21
|
end
|
20
22
|
|
21
|
-
rails = ENV['RAILS'] || '~>
|
23
|
+
rails = ENV['RAILS'] || '~> 6.0.4'
|
22
24
|
|
23
|
-
if rails == '
|
25
|
+
if rails == 'edge'
|
24
26
|
gem 'rails', github: 'rails/rails'
|
25
27
|
else
|
26
28
|
gem 'rails', rails
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
[](https://badge.fury.io/rb/paranoia)
|
2
|
+
[](https://github.com/rubysherpas/paranoia/actions/workflows/build.yml)
|
3
|
+
|
4
|
+
**Notice:**
|
2
5
|
|
3
6
|
`paranoia` has some surprising behaviour (like overriding ActiveRecord's `delete` and `destroy`) and is not recommended for new projects. See [`discard`'s README](https://github.com/jhawthorn/discard#why-not-paranoia-or-acts_as_paranoid) for more details.
|
4
7
|
|
@@ -100,6 +103,14 @@ If you really want it gone *gone*, call `really_destroy!`:
|
|
100
103
|
# => client
|
101
104
|
```
|
102
105
|
|
106
|
+
If you need skip updating timestamps for deleting records, call `really_destroy!(update_destroy_attributes: false)`.
|
107
|
+
When we call `really_destroy!(update_destroy_attributes: false)` on the parent `client`, then each child `email` will also have `really_destroy!(update_destroy_attributes: false)` called.
|
108
|
+
|
109
|
+
``` ruby
|
110
|
+
>> client.really_destroy!(update_destroy_attributes: false)
|
111
|
+
# => client
|
112
|
+
```
|
113
|
+
|
103
114
|
If you want to use a column other than `deleted_at`, you can pass it as an option:
|
104
115
|
|
105
116
|
``` ruby
|
@@ -190,11 +201,24 @@ client.restore(:recursive => true)
|
|
190
201
|
If you want to restore a record and only those dependently destroyed associated records that were deleted within 2 minutes of the object upon which they depend:
|
191
202
|
|
192
203
|
``` ruby
|
193
|
-
Client.restore(id, :recursive => true
|
204
|
+
Client.restore(id, :recursive => true, :recovery_window => 2.minutes)
|
194
205
|
# or
|
195
206
|
client.restore(:recursive => true, :recovery_window => 2.minutes)
|
196
207
|
```
|
197
208
|
|
209
|
+
If you want to trigger an after_commit callback when restoring a record:
|
210
|
+
|
211
|
+
``` ruby
|
212
|
+
class Client < ActiveRecord::Base
|
213
|
+
acts_as_paranoid after_restore_commit: true
|
214
|
+
|
215
|
+
after_commit :commit_called, on: :restore
|
216
|
+
# or
|
217
|
+
after_restore_commit :commit_called
|
218
|
+
...
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
198
222
|
Note that by default paranoia will not prevent that a soft destroyed object can't be associated with another object of a different model.
|
199
223
|
A Rails validator is provided should you require this functionality:
|
200
224
|
``` ruby
|
@@ -337,6 +361,21 @@ end
|
|
337
361
|
# => NoMethodError: undefined method `with_deleted' for #<Class:0x0123456>
|
338
362
|
```
|
339
363
|
|
364
|
+
#### delete_all:
|
365
|
+
|
366
|
+
The gem supports `delete_all` method, however it is disabled by default, to enable it add this in your `environment` file
|
367
|
+
|
368
|
+
``` ruby
|
369
|
+
Paranoia.delete_all_enabled = true
|
370
|
+
```
|
371
|
+
alternatively, you can enable/disable it for specific models as follow:
|
372
|
+
|
373
|
+
``` ruby
|
374
|
+
class User < ActiveRecord::Base
|
375
|
+
acts_as_paranoid(delete_all_enabled: true)
|
376
|
+
end
|
377
|
+
```
|
378
|
+
|
340
379
|
## Acts As Paranoid Migration
|
341
380
|
|
342
381
|
You can replace the older `acts_as_paranoid` methods as follows:
|
data/lib/paranoia/rspec.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
|
-
|
1
|
+
if defined?(RSpec)
|
2
|
+
require 'rspec/expectations'
|
2
3
|
|
3
|
-
# Validate the subject's class did call "acts_as_paranoid"
|
4
|
-
RSpec::Matchers.define :act_as_paranoid do
|
5
|
-
|
4
|
+
# Validate the subject's class did call "acts_as_paranoid"
|
5
|
+
RSpec::Matchers.define :act_as_paranoid do
|
6
|
+
match { |subject| subject.class.ancestors.include?(Paranoia) }
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
failure_message_proc = lambda do
|
9
|
+
"expected #{subject.class} to use `acts_as_paranoid`"
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
failure_message_when_negated_proc = lambda do
|
13
|
+
"expected #{subject.class} not to use `acts_as_paranoid`"
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
if respond_to?(:failure_message_when_negated)
|
17
|
+
failure_message(&failure_message_proc)
|
18
|
+
failure_message_when_negated(&failure_message_when_negated_proc)
|
19
|
+
else
|
20
|
+
# RSpec 2 compatibility:
|
21
|
+
failure_message_for_should(&failure_message_proc)
|
22
|
+
failure_message_for_should_not(&failure_message_when_negated_proc)
|
23
|
+
end
|
22
24
|
end
|
25
|
+
|
23
26
|
end
|
data/lib/paranoia/version.rb
CHANGED
data/lib/paranoia.rb
CHANGED
@@ -6,15 +6,11 @@ if [ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR] == [5, 2] ||
|
|
6
6
|
end
|
7
7
|
|
8
8
|
module Paranoia
|
9
|
-
@@default_sentinel_value = nil
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def self.default_sentinel_value
|
17
|
-
@@default_sentinel_value
|
10
|
+
class << self
|
11
|
+
# Change default values in a rails initializer
|
12
|
+
attr_accessor :default_sentinel_value,
|
13
|
+
:delete_all_enabled
|
18
14
|
end
|
19
15
|
|
20
16
|
def self.included(klazz)
|
@@ -24,6 +20,7 @@ module Paranoia
|
|
24
20
|
module Query
|
25
21
|
def paranoid? ; true ; end
|
26
22
|
|
23
|
+
# If you want to find all records, even those which are deleted
|
27
24
|
def with_deleted
|
28
25
|
if ActiveRecord::VERSION::STRING >= "4.1"
|
29
26
|
return unscope where: paranoia_column
|
@@ -31,6 +28,7 @@ module Paranoia
|
|
31
28
|
all.tap { |x| x.default_scoped = false }
|
32
29
|
end
|
33
30
|
|
31
|
+
# If you want to find only the deleted records
|
34
32
|
def only_deleted
|
35
33
|
if paranoia_sentinel_value.nil?
|
36
34
|
return with_deleted.where.not(paranoia_column => paranoia_sentinel_value)
|
@@ -40,11 +38,12 @@ module Paranoia
|
|
40
38
|
# these will not match != sentinel value because "NULL != value" is
|
41
39
|
# NULL under the sql standard
|
42
40
|
# Scoping with the table_name is mandatory to avoid ambiguous errors when joining tables.
|
43
|
-
scoped_quoted_paranoia_column = "#{self.table_name}.#{connection.quote_column_name(paranoia_column)}"
|
41
|
+
scoped_quoted_paranoia_column = "#{connection.quote_table_name(self.table_name)}.#{connection.quote_column_name(paranoia_column)}"
|
44
42
|
with_deleted.where("#{scoped_quoted_paranoia_column} IS NULL OR #{scoped_quoted_paranoia_column} != ?", paranoia_sentinel_value)
|
45
43
|
end
|
46
44
|
alias_method :deleted, :only_deleted
|
47
45
|
|
46
|
+
# If you want to restore a record
|
48
47
|
def restore(id_or_ids, opts = {})
|
49
48
|
ids = Array(id_or_ids).flatten
|
50
49
|
any_object_instead_of_id = ids.any? { |id| ActiveRecord::Base === id }
|
@@ -55,12 +54,22 @@ module Paranoia
|
|
55
54
|
end
|
56
55
|
ids.map { |id| only_deleted.find(id).restore!(opts) }
|
57
56
|
end
|
57
|
+
|
58
|
+
def paranoia_destroy_attributes
|
59
|
+
{
|
60
|
+
paranoia_column => current_time_from_proper_timezone
|
61
|
+
}.merge(timestamp_attributes_with_current_time)
|
62
|
+
end
|
63
|
+
|
64
|
+
def timestamp_attributes_with_current_time
|
65
|
+
timestamp_attributes_for_update_in_model.each_with_object({}) { |attr,hash| hash[attr] = current_time_from_proper_timezone }
|
66
|
+
end
|
58
67
|
end
|
59
68
|
|
60
69
|
def paranoia_destroy
|
61
|
-
|
62
|
-
run_callbacks(:destroy) do
|
63
|
-
@_disable_counter_cache =
|
70
|
+
with_transaction_returning_status do
|
71
|
+
result = run_callbacks(:destroy) do
|
72
|
+
@_disable_counter_cache = paranoia_destroyed?
|
64
73
|
result = paranoia_delete
|
65
74
|
next result unless result && ActiveRecord::VERSION::STRING >= '4.2'
|
66
75
|
each_counter_cached_associations do |association|
|
@@ -73,7 +82,9 @@ module Paranoia
|
|
73
82
|
@_disable_counter_cache = false
|
74
83
|
result
|
75
84
|
end
|
76
|
-
|
85
|
+
raise ActiveRecord::Rollback, "Not destroyed" unless paranoia_destroyed?
|
86
|
+
result
|
87
|
+
end || false
|
77
88
|
end
|
78
89
|
alias_method :destroy, :paranoia_destroy
|
79
90
|
|
@@ -83,7 +94,16 @@ module Paranoia
|
|
83
94
|
end
|
84
95
|
|
85
96
|
def trigger_transactional_callbacks?
|
86
|
-
super || @_trigger_destroy_callback && paranoia_destroyed?
|
97
|
+
super || @_trigger_destroy_callback && paranoia_destroyed? ||
|
98
|
+
@_trigger_restore_callback && !paranoia_destroyed?
|
99
|
+
end
|
100
|
+
|
101
|
+
def transaction_include_any_action?(actions)
|
102
|
+
super || actions.any? do |action|
|
103
|
+
if action == :restore
|
104
|
+
paranoia_after_restore_commit && @_trigger_restore_callback
|
105
|
+
end
|
106
|
+
end
|
87
107
|
end
|
88
108
|
|
89
109
|
def paranoia_delete
|
@@ -110,6 +130,10 @@ module Paranoia
|
|
110
130
|
if within_recovery_window?(recovery_window_range) && ((noop_if_frozen && !@attributes.frozen?) || !noop_if_frozen)
|
111
131
|
@_disable_counter_cache = !paranoia_destroyed?
|
112
132
|
write_attribute paranoia_column, paranoia_sentinel_value
|
133
|
+
if paranoia_after_restore_commit
|
134
|
+
@_trigger_restore_callback = true
|
135
|
+
add_to_transaction
|
136
|
+
end
|
113
137
|
update_columns(paranoia_restore_attributes)
|
114
138
|
each_counter_cached_associations do |association|
|
115
139
|
if send(association.reflection.name)
|
@@ -123,27 +147,31 @@ module Paranoia
|
|
123
147
|
end
|
124
148
|
|
125
149
|
self
|
150
|
+
ensure
|
151
|
+
if paranoia_after_restore_commit
|
152
|
+
@_trigger_restore_callback = false
|
153
|
+
end
|
126
154
|
end
|
127
155
|
alias :restore :restore!
|
128
156
|
|
129
157
|
def get_recovery_window_range(opts)
|
130
158
|
return opts[:recovery_window_range] if opts[:recovery_window_range]
|
131
159
|
return unless opts[:recovery_window]
|
132
|
-
(
|
160
|
+
(deletion_time - opts[:recovery_window]..deletion_time + opts[:recovery_window])
|
133
161
|
end
|
134
162
|
|
135
163
|
def within_recovery_window?(recovery_window_range)
|
136
164
|
return true unless recovery_window_range
|
137
|
-
recovery_window_range.cover?(
|
165
|
+
recovery_window_range.cover?(deletion_time)
|
138
166
|
end
|
139
167
|
|
140
168
|
def paranoia_destroyed?
|
141
|
-
|
169
|
+
paranoia_column_value != paranoia_sentinel_value
|
142
170
|
end
|
143
171
|
alias :deleted? :paranoia_destroyed?
|
144
172
|
|
145
|
-
def really_destroy!
|
146
|
-
|
173
|
+
def really_destroy!(update_destroy_attributes: true)
|
174
|
+
with_transaction_returning_status do
|
147
175
|
run_callbacks(:real_destroy) do
|
148
176
|
@_disable_counter_cache = paranoia_destroyed?
|
149
177
|
dependent_reflections = self.class.reflections.select do |name, reflection|
|
@@ -156,12 +184,14 @@ module Paranoia
|
|
156
184
|
# .paranoid? will work for both instances and classes
|
157
185
|
next unless association_data && association_data.paranoid?
|
158
186
|
if reflection.collection?
|
159
|
-
next association_data.with_deleted.
|
187
|
+
next association_data.with_deleted.find_each { |record|
|
188
|
+
record.really_destroy!(update_destroy_attributes: update_destroy_attributes)
|
189
|
+
}
|
160
190
|
end
|
161
|
-
association_data.really_destroy!
|
191
|
+
association_data.really_destroy!(update_destroy_attributes: update_destroy_attributes)
|
162
192
|
end
|
163
193
|
end
|
164
|
-
update_columns(paranoia_destroy_attributes)
|
194
|
+
update_columns(paranoia_destroy_attributes) if update_destroy_attributes
|
165
195
|
destroy_without_paranoia
|
166
196
|
end
|
167
197
|
end
|
@@ -169,24 +199,43 @@ module Paranoia
|
|
169
199
|
|
170
200
|
private
|
171
201
|
|
202
|
+
def counter_cache_disabled?
|
203
|
+
defined?(@_disable_counter_cache) && @_disable_counter_cache
|
204
|
+
end
|
205
|
+
|
206
|
+
def counter_cached_association_names
|
207
|
+
return [] if counter_cache_disabled?
|
208
|
+
super
|
209
|
+
end
|
210
|
+
|
172
211
|
def each_counter_cached_associations
|
173
|
-
|
212
|
+
return [] if counter_cache_disabled?
|
213
|
+
|
214
|
+
if defined?(super)
|
215
|
+
super
|
216
|
+
else
|
217
|
+
counter_cached_association_names.each do |name|
|
218
|
+
yield association(name)
|
219
|
+
end
|
220
|
+
end
|
174
221
|
end
|
175
222
|
|
176
223
|
def paranoia_restore_attributes
|
177
224
|
{
|
178
225
|
paranoia_column => paranoia_sentinel_value
|
179
|
-
}.merge(timestamp_attributes_with_current_time)
|
226
|
+
}.merge(self.class.timestamp_attributes_with_current_time)
|
180
227
|
end
|
181
228
|
|
182
|
-
|
183
|
-
{
|
184
|
-
paranoia_column => current_time_from_proper_timezone
|
185
|
-
}.merge(timestamp_attributes_with_current_time)
|
186
|
-
end
|
229
|
+
delegate :paranoia_destroy_attributes, to: 'self.class'
|
187
230
|
|
188
|
-
def
|
189
|
-
|
231
|
+
def paranoia_find_has_one_target(association)
|
232
|
+
association_foreign_key = association.options[:through].present? ? association.klass.primary_key : association.foreign_key
|
233
|
+
association_find_conditions = { association_foreign_key => self.id }
|
234
|
+
association_find_conditions[association.type] = self.class.name if association.type
|
235
|
+
|
236
|
+
scope = association.klass.only_deleted.where(association_find_conditions)
|
237
|
+
scope = scope.merge(association.scope) if association.scope
|
238
|
+
scope.first
|
190
239
|
end
|
191
240
|
|
192
241
|
# restore associated records that have been soft deleted when
|
@@ -212,42 +261,70 @@ module Paranoia
|
|
212
261
|
end
|
213
262
|
|
214
263
|
if association_data.nil? && association.macro.to_s == "has_one"
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
if association.type
|
219
|
-
association_polymorphic_type = association.type
|
220
|
-
association_find_conditions = { association_polymorphic_type => self.class.name.to_s, association_foreign_key => self.id }
|
221
|
-
else
|
222
|
-
association_find_conditions = { association_foreign_key => self.id }
|
223
|
-
end
|
224
|
-
|
225
|
-
association_class = association_class_name.constantize
|
226
|
-
if association_class.paranoid?
|
227
|
-
association_class.only_deleted.where(association_find_conditions).first
|
264
|
+
if association.klass.paranoid?
|
265
|
+
paranoia_find_has_one_target(association)
|
228
266
|
.try!(:restore, recursive: true, :recovery_window_range => recovery_window_range)
|
229
267
|
end
|
230
268
|
end
|
231
269
|
end
|
232
270
|
|
233
|
-
|
271
|
+
if ActiveRecord.version.to_s > '7'
|
272
|
+
# Method deleted in https://github.com/rails/rails/commit/dd5886d00a2d5f31ccf504c391aad93deb014eb8
|
273
|
+
@association_cache.clear if persisted? && destroyed_associations.present?
|
274
|
+
else
|
275
|
+
clear_association_cache if destroyed_associations.present?
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
module ActiveRecord
|
281
|
+
module Transactions
|
282
|
+
module RestoreSupport
|
283
|
+
def self.included(base)
|
284
|
+
base::ACTIONS << :restore unless base::ACTIONS.include?(:restore)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
module ClassMethods
|
289
|
+
def after_restore_commit(*args, &block)
|
290
|
+
set_options_for_callbacks!(args, on: :restore)
|
291
|
+
set_callback(:commit, :after, *args, &block)
|
292
|
+
end
|
293
|
+
end
|
234
294
|
end
|
235
295
|
end
|
236
296
|
|
297
|
+
module Paranoia::Relation
|
298
|
+
def paranoia_delete_all
|
299
|
+
update_all(klass.paranoia_destroy_attributes)
|
300
|
+
end
|
301
|
+
|
302
|
+
alias_method :delete_all, :paranoia_delete_all
|
303
|
+
end
|
304
|
+
|
237
305
|
ActiveSupport.on_load(:active_record) do
|
238
306
|
class ActiveRecord::Base
|
239
307
|
def self.acts_as_paranoid(options={})
|
308
|
+
if included_modules.include?(Paranoia)
|
309
|
+
puts "[WARN] #{self.name} is calling acts_as_paranoid more than once!"
|
310
|
+
|
311
|
+
return
|
312
|
+
end
|
313
|
+
|
240
314
|
define_model_callbacks :restore, :real_destroy
|
241
315
|
|
242
316
|
alias_method :really_destroyed?, :destroyed?
|
243
317
|
alias_method :really_delete, :delete
|
244
318
|
alias_method :destroy_without_paranoia, :destroy
|
319
|
+
class << self; delegate :really_delete_all, to: :all end
|
245
320
|
|
246
321
|
include Paranoia
|
247
|
-
class_attribute :paranoia_column, :paranoia_sentinel_value
|
322
|
+
class_attribute :paranoia_column, :paranoia_sentinel_value, :paranoia_after_restore_commit,
|
323
|
+
:delete_all_enabled
|
248
324
|
|
249
325
|
self.paranoia_column = (options[:column] || :deleted_at).to_s
|
250
326
|
self.paranoia_sentinel_value = options.fetch(:sentinel_value) { Paranoia.default_sentinel_value }
|
327
|
+
self.paranoia_after_restore_commit = options.fetch(:after_restore_commit) { false }
|
251
328
|
def self.paranoia_scope
|
252
329
|
where(paranoia_column => paranoia_sentinel_value)
|
253
330
|
end
|
@@ -263,6 +340,20 @@ ActiveSupport.on_load(:active_record) do
|
|
263
340
|
after_restore {
|
264
341
|
self.class.notify_observers(:after_restore, self) if self.class.respond_to?(:notify_observers)
|
265
342
|
}
|
343
|
+
|
344
|
+
if paranoia_after_restore_commit
|
345
|
+
ActiveRecord::Transactions.send(:include, ActiveRecord::Transactions::RestoreSupport)
|
346
|
+
end
|
347
|
+
|
348
|
+
self.delete_all_enabled = options[:delete_all_enabled] || Paranoia.delete_all_enabled
|
349
|
+
|
350
|
+
if self.delete_all_enabled
|
351
|
+
"#{self}::ActiveRecord_Relation".constantize.class_eval do
|
352
|
+
alias_method :really_delete_all, :delete_all
|
353
|
+
|
354
|
+
include Paranoia::Relation
|
355
|
+
end
|
356
|
+
end
|
266
357
|
end
|
267
358
|
|
268
359
|
# Please do not use this method in production.
|
@@ -285,9 +376,17 @@ ActiveSupport.on_load(:active_record) do
|
|
285
376
|
self.class.paranoia_column
|
286
377
|
end
|
287
378
|
|
379
|
+
def paranoia_column_value
|
380
|
+
send(paranoia_column)
|
381
|
+
end
|
382
|
+
|
288
383
|
def paranoia_sentinel_value
|
289
384
|
self.class.paranoia_sentinel_value
|
290
385
|
end
|
386
|
+
|
387
|
+
def deletion_time
|
388
|
+
paranoia_column_value.acts_like?(:time) ? paranoia_column_value : deleted_at
|
389
|
+
end
|
291
390
|
end
|
292
391
|
end
|
293
392
|
|