discard 1.0.0 → 1.1.0
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/.travis.yml +17 -7
- data/.yardopts +8 -0
- data/CHANGELOG.md +25 -0
- data/Gemfile +4 -0
- data/README.md +36 -17
- data/Rakefile +6 -7
- data/discard.gemspec +4 -3
- data/lib/discard/errors.rb +29 -0
- data/lib/discard/model.rb +89 -20
- data/lib/discard/version.rb +4 -1
- data/lib/discard.rb +3 -0
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2418547a70d311cc7d970ce492f505a0807db23a03eebe2ae1e1f7589a0edb8b
|
4
|
+
data.tar.gz: a45947c018416ee63a873fc0f94f143f20a6a5e34892420893bf3fe50b9a5406
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4111df84d2628abc622a64bcb730ef1acc1b5d509ed2c226a5f5b5ca96fecaaebf65bf39fa2f54392c9161198e496fd84aa78f72aebf921c21c9c3cdd18ac687
|
7
|
+
data.tar.gz: a881fee07ba21fbf6281f966ff51607a5f2acfb127a43fd35e3a9bfbc2c2dcdfec1962e14ba475e2c33dae81e0dca56f5de8172c4afe3e9536e5f36377d33b82
|
data/.travis.yml
CHANGED
@@ -1,14 +1,24 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.2.
|
5
|
-
- 2.3.
|
6
|
-
- 2.4.
|
7
|
-
- 2.5.
|
4
|
+
- 2.2.10
|
5
|
+
- 2.3.8
|
6
|
+
- 2.4.6
|
7
|
+
- 2.5.5
|
8
|
+
- 2.6.3
|
8
9
|
env:
|
9
10
|
matrix:
|
10
|
-
- RAILS_VERSION='~> 4.2.0'
|
11
|
-
- RAILS_VERSION='~> 5.0.0'
|
11
|
+
- RAILS_VERSION='~> 4.2.0' SQLITE_VERSION='~> 1.3.6'
|
12
|
+
- RAILS_VERSION='~> 5.0.0' SQLITE_VERSION='~> 1.3.6'
|
12
13
|
- RAILS_VERSION='~> 5.1.0'
|
13
14
|
- RAILS_VERSION='~> 5.2.x'
|
14
|
-
|
15
|
+
- RAILS_VERSION='~> 6.0.0.beta3'
|
16
|
+
|
17
|
+
matrix:
|
18
|
+
exclude:
|
19
|
+
- rvm: 2.2.10
|
20
|
+
env: RAILS_VERSION='~> 6.0.0.beta3'
|
21
|
+
- rvm: 2.3.8
|
22
|
+
env: RAILS_VERSION='~> 6.0.0.beta3'
|
23
|
+
- rvm: 2.4.6
|
24
|
+
env: RAILS_VERSION='~> 6.0.0.beta3'
|
data/.yardopts
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
### Unreleased
|
2
|
+
|
3
|
+
### Version 1.1.0
|
4
|
+
Release date: 2019-05-03
|
5
|
+
|
6
|
+
* Support for ActiveRecord 6
|
7
|
+
* `discard_all` and `undiscard_all` now return affected records
|
8
|
+
* Add `discard!` and `undiscard!`
|
9
|
+
|
10
|
+
### Version 1.0.0
|
11
|
+
Release date: 2018-03-16
|
12
|
+
|
13
|
+
* Add undiscard callbacks and `.undiscard_all`
|
14
|
+
|
15
|
+
### Version 0.2.0
|
16
|
+
Release date: 2017-11-22
|
17
|
+
|
18
|
+
* Add `.discard_all`
|
19
|
+
* Add `undiscarded` scope
|
20
|
+
* Add callbacks
|
21
|
+
|
22
|
+
### Version 0.1.0
|
23
|
+
Release date: 2017-04-28
|
24
|
+
|
25
|
+
* Initial version!
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -32,10 +32,15 @@ class Post < ActiveRecord::Base
|
|
32
32
|
end
|
33
33
|
```
|
34
34
|
|
35
|
+
You can either generate a migration using:
|
36
|
+
```
|
37
|
+
rails generate migration add_discarded_at_to_posts discarded_at:datetime:index
|
38
|
+
```
|
35
39
|
|
40
|
+
or create one yourself like the one below:
|
36
41
|
``` ruby
|
37
42
|
class AddDiscardToPosts < ActiveRecord::Migration[5.0]
|
38
|
-
def
|
43
|
+
def change
|
39
44
|
add_column :posts, :discarded_at, :datetime
|
40
45
|
add_index :posts, :discarded_at
|
41
46
|
end
|
@@ -43,9 +48,9 @@ end
|
|
43
48
|
```
|
44
49
|
|
45
50
|
|
46
|
-
|
51
|
+
#### Discard a record
|
47
52
|
|
48
|
-
```
|
53
|
+
```ruby
|
49
54
|
Post.all # => [#<Post id: 1, ...>]
|
50
55
|
Post.kept # => [#<Post id: 1, ...>]
|
51
56
|
Post.discarded # => []
|
@@ -60,7 +65,7 @@ Post.kept # => []
|
|
60
65
|
Post.discarded # => [#<Post id: 1, ...>]
|
61
66
|
```
|
62
67
|
|
63
|
-
|
68
|
+
***From a controller***
|
64
69
|
|
65
70
|
Controller actions need a small modification to discard records instead of deleting them. Just replace `destroy` with `discard`.
|
66
71
|
|
@@ -72,23 +77,23 @@ end
|
|
72
77
|
```
|
73
78
|
|
74
79
|
|
75
|
-
|
80
|
+
#### Undiscard a record
|
76
81
|
|
77
|
-
```
|
82
|
+
```ruby
|
78
83
|
post = Post.first # => #<Post id: 1, ...>
|
79
84
|
post.undiscard # => true
|
80
85
|
```
|
81
86
|
|
82
87
|
***From a controller***
|
83
88
|
|
84
|
-
```
|
89
|
+
```ruby
|
85
90
|
def update
|
86
91
|
@post.undiscard
|
87
92
|
redirect_to users_url, notice: "Post undiscarded"
|
88
93
|
end
|
89
94
|
```
|
90
95
|
|
91
|
-
|
96
|
+
#### Working with associations
|
92
97
|
|
93
98
|
Under paranoia, soft deleting a record will destroy any `dependent: :destroy`
|
94
99
|
associations. Probably not what you want! This leads to all dependent records
|
@@ -114,10 +119,10 @@ discarded. Just override the `kept` scope on the Comment model.
|
|
114
119
|
|
115
120
|
``` ruby
|
116
121
|
class Comment < ActiveRecord::Base
|
117
|
-
|
122
|
+
belongs_to :post
|
118
123
|
|
119
124
|
include Discard::Model
|
120
|
-
scope :kept, -> { undiscarded.joins(:
|
125
|
+
scope :kept, -> { undiscarded.joins(:post).merge(Post.kept) }
|
121
126
|
end
|
122
127
|
|
123
128
|
Comment.kept
|
@@ -133,7 +138,7 @@ SQL databases are very good at this, and performance should not be an issue.
|
|
133
138
|
In both of these cases restoring either of these records will do right thing!
|
134
139
|
|
135
140
|
|
136
|
-
|
141
|
+
#### Default scope
|
137
142
|
|
138
143
|
It's usually undesirable to add a default scope. It will take more effort to
|
139
144
|
work around and will cause more headaches. If you know you need a default scope, it's easy to add yourself ❤.
|
@@ -149,7 +154,7 @@ Post.with_discarded # All Posts
|
|
149
154
|
Post.with_discarded.discarded # Only discarded posts
|
150
155
|
```
|
151
156
|
|
152
|
-
|
157
|
+
#### Custom column
|
153
158
|
|
154
159
|
If you're migrating from paranoia, you might want to continue using the same
|
155
160
|
column.
|
@@ -161,12 +166,16 @@ class Post < ActiveRecord::Base
|
|
161
166
|
end
|
162
167
|
```
|
163
168
|
|
164
|
-
|
169
|
+
#### Callbacks
|
165
170
|
|
166
171
|
Callbacks can be run before, after, or around the discard and undiscard operations.
|
167
172
|
A likely use is discarding or deleting associated records (but see "Working with associations" for an alternative).
|
168
173
|
|
169
174
|
``` ruby
|
175
|
+
class Comment < ActiveRecord::Base
|
176
|
+
include Discard::Model
|
177
|
+
end
|
178
|
+
|
170
179
|
class Post < ActiveRecord::Base
|
171
180
|
include Discard::Model
|
172
181
|
|
@@ -182,15 +191,25 @@ class Post < ActiveRecord::Base
|
|
182
191
|
end
|
183
192
|
```
|
184
193
|
|
185
|
-
|
194
|
+
*Warning:* Please note that callbacks for save and update are run when discarding/undiscarding a record
|
195
|
+
|
196
|
+
|
197
|
+
#### Performance tuning
|
198
|
+
`discard_all` and `undiscard_all` is intended to behave like `destroy_all` which has callbacks, validations, and does one query per record. If performance is a big concern, you may consider replacing it with:
|
199
|
+
|
200
|
+
`scope.update_all(discarded_at: Time.current)`
|
201
|
+
or
|
202
|
+
`scope.update_all(discarded_at: nil)`
|
203
|
+
|
204
|
+
#### Working with Devise
|
186
205
|
|
187
206
|
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
207
|
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
208
|
|
190
|
-
```
|
209
|
+
```ruby
|
191
210
|
class User < ActiveRecord::Base
|
192
211
|
def active_for_authentication?
|
193
|
-
super && !
|
212
|
+
super && !discarded?
|
194
213
|
end
|
195
214
|
end
|
196
215
|
```
|
@@ -217,7 +236,7 @@ and awkward behaviour.
|
|
217
236
|
adding `.with_deleted` to associations or anywhere soft-deleted records
|
218
237
|
should be found. :disappointed:
|
219
238
|
* Adding `belongs_to :child, -> { with_deleted }` helps, but doesn't work for
|
220
|
-
joins and eager-loading.
|
239
|
+
joins and eager-loading [before Rails 5.2](https://github.com/rubysherpas/paranoia/issues/355)
|
221
240
|
* `delete` is overridden (`really_delete` will actually delete the record) :unamused:
|
222
241
|
* `destroy` is overridden (`really_destroy` will actually delete the record) :pensive:
|
223
242
|
* `dependent: :destroy` associations are deleted when performing soft-destroys :scream:
|
data/Rakefile
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
2
4
|
|
3
|
-
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
RSpec::Core::RakeTask.new(:spec)
|
6
|
-
rescue LoadError
|
7
|
-
end
|
5
|
+
RSpec::Core::RakeTask.new(:rspec)
|
8
6
|
|
9
|
-
|
7
|
+
desc 'Run the test suite'
|
8
|
+
task default: :rspec
|
data/discard.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'discard/version'
|
@@ -21,8 +22,8 @@ Gem::Specification.new do |spec|
|
|
21
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
23
|
spec.require_paths = ["lib"]
|
23
24
|
|
24
|
-
spec.add_dependency "activerecord",
|
25
|
-
spec.add_development_dependency "bundler"
|
25
|
+
spec.add_dependency "activerecord", ">= 4.2", "< 7"
|
26
|
+
spec.add_development_dependency "bundler"
|
26
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
28
|
spec.add_development_dependency "rspec", "~> 3.5.0"
|
28
29
|
spec.add_development_dependency "database_cleaner", "~> 1.5"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discard
|
4
|
+
# = Discard Errors
|
5
|
+
#
|
6
|
+
# Generic exception class.
|
7
|
+
class DiscardError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
# Raised by {Discard::Model#discard!}
|
11
|
+
class RecordNotDiscarded < DiscardError
|
12
|
+
attr_reader :record
|
13
|
+
|
14
|
+
def initialize(message = nil, record = nil)
|
15
|
+
@record = record
|
16
|
+
super(message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Raised by {Discard::Model#undiscard!}
|
21
|
+
class RecordNotUndiscarded < DiscardError
|
22
|
+
attr_reader :record
|
23
|
+
|
24
|
+
def initialize(message = nil, record = nil)
|
25
|
+
@record = record
|
26
|
+
super(message)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/discard/model.rb
CHANGED
@@ -1,4 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Discard
|
4
|
+
# Handles soft deletes of records.
|
5
|
+
#
|
6
|
+
# Options:
|
7
|
+
#
|
8
|
+
# - :discard_column - The columns used to track soft delete, defaults to `:discarded_at`.
|
2
9
|
module Model
|
3
10
|
extend ActiveSupport::Concern
|
4
11
|
|
@@ -15,42 +22,104 @@ module Discard
|
|
15
22
|
define_model_callbacks :undiscard
|
16
23
|
end
|
17
24
|
|
25
|
+
# :nodoc:
|
18
26
|
module ClassMethods
|
27
|
+
# Discards the records by instantiating each
|
28
|
+
# record and calling its {#discard} method.
|
29
|
+
# Each object's callbacks are executed.
|
30
|
+
# Returns the collection of objects that were discarded.
|
31
|
+
#
|
32
|
+
# Note: Instantiation, callback execution, and update of each
|
33
|
+
# record can be time consuming when you're discarding many records at
|
34
|
+
# once. It generates at least one SQL +UPDATE+ query per record (or
|
35
|
+
# possibly more, to enforce your callbacks). If you want to discard many
|
36
|
+
# rows quickly, without concern for their associations or callbacks, use
|
37
|
+
# #update_all(discarded_at: Time.current) instead.
|
38
|
+
#
|
39
|
+
# ==== Examples
|
40
|
+
#
|
41
|
+
# Person.where(age: 0..18).discard_all
|
19
42
|
def discard_all
|
20
|
-
|
43
|
+
kept.each(&:discard)
|
21
44
|
end
|
45
|
+
|
46
|
+
# Undiscards the records by instantiating each
|
47
|
+
# record and calling its {#undiscard} method.
|
48
|
+
# Each object's callbacks are executed.
|
49
|
+
# Returns the collection of objects that were undiscarded.
|
50
|
+
#
|
51
|
+
# Note: Instantiation, callback execution, and update of each
|
52
|
+
# record can be time consuming when you're undiscarding many records at
|
53
|
+
# once. It generates at least one SQL +UPDATE+ query per record (or
|
54
|
+
# possibly more, to enforce your callbacks). If you want to undiscard many
|
55
|
+
# rows quickly, without concern for their associations or callbacks, use
|
56
|
+
# #update_all(discarded_at: nil) instead.
|
57
|
+
#
|
58
|
+
# ==== Examples
|
59
|
+
#
|
60
|
+
# Person.where(age: 0..18).undiscard_all
|
22
61
|
def undiscard_all
|
23
|
-
|
62
|
+
discarded.each(&:undiscard)
|
24
63
|
end
|
25
64
|
end
|
26
65
|
|
27
|
-
# @return [
|
66
|
+
# @return [Boolean] true if this record has been discarded, otherwise false
|
28
67
|
def discarded?
|
29
|
-
|
68
|
+
self[self.class.discard_column].present?
|
30
69
|
end
|
31
70
|
|
32
|
-
#
|
71
|
+
# Discard the record in the database
|
72
|
+
#
|
73
|
+
# @return [Boolean] true if successful, otherwise false
|
33
74
|
def discard
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
self[self.class.discard_column] = Time.current
|
38
|
-
save
|
39
|
-
end
|
40
|
-
end
|
75
|
+
return if discarded?
|
76
|
+
run_callbacks(:discard) do
|
77
|
+
update_attribute(self.class.discard_column, Time.current)
|
41
78
|
end
|
42
79
|
end
|
43
80
|
|
44
|
-
#
|
81
|
+
# Discard the record in the database
|
82
|
+
#
|
83
|
+
# There's a series of callbacks associated with #discard!. If the
|
84
|
+
# <tt>before_discard</tt> callback throws +:abort+ the action is cancelled
|
85
|
+
# and #discard! raises {Discard::RecordNotDiscarded}.
|
86
|
+
#
|
87
|
+
# @return [Boolean] true if successful
|
88
|
+
# @raise {Discard::RecordNotDiscarded}
|
89
|
+
def discard!
|
90
|
+
discard || _raise_record_not_discarded
|
91
|
+
end
|
92
|
+
|
93
|
+
# Undiscard the record in the database
|
94
|
+
#
|
95
|
+
# @return [Boolean] true if successful, otherwise false
|
45
96
|
def undiscard
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
self[self.class.discard_column] = nil
|
50
|
-
save
|
51
|
-
end
|
52
|
-
end
|
97
|
+
return unless discarded?
|
98
|
+
run_callbacks(:undiscard) do
|
99
|
+
update_attribute(self.class.discard_column, nil)
|
53
100
|
end
|
54
101
|
end
|
102
|
+
|
103
|
+
# Discard the record in the database
|
104
|
+
#
|
105
|
+
# There's a series of callbacks associated with #undiscard!. If the
|
106
|
+
# <tt>before_undiscard</tt> callback throws +:abort+ the action is cancelled
|
107
|
+
# and #undiscard! raises {Discard::RecordNotUndiscarded}.
|
108
|
+
#
|
109
|
+
# @return [Boolean] true if successful
|
110
|
+
# @raise {Discard::RecordNotUndiscarded}
|
111
|
+
def undiscard!
|
112
|
+
undiscard || _raise_record_not_undiscarded
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def _raise_record_not_discarded
|
118
|
+
raise ::Discard::RecordNotDiscarded.new("Failed to discard the record", self)
|
119
|
+
end
|
120
|
+
|
121
|
+
def _raise_record_not_undiscarded
|
122
|
+
raise ::Discard::RecordNotUndiscarded.new("Failed to undiscard the record", self)
|
123
|
+
end
|
55
124
|
end
|
56
125
|
end
|
data/lib/discard/version.rb
CHANGED
data/lib/discard.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: 1.
|
4
|
+
version: 1.1.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: 2019-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '4.2'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '7'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,21 +29,21 @@ dependencies:
|
|
29
29
|
version: '4.2'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '7'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: bundler
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "
|
37
|
+
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '0'
|
40
40
|
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
|
-
- - "
|
44
|
+
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
46
|
+
version: '0'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rake
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,6 +125,8 @@ files:
|
|
125
125
|
- ".gitignore"
|
126
126
|
- ".rspec"
|
127
127
|
- ".travis.yml"
|
128
|
+
- ".yardopts"
|
129
|
+
- CHANGELOG.md
|
128
130
|
- CODE_OF_CONDUCT.md
|
129
131
|
- Gemfile
|
130
132
|
- LICENSE.txt
|
@@ -134,6 +136,7 @@ files:
|
|
134
136
|
- bin/setup
|
135
137
|
- discard.gemspec
|
136
138
|
- lib/discard.rb
|
139
|
+
- lib/discard/errors.rb
|
137
140
|
- lib/discard/model.rb
|
138
141
|
- lib/discard/version.rb
|
139
142
|
homepage: https://github.com/jhawthorn/discard
|
@@ -155,8 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
158
|
- !ruby/object:Gem::Version
|
156
159
|
version: '0'
|
157
160
|
requirements: []
|
158
|
-
|
159
|
-
rubygems_version: 2.7.6
|
161
|
+
rubygems_version: 3.0.1
|
160
162
|
signing_key:
|
161
163
|
specification_version: 4
|
162
164
|
summary: ActiveRecord soft-deletes done right
|