acts_as_paranoid 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a57d617e3e2f52608fefad1c84bba5d6825d1b9ea96e681ade8739461ef35796
4
- data.tar.gz: 549ef0573150ba7f30740a6145cb6b52ef2916b3584199fc0bab8eba17271eb0
3
+ metadata.gz: b0a0cec714f9701fb054580a39b677e73a3cfd3fa1909afd29dfedbbfdcb82d0
4
+ data.tar.gz: bef6e1c18c141b3035d5a4b3c33cad22a398d06250f1f09730ae39662e48a0ca
5
5
  SHA512:
6
- metadata.gz: 13ac9918fc0dc45e93d02ad799f20fe9c13afd4eba4a0b81c52201bc495a690dc80b1ab9ba9e700b7d072a61c5973dcf8d134a1049c581d61c1c0120f4444d59
7
- data.tar.gz: 6bba5d24a72d45b5de90670e1c577d6172160d1ee5d3850a3d9dd3845eb2fd73060a6d441d738b99dc9aea516189a6807c0406f377997dddf8ac443f88db6fbd
6
+ metadata.gz: 804904753df983b7e366d4a6af8f4c80b24cda018a2cd2534721f7d943cc23556a9a1a9c77a7f37de335c60a3fde99639036315f235b303a60fdefc871e693b9
7
+ data.tar.gz: 40e9a9229af5706aa62e319e49c74b60b2d89e8880d46f6106ec51ef44e1e4811715b514b5e691500abc65805dd521c6741130f8eba0b892d2738cfe8efca23a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,51 @@
2
2
 
3
3
  Notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.8.0
6
+
7
+ * Do not set `paranoid_value` when destroying fully ([#238], by [Aymeric Le Dorze][aymeric-ledorze])
8
+ * Make helper methods for dependent associations private ([#239], by [Matijs van Zuijlen][mvz])
9
+ * Raise ActiveRecord::RecordNotDestroyed if destroy returns false ([#240], by [Hao Liu][leomayleomay])
10
+ * Make unscoping by `with_deleted` less blunt ([#241], by [Matijs van Zuijlen][mvz])
11
+ * Drop support for Ruby 2.4 and 2.5 ([#243] and [#245] by [Matijs van Zuijlen][mvz])
12
+ * Remove deprecated methods ([#244] by [Matijs van Zuijlen][mvz])
13
+ * Remove test files from the gem ([#261] by [Matijs van Zuijlen][mvz])
14
+ * Add support for Rails 7 ([#262] by [Vederis Leunardus][cloudsbird])
15
+
16
+ ## 0.7.3
17
+
18
+ ## Improvements
19
+
20
+ * Fix deletion time scopes ([#212] by [Matijs van Zuijlen][mvz])
21
+ * Reload `has_one` associations after dependent recovery ([#214],
22
+ by [Matijs van Zuijlen][mvz])
23
+ * Make dependent recovery work when parent is non-optional ([#227],
24
+ by [Matijs van Zuijlen][mvz])
25
+ * Avoid querying nil `belongs_to` associations when recovering ([#219],
26
+ by [Matijs van Zuijlen][mvz])
27
+ * On relations, deprecate `destroy!` in favour of `destroy_fully!` ([#222],
28
+ by [Matijs van Zuijlen][mvz])
29
+ * Deprecate the undocumented `:recovery_value` setting. Calculate the correct
30
+ value instead. ([#220], by [Matijs van Zuijlen][mvz])
31
+
32
+ ## Developer experience
33
+
34
+ * Log ActiveRecord activity to a visible log during tests ([#218],
35
+ by [Matijs van Zuijlen][mvz])
36
+
37
+ ## 0.7.2
38
+
39
+ * Do not set boolean column to NULL on recovery if nulls are not allowed
40
+ ([#193], by [Shodai Suzuki][soartec-lab])
41
+ * Add a CONTRIBUTING.md file ([#207], by [Matijs van Zuijlen][mvz])
42
+
43
+ ## 0.7.1
44
+
45
+ * Support Rails 6.1 ([#191], by [Matijs van Zuijlen][mvz])
46
+ * Support `belongs_to` with both `:touch` and `:counter_cache` options ([#208],
47
+ by [Matijs van Zuijlen][mvz] with [Paul Druziak][pauldruziak])
48
+ * Support Ruby 3.0 ([#209], by [Matijs van Zuijlen][mvz])
49
+
5
50
  ## 0.7.0
6
51
 
7
52
  ### Breaking changes
@@ -70,21 +115,46 @@ Notable changes to this project will be documented in this file.
70
115
  [AlexWheeler]: https://github.com/AlexWheeler
71
116
  [RomainAlexandre]: https://github.com/RomainAlexandre
72
117
  [avinoth]: https://github.com/avinoth
118
+ [cloudsbird]: https://github.com/cloudsbird
73
119
  [aymeric-ledorze]: https://github.com/aymeric-ledorze
74
120
  [danielricecodes]: https://github.com/danielricecodes
75
121
  [jbryant92]: https://github.com/jbryant92
76
122
  [kevinmcalear]: https://github.com/kevinmcalear
123
+ [leomayleomay]: https://github.com/leomayleomay
77
124
  [marycodes2]: https://github.com/marycodes2
78
125
  [mvz]: https://github.com/mvz
79
126
  [nedcampion]: https://github.com/nedcampion
80
127
  [ri4a]: https://github.com/ri4a
128
+ [pauldruziak]: https://github.com/pauldruziak
81
129
  [shadydealer]: https://github.com/shadydealer
130
+ [soartec-lab]: https://github.com/soartec-lab
82
131
  [thebravoman]: https://github.com/thebravoman
83
132
  [valeriecodes]: https://github.com/valeriecodes
84
133
  [wtfspm]: https://github.com/wtfspm
85
134
 
86
135
  <!-- issues & pull requests -->
87
136
 
137
+ [#262]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/262
138
+ [#261]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/261
139
+ [#245]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/245
140
+ [#244]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/244
141
+ [#243]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/243
142
+ [#241]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/241
143
+ [#240]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/240
144
+ [#239]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/239
145
+ [#238]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/238
146
+ [#227]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/227
147
+ [#222]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/222
148
+ [#220]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/220
149
+ [#219]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/219
150
+ [#218]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/218
151
+ [#214]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/214
152
+ [#212]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/212
153
+ [#209]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/209
154
+ [#208]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/208
155
+ [#207]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/207
156
+ [#193]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/193
157
+ [#191]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/191
88
158
  [#175]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/175
89
159
  [#173]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/173
90
160
  [#171]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/171
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,58 @@
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 provide as much information as possible, and
9
+ follow the guidelines below to make it easier for us to figure out what's going
10
+ on. If you miss any of these points we will probably ask you to improve the
11
+ 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
+ To send pull requests or patches, please follow the instructions below.
29
+ **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
+ - Create a separate branch for your feature based off of latest master.
36
+ - Do not include changes that are irrelevant to your feature in the same
37
+ commit.
38
+ - Keep an eye on the build results in GitHub Actions. If the build fails and it
39
+ seems due to your changes, please update your pull request with a fix.
40
+
41
+ ### The review process
42
+
43
+ - We will try to review your pull request as soon as possible but we can make no
44
+ guarantees. Feel free to ping us now and again.
45
+ - We will probably ask you to rebase your branch on current master at some point
46
+ during the review process.
47
+ If you are unsure how to do this,
48
+ [this in-depth guide](https://git-rebase.io/) should help out.
49
+ - If you have any unclear commit messages, work-in-progress commits, or commits
50
+ that just fix a mistake in a previous commits, we will ask you to clean up
51
+ the history.
52
+ Again, [the git-rebase guide](https://git-rebase.io/) should help out.
53
+ - At the end of the review process we may still choose not to merge your pull
54
+ request. For example, this could happen if we decide the proposed feature
55
+ should not be part of ActsAsParanoid, or if the technical implementation does not
56
+ match where we want to go with the architecture the project.
57
+ - We will generally not merge any pull requests that make the build fail, unless
58
+ it's very clearly not related to the changes in the pull request.
data/README.md CHANGED
@@ -9,9 +9,9 @@ recoverable later.
9
9
 
10
10
  ## Support
11
11
 
12
- **This branch targets Rails 5.2+ and Ruby 2.4+ only**
12
+ **This branch targets Rails 5.2+ and Ruby 2.5+ only**
13
13
 
14
- If you're working with Rails 5.1 and earlier, or with Ruby 2.3 or earlier,
14
+ If you're working with Rails 5.1 and earlier, or with Ruby 2.4 or earlier,
15
15
  please switch to the corresponding branch or require an older version of the
16
16
  `acts_as_paranoid` gem.
17
17
 
@@ -27,7 +27,7 @@ please switch to the corresponding branch or require an older version of the
27
27
  #### Install gem:
28
28
 
29
29
  ```ruby
30
- gem 'acts_as_paranoid', '~> 0.7.0'
30
+ gem 'acts_as_paranoid'
31
31
  ```
32
32
 
33
33
  ```shell
@@ -255,9 +255,9 @@ Paranoiac.create(name: 'foo').destroy
255
255
  Paranoiac.with_deleted.first.deleted? #=> true
256
256
  ```
257
257
 
258
- After the first call to .destroy the object is deleted?
258
+ After the first call to `.destroy` the object is `deleted?`.
259
259
 
260
- You can check if the object is fully destroyed with destroyed_fully? or deleted_fully?.
260
+ You can check if the object is fully destroyed with `destroyed_fully?` or `deleted_fully?`.
261
261
 
262
262
  ```ruby
263
263
  Paranoiac.create(name: 'foo').destroy
@@ -88,6 +88,14 @@ module ActsAsParanoid
88
88
  end
89
89
  end
90
90
 
91
+ def recovery_value
92
+ if boolean_type_not_nullable?
93
+ false
94
+ else
95
+ nil
96
+ end
97
+ end
98
+
91
99
  protected
92
100
 
93
101
  def define_deleted_time_scopes
@@ -96,19 +104,25 @@ module ActsAsParanoid
96
104
  }
97
105
 
98
106
  scope :deleted_after_time, lambda { |time|
99
- where("#{table_name}.#{paranoid_column} > ?", time)
107
+ only_deleted
108
+ .where("#{table_name}.#{paranoid_column} > ?", time)
100
109
  }
101
110
  scope :deleted_before_time, lambda { |time|
102
- where("#{table_name}.#{paranoid_column} < ?", time)
111
+ only_deleted
112
+ .where("#{table_name}.#{paranoid_column} < ?", time)
103
113
  }
104
114
  end
105
115
 
106
116
  def without_paranoid_default_scope
107
117
  scope = all
108
118
 
119
+ # unscope avoids applying the default scope when using this scope for associations
109
120
  scope = scope.unscope(where: paranoid_column)
110
- # Fix problems with unscope group chain
111
- scope = scope.unscoped if scope.to_sql.include? paranoid_default_scope.to_sql
121
+
122
+ paranoid_where_clause =
123
+ ActiveRecord::Relation::WhereClause.new([paranoid_default_scope])
124
+
125
+ scope.where_clause = all.where_clause - paranoid_where_clause
112
126
 
113
127
  scope
114
128
  end
@@ -138,11 +152,10 @@ module ActsAsParanoid
138
152
  # Handle composite keys, otherwise we would just use
139
153
  # `self.class.primary_key.to_sym => self.id`.
140
154
  self.class
141
- .delete_all!(Hash[[Array(self.class.primary_key), Array(id)].transpose])
155
+ .delete_all!([Array(self.class.primary_key), Array(id)].transpose.to_h)
142
156
  decrement_counters_on_associations
143
157
  end
144
158
 
145
- stale_paranoid_value
146
159
  @destroyed = true
147
160
  freeze
148
161
  end
@@ -150,6 +163,12 @@ module ActsAsParanoid
150
163
  end
151
164
 
152
165
  def destroy!
166
+ destroy || raise(
167
+ ActiveRecord::RecordNotDestroyed.new("Failed to destroy the record", self)
168
+ )
169
+ end
170
+
171
+ def destroy
153
172
  if !deleted?
154
173
  with_transaction_returning_status do
155
174
  run_callbacks :destroy do
@@ -157,7 +176,7 @@ module ActsAsParanoid
157
176
  # Handle composite keys, otherwise we would just use
158
177
  # `self.class.primary_key.to_sym => self.id`.
159
178
  self.class
160
- .delete_all(Hash[[Array(self.class.primary_key), Array(id)].transpose])
179
+ .delete_all([Array(self.class.primary_key), Array(id)].transpose.to_h)
161
180
  decrement_counters_on_associations
162
181
  end
163
182
 
@@ -172,8 +191,6 @@ module ActsAsParanoid
172
191
  end
173
192
  end
174
193
 
175
- alias destroy destroy!
176
-
177
194
  def recover(options = {})
178
195
  return if !deleted?
179
196
 
@@ -185,16 +202,16 @@ module ActsAsParanoid
185
202
 
186
203
  self.class.transaction do
187
204
  run_callbacks :recover do
188
- if options[:recursive]
189
- recover_dependent_associations(options[:recovery_window], options)
190
- end
191
205
  increment_counters_on_associations
192
- self.paranoid_value = self.class.paranoid_configuration[:recovery_value]
193
- if options[:raise_error]
194
- save!
195
- else
196
- save
197
- end
206
+ deleted_value = paranoid_value
207
+ self.paranoid_value = self.class.recovery_value
208
+ result = if options[:raise_error]
209
+ save!
210
+ else
211
+ save
212
+ end
213
+ recover_dependent_associations(deleted_value, options) if options[:recursive]
214
+ result
198
215
  end
199
216
  end
200
217
  end
@@ -205,34 +222,6 @@ module ActsAsParanoid
205
222
  recover(options)
206
223
  end
207
224
 
208
- def recover_dependent_associations(window, options)
209
- self.class.dependent_associations.each do |reflection|
210
- next unless (klass = get_reflection_class(reflection)).paranoid?
211
-
212
- scope = klass.only_deleted.merge(get_association_scope(reflection: reflection))
213
-
214
- # We can only recover by window if both parent and dependant have a
215
- # paranoid column type of :time.
216
- if self.class.paranoid_column_type == :time && klass.paranoid_column_type == :time
217
- scope = scope.deleted_inside_time_window(paranoid_value, window)
218
- end
219
-
220
- scope.each do |object|
221
- object.recover(options)
222
- end
223
- end
224
- end
225
-
226
- def destroy_dependent_associations!
227
- self.class.dependent_associations.each do |reflection|
228
- next unless (klass = get_reflection_class(reflection)).paranoid?
229
-
230
- klass
231
- .only_deleted.merge(get_association_scope(reflection: reflection))
232
- .each(&:destroy!)
233
- end
234
- end
235
-
236
225
  def deleted?
237
226
  return true if @destroyed
238
227
 
@@ -255,32 +244,68 @@ module ActsAsParanoid
255
244
 
256
245
  private
257
246
 
258
- def get_association_scope(reflection:)
259
- ActiveRecord::Associations::AssociationScope.scope(association(reflection.name))
247
+ def recover_dependent_associations(deleted_value, options)
248
+ self.class.dependent_associations.each do |reflection|
249
+ recover_dependent_association(reflection, deleted_value, options)
250
+ end
260
251
  end
261
252
 
262
- def get_reflection_class(reflection)
263
- if reflection.macro == :belongs_to && reflection.options.include?(:polymorphic)
264
- send(reflection.foreign_type).constantize
265
- else
266
- reflection.klass
253
+ def destroy_dependent_associations!
254
+ self.class.dependent_associations.each do |reflection|
255
+ assoc = association(reflection.name)
256
+ next unless (klass = assoc.klass).paranoid?
257
+
258
+ klass
259
+ .only_deleted.merge(get_association_scope(assoc))
260
+ .each(&:destroy!)
267
261
  end
268
262
  end
269
263
 
264
+ def recover_dependent_association(reflection, deleted_value, options)
265
+ assoc = association(reflection.name)
266
+ return unless (klass = assoc.klass).paranoid?
267
+
268
+ if reflection.belongs_to? && attributes[reflection.association_foreign_key].nil?
269
+ return
270
+ end
271
+
272
+ scope = klass.only_deleted.merge(get_association_scope(assoc))
273
+
274
+ # We can only recover by window if both parent and dependant have a
275
+ # paranoid column type of :time.
276
+ if self.class.paranoid_column_type == :time && klass.paranoid_column_type == :time
277
+ scope = scope.deleted_inside_time_window(deleted_value, options[:recovery_window])
278
+ end
279
+
280
+ recovered = false
281
+ scope.each do |object|
282
+ object.recover(options)
283
+ recovered = true
284
+ end
285
+
286
+ assoc.reload if recovered && reflection.has_one? && assoc.loaded?
287
+ end
288
+
289
+ def get_association_scope(dependent_association)
290
+ ActiveRecord::Associations::AssociationScope.scope(dependent_association)
291
+ end
292
+
270
293
  def paranoid_value=(value)
271
294
  write_attribute(self.class.paranoid_column, value)
272
295
  end
273
296
 
274
297
  def update_counters_on_associations(method_sym)
275
- return unless [:decrement_counter, :increment_counter].include? method_sym
276
-
277
298
  each_counter_cached_association_reflection do |assoc_reflection|
299
+ reflection_options = assoc_reflection.options
300
+ next unless reflection_options[:counter_cache]
301
+
278
302
  associated_object = send(assoc_reflection.name)
279
303
  next unless associated_object
280
304
 
281
305
  counter_cache_column = assoc_reflection.counter_cache_column
282
306
  associated_object.class.send(method_sym, counter_cache_column,
283
307
  associated_object.id)
308
+ associated_object.touch if reflection_options[:touch]
284
309
  end
285
310
  end
286
311
 
@@ -29,7 +29,7 @@ module ActsAsParanoid
29
29
  end
30
30
  end
31
31
 
32
- def destroy!(id_or_array)
32
+ def destroy_fully!(id_or_array)
33
33
  where(primary_key => id_or_array).orig_delete_all
34
34
  end
35
35
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActsAsParanoid
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
@@ -27,18 +27,18 @@ module ActsAsParanoid
27
27
  column_type: "time",
28
28
  recover_dependent_associations: true,
29
29
  dependent_recovery_window: 2.minutes,
30
- recovery_value: nil,
31
30
  double_tap_destroys_fully: true
32
31
  }
33
32
  if options[:column_type] == "string"
34
33
  paranoid_configuration.merge!(deleted_value: "deleted")
35
34
  end
36
- paranoid_configuration.merge!(allow_nulls: true) if options[:column_type] == "boolean"
35
+
37
36
  paranoid_configuration.merge!(options) # user options
38
37
 
39
38
  unless %w[time boolean string].include? paranoid_configuration[:column_type]
40
- raise ArgumentError, "'time', 'boolean' or 'string' expected" \
41
- " for :column_type option, got #{paranoid_configuration[:column_type]}"
39
+ raise ArgumentError,
40
+ "'time', 'boolean' or 'string' expected for :column_type option," \
41
+ " got #{paranoid_configuration[:column_type]}"
42
42
  end
43
43
 
44
44
  return if paranoid?