discard 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +6 -4
- data/README.md +90 -38
- data/lib/discard/model.rb +16 -3
- data/lib/discard/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 99f47416f1c4bdc6ed76c7814e6f1b88baea019acf8d38abbbbf709d42db0efa
|
4
|
+
data.tar.gz: c6699d73d311300e8cdeecd1cb2498c3b25197a10292f28993abc237ad1abd5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebd11402b212e931423a98484b598675eac16ef4eb878a701fd1479da488bd503058d0679053be16b43bab4e651b50cd512c6d14f19d131dae02fbcbc3396c78
|
7
|
+
data.tar.gz: f5baab64f5a951f1c55c73615490587d44291296645c4e4317c6918dcdc52675c3fa1856f86db03de2341518d9836ab2ad7e011d914599ebbc27d425406f5ccf
|
data/.travis.yml
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.2.
|
5
|
-
- 2.3.
|
6
|
-
- 2.4.
|
4
|
+
- 2.2.9
|
5
|
+
- 2.3.6
|
6
|
+
- 2.4.3
|
7
|
+
- 2.5.0
|
7
8
|
env:
|
8
9
|
matrix:
|
9
10
|
- RAILS_VERSION='~> 4.2.0'
|
10
11
|
- RAILS_VERSION='~> 5.0.0'
|
11
|
-
- RAILS_VERSION='~> 5.1.
|
12
|
+
- RAILS_VERSION='~> 5.1.0'
|
13
|
+
- RAILS_VERSION='~> 5.2.x'
|
12
14
|
before_install: gem install bundler -v 1.14.6
|
data/README.md
CHANGED
@@ -8,42 +8,12 @@ Soft deletes for ActiveRecord done right.
|
|
8
8
|
|
9
9
|
A simple ActiveRecord mixin to add conventions for flagging records as discarded.
|
10
10
|
|
11
|
-
## Why should I use this?
|
12
|
-
|
13
|
-
I've worked with and have helped maintain
|
14
|
-
[paranoia](https://github.com/rubysherpas/paranoia) for a while. I'm convinced
|
15
|
-
it does the wrong thing for most cases.
|
16
|
-
|
17
|
-
Paranoia and
|
18
|
-
[acts_as_paranoid](https://github.com/ActsAsParanoid/acts_as_paranoid) both
|
19
|
-
attempt to emulate deletes by setting a column and adding a default scope on the
|
20
|
-
model. This requires some ActiveRecord hackery, and leads to some surprising
|
21
|
-
and awkward behaviour.
|
22
|
-
|
23
|
-
* A default scope is added to hide soft-deleted records, which necessitates
|
24
|
-
adding `.with_deleted` to associations or anywhere soft-deleted records
|
25
|
-
should be found. :disappointed:
|
26
|
-
* Adding `belongs_to :child, -> { with_deleted }` helps, but doesn't work for
|
27
|
-
joins and eager-loading.
|
28
|
-
* `delete` is overridden (`really_delete` will actually delete the record) :unamused:
|
29
|
-
* `destroy` is overridden (`really_destroy` will actually delete the record) :pensive:
|
30
|
-
* `dependent: :destroy` associations are deleted when performing soft-destroys :scream:
|
31
|
-
* requiring any dependent records to also be `acts_as_paranoid` to avoid losing data. :grimacing:
|
32
|
-
|
33
|
-
There are some use cases where these behaviours make sense: if you really did
|
34
|
-
want to _almost_ delete the record. More often developers are just looking to
|
35
|
-
hide some records, or mark them as inactive.
|
36
|
-
|
37
|
-
Discard takes a different approach. It doesn't override any ActiveRecord
|
38
|
-
methods and instead simply provides convenience methods and scopes for
|
39
|
-
discarding (hiding), restoring, and querying records.
|
40
|
-
|
41
11
|
## Installation
|
42
12
|
|
43
13
|
Add this line to your application's Gemfile:
|
44
14
|
|
45
15
|
```ruby
|
46
|
-
gem 'discard'
|
16
|
+
gem 'discard', '~> 1.0'
|
47
17
|
```
|
48
18
|
|
49
19
|
And then execute:
|
@@ -67,21 +37,23 @@ end
|
|
67
37
|
class AddDiscardToPosts < ActiveRecord::Migration[5.0]
|
68
38
|
def up
|
69
39
|
add_column :posts, :discarded_at, :datetime
|
40
|
+
add_index :posts, :discarded_at
|
70
41
|
end
|
71
42
|
end
|
72
43
|
```
|
73
44
|
|
74
45
|
|
75
46
|
**Discard a record**
|
47
|
+
|
76
48
|
```
|
77
49
|
Post.all # => [#<Post id: 1, ...>]
|
78
50
|
Post.kept # => [#<Post id: 1, ...>]
|
79
51
|
Post.discarded # => []
|
80
52
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
53
|
+
post = Post.first # => #<Post id: 1, ...>
|
54
|
+
post.discard # => true
|
55
|
+
post.discarded? # => true
|
56
|
+
post.discarded_at # => 2017-04-18 18:49:49 -0700
|
85
57
|
|
86
58
|
Post.all # => [#<Post id: 1, ...>]
|
87
59
|
Post.kept # => []
|
@@ -99,6 +71,23 @@ def destroy
|
|
99
71
|
end
|
100
72
|
```
|
101
73
|
|
74
|
+
|
75
|
+
**Undiscard a record**
|
76
|
+
|
77
|
+
```
|
78
|
+
post = Post.first # => #<Post id: 1, ...>
|
79
|
+
post.undiscard # => true
|
80
|
+
```
|
81
|
+
|
82
|
+
***From a controller***
|
83
|
+
|
84
|
+
```
|
85
|
+
def update
|
86
|
+
@post.undiscard
|
87
|
+
redirect_to users_url, notice: "Post undiscarded"
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
102
91
|
**Working with associations**
|
103
92
|
|
104
93
|
Under paranoia, soft deleting a record will destroy any `dependent: :destroy`
|
@@ -172,12 +161,75 @@ class Post < ActiveRecord::Base
|
|
172
161
|
end
|
173
162
|
```
|
174
163
|
|
164
|
+
**Callbacks**
|
165
|
+
|
166
|
+
Callbacks can be run before, after, or around the discard and undiscard operations.
|
167
|
+
A likely use is discarding or deleting associated records (but see "Working with associations" for an alternative).
|
168
|
+
|
169
|
+
``` ruby
|
170
|
+
class Post < ActiveRecord::Base
|
171
|
+
include Discard::Model
|
172
|
+
|
173
|
+
has_many :comments
|
174
|
+
|
175
|
+
after_discard do
|
176
|
+
comments.discard_all
|
177
|
+
end
|
178
|
+
|
179
|
+
after_undiscard do
|
180
|
+
comments.undiscard_all
|
181
|
+
end
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
**Working with Devise**
|
186
|
+
|
187
|
+
A common use case is to apply discard to a User record. Even though a user has been discarded they can still login and continue their session.
|
188
|
+
If you are using Devise and wish for discarded users to be unable to login and stop their session you can override Devise's method.
|
189
|
+
|
190
|
+
```
|
191
|
+
class User < ActiveRecord::Base
|
192
|
+
def active_for_authentication?
|
193
|
+
super && !discarded_at
|
194
|
+
end
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
175
198
|
## Non-features
|
176
199
|
|
177
|
-
* Restoring records (this will probably be added)
|
178
|
-
* Discarding dependent records (this will likely be added)
|
179
|
-
* Callbacks (this will probably be added)
|
180
200
|
* Special handling of AR counter cache columns - The counter cache counts the total number of records, both kept and discarded.
|
201
|
+
* Recursive discards (like AR's dependent: destroy) - This can be avoided using queries (See "Working with associations") or emulated using callbacks.
|
202
|
+
* Recursive restores - This concept is fundamentally broken, but not necessary if the recursive discards are avoided.
|
203
|
+
|
204
|
+
## Why not paranoia or acts_as_paranoid?
|
205
|
+
|
206
|
+
I've worked with and have helped maintain
|
207
|
+
[paranoia](https://github.com/rubysherpas/paranoia) for a while. I'm convinced
|
208
|
+
it does the wrong thing for most cases.
|
209
|
+
|
210
|
+
Paranoia and
|
211
|
+
[acts_as_paranoid](https://github.com/ActsAsParanoid/acts_as_paranoid) both
|
212
|
+
attempt to emulate deletes by setting a column and adding a default scope on the
|
213
|
+
model. This requires some ActiveRecord hackery, and leads to some surprising
|
214
|
+
and awkward behaviour.
|
215
|
+
|
216
|
+
* A default scope is added to hide soft-deleted records, which necessitates
|
217
|
+
adding `.with_deleted` to associations or anywhere soft-deleted records
|
218
|
+
should be found. :disappointed:
|
219
|
+
* Adding `belongs_to :child, -> { with_deleted }` helps, but doesn't work for
|
220
|
+
joins and eager-loading.
|
221
|
+
* `delete` is overridden (`really_delete` will actually delete the record) :unamused:
|
222
|
+
* `destroy` is overridden (`really_destroy` will actually delete the record) :pensive:
|
223
|
+
* `dependent: :destroy` associations are deleted when performing soft-destroys :scream:
|
224
|
+
* requiring any dependent records to also be `acts_as_paranoid` to avoid losing data. :grimacing:
|
225
|
+
|
226
|
+
There are some use cases where these behaviours make sense: if you really did
|
227
|
+
want to _almost_ delete the record. More often developers are just looking to
|
228
|
+
hide some records, or mark them as inactive.
|
229
|
+
|
230
|
+
Discard takes a different approach. It doesn't override any ActiveRecord
|
231
|
+
methods and instead simply provides convenience methods and scopes for
|
232
|
+
discarding (hiding), restoring, and querying records.
|
181
233
|
|
182
234
|
## Development
|
183
235
|
|
data/lib/discard/model.rb
CHANGED
@@ -12,18 +12,24 @@ module Discard
|
|
12
12
|
scope :with_discarded, ->{ unscope(where: discard_column) }
|
13
13
|
|
14
14
|
define_model_callbacks :discard
|
15
|
+
define_model_callbacks :undiscard
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
+
module ClassMethods
|
18
19
|
def discard_all
|
19
20
|
all.each(&:discard)
|
20
21
|
end
|
22
|
+
def undiscard_all
|
23
|
+
all.each(&:undiscard)
|
24
|
+
end
|
21
25
|
end
|
22
26
|
|
27
|
+
# @return [true,false] true if this record has been discarded, otherwise false
|
23
28
|
def discarded?
|
24
29
|
!!self[self.class.discard_column]
|
25
30
|
end
|
26
31
|
|
32
|
+
# @return [true,false] true if successful, otherwise false
|
27
33
|
def discard
|
28
34
|
unless discarded?
|
29
35
|
with_transaction_returning_status do
|
@@ -35,9 +41,16 @@ module Discard
|
|
35
41
|
end
|
36
42
|
end
|
37
43
|
|
44
|
+
# @return [true,false] true if successful, otherwise false
|
38
45
|
def undiscard
|
39
|
-
|
40
|
-
|
46
|
+
if discarded?
|
47
|
+
with_transaction_returning_status do
|
48
|
+
run_callbacks(:undiscard) do
|
49
|
+
self[self.class.discard_column] = nil
|
50
|
+
save
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
41
54
|
end
|
42
55
|
end
|
43
56
|
end
|
data/lib/discard/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: discard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Hawthorn
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -156,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
156
156
|
version: '0'
|
157
157
|
requirements: []
|
158
158
|
rubyforge_project:
|
159
|
-
rubygems_version: 2.6
|
159
|
+
rubygems_version: 2.7.6
|
160
160
|
signing_key:
|
161
161
|
specification_version: 4
|
162
162
|
summary: ActiveRecord soft-deletes done right
|