acts_as_paranoid 0.6.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +113 -8
- data/CONTRIBUTING.md +59 -0
- data/README.md +151 -57
- data/lib/acts_as_paranoid.rb +37 -30
- data/lib/acts_as_paranoid/associations.rb +21 -17
- data/lib/acts_as_paranoid/core.rb +112 -75
- data/lib/acts_as_paranoid/relation.rb +2 -0
- data/lib/acts_as_paranoid/validations.rb +8 -74
- data/lib/acts_as_paranoid/version.rb +3 -1
- data/test/test_associations.rb +119 -43
- data/test/test_core.rb +253 -57
- data/test/test_default_scopes.rb +7 -5
- data/test/test_helper.rb +134 -64
- data/test/test_inheritance.rb +3 -1
- data/test/test_relations.rb +18 -10
- data/test/test_validations.rb +9 -7
- metadata +72 -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: 73cbdef60cb81f9506a3763c13b0380f83169b8c68f0b914305d828b90202d25
|
4
|
+
data.tar.gz: 2023f4db3125ff3db3dd9dc7b0fb0142e92d6f8f54bc433557d5dd0dc40fb853
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c7b2ff7d8655621915c11630012ff946a196b46fd0a175facad75d90aa4f6ecae1ae6df1e627f9cf778faa46d26fb559b275e62101a4ba402007d545014f43d
|
7
|
+
data.tar.gz: ec47e5232127609e3e83b9bf22b142a821e955b55a77e082f21ceaeb5e62c390253fee6815cfc3a4a795257eb68c15f29eb5adbad8e511703718e4278d84e52c
|
data/CHANGELOG.md
CHANGED
@@ -2,15 +2,77 @@
|
|
2
2
|
|
3
3
|
Notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
+
## 0.7.2
|
6
|
+
|
7
|
+
* Do not set boolean column to NULL on recovery if nulls are not allowed
|
8
|
+
([#193], by [Shodai Suzuki][soartec-lab])
|
9
|
+
* Add a CONTRIBUTING.md file ([#207], by [Matijs van Zuijlen][mvz])
|
10
|
+
|
11
|
+
## 0.7.1
|
12
|
+
|
13
|
+
* Support Rails 6.1 ([#191], by [Matijs van Zuijlen][mvz])
|
14
|
+
* Support `belongs_to` with both `:touch` and `:counter_cache` options ([#208],
|
15
|
+
by [Matijs van Zuijlen][mvz] with [Paul Druziak][pauldruziak])
|
16
|
+
* Support Ruby 3.0 ([#209], by [Matijs van Zuijlen][mvz])
|
17
|
+
|
18
|
+
## 0.7.0
|
19
|
+
|
20
|
+
### Breaking changes
|
21
|
+
|
22
|
+
* Support Rails 5.2+ only ([#126], by [Daniel Rice][danielricecodes])
|
23
|
+
* Update set of supported rubies to 2.4-2.7 ([#144], [#173] by [Matijs van Zuijlen][mvz])
|
24
|
+
|
25
|
+
### Improvements
|
26
|
+
|
27
|
+
* Handle `with_deleted` association option as a scope ([#147], by [Matijs van Zuijlen][mvz])
|
28
|
+
* Simplify validation override ([#158], by [Matijs van Zuijlen][mvz])
|
29
|
+
* Use correct unscope syntax so unscope works on Rails Edge ([#160],
|
30
|
+
by [Matijs van Zuijlen][mvz])
|
31
|
+
* Fix ruby 2.7 keyword argument deprecation warning ([#161], by [Jon Riddle][wtfspm])
|
32
|
+
|
33
|
+
### Documentation
|
34
|
+
|
35
|
+
* Document save after destroy behavior ([#146], by [Matijs van Zuijlen][mvz])
|
36
|
+
* Update version number instructions for installing gem ([#164],
|
37
|
+
by [Kevin McAlear][kevinmcalear])
|
38
|
+
* Add example with `destroyed_fully?` and `deleted_fully?` to the readme ([#170],
|
39
|
+
by [Kiril Mitov][thebravoman])
|
40
|
+
|
41
|
+
### Internal
|
42
|
+
|
43
|
+
* Improve code quality using RuboCop ([#148], [#152], [#159], [#163], [#171] and [#173],
|
44
|
+
by [Matijs van Zuijlen][mvz])
|
45
|
+
* Measure code coverage using SimpleCov ([#150] and [#175] by [Matijs van Zuijlen][mvz])
|
46
|
+
* Silence warnings emitted during tests ([#156], by [Matijs van Zuijlen][mvz])
|
47
|
+
* Make rake tasks more robust and intuitive ([#157], by [Matijs van Zuijlen][mvz])
|
48
|
+
|
49
|
+
## 0.6.3
|
50
|
+
|
51
|
+
* Update Travis CI configuration ([#137], by [Matijs van Zuijlen][mvz])
|
52
|
+
* Add predicate to check if record was soft deleted or hard deleted ([#136],
|
53
|
+
by [Aymeric Le Dorze][aymeric-ledorze])
|
54
|
+
* Add support for recover! method ([#75], by [vinoth][avinoth])
|
55
|
+
* Fix a record being dirty after destroying it ([#135], by
|
56
|
+
[Aymeric Le Dorze][aymeric-ledorze])
|
57
|
+
|
58
|
+
## 0.6.2
|
59
|
+
|
60
|
+
* Prevent recovery of non-deleted records
|
61
|
+
([#133], by [Mary Beliveau][marycodes2] and [Valerie Woolard][valeriecodes])
|
62
|
+
* Allow model to set `table_name` after `acts_as_paranoid` macro
|
63
|
+
([#131], by [Alex Wheeler][AlexWheeler])
|
64
|
+
* Make counter cache work with a custom column name and with optional
|
65
|
+
associations ([#123], by [Ned Campion][nedcampion])
|
66
|
+
|
5
67
|
## 0.6.1
|
6
68
|
|
7
|
-
* Add support for Rails 6 ([#124], by [Daniel Rice]
|
8
|
-
[Josh Bryant]
|
69
|
+
* Add support for Rails 6 ([#124], by [Daniel Rice][danielricecodes],
|
70
|
+
[Josh Bryant][jbryant92], and [Romain Alexandre][RomainAlexandre])
|
9
71
|
* Add support for incrementing and decrementing counter cache columns on
|
10
72
|
associated objects ([#119], by [Dimitar Lukanov][shadydealer])
|
11
73
|
* Add `:double_tap_destroys_fully` option, with default `true` ([#116],
|
12
|
-
by [Michael Riviera][ri4a])
|
13
|
-
* Officially support Ruby 2.6 ([#114], by [Matijs van Zuijlen][mvz])
|
74
|
+
by [Michael Riviera][ri4a])
|
75
|
+
* Officially support Ruby 2.6 ([#114], by [Matijs van Zuijlen][mvz])
|
14
76
|
|
15
77
|
## 0.6.0 and earlier
|
16
78
|
|
@@ -18,15 +80,58 @@ Notable changes to this project will be documented in this file.
|
|
18
80
|
|
19
81
|
<!-- Contributors -->
|
20
82
|
|
21
|
-
[
|
22
|
-
[
|
23
|
-
[
|
83
|
+
[AlexWheeler]: https://github.com/AlexWheeler
|
84
|
+
[RomainAlexandre]: https://github.com/RomainAlexandre
|
85
|
+
[avinoth]: https://github.com/avinoth
|
86
|
+
[aymeric-ledorze]: https://github.com/aymeric-ledorze
|
24
87
|
[danielricecodes]: https://github.com/danielricecodes
|
25
88
|
[jbryant92]: https://github.com/jbryant92
|
26
|
-
[
|
89
|
+
[kevinmcalear]: https://github.com/kevinmcalear
|
90
|
+
[marycodes2]: https://github.com/marycodes2
|
91
|
+
[mvz]: https://github.com/mvz
|
92
|
+
[nedcampion]: https://github.com/nedcampion
|
93
|
+
[ri4a]: https://github.com/ri4a
|
94
|
+
[pauldruziak]: https://github.com/pauldruziak
|
95
|
+
[shadydealer]: https://github.com/shadydealer
|
96
|
+
[soartec-lab]: https://github.com/soartec-lab
|
97
|
+
[thebravoman]: https://github.com/thebravoman
|
98
|
+
[valeriecodes]: https://github.com/valeriecodes
|
99
|
+
[wtfspm]: https://github.com/wtfspm
|
27
100
|
|
28
101
|
<!-- issues & pull requests -->
|
29
102
|
|
103
|
+
[#209]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/209
|
104
|
+
[#208]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/208
|
105
|
+
[#207]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/207
|
106
|
+
[#193]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/193
|
107
|
+
[#191]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/191
|
108
|
+
[#175]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/175
|
109
|
+
[#173]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/173
|
110
|
+
[#171]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/171
|
111
|
+
[#170]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/170
|
112
|
+
[#164]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/164
|
113
|
+
[#163]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/163
|
114
|
+
[#161]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/161
|
115
|
+
[#160]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/160
|
116
|
+
[#159]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/159
|
117
|
+
[#158]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/158
|
118
|
+
[#157]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/157
|
119
|
+
[#156]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/156
|
120
|
+
[#152]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/152
|
121
|
+
[#150]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/150
|
122
|
+
[#148]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/148
|
123
|
+
[#147]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/147
|
124
|
+
[#146]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/146
|
125
|
+
[#144]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/144
|
126
|
+
[#137]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/137
|
127
|
+
[#136]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/136
|
128
|
+
[#135]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/135
|
129
|
+
[#133]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/133
|
130
|
+
[#131]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/131
|
131
|
+
[#126]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/126
|
132
|
+
[#124]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/124
|
133
|
+
[#123]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/123
|
30
134
|
[#119]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/119
|
31
135
|
[#116]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/116
|
32
136
|
[#114]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/114
|
137
|
+
[#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
|