acts_as_paranoid 0.6.0 → 0.6.3

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: 45239a76ba68990d6273016fdba8c582ecd44d93475ebabf5ae6717b5c58ddcc
4
- data.tar.gz: 330ed8379df4137f3016de1db1def095ae268b3f99a6dce075b4fea8adf4eb42
3
+ metadata.gz: 5e2b91eb0df1ed0d8cfb306f7272860c3f03e1da75259b202d222c1fece04927
4
+ data.tar.gz: 6dc0038fd41642f40e774e32eebe18ee5da6eae6750077781902aed17975ad1e
5
5
  SHA512:
6
- metadata.gz: a2f9c1437125d81959809addcdbee54003711ad8f339b2d1f78987b81aaab3766ac813563c110879f594b127ca90d59186ba84ca0079eeec5e69005b9e0dae08
7
- data.tar.gz: 51f5f32565a0ae3f90de5f7afe517372a55c63c887c9de7ed07791f196b03e568f9cc275586c43f9e19806c5f45ef1431de4f446c819344cfebda64c0608d997
6
+ metadata.gz: 1587bb00c56acbb5daf5929ef4588f89622eb83141258c2f686ce5ed55719849fa17064bc011cf6cd3720d05f7900f1121442ba6abb71cdcc977f637800b00dc
7
+ data.tar.gz: 026be4a7b7eec225d6a1113af33aa610438bc4f895d8539e1bf6159f8bfe07bda3add7733ecdec982ac85a4b28f1e3abc9a4cd85ab56f07bc6f9fcb4bd20211b
data/CHANGELOG.md ADDED
@@ -0,0 +1,64 @@
1
+ # CHANGELOG
2
+
3
+ Notable changes to this project will be documented in this file.
4
+
5
+ ## 0.6.3
6
+
7
+ * Update Travis CI configuration ([#137], by [Matijs van Zuijlen][mvz]).
8
+ * Add predicate to check if record was soft deleted or hard deleted ([#136],
9
+ by [Aymeric Le Dorze][aymeric-ledorze]).
10
+ * Add support for recover! method ([#75], by [vinoth][avinoth]).
11
+ * Fix a record being dirty after destroying it ([#135], by
12
+ [Aymeric Le Dorze][aymeric-ledorze]).
13
+
14
+ ## 0.6.2
15
+
16
+ * Prevent recovery of non-deleted records
17
+ ([#133], by [Mary Beliveau][marycodes2] and [Valerie Woolard][valeriecodes])
18
+ * Allow model to set `table_name` after `acts_as_paranoid` macro
19
+ ([#131], by [Alex Wheeler][AlexWheeler]).
20
+ * Make counter cache work with a custom column name and with optional
21
+ associations ([#123], by [Ned Campion][nedcampion]).
22
+
23
+ ## 0.6.1
24
+
25
+ * Add support for Rails 6 ([#124], by [Daniel Rice][danielricecodes],
26
+ [Josh Bryant][jbryant92], and [Romain Alexandre][RomainAlexandre])
27
+ * Add support for incrementing and decrementing counter cache columns on
28
+ associated objects ([#119], by [Dimitar Lukanov][shadydealer])
29
+ * Add `:double_tap_destroys_fully` option, with default `true` ([#116],
30
+ by [Michael Riviera][ri4a]).
31
+ * Officially support Ruby 2.6 ([#114], by [Matijs van Zuijlen][mvz]).
32
+
33
+ ## 0.6.0 and earlier
34
+
35
+ (To be added)
36
+
37
+ <!-- Contributors -->
38
+
39
+ [ri4a]: https://github.com/ri4a
40
+ [mvz]: https://github.com/mvz
41
+ [shadydealer]: https://github.com/shadydealer
42
+ [danielricecodes]: https://github.com/danielricecodes
43
+ [jbryant92]: https://github.com/jbryant92
44
+ [nedcampion]: https://github.com/nedcampion
45
+ [RomainAlexandre]: https://github.com/RomainAlexandre
46
+ [AlexWheeler]: https://github.com/AlexWheeler
47
+ [marycodes2]: https://github.com/marycodes2
48
+ [valeriecodes]: https://github.com/valeriecodes
49
+ [aymeric-ledorze]: https://github.com/aymeric-ledorze
50
+ [avinoth]: https://github.com/avinoth
51
+
52
+ <!-- issues & pull requests -->
53
+
54
+ [#137]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/137
55
+ [#136]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/136
56
+ [#135]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/135
57
+ [#133]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/133
58
+ [#131]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/131
59
+ [#124]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/124
60
+ [#123]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/123
61
+ [#119]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/119
62
+ [#116]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/116
63
+ [#114]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/114
64
+ [#75]: https://github.com/ActsAsParanoid/acts_as_paranoid/pull/75
data/README.md CHANGED
@@ -9,12 +9,12 @@ recoverable later.
9
9
 
10
10
  ## Support
11
11
 
12
- **This branch targets Rails 4.2, 5.0 and 5.1, with experimental support for 5.2**
12
+ **This branch targets Rails 4.2, 5.0 and 5.1, with experimental support for 5.2+**
13
13
 
14
- If you're working with another version, switch to the corresponding branch, or
14
+ If you're working with Rails 4.1-, switch to the corresponding branch, or
15
15
  require an older version of the `acts_as_paranoid` gem.
16
16
 
17
- ### Known issues with Rails 5.2
17
+ ### Known issues with Rails 5.2+
18
18
 
19
19
  * Using acts_as_paranoid and ActiveStorage on the same model
20
20
  [leads to a SystemStackError](https://github.com/ActsAsParanoid/acts_as_paranoid/issues/103).
@@ -114,6 +114,10 @@ p.destroy # does NOT delete the first record, just hides it
114
114
  Paranoiac.only_deleted.where(:id => p.id).first.destroy # deletes the first record from the database
115
115
  ```
116
116
 
117
+ This behaviour can be disabled by setting the configuration option. In a future version, `false` will be the default setting.
118
+
119
+ - `:double_tap_destroys_fully => false`
120
+
117
121
  ### Recovery
118
122
 
119
123
  Recovery is easy. Just invoke `recover` on it, like this:
@@ -167,6 +171,22 @@ or in the recover statement
167
171
  Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:recovery_window => 30.seconds)
168
172
  ```
169
173
 
174
+ ### recover!
175
+
176
+ You can invoke `recover!` if you wish to raise an error if the recovery fails. The error generally stems from ActiveRecord.
177
+
178
+ ```
179
+ Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover!
180
+
181
+ ### => ActiveRecord::RecordInvalid: Validation failed: Name already exists
182
+ ```
183
+
184
+ Optionally, you may also raise the error by passing `:raise_error => true` to the `recover` method. This behaves the same as `recover!`.
185
+
186
+ ```
187
+ Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:raise_error => true)
188
+ ```
189
+
170
190
  ### Validation
171
191
  ActiveRecord's built-in uniqueness validation does not account for records deleted by ActsAsParanoid. If you want to check for uniqueness among non-deleted records only, use the macro `validates_as_paranoid` in your model. Then, instead of using `validates_uniqueness_of`, use `validates_uniqueness_of_without_deleted`. This will keep deleted records from counting against the uniqueness check.
172
192
 
@@ -259,11 +279,26 @@ child.parent #=> nil
259
279
  child.parent_including_deleted #=> Parent (it works!)
260
280
  ```
261
281
 
282
+ ### Callbacks
283
+
284
+ There are couple of callbacks that you may use when dealing with deletion and recovery of objects. There is `before_recover` and `after_recover` which will be
285
+ triggered before and after the recovery of an object respectively.
286
+
287
+ Default ActiveRecord callbaks such as `before_destroy` and `after_destroy` will be triggered around `.destroy!` and `.destroy_fully!`.
288
+
289
+ ```
290
+ class Paranoiac < ActiveRecord::Base
291
+ acts_as_paranoid
292
+
293
+ before_recover :set_counts
294
+ after_recover :update_logs
295
+ end
296
+ ```
297
+
262
298
  ## Caveats
263
299
 
264
300
  Watch out for these caveats:
265
301
 
266
-
267
302
  - You cannot use scopes named `with_deleted` and `only_deleted`
268
303
  - You cannot use scopes named `deleted_inside_time_window`, `deleted_before_time`, `deleted_after_time` **if** your paranoid column's type is `time`
269
304
  - You cannot name association `*_with_deleted`
@@ -283,5 +318,6 @@ Watch out for these caveats:
283
318
  * To [Jean Boussier](https://github.com/byroot) for initial Rails 4.0.0 support
284
319
  * To [Matijs van Zuijlen](https://github.com/mvz) for Rails 4.1 and 4.2 support
285
320
  * To [Andrey Ponomarenko](https://github.com/sjke) for Rails 5 support
321
+ * To [Daniel Rice](https://github.com/danielricecodes), [Josh Bryant](https://github.com/jbryant92), and [Romain Alexandre](https://github.com/RomainAlexandre) for Rails 6.0 support.
286
322
 
287
323
  See `LICENSE`.
@@ -17,16 +17,18 @@ module ActsAsParanoid
17
17
  def acts_as_paranoid(options = {})
18
18
  raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
19
19
 
20
- class_attribute :paranoid_configuration, :paranoid_column_reference
20
+ class_attribute :paranoid_configuration
21
21
 
22
- self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes, :recovery_value => nil }
22
+ self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes, :recovery_value => nil, double_tap_destroys_fully: true }
23
23
  self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
24
24
  self.paranoid_configuration.merge!({ :allow_nulls => true }) if options[:column_type] == "boolean"
25
25
  self.paranoid_configuration.merge!(options) # user options
26
26
 
27
27
  raise ArgumentError, "'time', 'boolean' or 'string' expected for :column_type option, got #{paranoid_configuration[:column_type]}" unless ['time', 'boolean', 'string'].include? paranoid_configuration[:column_type]
28
28
 
29
- self.paranoid_column_reference = "#{self.table_name}.#{paranoid_configuration[:column]}"
29
+ def self.paranoid_column_reference
30
+ "#{self.table_name}.#{paranoid_configuration[:column]}"
31
+ end
30
32
 
31
33
  return if paranoid?
32
34
 
@@ -31,7 +31,7 @@ module ActsAsParanoid
31
31
  association = association(:#{target})
32
32
  return nil if association.options[:polymorphic] && association.klass.nil?
33
33
  return #{target}_without_unscoped(*args) unless association.klass.paranoid?
34
- association.klass.with_deleted.scoping { #{target}_without_unscoped(*args) }
34
+ association.klass.with_deleted.scoping { association.klass.unscoped { #{target}_without_unscoped(*args) } }
35
35
  end
36
36
  alias_method :#{target}_without_unscoped, :#{target}
37
37
  alias_method :#{target}, :#{target}_with_unscoped
@@ -108,7 +108,7 @@ module ActsAsParanoid
108
108
  # Straight from ActiveRecord 5.1!
109
109
  def delete
110
110
  self.class.delete(id) if persisted?
111
- @destroyed = true
111
+ stale_paranoid_value
112
112
  freeze
113
113
  end
114
114
 
@@ -116,9 +116,16 @@ module ActsAsParanoid
116
116
  with_transaction_returning_status do
117
117
  run_callbacks :destroy do
118
118
  destroy_dependent_associations!
119
- # Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
120
- self.class.delete_all!(Hash[[Array(self.class.primary_key), Array(self.id)].transpose]) if persisted?
121
- self.paranoid_value = self.class.delete_now_value
119
+
120
+ if persisted?
121
+ # Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
122
+ self.class.delete_all!(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
123
+
124
+ decrement_counters_on_associations
125
+ end
126
+
127
+ stale_paranoid_value
128
+ @destroyed = true
122
129
  freeze
123
130
  end
124
131
  end
@@ -128,40 +135,57 @@ module ActsAsParanoid
128
135
  if !deleted?
129
136
  with_transaction_returning_status do
130
137
  run_callbacks :destroy do
131
- # Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
132
- @_trigger_destroy_callback = if persisted?
133
- self.class.delete_all(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
134
- else
135
- true
136
- end
137
138
 
138
- self.paranoid_value = self.class.delete_now_value
139
+ if persisted?
140
+ # Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
141
+ self.class.delete_all(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
142
+
143
+ decrement_counters_on_associations
144
+ end
145
+
146
+ @_trigger_destroy_callback = true
147
+
148
+ stale_paranoid_value
139
149
  self
140
150
  end
141
151
  end
142
152
  else
143
- destroy_fully!
153
+ if paranoid_configuration[:double_tap_destroys_fully]
154
+ destroy_fully!
155
+ end
144
156
  end
145
157
  end
146
158
 
147
159
  alias_method :destroy, :destroy!
148
160
 
149
161
  def recover(options={})
162
+ return if !self.deleted?
150
163
  options = {
151
164
  :recursive => self.class.paranoid_configuration[:recover_dependent_associations],
152
- :recovery_window => self.class.paranoid_configuration[:dependent_recovery_window]
165
+ :recovery_window => self.class.paranoid_configuration[:dependent_recovery_window],
166
+ :raise_error => false
153
167
  }.merge(options)
154
168
 
155
169
  self.class.transaction do
156
170
  run_callbacks :recover do
157
171
  recover_dependent_associations(options[:recovery_window], options) if options[:recursive]
158
-
172
+ increment_counters_on_associations
159
173
  self.paranoid_value = self.class.paranoid_configuration[:recovery_value]
160
- self.save
174
+ if options[:raise_error]
175
+ self.save!
176
+ else
177
+ self.save
178
+ end
161
179
  end
162
180
  end
163
181
  end
164
182
 
183
+ def recover!(options={})
184
+ options[:raise_error] = true
185
+
186
+ recover(options)
187
+ end
188
+
165
189
  def recover_dependent_associations(window, options)
166
190
  self.class.dependent_associations.each do |reflection|
167
191
  next unless (klass = get_reflection_class(reflection)).paranoid?
@@ -169,7 +193,11 @@ module ActsAsParanoid
169
193
  scope = klass.only_deleted
170
194
 
171
195
  # Merge in the association's scope
172
- scope = scope.merge(association(reflection.name).association_scope)
196
+ scope = if ActiveRecord::VERSION::MAJOR >= 6
197
+ scope.merge(ActiveRecord::Associations::AssociationScope.scope(association(reflection.name)))
198
+ else
199
+ scope.merge(association(reflection.name).association_scope)
200
+ end
173
201
 
174
202
  # We can only recover by window if both parent and dependant have a
175
203
  # paranoid column type of :time.
@@ -190,7 +218,11 @@ module ActsAsParanoid
190
218
  scope = klass.only_deleted
191
219
 
192
220
  # Merge in the association's scope
193
- scope = scope.merge(association(reflection.name).association_scope)
221
+ scope = if ActiveRecord::VERSION::MAJOR >= 6
222
+ scope.merge(ActiveRecord::Associations::AssociationScope.scope(association(reflection.name)))
223
+ else
224
+ scope.merge(association(reflection.name).association_scope)
225
+ end
194
226
 
195
227
  scope.each do |object|
196
228
  object.destroy!
@@ -199,7 +231,7 @@ module ActsAsParanoid
199
231
  end
200
232
 
201
233
  def deleted?
202
- !if self.class.string_type_with_deleted_value?
234
+ @destroyed || !if self.class.string_type_with_deleted_value?
203
235
  paranoid_value != self.class.delete_now_value || paranoid_value.nil?
204
236
  elsif self.class.boolean_type_not_nullable?
205
237
  paranoid_value == false
@@ -210,6 +242,12 @@ module ActsAsParanoid
210
242
 
211
243
  alias_method :destroyed?, :deleted?
212
244
 
245
+ def deleted_fully?
246
+ @destroyed
247
+ end
248
+
249
+ alias_method :destroyed_fully?, :deleted_fully?
250
+
213
251
  private
214
252
 
215
253
  def get_reflection_class(reflection)
@@ -223,5 +261,36 @@ module ActsAsParanoid
223
261
  def paranoid_value=(value)
224
262
  self.send("#{self.class.paranoid_column}=", value)
225
263
  end
264
+
265
+ def update_counters_on_associations method_sym
266
+
267
+ return unless [:decrement_counter, :increment_counter].include? method_sym
268
+
269
+ each_counter_cached_association_reflection do |assoc_reflection|
270
+ if associated_object = send(assoc_reflection.name)
271
+ counter_cache_column = assoc_reflection.counter_cache_column
272
+ associated_object.class.send(method_sym, counter_cache_column, associated_object.id)
273
+ end
274
+ end
275
+ end
276
+
277
+ def each_counter_cached_association_reflection
278
+ _reflections.each do |name, reflection|
279
+ yield reflection if reflection.belongs_to? && reflection.counter_cache_column
280
+ end
281
+ end
282
+
283
+ def increment_counters_on_associations
284
+ update_counters_on_associations :increment_counter
285
+ end
286
+
287
+ def decrement_counters_on_associations
288
+ update_counters_on_associations :decrement_counter
289
+ end
290
+
291
+ def stale_paranoid_value
292
+ self.paranoid_value = self.class.delete_now_value
293
+ clear_attribute_changes([self.class.paranoid_column])
294
+ end
226
295
  end
227
296
  end
@@ -8,10 +8,9 @@ module ActsAsParanoid
8
8
 
9
9
  class UniquenessWithoutDeletedValidator
10
10
  def self.[](version)
11
- version = version.to_s
12
- name = "V#{version.tr('.', '_')}"
11
+ name = "V#{version.to_s.tr('.', '_')}"
13
12
  unless constants.include? name.to_sym
14
- raise "Unknown validator version #{version.inspect}; expected one of #{constants.sort.join(', ')}"
13
+ raise "Unknown validator version #{name.inspect}; expected one of #{constants.sort.join(', ')}"
15
14
  end
16
15
  const_get name
17
16
  end
@@ -38,8 +37,11 @@ module ActsAsParanoid
38
37
  protected
39
38
 
40
39
  def build_relation(klass, attribute, value)
41
- return super(klass, klass.arel_table, attribute, value) if ActiveRecord::VERSION::MINOR == 0
42
- super
40
+ if ActiveRecord::VERSION::MINOR == 0 && ActiveRecord::VERSION::MAJOR == 5
41
+ return super(klass, klass.arel_table, attribute, value)
42
+ else
43
+ super
44
+ end
43
45
  end
44
46
  end
45
47
 
@@ -75,6 +77,9 @@ module ActsAsParanoid
75
77
  end
76
78
  end
77
79
  end
80
+
81
+ class V6 < V5
82
+ end
78
83
  end
79
84
 
80
85
  module ClassMethods
@@ -1,3 +1,3 @@
1
1
  module ActsAsParanoid
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.3"
3
3
  end
@@ -41,6 +41,48 @@ class AssociationsTest < ParanoidBaseTest
41
41
  includes_values = ParanoidTime.includes(:not_paranoid).includes_values
42
42
 
43
43
  assert_equal includes_values, paranoid_has_many_dependant.association(:paranoid_time_with_scope).scope.includes_values
44
+
45
+ paranoid_time = ParanoidTime.create(name: 'not-hello')
46
+ paranoid_has_many_dependant.paranoid_time = paranoid_time
47
+ paranoid_has_many_dependant.save!
48
+
49
+ assert_nil paranoid_has_many_dependant.paranoid_time_with_scope
50
+
51
+ paranoid_time.update(name: 'hello')
52
+
53
+ paranoid_has_many_dependant.reload
54
+
55
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_scope
56
+
57
+ paranoid_time.destroy
58
+
59
+ paranoid_has_many_dependant.reload
60
+
61
+ assert_nil paranoid_has_many_dependant.paranoid_time_with_scope
62
+ end
63
+
64
+ def test_belongs_to_with_scope_and_deleted_option
65
+ paranoid_has_many_dependant = ParanoidHasManyDependant.new
66
+ includes_values = ParanoidTime.includes(:not_paranoid).includes_values
67
+
68
+ assert_equal includes_values, paranoid_has_many_dependant
69
+ .association(:paranoid_time_with_scope_with_deleted).scope.includes_values
70
+
71
+ paranoid_time = ParanoidTime.create(name: 'not-hello')
72
+ paranoid_has_many_dependant.paranoid_time = paranoid_time
73
+ paranoid_has_many_dependant.save!
74
+
75
+ assert_nil paranoid_has_many_dependant.paranoid_time_with_scope_with_deleted
76
+
77
+ paranoid_time.update(name: 'hello')
78
+ paranoid_has_many_dependant.reload
79
+
80
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_scope_with_deleted
81
+
82
+ paranoid_time.destroy
83
+ paranoid_has_many_dependant.reload
84
+
85
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_scope_with_deleted
44
86
  end
45
87
 
46
88
  def test_belongs_to_with_deleted
@@ -51,9 +93,10 @@ class AssociationsTest < ParanoidBaseTest
51
93
  assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted
52
94
 
53
95
  paranoid_time.destroy
96
+ paranoid_has_many_dependant.reload
54
97
 
55
- assert_nil paranoid_has_many_dependant.reload.paranoid_time
56
- assert_equal paranoid_time, paranoid_has_many_dependant.reload.paranoid_time_with_deleted
98
+ assert_nil paranoid_has_many_dependant.paranoid_time
99
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted
57
100
  end
58
101
 
59
102
  def test_belongs_to_polymorphic_with_deleted
@@ -228,7 +271,7 @@ class AssociationsTest < ParanoidBaseTest
228
271
  end
229
272
 
230
273
  def test_mass_assignment_of_paranoid_column_enabled
231
- if ActiveRecord::VERSION::MAJOR > 4 && ActiveRecord::VERSION::MINOR > 1
274
+ if Gem.loaded_specs['activerecord'].version >= Gem::Version.new('5.2.0')
232
275
  skip 'Creation as deleted is not supported with Rails >= 5.2'
233
276
  end
234
277
  now = Time.now
data/test/test_core.rb CHANGED
@@ -69,6 +69,13 @@ class ParanoidTest < ParanoidBaseTest
69
69
  assert_not_nil pt.paranoid_value
70
70
  end
71
71
 
72
+ def test_non_persisted_delete
73
+ pt = ParanoidTime.new
74
+ assert_nil pt.paranoid_value
75
+ pt.delete
76
+ assert_not_nil pt.paranoid_value
77
+ end
78
+
72
79
  def test_non_persisted_destroy!
73
80
  pt = ParanoidTime.new
74
81
  assert_nil pt.paranoid_value
@@ -94,6 +101,15 @@ class ParanoidTest < ParanoidBaseTest
94
101
  assert_equal 1, ParanoidString.count
95
102
  end
96
103
 
104
+ def test_recovery!
105
+ ParanoidBoolean.first.destroy
106
+ ParanoidBoolean.create(:name => 'paranoid')
107
+
108
+ assert_raise do
109
+ ParanoidBoolean.only_deleted.first.recover!
110
+ end
111
+ end
112
+
97
113
  def setup_recursive_tests
98
114
  @paranoid_time_object = ParanoidTime.first
99
115
 
@@ -265,6 +281,24 @@ class ParanoidTest < ParanoidBaseTest
265
281
  assert_equal 0, HasOneNotParanoid.count
266
282
  end
267
283
 
284
+ def test_dirty
285
+ pt = ParanoidTime.create
286
+ pt.destroy
287
+ assert_not pt.changed?
288
+ end
289
+
290
+ def test_delete_dirty
291
+ pt = ParanoidTime.create
292
+ pt.delete
293
+ assert_not pt.changed?
294
+ end
295
+
296
+ def test_destroy_fully_dirty
297
+ pt = ParanoidTime.create
298
+ pt.destroy_fully!
299
+ assert_not pt.changed?
300
+ end
301
+
268
302
  def test_deleted?
269
303
  ParanoidTime.first.destroy
270
304
  assert ParanoidTime.with_deleted.first.deleted?
@@ -273,6 +307,43 @@ class ParanoidTest < ParanoidBaseTest
273
307
  assert ParanoidString.with_deleted.first.deleted?
274
308
  end
275
309
 
310
+ def test_delete_deleted?
311
+ ParanoidTime.first.delete
312
+ assert ParanoidTime.with_deleted.first.deleted?
313
+
314
+ ParanoidString.first.delete
315
+ assert ParanoidString.with_deleted.first.deleted?
316
+ end
317
+
318
+ def test_destroy_fully_deleted?
319
+ object = ParanoidTime.first
320
+ object.destroy_fully!
321
+ assert object.deleted?
322
+
323
+ object = ParanoidString.first
324
+ object.destroy_fully!
325
+ assert object.deleted?
326
+ end
327
+
328
+ def test_deleted_fully?
329
+ ParanoidTime.first.destroy
330
+ assert_not ParanoidTime.with_deleted.first.deleted_fully?
331
+
332
+ ParanoidString.first.destroy
333
+ assert ParanoidString.with_deleted.first.deleted?
334
+ end
335
+
336
+ def test_delete_deleted_fully?
337
+ ParanoidTime.first.delete
338
+ assert_not ParanoidTime.with_deleted.first.deleted_fully?
339
+ end
340
+
341
+ def test_destroy_fully_deleted_fully?
342
+ object = ParanoidTime.first
343
+ object.destroy_fully!
344
+ assert object.deleted_fully?
345
+ end
346
+
276
347
  def test_paranoid_destroy_callbacks
277
348
  @paranoid_with_callback = ParanoidWithCallback.first
278
349
  ParanoidWithCallback.transaction do
@@ -312,6 +383,14 @@ class ParanoidTest < ParanoidBaseTest
312
383
  assert @paranoid_with_callback.called_after_recover
313
384
  end
314
385
 
386
+ def test_recovery_callbacks_without_destroy
387
+ @paranoid_with_callback = ParanoidWithCallback.first
388
+ @paranoid_with_callback.recover
389
+
390
+ assert_nil @paranoid_with_callback.called_before_recover
391
+ assert_nil @paranoid_with_callback.called_after_recover
392
+ end
393
+
315
394
  def test_delete_by_multiple_id_is_paranoid
316
395
  model_a = ParanoidBelongsDependant.create
317
396
  model_b = ParanoidBelongsDependant.create
@@ -437,4 +516,94 @@ class ParanoidTest < ParanoidBaseTest
437
516
  2.times { ps.destroy }
438
517
  assert_equal 0, ParanoidBooleanNotNullable.with_deleted.where(:id => ps).count
439
518
  end
519
+
520
+ def test_no_double_tap_destroys_fully
521
+ ps = ParanoidNoDoubleTapDestroysFully.create!()
522
+ 2.times { ps.destroy }
523
+ assert_equal 1, ParanoidNoDoubleTapDestroysFully.with_deleted.where(:id => ps).count
524
+ end
525
+
526
+ def test_decrement_counters
527
+ paranoid_boolean = ParanoidBoolean.create!()
528
+ paranoid_with_counter_cache = ParanoidWithCounterCache.create!(paranoid_boolean: paranoid_boolean)
529
+
530
+ assert_equal 1, paranoid_boolean.paranoid_with_counter_caches_count
531
+
532
+ paranoid_with_counter_cache.destroy
533
+
534
+ assert_equal 0, paranoid_boolean.reload.paranoid_with_counter_caches_count
535
+ end
536
+
537
+ def test_decrement_custom_counters
538
+ paranoid_boolean = ParanoidBoolean.create!()
539
+ paranoid_with_custom_counter_cache = ParanoidWithCustomCounterCache.create!(paranoid_boolean: paranoid_boolean)
540
+
541
+ assert_equal 1, paranoid_boolean.custom_counter_cache
542
+
543
+ paranoid_with_custom_counter_cache.destroy
544
+
545
+ assert_equal 0, paranoid_boolean.reload.custom_counter_cache
546
+ end
547
+
548
+ def test_destroy_with_optional_belongs_to_and_counter_cache
549
+ ps = ParanoidWithCounterCacheOnOptionalBelognsTo.create!()
550
+ ps.destroy
551
+ assert_equal 1, ParanoidWithCounterCacheOnOptionalBelognsTo.only_deleted.where(:id => ps).count
552
+ end
553
+
554
+ def test_hard_destroy_decrement_counters
555
+ paranoid_boolean = ParanoidBoolean.create!()
556
+ paranoid_with_counter_cache = ParanoidWithCounterCache.create!(paranoid_boolean: paranoid_boolean)
557
+
558
+ assert_equal 1, paranoid_boolean.paranoid_with_counter_caches_count
559
+
560
+ paranoid_with_counter_cache.destroy_fully!
561
+
562
+ assert_equal 0, paranoid_boolean.reload.paranoid_with_counter_caches_count
563
+ end
564
+
565
+ def test_hard_destroy_decrement_custom_counters
566
+ paranoid_boolean = ParanoidBoolean.create!()
567
+ paranoid_with_custom_counter_cache = ParanoidWithCustomCounterCache.create!(paranoid_boolean: paranoid_boolean)
568
+
569
+ assert_equal 1, paranoid_boolean.custom_counter_cache
570
+
571
+ paranoid_with_custom_counter_cache.destroy_fully!
572
+
573
+ assert_equal 0, paranoid_boolean.reload.custom_counter_cache
574
+ end
575
+
576
+ def test_increment_counters
577
+ paranoid_boolean = ParanoidBoolean.create!()
578
+ paranoid_with_counter_cache = ParanoidWithCounterCache.create!(paranoid_boolean: paranoid_boolean)
579
+
580
+ assert_equal 1, paranoid_boolean.paranoid_with_counter_caches_count
581
+
582
+ paranoid_with_counter_cache.destroy
583
+
584
+ assert_equal 0, paranoid_boolean.reload.paranoid_with_counter_caches_count
585
+
586
+ paranoid_with_counter_cache.recover
587
+
588
+ assert_equal 1, paranoid_boolean.reload.paranoid_with_counter_caches_count
589
+ end
590
+
591
+ def test_increment_custom_counters
592
+ paranoid_boolean = ParanoidBoolean.create!()
593
+ paranoid_with_custom_counter_cache = ParanoidWithCustomCounterCache.create!(paranoid_boolean: paranoid_boolean)
594
+
595
+ assert_equal 1, paranoid_boolean.custom_counter_cache
596
+
597
+ paranoid_with_custom_counter_cache.destroy
598
+
599
+ assert_equal 0, paranoid_boolean.reload.custom_counter_cache
600
+
601
+ paranoid_with_custom_counter_cache.recover
602
+
603
+ assert_equal 1, paranoid_boolean.reload.custom_counter_cache
604
+ end
605
+
606
+ def test_explicitly_setting_table_name_after_acts_as_paranoid_macro
607
+ assert_equal "explicit_table.deleted_at", ParanoidWithExplicitTableNameAfterMacro.paranoid_column_reference
608
+ end
440
609
  end
@@ -4,7 +4,6 @@ class MultipleDefaultScopesTest < ParanoidBaseTest
4
4
  def setup
5
5
  setup_db
6
6
 
7
- # Naturally, the default scope for humans is male. Sexism++
8
7
  ParanoidPolygon.create! :sides => 3
9
8
  ParanoidPolygon.create! :sides => 3
10
9
  ParanoidPolygon.create! :sides => 3
data/test/test_helper.rb CHANGED
@@ -31,7 +31,8 @@ def setup_db
31
31
  t.string :name
32
32
  t.boolean :is_deleted
33
33
  t.integer :paranoid_time_id
34
-
34
+ t.integer :paranoid_with_counter_caches_count
35
+ t.integer :custom_counter_cache
35
36
  timestamps t
36
37
  end
37
38
 
@@ -148,7 +149,7 @@ def setup_db
148
149
  timestamps t
149
150
  end
150
151
 
151
- create_table :paranoid_forests do |t|
152
+ create_table :paranoid_forests do |t|
152
153
  t.string :name
153
154
  t.boolean :rainforest
154
155
  t.datetime :deleted_at
@@ -208,6 +209,18 @@ def setup_db
208
209
 
209
210
  timestamps t
210
211
  end
212
+
213
+ create_table :paranoid_no_double_tap_destroys_fullies do |t|
214
+ t.datetime :deleted_at
215
+ end
216
+
217
+ create_table :paranoid_with_counter_caches do |t|
218
+ t.string :name
219
+ t.datetime :deleted_at
220
+ t.integer :paranoid_boolean_id
221
+
222
+ timestamps t
223
+ end
211
224
  end
212
225
  end
213
226
 
@@ -247,6 +260,8 @@ class ParanoidBoolean < ActiveRecord::Base
247
260
 
248
261
  belongs_to :paranoid_time
249
262
  has_one :paranoid_has_one_dependant, :dependent => :destroy
263
+ has_many :paranoid_with_counter_cache, :dependent => :destroy
264
+ has_many :paranoid_with_custom_counter_cache, :dependent => :destroy
250
265
  end
251
266
 
252
267
  class ParanoidString < ActiveRecord::Base
@@ -256,6 +271,10 @@ end
256
271
  class NotParanoid < ActiveRecord::Base
257
272
  end
258
273
 
274
+ class ParanoidNoDoubleTapDestroysFully < ActiveRecord::Base
275
+ acts_as_paranoid :double_tap_destroys_fully => false
276
+ end
277
+
259
278
  class HasOneNotParanoid < ActiveRecord::Base
260
279
  belongs_to :paranoid_time, :with_deleted => true
261
280
  end
@@ -265,11 +284,35 @@ class DoubleHasOneNotParanoid < HasOneNotParanoid
265
284
  belongs_to :paranoid_time, :with_deleted => true
266
285
  end
267
286
 
287
+ class ParanoidWithCounterCache < ActiveRecord::Base
288
+ acts_as_paranoid
289
+ belongs_to :paranoid_boolean, :counter_cache => true
290
+ end
291
+
292
+ class ParanoidWithCustomCounterCache < ActiveRecord::Base
293
+ self.table_name = "paranoid_with_counter_caches"
294
+
295
+ acts_as_paranoid
296
+ belongs_to :paranoid_boolean, counter_cache: :custom_counter_cache
297
+ end
298
+
299
+ class ParanoidWithCounterCacheOnOptionalBelognsTo < ActiveRecord::Base
300
+ self.table_name = "paranoid_with_counter_caches"
301
+
302
+ acts_as_paranoid
303
+ if ActiveRecord::VERSION::MAJOR < 5
304
+ belongs_to :paranoid_boolean, counter_cache: true, required: false
305
+ else
306
+ belongs_to :paranoid_boolean, counter_cache: true, optional: true
307
+ end
308
+ end
309
+
268
310
  class ParanoidHasManyDependant < ActiveRecord::Base
269
311
  acts_as_paranoid
270
312
  belongs_to :paranoid_time
271
- belongs_to :paranoid_time_with_scope, -> { includes(:not_paranoid) }, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id
313
+ belongs_to :paranoid_time_with_scope, -> { where(name: 'hello').includes(:not_paranoid) }, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id
272
314
  belongs_to :paranoid_time_with_deleted, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :with_deleted => true
315
+ belongs_to :paranoid_time_with_scope_with_deleted, -> { where(name: 'hello').includes(:not_paranoid) }, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :with_deleted => true
273
316
  belongs_to :paranoid_time_polymorphic_with_deleted, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :polymorphic => true, :with_deleted => true
274
317
 
275
318
  belongs_to :paranoid_belongs_dependant, :dependent => :destroy
@@ -470,3 +513,7 @@ class ParanoidBooleanNotNullable < ActiveRecord::Base
470
513
  acts_as_paranoid column: 'deleted', column_type: 'boolean', allow_nulls: false
471
514
  end
472
515
 
516
+ class ParanoidWithExplicitTableNameAfterMacro < ActiveRecord::Base
517
+ acts_as_paranoid
518
+ self.table_name = "explicit_table"
519
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_paranoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zachary Scott
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-06-07 00:00:00.000000000 Z
13
+ date: 2020-01-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '4.2'
22
22
  - - "<"
23
23
  - !ruby/object:Gem::Version
24
- version: '6.0'
24
+ version: '7.0'
25
25
  type: :runtime
26
26
  prerelease: false
27
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -31,7 +31,7 @@ dependencies:
31
31
  version: '4.2'
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
- version: '6.0'
34
+ version: '7.0'
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: activesupport
37
37
  requirement: !ruby/object:Gem::Requirement
@@ -41,7 +41,7 @@ dependencies:
41
41
  version: '4.2'
42
42
  - - "<"
43
43
  - !ruby/object:Gem::Version
44
- version: '6.0'
44
+ version: '7.0'
45
45
  type: :runtime
46
46
  prerelease: false
47
47
  version_requirements: !ruby/object:Gem::Requirement
@@ -51,21 +51,27 @@ dependencies:
51
51
  version: '4.2'
52
52
  - - "<"
53
53
  - !ruby/object:Gem::Version
54
- version: '6.0'
54
+ version: '7.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '1.5'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '3.0'
62
65
  type: :development
63
66
  prerelease: false
64
67
  version_requirements: !ruby/object:Gem::Requirement
65
68
  requirements:
66
- - - "~>"
69
+ - - ">="
67
70
  - !ruby/object:Gem::Version
68
71
  version: '1.5'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
69
75
  - !ruby/object:Gem::Dependency
70
76
  name: rake
71
77
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +120,20 @@ dependencies:
114
120
  - - "<="
115
121
  - !ruby/object:Gem::Version
116
122
  version: '6.0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: pry
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
117
137
  description: Check the home page for more in-depth information.
118
138
  email:
119
139
  - e@zzak.io
@@ -121,6 +141,7 @@ executables: []
121
141
  extensions: []
122
142
  extra_rdoc_files: []
123
143
  files:
144
+ - CHANGELOG.md
124
145
  - LICENSE
125
146
  - README.md
126
147
  - lib/acts_as_paranoid.rb
@@ -157,8 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
178
  - !ruby/object:Gem::Version
158
179
  version: '0'
159
180
  requirements: []
160
- rubyforge_project:
161
- rubygems_version: 2.7.6
181
+ rubygems_version: 3.1.2
162
182
  signing_key:
163
183
  specification_version: 4
164
184
  summary: Active Record plugin which allows you to hide and restore records without