acts_as_paranoid 0.6.2 → 0.7.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 +126 -10
- data/CONTRIBUTING.md +59 -0
- data/README.md +151 -57
- data/lib/acts_as_paranoid.rb +29 -29
- data/lib/acts_as_paranoid/associations.rb +21 -17
- data/lib/acts_as_paranoid/core.rb +151 -95
- data/lib/acts_as_paranoid/relation.rb +9 -0
- data/lib/acts_as_paranoid/validations.rb +8 -74
- data/lib/acts_as_paranoid/version.rb +3 -1
- data/test/test_associations.rb +118 -46
- data/test/test_core.rb +264 -66
- data/test/test_default_scopes.rb +20 -7
- data/test/test_dependent_recovery.rb +54 -0
- data/test/test_deprecated_behavior.rb +30 -0
- data/test/test_helper.rb +130 -95
- data/test/test_inheritance.rb +3 -1
- data/test/test_relations.rb +98 -16
- data/test/test_table_namespace.rb +40 -0
- data/test/test_validations.rb +27 -7
- metadata +78 -32
- data/lib/acts_as_paranoid/preloader_association.rb +0 -16
- data/test/test_preloader_association.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a0dd97ab43e1d7a8a72327a1abf5df84ae9d3ff36c38caed38a808aacef6b01
|
4
|
+
data.tar.gz: a628f74884aff858620127b163b1998b5d790a0cfcbff4ad56e9851f962a3caf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1488e86f1437d179e8aef1bc8b2e6da851a29d44ef9a93d7d2a60685bae36da50d69ec74bb19f6c4751257ddce5af556994c1d38beaef037b5753c063fdf409a
|
7
|
+
data.tar.gz: 630cec48256d5baf5e859f138fcc74b0a3718f6283ee08d47b36fa96cca9feba16bd6a2e1e4a3eeb3ecc210238d5ac8eb4ea31374ca8b0cfa07b57bdb9392368
|
data/CHANGELOG.md
CHANGED
@@ -2,14 +2,88 @@
|
|
2
2
|
|
3
3
|
Notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
+
## 0.7.3
|
6
|
+
|
7
|
+
## Improvements
|
8
|
+
|
9
|
+
* Fix deletion time scopes ([#212] by [Matijs van Zuijlen][mvz])
|
10
|
+
* Reload `has_one` associations after dependent recovery ([#214],
|
11
|
+
by [Matijs van Zuijlen][mvz])
|
12
|
+
* Make dependent recovery work when parent is non-optional ([#227],
|
13
|
+
by [Matijs van Zuijlen][mvz])
|
14
|
+
* Avoid querying nil `belongs_to` associations when recovering ([#219],
|
15
|
+
by [Matijs van Zuijlen][mvz])
|
16
|
+
* On relations, deprecate `destroy!` in favour of `destroy_fully!` ([#222],
|
17
|
+
by [Matijs van Zuijlen][mvz])
|
18
|
+
* Deprecate the undocumented `:recovery_value` setting. Calculate the correct
|
19
|
+
value instead. ([#220], by [Matijs van Zuijlen][mvz])
|
20
|
+
|
21
|
+
## Developer experience
|
22
|
+
|
23
|
+
* Log ActiveRecord activity to a visible log during tests ([#218],
|
24
|
+
by [Matijs van Zuijlen][mvz])
|
25
|
+
|
26
|
+
## 0.7.2
|
27
|
+
|
28
|
+
* Do not set boolean column to NULL on recovery if nulls are not allowed
|
29
|
+
([#193], by [Shodai Suzuki][soartec-lab])
|
30
|
+
* Add a CONTRIBUTING.md file ([#207], by [Matijs van Zuijlen][mvz])
|
31
|
+
|
32
|
+
## 0.7.1
|
33
|
+
|
34
|
+
* Support Rails 6.1 ([#191], by [Matijs van Zuijlen][mvz])
|
35
|
+
* Support `belongs_to` with both `:touch` and `:counter_cache` options ([#208],
|
36
|
+
by [Matijs van Zuijlen][mvz] with [Paul Druziak][pauldruziak])
|
37
|
+
* Support Ruby 3.0 ([#209], by [Matijs van Zuijlen][mvz])
|
38
|
+
|
39
|
+
## 0.7.0
|
40
|
+
|
41
|
+
### Breaking changes
|
42
|
+
|
43
|
+
* Support Rails 5.2+ only ([#126], by [Daniel Rice][danielricecodes])
|
44
|
+
* Update set of supported rubies to 2.4-2.7 ([#144], [#173] by [Matijs van Zuijlen][mvz])
|
45
|
+
|
46
|
+
### Improvements
|
47
|
+
|
48
|
+
* Handle `with_deleted` association option as a scope ([#147], by [Matijs van Zuijlen][mvz])
|
49
|
+
* Simplify validation override ([#158], by [Matijs van Zuijlen][mvz])
|
50
|
+
* Use correct unscope syntax so unscope works on Rails Edge ([#160],
|
51
|
+
by [Matijs van Zuijlen][mvz])
|
52
|
+
* Fix ruby 2.7 keyword argument deprecation warning ([#161], by [Jon Riddle][wtfspm])
|
53
|
+
|
54
|
+
### Documentation
|
55
|
+
|
56
|
+
* Document save after destroy behavior ([#146], by [Matijs van Zuijlen][mvz])
|
57
|
+
* Update version number instructions for installing gem ([#164],
|
58
|
+
by [Kevin McAlear][kevinmcalear])
|
59
|
+
* Add example with `destroyed_fully?` and `deleted_fully?` to the readme ([#170],
|
60
|
+
by [Kiril Mitov][thebravoman])
|
61
|
+
|
62
|
+
### Internal
|
63
|
+
|
64
|
+
* Improve code quality using RuboCop ([#148], [#152], [#159], [#163], [#171] and [#173],
|
65
|
+
by [Matijs van Zuijlen][mvz])
|
66
|
+
* Measure code coverage using SimpleCov ([#150] and [#175] by [Matijs van Zuijlen][mvz])
|
67
|
+
* Silence warnings emitted during tests ([#156], by [Matijs van Zuijlen][mvz])
|
68
|
+
* Make rake tasks more robust and intuitive ([#157], by [Matijs van Zuijlen][mvz])
|
69
|
+
|
70
|
+
## 0.6.3
|
71
|
+
|
72
|
+
* Update Travis CI configuration ([#137], by [Matijs van Zuijlen][mvz])
|
73
|
+
* Add predicate to check if record was soft deleted or hard deleted ([#136],
|
74
|
+
by [Aymeric Le Dorze][aymeric-ledorze])
|
75
|
+
* Add support for recover! method ([#75], by [vinoth][avinoth])
|
76
|
+
* Fix a record being dirty after destroying it ([#135], by
|
77
|
+
[Aymeric Le Dorze][aymeric-ledorze])
|
78
|
+
|
5
79
|
## 0.6.2
|
6
80
|
|
7
81
|
* Prevent recovery of non-deleted records
|
8
82
|
([#133], by [Mary Beliveau][marycodes2] and [Valerie Woolard][valeriecodes])
|
9
83
|
* Allow model to set `table_name` after `acts_as_paranoid` macro
|
10
|
-
([#131], by [Alex Wheeler][AlexWheeler])
|
84
|
+
([#131], by [Alex Wheeler][AlexWheeler])
|
11
85
|
* Make counter cache work with a custom column name and with optional
|
12
|
-
associations ([#123], by [Ned Campion][nedcampion])
|
86
|
+
associations ([#123], by [Ned Campion][nedcampion])
|
13
87
|
|
14
88
|
## 0.6.1
|
15
89
|
|
@@ -18,8 +92,8 @@ Notable changes to this project will be documented in this file.
|
|
18
92
|
* Add support for incrementing and decrementing counter cache columns on
|
19
93
|
associated objects ([#119], by [Dimitar Lukanov][shadydealer])
|
20
94
|
* Add `:double_tap_destroys_fully` option, with default `true` ([#116],
|
21
|
-
by [Michael Riviera][ri4a])
|
22
|
-
* Officially support Ruby 2.6 ([#114], by [Matijs van Zuijlen][mvz])
|
95
|
+
by [Michael Riviera][ri4a])
|
96
|
+
* Officially support Ruby 2.6 ([#114], by [Matijs van Zuijlen][mvz])
|
23
97
|
|
24
98
|
## 0.6.0 and earlier
|
25
99
|
|
@@ -27,23 +101,65 @@ Notable changes to this project will be documented in this file.
|
|
27
101
|
|
28
102
|
<!-- Contributors -->
|
29
103
|
|
30
|
-
[
|
31
|
-
[
|
32
|
-
[
|
104
|
+
[AlexWheeler]: https://github.com/AlexWheeler
|
105
|
+
[RomainAlexandre]: https://github.com/RomainAlexandre
|
106
|
+
[avinoth]: https://github.com/avinoth
|
107
|
+
[aymeric-ledorze]: https://github.com/aymeric-ledorze
|
33
108
|
[danielricecodes]: https://github.com/danielricecodes
|
34
109
|
[jbryant92]: https://github.com/jbryant92
|
35
|
-
[
|
36
|
-
[RomainAlexandre]: https://github.com/RomainAlexandre
|
37
|
-
[AlexWheeler]: https://github.com/AlexWheeler
|
110
|
+
[kevinmcalear]: https://github.com/kevinmcalear
|
38
111
|
[marycodes2]: https://github.com/marycodes2
|
112
|
+
[mvz]: https://github.com/mvz
|
113
|
+
[nedcampion]: https://github.com/nedcampion
|
114
|
+
[ri4a]: https://github.com/ri4a
|
115
|
+
[pauldruziak]: https://github.com/pauldruziak
|
116
|
+
[shadydealer]: https://github.com/shadydealer
|
117
|
+
[soartec-lab]: https://github.com/soartec-lab
|
118
|
+
[thebravoman]: https://github.com/thebravoman
|
39
119
|
[valeriecodes]: https://github.com/valeriecodes
|
120
|
+
[wtfspm]: https://github.com/wtfspm
|
40
121
|
|
41
122
|
<!-- issues & pull requests -->
|
42
123
|
|
124
|
+
[#227]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/227
|
125
|
+
[#222]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/222
|
126
|
+
[#220]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/220
|
127
|
+
[#219]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/219
|
128
|
+
[#218]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/218
|
129
|
+
[#214]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/214
|
130
|
+
[#212]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/212
|
131
|
+
[#209]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/209
|
132
|
+
[#208]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/208
|
133
|
+
[#207]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/207
|
134
|
+
[#193]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/193
|
135
|
+
[#191]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/191
|
136
|
+
[#175]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/175
|
137
|
+
[#173]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/173
|
138
|
+
[#171]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/171
|
139
|
+
[#170]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/170
|
140
|
+
[#164]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/164
|
141
|
+
[#163]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/163
|
142
|
+
[#161]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/161
|
143
|
+
[#160]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/160
|
144
|
+
[#159]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/159
|
145
|
+
[#158]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/158
|
146
|
+
[#157]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/157
|
147
|
+
[#156]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/156
|
148
|
+
[#152]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/152
|
149
|
+
[#150]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/150
|
150
|
+
[#148]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/148
|
151
|
+
[#147]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/147
|
152
|
+
[#146]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/146
|
153
|
+
[#144]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/144
|
154
|
+
[#137]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/137
|
155
|
+
[#136]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/136
|
156
|
+
[#135]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/135
|
43
157
|
[#133]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/133
|
44
158
|
[#131]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/131
|
159
|
+
[#126]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/126
|
45
160
|
[#124]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/124
|
46
161
|
[#123]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/123
|
47
162
|
[#119]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/119
|
48
163
|
[#116]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/116
|
49
164
|
[#114]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/114
|
165
|
+
[#75]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/75
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Contributing to ActsAsParanoid
|
2
|
+
|
3
|
+
We welcome all contributions to ActsAsParanoid. Below are some guidelines to
|
4
|
+
help the process of handling issues and pull requests go smoothly.
|
5
|
+
|
6
|
+
## Issues
|
7
|
+
|
8
|
+
When creating an issue, please try to provide as much information as possible.
|
9
|
+
Also, please follow the guidelines below to make it easier for us to figure out
|
10
|
+
what's going on. If you miss any of these points we will probably ask you to
|
11
|
+
improve the ticket.
|
12
|
+
|
13
|
+
- Include a clear title describing the problem
|
14
|
+
- Describe what you are trying to achieve
|
15
|
+
- Describe what you did, preferably including relevant code
|
16
|
+
- Describe what you expected to happen
|
17
|
+
- Describe what happened instead, possibly including relevant output
|
18
|
+
- Use [code blocks](https://github.github.com/gfm/#fenced-code-blocks) to
|
19
|
+
format any code and output in your ticket to make it readable.
|
20
|
+
|
21
|
+
## Pull requests
|
22
|
+
|
23
|
+
If you have an idea for a particular feature, it's probably best to create a
|
24
|
+
GitHub issue for it before trying to implement it yourself. That way, we can
|
25
|
+
discuss the feature and whether it makes sense to include in ActsAsParanoid itself
|
26
|
+
before putting in the work to implement it.
|
27
|
+
|
28
|
+
If you want to send pull requests or patches, try to follow the instructions
|
29
|
+
below. **If you get stuck, please make a pull request anyway and we'll try to
|
30
|
+
help out.**
|
31
|
+
|
32
|
+
- Make sure `rake test` runs without reporting any failures.
|
33
|
+
- Add tests for your feature. Otherwise, we can't see if it works or if we
|
34
|
+
break it later.
|
35
|
+
- Make sure latest master merges cleanly with your branch. Things might
|
36
|
+
have moved around since you forked.
|
37
|
+
- Try not to include changes that are irrelevant to your feature in the
|
38
|
+
same commit.
|
39
|
+
- Keep an eye on the build results in GitHub Actions. If the build fails and it
|
40
|
+
seems due to your changes, please update your pull request with a fix.
|
41
|
+
|
42
|
+
### The review process
|
43
|
+
|
44
|
+
- We will try to review your pull request as soon as possible but we can make no
|
45
|
+
guarantees. Feel free to ping us now and again.
|
46
|
+
- We will probably ask you to rebase your branch on current master at some point
|
47
|
+
during the review process.
|
48
|
+
If you are unsure how to do this,
|
49
|
+
[this in-depth guide](https://git-rebase.io/) should help out.
|
50
|
+
- If you have any unclear commit messages, work-in-progress commits, or commits
|
51
|
+
that just fix a mistake in a previous commits, we will ask you to clean up
|
52
|
+
the history.
|
53
|
+
Again, [the git-rebase guide](https://git-rebase.io/) should help out.
|
54
|
+
- At the end of the review process we may still choose not to merge your pull
|
55
|
+
request. For example, this could happen if we decide the proposed feature
|
56
|
+
should not be part of ActsAsParanoid, or if the technical implementation does not
|
57
|
+
match where we want to go with the architecture the project.
|
58
|
+
- We will generally not merge any pull requests that make the build fail, unless
|
59
|
+
it's very clearly not related to the changes in the pull request.
|
data/README.md
CHANGED
@@ -9,35 +9,38 @@ recoverable later.
|
|
9
9
|
|
10
10
|
## Support
|
11
11
|
|
12
|
-
**This branch targets Rails
|
12
|
+
**This branch targets Rails 5.2+ and Ruby 2.4+ only**
|
13
13
|
|
14
|
-
If you're working with Rails
|
15
|
-
require an older version of the
|
14
|
+
If you're working with Rails 5.1 and earlier, or with Ruby 2.3 or earlier,
|
15
|
+
please switch to the corresponding branch or require an older version of the
|
16
|
+
`acts_as_paranoid` gem.
|
16
17
|
|
17
|
-
### Known issues
|
18
|
+
### Known issues
|
18
19
|
|
19
|
-
* Using acts_as_paranoid and ActiveStorage on the same model
|
20
|
+
* Using `acts_as_paranoid` and ActiveStorage on the same model
|
20
21
|
[leads to a SystemStackError](https://github.com/ActsAsParanoid/acts_as_paranoid/issues/103).
|
21
|
-
* You cannot directly create a model in a deleted state
|
22
|
+
* You cannot directly create a model in a deleted state, or update a model
|
23
|
+
after it's been deleted.
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
27
|
#### Install gem:
|
26
28
|
|
27
|
-
```
|
28
|
-
gem 'acts_as_paranoid', '~> 0.
|
29
|
+
```ruby
|
30
|
+
gem 'acts_as_paranoid', '~> 0.7.0'
|
29
31
|
```
|
30
|
-
|
32
|
+
|
33
|
+
```shell
|
31
34
|
bundle install
|
32
35
|
```
|
33
36
|
|
34
37
|
#### Create migration
|
35
38
|
|
36
|
-
```
|
39
|
+
```shell
|
37
40
|
bin/rails generate migration AddDeletedAtToParanoiac deleted_at:datetime:index
|
38
41
|
```
|
39
42
|
|
40
|
-
#### Enable ActsAsParanoid
|
43
|
+
#### Enable ActsAsParanoid
|
41
44
|
|
42
45
|
```ruby
|
43
46
|
class Paranoiac < ActiveRecord::Base
|
@@ -45,31 +48,42 @@ class Paranoiac < ActiveRecord::Base
|
|
45
48
|
end
|
46
49
|
```
|
47
50
|
|
48
|
-
|
49
|
-
|
50
|
-
- `:column => 'deleted_at'`
|
51
|
-
- `:column_type => 'time'`
|
51
|
+
By default, ActsAsParanoid assumes a record's *deletion* is stored in a
|
52
|
+
`datetime` column called `deleted_at`.
|
52
53
|
|
53
54
|
### Options
|
54
55
|
|
55
|
-
If you are using a different column name and type to store a record's
|
56
|
+
If you are using a different column name and type to store a record's
|
57
|
+
*deletion*, you can specify them as follows:
|
56
58
|
|
57
|
-
-
|
58
|
-
-
|
59
|
+
- `column: 'deleted'`
|
60
|
+
- `column_type: 'boolean'`
|
59
61
|
|
60
|
-
While *column* can be anything (as long as it exists in your database), *type*
|
62
|
+
While *column* can be anything (as long as it exists in your database), *type*
|
63
|
+
is restricted to:
|
61
64
|
|
62
65
|
- `boolean`
|
63
66
|
- `time` or
|
64
67
|
- `string`
|
65
68
|
|
66
|
-
|
69
|
+
Note that the `time` type corresponds to the database column type `datetime`
|
70
|
+
in your Rails migrations and schema.
|
67
71
|
|
68
|
-
If your column type is a `
|
72
|
+
If your column type is a `string`, you can also specify which value to use when
|
73
|
+
marking an object as deleted by passing `:deleted_value` (default is
|
74
|
+
"deleted"). Any records with a non-matching value in this column will be
|
75
|
+
treated normally, i.e., as not deleted.
|
76
|
+
|
77
|
+
If your column type is a `boolean`, it is possible to specify `allow_nulls`
|
78
|
+
option which is `true` by default. When set to `false`, entities that have
|
79
|
+
`false` value in this column will be considered not deleted, and those which
|
80
|
+
have `true` will be considered deleted. When `true` everything that has a
|
81
|
+
not-null value will be considered deleted.
|
69
82
|
|
70
83
|
### Filtering
|
71
84
|
|
72
|
-
If a record is deleted by ActsAsParanoid, it won't be retrieved when accessing
|
85
|
+
If a record is deleted by ActsAsParanoid, it won't be retrieved when accessing
|
86
|
+
the database.
|
73
87
|
|
74
88
|
So, `Paranoiac.all` will **not** include the **deleted records**.
|
75
89
|
|
@@ -80,7 +94,8 @@ Paranoiac.only_deleted # retrieves only the deleted records
|
|
80
94
|
Paranoiac.with_deleted # retrieves all records, deleted or not
|
81
95
|
```
|
82
96
|
|
83
|
-
When using the default `column_type` of `'time'`, the following extra scopes
|
97
|
+
When using the default `column_type` of `'time'`, the following extra scopes
|
98
|
+
are provided:
|
84
99
|
|
85
100
|
```ruby
|
86
101
|
time = Time.now
|
@@ -100,23 +115,32 @@ In order to really delete a record, just use:
|
|
100
115
|
paranoiac.destroy_fully!
|
101
116
|
Paranoiac.delete_all!(conditions)
|
102
117
|
```
|
103
|
-
**NOTE:** The `.destroy!` method is still usable, but equivalent to `.destroy`. It just hides the object.
|
104
118
|
|
105
|
-
|
119
|
+
**NOTE:** The `.destroy!` method is still usable, but equivalent to `.destroy`.
|
120
|
+
It just hides the object.
|
121
|
+
|
122
|
+
Alternatively you can permanently delete a record by calling `destroy` or
|
123
|
+
`delete_all` on the object **twice**.
|
106
124
|
|
107
|
-
If a record was already deleted (hidden by `ActsAsParanoid`) and you delete it
|
125
|
+
If a record was already deleted (hidden by `ActsAsParanoid`) and you delete it
|
126
|
+
again, it will be removed from the database.
|
108
127
|
|
109
128
|
Take this example:
|
110
129
|
|
111
130
|
```ruby
|
112
131
|
p = Paranoiac.first
|
113
|
-
|
114
|
-
|
132
|
+
|
133
|
+
# does NOT delete the first record, just hides it
|
134
|
+
p.destroy
|
135
|
+
|
136
|
+
# deletes the first record from the database
|
137
|
+
Paranoiac.only_deleted.where(id: p.id).first.destroy
|
115
138
|
```
|
116
139
|
|
117
|
-
This behaviour can be disabled by setting the configuration option. In a future
|
140
|
+
This behaviour can be disabled by setting the configuration option. In a future
|
141
|
+
version, `false` will be the default setting.
|
118
142
|
|
119
|
-
-
|
143
|
+
- `double_tap_destroys_fully: false`
|
120
144
|
|
121
145
|
### Recovery
|
122
146
|
|
@@ -126,34 +150,39 @@ Recovery is easy. Just invoke `recover` on it, like this:
|
|
126
150
|
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover
|
127
151
|
```
|
128
152
|
|
129
|
-
All associations marked as
|
153
|
+
All associations marked as `dependent: :destroy` are also recursively recovered.
|
130
154
|
|
131
|
-
If you would like to disable this behavior, you can call `recover` with the
|
155
|
+
If you would like to disable this behavior, you can call `recover` with the
|
156
|
+
`recursive` option:
|
132
157
|
|
133
158
|
```ruby
|
134
|
-
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:
|
159
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(recursive: false)
|
135
160
|
```
|
136
161
|
|
137
|
-
If you would like to change this default behavior for one model, you can use
|
162
|
+
If you would like to change this default behavior for one model, you can use
|
163
|
+
the `recover_dependent_associations` option
|
138
164
|
|
139
165
|
```ruby
|
140
166
|
class Paranoiac < ActiveRecord::Base
|
141
|
-
acts_as_paranoid :
|
167
|
+
acts_as_paranoid recover_dependent_associations: false
|
142
168
|
end
|
143
169
|
```
|
144
170
|
|
145
|
-
By default, dependent records will be recovered if they were deleted within 2
|
171
|
+
By default, dependent records will be recovered if they were deleted within 2
|
172
|
+
minutes of the object upon which they depend.
|
146
173
|
|
147
|
-
This restores the objects to the state before the recursive deletion without
|
174
|
+
This restores the objects to the state before the recursive deletion without
|
175
|
+
restoring other objects that were deleted earlier.
|
148
176
|
|
149
|
-
The behavior is only available when both parent and dependant are using
|
177
|
+
The behavior is only available when both parent and dependant are using
|
178
|
+
timestamp fields to mark deletion, which is the default behavior.
|
150
179
|
|
151
180
|
This window can be changed with the `dependent_recovery_window` option:
|
152
181
|
|
153
182
|
```ruby
|
154
183
|
class Paranoiac < ActiveRecord::Base
|
155
184
|
acts_as_paranoid
|
156
|
-
has_many :paranoids, :
|
185
|
+
has_many :paranoids, dependent: :destroy
|
157
186
|
end
|
158
187
|
|
159
188
|
class Paranoid < ActiveRecord::Base
|
@@ -161,18 +190,42 @@ class Paranoid < ActiveRecord::Base
|
|
161
190
|
|
162
191
|
# Paranoid objects will be recovered alongside Paranoic objects
|
163
192
|
# if they were deleted within 10 minutes of the Paranoic object
|
164
|
-
acts_as_paranoid :
|
193
|
+
acts_as_paranoid dependent_recovery_window: 10.minutes
|
165
194
|
end
|
166
195
|
```
|
167
196
|
|
168
197
|
or in the recover statement
|
169
198
|
|
170
199
|
```ruby
|
171
|
-
Paranoiac.only_deleted.where("name = ?", "not dead yet").first
|
200
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first
|
201
|
+
.recover(recovery_window: 30.seconds)
|
202
|
+
```
|
203
|
+
|
204
|
+
### recover!
|
205
|
+
|
206
|
+
You can invoke `recover!` if you wish to raise an error if the recovery fails.
|
207
|
+
The error generally stems from ActiveRecord.
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover!
|
211
|
+
# => ActiveRecord::RecordInvalid: Validation failed: Name already exists
|
212
|
+
```
|
213
|
+
|
214
|
+
Optionally, you may also raise the error by passing `raise_error: true` to the
|
215
|
+
`recover` method. This behaves the same as `recover!`.
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(raise_error: true)
|
172
219
|
```
|
173
220
|
|
174
221
|
### Validation
|
175
|
-
|
222
|
+
|
223
|
+
ActiveRecord's built-in uniqueness validation does not account for records
|
224
|
+
deleted by ActsAsParanoid. If you want to check for uniqueness among
|
225
|
+
non-deleted records only, use the macro `validates_as_paranoid` in your model.
|
226
|
+
Then, instead of using `validates_uniqueness_of`, use
|
227
|
+
`validates_uniqueness_of_without_deleted`. This will keep deleted records from
|
228
|
+
counting against the uniqueness check.
|
176
229
|
|
177
230
|
```ruby
|
178
231
|
class Paranoiac < ActiveRecord::Base
|
@@ -181,10 +234,10 @@ class Paranoiac < ActiveRecord::Base
|
|
181
234
|
validates_uniqueness_of_without_deleted :name
|
182
235
|
end
|
183
236
|
|
184
|
-
p1 = Paranoiac.create(:
|
237
|
+
p1 = Paranoiac.create(name: 'foo')
|
185
238
|
p1.destroy
|
186
239
|
|
187
|
-
p2 = Paranoiac.new(:
|
240
|
+
p2 = Paranoiac.new(name: 'foo')
|
188
241
|
p2.valid? #=> true
|
189
242
|
p2.save
|
190
243
|
|
@@ -192,16 +245,34 @@ p1.recover #=> fails validation!
|
|
192
245
|
```
|
193
246
|
|
194
247
|
### Status
|
195
|
-
|
248
|
+
|
249
|
+
A paranoid object could be deleted or destroyed fully.
|
250
|
+
|
251
|
+
You can check if the object is deleted with the `deleted?` helper
|
196
252
|
|
197
253
|
```ruby
|
198
|
-
Paranoiac.create(:
|
254
|
+
Paranoiac.create(name: 'foo').destroy
|
199
255
|
Paranoiac.with_deleted.first.deleted? #=> true
|
200
256
|
```
|
201
257
|
|
258
|
+
After the first call to `.destroy` the object is `deleted?`.
|
259
|
+
|
260
|
+
You can check if the object is fully destroyed with `destroyed_fully?` or `deleted_fully?`.
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
Paranoiac.create(name: 'foo').destroy
|
264
|
+
Paranoiac.with_deleted.first.deleted? #=> true
|
265
|
+
Paranoiac.with_deleted.first.destroyed_fully? #=> false
|
266
|
+
p1 = Paranoiac.with_deleted.first
|
267
|
+
p1.destroy # this fully destroys the object
|
268
|
+
p1.destroyed_fully? #=> true
|
269
|
+
p1.deleted_fully? #=> true
|
270
|
+
```
|
271
|
+
|
202
272
|
### Scopes
|
203
273
|
|
204
|
-
As you've probably guessed, `with_deleted` and `only_deleted` are scopes. You
|
274
|
+
As you've probably guessed, `with_deleted` and `only_deleted` are scopes. You
|
275
|
+
can, however, chain them freely with other scopes you might have.
|
205
276
|
|
206
277
|
For example:
|
207
278
|
|
@@ -220,10 +291,10 @@ You can work freely with scopes and it will just work:
|
|
220
291
|
```ruby
|
221
292
|
class Paranoiac < ActiveRecord::Base
|
222
293
|
acts_as_paranoid
|
223
|
-
scope :pretty, where(:
|
294
|
+
scope :pretty, where(pretty: true)
|
224
295
|
end
|
225
296
|
|
226
|
-
Paranoiac.create(:
|
297
|
+
Paranoiac.create(pretty: true)
|
227
298
|
|
228
299
|
Paranoiac.pretty.count #=> 1
|
229
300
|
Paranoiac.only_deleted.count #=> 0
|
@@ -240,11 +311,13 @@ Paranoiac.pretty.only_deleted.count #=> 1
|
|
240
311
|
|
241
312
|
Associations are also supported.
|
242
313
|
|
243
|
-
From the simplest behaviors you'd expect to more nifty things like the ones
|
314
|
+
From the simplest behaviors you'd expect to more nifty things like the ones
|
315
|
+
mentioned previously or the usage of the `:with_deleted` option with
|
316
|
+
`belongs_to`
|
244
317
|
|
245
318
|
```ruby
|
246
319
|
class Parent < ActiveRecord::Base
|
247
|
-
has_many :children, :
|
320
|
+
has_many :children, class_name: "ParanoiacChild"
|
248
321
|
end
|
249
322
|
|
250
323
|
class ParanoiacChild < ActiveRecord::Base
|
@@ -252,7 +325,8 @@ class ParanoiacChild < ActiveRecord::Base
|
|
252
325
|
belongs_to :parent
|
253
326
|
|
254
327
|
# You may need to provide a foreign_key like this
|
255
|
-
belongs_to :parent_including_deleted, :
|
328
|
+
belongs_to :parent_including_deleted, class_name: "Parent",
|
329
|
+
foreign_key: 'parent_id', with_deleted: true
|
256
330
|
end
|
257
331
|
|
258
332
|
parent = Parent.first
|
@@ -263,18 +337,38 @@ child.parent #=> nil
|
|
263
337
|
child.parent_including_deleted #=> Parent (it works!)
|
264
338
|
```
|
265
339
|
|
340
|
+
### Callbacks
|
341
|
+
|
342
|
+
There are couple of callbacks that you may use when dealing with deletion and
|
343
|
+
recovery of objects. There is `before_recover` and `after_recover` which will
|
344
|
+
be triggered before and after the recovery of an object respectively.
|
345
|
+
|
346
|
+
Default ActiveRecord callbacks such as `before_destroy` and `after_destroy` will
|
347
|
+
be triggered around `.destroy!` and `.destroy_fully!`.
|
348
|
+
|
349
|
+
```ruby
|
350
|
+
class Paranoiac < ActiveRecord::Base
|
351
|
+
acts_as_paranoid
|
352
|
+
|
353
|
+
before_recover :set_counts
|
354
|
+
after_recover :update_logs
|
355
|
+
end
|
356
|
+
```
|
357
|
+
|
266
358
|
## Caveats
|
267
359
|
|
268
360
|
Watch out for these caveats:
|
269
361
|
|
270
|
-
-
|
271
|
-
-
|
272
|
-
|
273
|
-
|
362
|
+
- You cannot use scopes named `with_deleted` and `only_deleted`
|
363
|
+
- You cannot use scopes named `deleted_inside_time_window`,
|
364
|
+
`deleted_before_time`, `deleted_after_time` **if** your paranoid column's
|
365
|
+
type is `time`
|
366
|
+
- You cannot name association `*_with_deleted`
|
367
|
+
- `unscoped` will return all records, deleted or not
|
274
368
|
|
275
369
|
# Acknowledgements
|
276
370
|
|
277
|
-
* To [Rick Olson](https://github.com/technoweenie) for creating acts_as_paranoid
|
371
|
+
* To [Rick Olson](https://github.com/technoweenie) for creating `acts_as_paranoid`
|
278
372
|
* To [cheerfulstoic](https://github.com/cheerfulstoic) for adding recursive recovery
|
279
373
|
* To [Jonathan Vaught](https://github.com/gravelpup) for adding paranoid validations
|
280
374
|
* To [Geoffrey Hichborn](https://github.com/phene) for improving the overral code quality and adding support for after_commit
|