paranoia 2.2.1 → 2.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a081d1169b0d0b18a5815aafe4bafdeab81e62fd
4
- data.tar.gz: 4b38ec1db06051b0c5ada74f9405f935ba39624b
3
+ metadata.gz: 3d592d028e0a4ad7c1909649973803af1ef85102
4
+ data.tar.gz: a4fb1bb5daec2bd6b61c4beaa9b2ce2d415a2661
5
5
  SHA512:
6
- metadata.gz: e80496f3e472a7ecef7abee20f49d394fb884bb8e81313c92e6f1915b2f805d9f14ef1ef930f47ccdf97300df51f25458881d465df4355f85d2a6066c0b92671
7
- data.tar.gz: 205530a0638b6d1d1f455cfae490e2c9e77f080ef4fdfd63d6fdafba849ebc7615ff34f091479b011a02cd9bd7919f27061a0f608be33be22b9801041d8277b7
6
+ metadata.gz: 7a7d46b3e08208ae25969ddfe75d30ed0651ac649f834dd1ccb872197ce22ef93427281ce5c396c96d36a95f52ba53e54b81910970cedcd1ecd65d040502e819
7
+ data.tar.gz: 86866ca4cfd172783fbf7c7363ece9c1aea905129e210b228a315a34f2d2d6f343d5bbdd95cf96dfd8b3aa6d6d52e5391d4c026a84434a2f53fd6d23d37db6ea
@@ -2,25 +2,25 @@ sudo: false
2
2
  language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.1.10
6
5
  - 2.2.6
7
6
  - 2.3.3
7
+ - 2.4.1
8
8
  - jruby-9.1.6.0
9
9
 
10
10
  env:
11
11
  matrix:
12
- - RAILS='~> 4.1.16'
13
12
  - RAILS='~> 4.2.7.1'
14
13
  - RAILS='~> 5.0.0.1'
14
+ - RAILS='~> 5.1.0.rc1'
15
15
 
16
16
  matrix:
17
17
  exclude:
18
- - env: RAILS='~> 5.0.0.1'
19
- rvm: 2.1.10
18
+ - env: RAILS='~> 5.1.0.rc1'
19
+ rvm: 2.2.6
20
20
  allow_failures:
21
- - env: RAILS='~> 4.1.16'
22
- rvm: jruby-9.1.6.0
23
21
  - env: RAILS='~> 4.2.7.1'
24
22
  rvm: jruby-9.1.6.0
25
23
  - env: RAILS='~> 5.0.0.1'
26
24
  rvm: jruby-9.1.6.0
25
+ - env: RAILS='~> 5.1.0.rc1'
26
+ rvm: jruby-9.1.6.0
@@ -1,6 +1,31 @@
1
1
  # paranoia Changelog
2
2
 
3
- ## 2.2.2 (Unreleased)
3
+ ## 2.3.0 (2017-04-14)
4
+
5
+ * [#393](https://github.com/rubysherpas/paranoia/pull/393) Drop support for Rails 4.1 and begin supporting Rails 5.1.
6
+
7
+ [Miklós Fazekas (@mfazekas)](https://github.com/mfazekas)
8
+
9
+ * [#391](https://github.com/rubysherpas/paranoia/pull/391) Use Contributor Covenant Version 1.4
10
+
11
+ [Ben A. Morgan (@BenMorganIO)](https://github.com/BenMorganIO)
12
+
13
+ * [#390](https://github.com/rubysherpas/paranoia/pull/390) Fix counter cache with double destroy, really_destroy, and restore
14
+
15
+ [Chris Oliver (@excid3)](https://github.com/excid3)
16
+
17
+ * [#389](https://github.com/rubysherpas/paranoia/pull/389) Added association not soft destroyed validator
18
+
19
+ _Fixes [#380](https://github.com/rubysherpas/paranoia/issues/380)_
20
+
21
+ [Edward Poot (@edwardmp)](https://github.com/edwardmp)
22
+
23
+ * [#383](https://github.com/rubysherpas/paranoia/pull/383) Add recovery window feature
24
+
25
+ _Fixes [#359](https://github.com/rubysherpas/paranoia/issues/359)_
26
+
27
+ [Andrzej Piątyszek (@konto-andrzeja)](https://github.com/konto-andrzeja)
28
+
4
29
 
5
30
  ## 2.2.1 (2017-02-15)
6
31
 
@@ -46,7 +71,7 @@
46
71
 
47
72
  * `#destroyed?` is no longer overridden. Use `#paranoia_destroyed?` for the existing behaviour. [Washington Luiz](https://github.com/huoxito)
48
73
  * `#persisted?` is no longer overridden.
49
- * ActiveRecord 4.0 no longer has `#destroy!` as an alias for `#really_destroy`.
74
+ * ActiveRecord 4.0 no longer has `#destroy!` as an alias for `#really_destroy!`.
50
75
  * `#destroy` will now raise an exception if called on a readonly record.
51
76
  * `#destroy` on a hard deleted record is now a successful noop.
52
77
  * `#destroy` on a new record will set deleted_at (previously this raised an error)
@@ -54,7 +79,7 @@
54
79
 
55
80
  ### Bug Fixes
56
81
 
57
- * Calling `#destroy` twice will not hard-delete records. Use `#really_destroy` if this is desired.
82
+ * Calling `#destroy` twice will not hard-delete records. Use `#really_destroy!` if this is desired.
58
83
  * Fix errors on non-paranoid has_one dependent associations
59
84
 
60
85
  ## 2.0.5 (2015-01-22)
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at ben@benmorgan.io. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/README.md CHANGED
@@ -181,6 +181,21 @@ Client.restore(id, :recursive => true)
181
181
  client.restore(:recursive => true)
182
182
  ```
183
183
 
184
+ If you want to restore a record and only those dependently destroyed associated records that were deleted within 2 minutes of the object upon which they depend:
185
+
186
+ ``` ruby
187
+ Client.restore(id, :recursive => true. :recovery_window => 2.minutes)
188
+ # or
189
+ client.restore(:recursive => true, :recovery_window => 2.minutes)
190
+ ```
191
+
192
+ Note that by default paranoia will not prevent that a soft destroyed object can't be associated with another object of a different model.
193
+ A Rails validator is provided should you require this functionality:
194
+ ``` ruby
195
+ validates :some_assocation, association_not_soft_destroyed: true
196
+ ```
197
+ This validator makes sure that `some_assocation` is not soft destroyed. If the object is soft destroyed the main object is rendered invalid and an validation error is added.
198
+
184
199
  For more information, please look at the tests.
185
200
 
186
201
  #### About indexes:
@@ -76,6 +76,7 @@ module Paranoia
76
76
  def destroy
77
77
  transaction do
78
78
  run_callbacks(:destroy) do
79
+ @_disable_counter_cache = deleted?
79
80
  result = delete
80
81
  next result unless result && ActiveRecord::VERSION::STRING >= '4.2'
81
82
  each_counter_cached_associations do |association|
@@ -84,6 +85,7 @@ module Paranoia
84
85
  next unless send(association.reflection.name)
85
86
  association.decrement_counters
86
87
  end
88
+ @_disable_counter_cache = false
87
89
  result
88
90
  end
89
91
  end
@@ -105,14 +107,22 @@ module Paranoia
105
107
  def restore!(opts = {})
106
108
  self.class.transaction do
107
109
  run_callbacks(:restore) do
110
+ recovery_window_range = get_recovery_window_range(opts)
108
111
  # Fixes a bug where the build would error because attributes were frozen.
109
112
  # This only happened on Rails versions earlier than 4.1.
110
113
  noop_if_frozen = ActiveRecord.version < Gem::Version.new("4.1")
111
- if (noop_if_frozen && !@attributes.frozen?) || !noop_if_frozen
114
+ if within_recovery_window?(recovery_window_range) && ((noop_if_frozen && !@attributes.frozen?) || !noop_if_frozen)
115
+ @_disable_counter_cache = !deleted?
112
116
  write_attribute paranoia_column, paranoia_sentinel_value
113
117
  update_columns(paranoia_restore_attributes)
118
+ each_counter_cached_associations do |association|
119
+ if send(association.reflection.name)
120
+ association.increment_counters
121
+ end
122
+ end
123
+ @_disable_counter_cache = false
114
124
  end
115
- restore_associated_records if opts[:recursive]
125
+ restore_associated_records(recovery_window_range) if opts[:recursive]
116
126
  end
117
127
  end
118
128
 
@@ -120,6 +130,17 @@ module Paranoia
120
130
  end
121
131
  alias :restore :restore!
122
132
 
133
+ def get_recovery_window_range(opts)
134
+ return opts[:recovery_window_range] if opts[:recovery_window_range]
135
+ return unless opts[:recovery_window]
136
+ (deleted_at - opts[:recovery_window]..deleted_at + opts[:recovery_window])
137
+ end
138
+
139
+ def within_recovery_window?(recovery_window_range)
140
+ return true unless recovery_window_range
141
+ recovery_window_range.cover?(deleted_at)
142
+ end
143
+
123
144
  def paranoia_destroyed?
124
145
  send(paranoia_column) != paranoia_sentinel_value
125
146
  end
@@ -128,6 +149,7 @@ module Paranoia
128
149
  def really_destroy!
129
150
  transaction do
130
151
  run_callbacks(:real_destroy) do
152
+ @_disable_counter_cache = deleted?
131
153
  dependent_reflections = self.class.reflections.select do |name, reflection|
132
154
  reflection.options[:dependent] == :destroy
133
155
  end
@@ -151,6 +173,10 @@ module Paranoia
151
173
 
152
174
  private
153
175
 
176
+ def each_counter_cached_associations
177
+ !@_disable_counter_cache && defined?(super) ? super : []
178
+ end
179
+
154
180
  def paranoia_restore_attributes
155
181
  {
156
182
  paranoia_column => paranoia_sentinel_value
@@ -169,7 +195,7 @@ module Paranoia
169
195
 
170
196
  # restore associated records that have been soft deleted when
171
197
  # we called #destroy
172
- def restore_associated_records
198
+ def restore_associated_records(recovery_window_range = nil)
173
199
  destroyed_associations = self.class.reflect_on_all_associations.select do |association|
174
200
  association.options[:dependent] == :destroy
175
201
  end
@@ -180,9 +206,11 @@ module Paranoia
180
206
  unless association_data.nil?
181
207
  if association_data.paranoid?
182
208
  if association.collection?
183
- association_data.only_deleted.each { |record| record.restore(:recursive => true) }
209
+ association_data.only_deleted.each do |record|
210
+ record.restore(:recursive => true, :recovery_window_range => recovery_window_range)
211
+ end
184
212
  else
185
- association_data.restore(:recursive => true)
213
+ association_data.restore(:recursive => true, :recovery_window_range => recovery_window_range)
186
214
  end
187
215
  end
188
216
  end
@@ -200,7 +228,8 @@ module Paranoia
200
228
 
201
229
  association_class = association_class_name.constantize
202
230
  if association_class.paranoid?
203
- association_class.only_deleted.where(association_find_conditions).first.try!(:restore, recursive: true)
231
+ association_class.only_deleted.where(association_find_conditions).first
232
+ .try!(:restore, recursive: true, :recovery_window_range => recovery_window_range)
204
233
  end
205
234
  end
206
235
  end
@@ -269,8 +298,8 @@ require 'paranoia/rspec' if defined? RSpec
269
298
  module ActiveRecord
270
299
  module Validations
271
300
  module UniquenessParanoiaValidator
272
- def build_relation(klass, table, attribute, value)
273
- relation = super(klass, table, attribute, value)
301
+ def build_relation(klass, *args)
302
+ relation = super
274
303
  return relation unless klass.respond_to?(:paranoia_column)
275
304
  arel_paranoia_scope = klass.arel_table[klass.paranoia_column].eq(klass.paranoia_sentinel_value)
276
305
  if ActiveRecord::VERSION::STRING >= "5.0"
@@ -284,5 +313,14 @@ module ActiveRecord
284
313
  class UniquenessValidator < ActiveModel::EachValidator
285
314
  prepend UniquenessParanoiaValidator
286
315
  end
316
+
317
+ class AssociationNotSoftDestroyedValidator < ActiveModel::EachValidator
318
+ def validate_each(record, attribute, value)
319
+ # if association is soft destroyed, add an error
320
+ if value.present? && value.deleted?
321
+ record.errors[attribute] << 'has been soft-deleted'
322
+ end
323
+ end
324
+ end
287
325
  end
288
326
  end
@@ -1,3 +1,3 @@
1
1
  module Paranoia
2
- VERSION = '2.2.1'.freeze
2
+ VERSION = '2.3.0'.freeze
3
3
  end
@@ -21,6 +21,7 @@ def setup!
21
21
  'paranoid_model_with_foreign_key_belongs' => 'parent_model_id INTEGER, deleted_at DATETIME, has_one_foreign_key_id INTEGER',
22
22
  'paranoid_model_with_timestamps' => 'parent_model_id INTEGER, created_at DATETIME, updated_at DATETIME, deleted_at DATETIME',
23
23
  'not_paranoid_model_with_belongs' => 'parent_model_id INTEGER, paranoid_model_with_has_one_id INTEGER',
24
+ 'not_paranoid_model_with_belongs_and_assocation_not_soft_destroyed_validator' => 'parent_model_id INTEGER, paranoid_model_with_has_one_id INTEGER',
24
25
  'paranoid_model_with_has_one_and_builds' => 'parent_model_id INTEGER, color VARCHAR(32), deleted_at DATETIME, has_one_foreign_key_id INTEGER',
25
26
  'featureful_models' => 'deleted_at DATETIME, name VARCHAR(32)',
26
27
  'plain_models' => 'deleted_at DATETIME',
@@ -117,9 +118,9 @@ class ParanoiaTest < test_framework
117
118
  model.remove_called_variables # clear called callback flags
118
119
  model.destroy
119
120
 
120
- assert_equal nil, model.instance_variable_get(:@update_callback_called)
121
- assert_equal nil, model.instance_variable_get(:@save_callback_called)
122
- assert_equal nil, model.instance_variable_get(:@validate_called)
121
+ assert_nil model.instance_variable_get(:@update_callback_called)
122
+ assert_nil model.instance_variable_get(:@save_callback_called)
123
+ assert_nil model.instance_variable_get(:@validate_called)
123
124
 
124
125
  assert model.instance_variable_get(:@destroy_callback_called)
125
126
  assert model.instance_variable_get(:@after_destroy_callback_called)
@@ -133,12 +134,12 @@ class ParanoiaTest < test_framework
133
134
  model.remove_called_variables # clear called callback flags
134
135
  model.delete
135
136
 
136
- assert_equal nil, model.instance_variable_get(:@update_callback_called)
137
- assert_equal nil, model.instance_variable_get(:@save_callback_called)
138
- assert_equal nil, model.instance_variable_get(:@validate_called)
139
- assert_equal nil, model.instance_variable_get(:@destroy_callback_called)
140
- assert_equal nil, model.instance_variable_get(:@after_destroy_callback_called)
141
- assert_equal nil, model.instance_variable_get(:@after_commit_callback_called)
137
+ assert_nil model.instance_variable_get(:@update_callback_called)
138
+ assert_nil model.instance_variable_get(:@save_callback_called)
139
+ assert_nil model.instance_variable_get(:@validate_called)
140
+ assert_nil model.instance_variable_get(:@destroy_callback_called)
141
+ assert_nil model.instance_variable_get(:@after_destroy_callback_called)
142
+ assert_nil model.instance_variable_get(:@after_commit_callback_called)
142
143
  end
143
144
 
144
145
  def test_delete_in_transaction_behavior_for_plain_models_callbacks
@@ -149,11 +150,11 @@ class ParanoiaTest < test_framework
149
150
  model.delete
150
151
  end
151
152
 
152
- assert_equal nil, model.instance_variable_get(:@update_callback_called)
153
- assert_equal nil, model.instance_variable_get(:@save_callback_called)
154
- assert_equal nil, model.instance_variable_get(:@validate_called)
155
- assert_equal nil, model.instance_variable_get(:@destroy_callback_called)
156
- assert_equal nil, model.instance_variable_get(:@after_destroy_callback_called)
153
+ assert_nil model.instance_variable_get(:@update_callback_called)
154
+ assert_nil model.instance_variable_get(:@save_callback_called)
155
+ assert_nil model.instance_variable_get(:@validate_called)
156
+ assert_nil model.instance_variable_get(:@destroy_callback_called)
157
+ assert_nil model.instance_variable_get(:@after_destroy_callback_called)
157
158
  assert model.instance_variable_get(:@after_commit_callback_called)
158
159
  end
159
160
 
@@ -226,7 +227,7 @@ class ParanoiaTest < test_framework
226
227
  end
227
228
 
228
229
  def test_default_sentinel_value
229
- assert_equal nil, ParanoidModel.paranoia_sentinel_value
230
+ assert_nil ParanoidModel.paranoia_sentinel_value
230
231
  end
231
232
 
232
233
  def test_without_default_scope_option
@@ -375,7 +376,7 @@ class ParanoiaTest < test_framework
375
376
  model = CallbackModel.new
376
377
  model.save
377
378
  model.delete
378
- assert_equal nil, model.instance_variable_get(:@destroy_callback_called)
379
+ assert_nil model.instance_variable_get(:@destroy_callback_called)
379
380
  end
380
381
 
381
382
  def test_destroy_behavior_for_callbacks
@@ -580,6 +581,38 @@ class ParanoiaTest < test_framework
580
581
  refute c.paranoia_destroyed?
581
582
  end
582
583
 
584
+ def test_restore_with_associations_using_recovery_window
585
+ parent = ParentModel.create
586
+ first_child = parent.very_related_models.create
587
+ second_child = parent.very_related_models.create
588
+
589
+ parent.destroy
590
+ second_child.update(deleted_at: parent.deleted_at + 11.minutes)
591
+
592
+ parent.restore!(:recursive => true)
593
+ assert_equal true, parent.deleted_at.nil?
594
+ assert_equal true, first_child.reload.deleted_at.nil?
595
+ assert_equal true, second_child.reload.deleted_at.nil?
596
+
597
+ parent.destroy
598
+ second_child.update(deleted_at: parent.deleted_at + 11.minutes)
599
+
600
+ parent.restore(:recursive => true, :recovery_window => 10.minutes)
601
+ assert_equal true, parent.deleted_at.nil?
602
+ assert_equal true, first_child.reload.deleted_at.nil?
603
+ assert_equal false, second_child.reload.deleted_at.nil?
604
+
605
+ second_child.restore
606
+ parent.destroy
607
+ first_child.update(deleted_at: parent.deleted_at - 11.minutes)
608
+ second_child.update(deleted_at: parent.deleted_at - 9.minutes)
609
+
610
+ ParentModel.restore(parent.id, :recursive => true, :recovery_window => 10.minutes)
611
+ assert_equal true, parent.reload.deleted_at.nil?
612
+ assert_equal false, first_child.reload.deleted_at.nil?
613
+ assert_equal true, second_child.reload.deleted_at.nil?
614
+ end
615
+
583
616
  def test_restore_with_associations
584
617
  parent = ParentModel.create
585
618
  first_child = parent.very_related_models.create
@@ -916,8 +949,8 @@ class ParanoiaTest < test_framework
916
949
  parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
917
950
  related_model = parent_model_with_counter_cache_column.related_models.create
918
951
 
919
- assert_equal nil, related_model.instance_variable_get(:@after_destroy_callback_called)
920
- assert_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
952
+ assert_nil related_model.instance_variable_get(:@after_destroy_callback_called)
953
+ assert_nil related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
921
954
 
922
955
  related_model.destroy
923
956
 
@@ -932,6 +965,18 @@ class ParanoiaTest < test_framework
932
965
  related.valid?
933
966
  end
934
967
 
968
+ def test_assocation_not_soft_destroyed_validator
969
+ notParanoidModel = NotParanoidModelWithBelongsAndAssocationNotSoftDestroyedValidator.create
970
+ parentModel = ParentModel.create
971
+ assert notParanoidModel.valid?
972
+
973
+ notParanoidModel.parent_model = parentModel
974
+ assert notParanoidModel.valid?
975
+ parentModel.destroy
976
+ assert !notParanoidModel.valid?
977
+ assert notParanoidModel.errors.full_messages.include? "Parent model has been soft-deleted"
978
+ end
979
+
935
980
  # TODO: find a fix for Rails 4.1
936
981
  if ActiveRecord::VERSION::STRING !~ /\A4\.1/
937
982
  def test_counter_cache_column_update_on_really_destroy
@@ -950,14 +995,52 @@ class ParanoiaTest < test_framework
950
995
  parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
951
996
  related_model = parent_model_with_counter_cache_column.related_models.create
952
997
 
953
- assert_equal nil, related_model.instance_variable_get(:@after_destroy_callback_called)
954
- assert_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
998
+ assert_nil related_model.instance_variable_get(:@after_destroy_callback_called)
999
+ assert_nil related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
955
1000
 
956
1001
  related_model.really_destroy!
957
1002
 
958
1003
  assert related_model.instance_variable_get(:@after_destroy_callback_called)
959
1004
  assert related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
960
1005
  end
1006
+
1007
+ def test_counter_cache_column_on_double_destroy
1008
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
1009
+ related_model = parent_model_with_counter_cache_column.related_models.create
1010
+
1011
+ related_model.destroy
1012
+ related_model.destroy
1013
+ assert_equal 0, parent_model_with_counter_cache_column.reload.related_models_count
1014
+ end
1015
+
1016
+ def test_counter_cache_column_on_double_restore
1017
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
1018
+ related_model = parent_model_with_counter_cache_column.related_models.create
1019
+
1020
+ related_model.destroy
1021
+ related_model.restore
1022
+ related_model.restore
1023
+ assert_equal 1, parent_model_with_counter_cache_column.reload.related_models_count
1024
+ end
1025
+
1026
+ def test_counter_cache_column_on_destroy_and_really_destroy
1027
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
1028
+ related_model = parent_model_with_counter_cache_column.related_models.create
1029
+
1030
+ related_model.destroy
1031
+ related_model.really_destroy!
1032
+ assert_equal 0, parent_model_with_counter_cache_column.reload.related_models_count
1033
+ end
1034
+
1035
+ def test_counter_cache_column_on_restore
1036
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
1037
+ related_model = parent_model_with_counter_cache_column.related_models.create
1038
+
1039
+ related_model.destroy
1040
+ assert_equal 0, parent_model_with_counter_cache_column.reload.related_models_count
1041
+ related_model.restore
1042
+ assert_equal 1, parent_model_with_counter_cache_column.reload.related_models_count
1043
+ end
961
1044
  end
962
1045
 
963
1046
  private
@@ -1233,6 +1316,11 @@ class NotParanoidModelWithBelong < ActiveRecord::Base
1233
1316
  belongs_to :paranoid_model_with_has_one
1234
1317
  end
1235
1318
 
1319
+ class NotParanoidModelWithBelongsAndAssocationNotSoftDestroyedValidator < NotParanoidModelWithBelong
1320
+ belongs_to :parent_model
1321
+ validates :parent_model, association_not_soft_destroyed: true
1322
+ end
1323
+
1236
1324
  class FlaggedModel < PlainModel
1237
1325
  acts_as_paranoid :flag_column => :is_deleted
1238
1326
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paranoia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - radarlistener@gmail.com
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-16 00:00:00.000000000 Z
11
+ date: 2017-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -76,6 +76,7 @@ files:
76
76
  - ".gitignore"
77
77
  - ".travis.yml"
78
78
  - CHANGELOG.md
79
+ - CODE_OF_CONDUCT.md
79
80
  - CONTRIBUTING.md
80
81
  - Gemfile
81
82
  - LICENSE
@@ -106,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
107
  version: 1.3.6
107
108
  requirements: []
108
109
  rubyforge_project:
109
- rubygems_version: 2.6.9
110
+ rubygems_version: 2.6.11
110
111
  signing_key:
111
112
  specification_version: 4
112
113
  summary: Paranoia is a re-implementation of acts_as_paranoid for Rails 3, 4, and 5,