paranoia 2.1.0 → 2.1.1

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: f0cb57d2b73781793a6c745fb89bb9d7793fecb3
4
- data.tar.gz: f53171d8795a238508541336bbf6e0aa6082959e
3
+ metadata.gz: eb9d6ab15bd6fb2e445d1cbb7d83d93c920f94f0
4
+ data.tar.gz: d48bc9001fcdbb023d9312c1706dcba6f602c07d
5
5
  SHA512:
6
- metadata.gz: 42eab22150eb1e35f7c49b430949905ddf7590695ab6cb8df7cadbd0012ad4038375f859ba63cc200f0b3fe2bde8a0941aeaf51a9daca4a0803ad5d7a3a4841d
7
- data.tar.gz: f88c66a92dd499958c14b2c1c4443334522c524270eaab270166ad702db7fa1ae47c4f927a79795fa57f157d8dc7a628f4fce9025e2606e3583dc3b7430c0ba5
6
+ metadata.gz: 2609ec5884f55c9d466a69a7d8df51ed380706c54ec47feab1be36666b63c617f4c1abb133e77f2f05552d23df4182170b0d6934938aacadbf9511214a447a39
7
+ data.tar.gz: 74b632024df0628881ac8ed4d338ac301470ff4a0ce985425055aed32537d0315c2c095b4c8f92ebb5467cc9f750f5ab878e06b1092e71c4582cdd82d17ad7a7
data/README.md CHANGED
@@ -25,7 +25,7 @@ gem "paranoia", "~> 2.0"
25
25
  Of course you can install this from GitHub as well:
26
26
 
27
27
  ``` ruby
28
- gem "paranoia", :github => "radar/paranoia", :branch => "master"
28
+ gem "paranoia", :github => "radar/paranoia", :branch => "rails3"
29
29
  # or
30
30
  gem "paranoia", :github => "radar/paranoia", :branch => "rails4"
31
31
  ```
@@ -124,6 +124,16 @@ def product
124
124
  end
125
125
  ```
126
126
 
127
+ If you want to include associated soft-deleted objects, you can (un)scope the association:
128
+
129
+ ``` ruby
130
+ class Person < ActiveRecord::Base
131
+ belongs_to :group, -> { with_deleted }
132
+ end
133
+
134
+ Person.includes(:group).all
135
+ ```
136
+
127
137
  If you want to find all records, even those which are deleted:
128
138
 
129
139
  ``` ruby
@@ -184,27 +194,6 @@ You can replace the older `acts_as_paranoid` methods as follows:
184
194
  The `recover` method in `acts_as_paranoid` runs `update` callbacks. Paranoia's
185
195
  `restore` method does not do this.
186
196
 
187
- ## Support for Unique Keys with Null Values
188
-
189
- Most databases ignore null columns when it comes to resolving unique index
190
- constraints. This means unique constraints that involve nullable columns may be
191
- problematic. Instead of using `NULL` to represent a not-deleted row, you can pick
192
- a value that you want paranoia to mean not deleted. Note that you can/should
193
- now apply a `NOT NULL` constraint to your `deleted_at` column.
194
-
195
- Per model:
196
-
197
- ```ruby
198
- # pick some value
199
- acts_as_paranoid sentinel_value: DateTime.new(0)
200
- ```
201
-
202
- or globally in a rails initializer, e.g. `config/initializer/paranoia.rb`
203
-
204
- ```ruby
205
- Paranoia.default_sentinel_value = DateTime.new(0)
206
- ```
207
-
208
197
  ## License
209
198
 
210
199
  This gem is released under the MIT license.
@@ -33,8 +33,15 @@ module Paranoia
33
33
  end
34
34
  alias :deleted :only_deleted
35
35
 
36
- def restore(id, opts = {})
37
- Array(id).flatten.map { |one_id| only_deleted.find(one_id).restore!(opts) }
36
+ def restore(id_or_ids, opts = {})
37
+ ids = Array(id_or_ids).flatten
38
+ any_object_instead_of_id = ids.any? { |id| ActiveRecord::Base === id }
39
+ if any_object_instead_of_id
40
+ ids.map! { |id| ActiveRecord::Base === id ? id.id : id }
41
+ ActiveSupport::Deprecation.warn("You are passing an instance of ActiveRecord::Base to `restore`. " \
42
+ "Please pass the id of the object by calling `.id`")
43
+ end
44
+ ids.map { |id| only_deleted.find(id).restore!(opts) }
38
45
  end
39
46
  end
40
47
 
@@ -59,7 +66,18 @@ module Paranoia
59
66
  def destroy
60
67
  transaction do
61
68
  run_callbacks(:destroy) do
62
- touch_paranoia_column
69
+ result = touch_paranoia_column
70
+ if result && ActiveRecord::VERSION::STRING >= '4.2'
71
+ each_counter_cached_associations do |association|
72
+ foreign_key = association.reflection.foreign_key.to_sym
73
+ unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
74
+ if send(association.reflection.name)
75
+ association.decrement_counters
76
+ end
77
+ end
78
+ end
79
+ end
80
+ result
63
81
  end
64
82
  end
65
83
  end
@@ -95,8 +113,7 @@ module Paranoia
95
113
 
96
114
  # touch paranoia column.
97
115
  # insert time to paranoia column.
98
- # @param with_transaction [Boolean] exec with ActiveRecord Transactions.
99
- def touch_paranoia_column(with_transaction=false)
116
+ def touch_paranoia_column
100
117
  raise ActiveRecord::ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
101
118
  if persisted?
102
119
  touch(paranoia_column)
@@ -1,3 +1,3 @@
1
1
  module Paranoia
2
- VERSION = "2.1.0"
2
+ VERSION = "2.1.1"
3
3
  end
@@ -12,27 +12,32 @@ end
12
12
 
13
13
  def setup!
14
14
  connect!
15
- ActiveRecord::Base.connection.execute 'CREATE TABLE parent_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
16
- ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME)'
17
- ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_id INTEGER)'
18
- ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_build_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_and_build_id INTEGER, name VARCHAR(32))'
19
- ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_anthor_class_name_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_id INTEGER)'
20
- ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_foreign_key_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME, has_one_foreign_key_id INTEGER)'
21
- ActiveRecord::Base.connection.execute 'CREATE TABLE not_paranoid_model_with_belongs (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, paranoid_model_with_has_one_id INTEGER)'
22
- ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_model_with_has_one_and_builds (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, color VARCHAR(32), deleted_at DATETIME, has_one_foreign_key_id INTEGER)'
23
- ActiveRecord::Base.connection.execute 'CREATE TABLE featureful_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME, name VARCHAR(32))'
24
- ActiveRecord::Base.connection.execute 'CREATE TABLE plain_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
25
- ActiveRecord::Base.connection.execute 'CREATE TABLE callback_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
26
- ActiveRecord::Base.connection.execute 'CREATE TABLE fail_callback_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
27
- ActiveRecord::Base.connection.execute 'CREATE TABLE related_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER NOT NULL, deleted_at DATETIME)'
28
- ActiveRecord::Base.connection.execute 'CREATE TABLE asplode_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME)'
29
- ActiveRecord::Base.connection.execute 'CREATE TABLE employers (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
30
- ActiveRecord::Base.connection.execute 'CREATE TABLE employees (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
31
- ActiveRecord::Base.connection.execute 'CREATE TABLE jobs (id INTEGER NOT NULL PRIMARY KEY, employer_id INTEGER NOT NULL, employee_id INTEGER NOT NULL, deleted_at DATETIME)'
32
- ActiveRecord::Base.connection.execute 'CREATE TABLE custom_column_models (id INTEGER NOT NULL PRIMARY KEY, destroyed_at DATETIME)'
33
- ActiveRecord::Base.connection.execute 'CREATE TABLE custom_sentinel_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME NOT NULL)'
34
- ActiveRecord::Base.connection.execute 'CREATE TABLE non_paranoid_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER)'
35
- ActiveRecord::Base.connection.execute 'CREATE TABLE polymorphic_models (id INTEGER NOT NULL PRIMARY KEY, parent_id INTEGER, parent_type STRING, deleted_at DATETIME)'
15
+ {
16
+ 'parent_model_with_counter_cache_columns' => 'related_models_count INTEGER DEFAULT 0',
17
+ 'parent_models' => 'deleted_at DATETIME',
18
+ 'paranoid_models' => 'parent_model_id INTEGER, deleted_at DATETIME',
19
+ 'paranoid_model_with_belongs' => 'parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_id INTEGER',
20
+ 'paranoid_model_with_build_belongs' => 'parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_and_build_id INTEGER, name VARCHAR(32)',
21
+ 'paranoid_model_with_anthor_class_name_belongs' => 'parent_model_id INTEGER, deleted_at DATETIME, paranoid_model_with_has_one_id INTEGER',
22
+ 'paranoid_model_with_foreign_key_belongs' => 'parent_model_id INTEGER, deleted_at DATETIME, has_one_foreign_key_id INTEGER',
23
+ 'not_paranoid_model_with_belongs' => 'parent_model_id INTEGER, paranoid_model_with_has_one_id INTEGER',
24
+ 'paranoid_model_with_has_one_and_builds' => 'parent_model_id INTEGER, color VARCHAR(32), deleted_at DATETIME, has_one_foreign_key_id INTEGER',
25
+ 'featureful_models' => 'deleted_at DATETIME, name VARCHAR(32)',
26
+ 'plain_models' => 'deleted_at DATETIME',
27
+ 'callback_models' => 'deleted_at DATETIME',
28
+ 'fail_callback_models' => 'deleted_at DATETIME',
29
+ 'related_models' => 'parent_model_id INTEGER, parent_model_with_counter_cache_column_id INTEGER, deleted_at DATETIME',
30
+ 'asplode_models' => 'parent_model_id INTEGER, deleted_at DATETIME',
31
+ 'employers' => 'deleted_at DATETIME',
32
+ 'employees' => 'deleted_at DATETIME',
33
+ 'jobs' => 'employer_id INTEGER NOT NULL, employee_id INTEGER NOT NULL, deleted_at DATETIME',
34
+ 'custom_column_models' => 'destroyed_at DATETIME',
35
+ 'custom_sentinel_models' => 'deleted_at DATETIME NOT NULL',
36
+ 'non_paranoid_models' => 'parent_model_id INTEGER',
37
+ 'polymorphic_models' => 'parent_id INTEGER, parent_type STRING, deleted_at DATETIME'
38
+ }.each do |table_name, columns_as_sql_string|
39
+ ActiveRecord::Base.connection.execute "CREATE TABLE #{table_name} (id INTEGER NOT NULL PRIMARY KEY, #{columns_as_sql_string})"
40
+ end
36
41
  end
37
42
 
38
43
  class WithDifferentConnection < ActiveRecord::Base
@@ -758,6 +763,56 @@ class ParanoiaTest < test_framework
758
763
  assert_equal 0, polymorphic.class.count
759
764
  end
760
765
 
766
+ def test_counter_cache_column_update_on_destroy#_and_restore_and_really_destroy
767
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
768
+ related_model = parent_model_with_counter_cache_column.related_models.create
769
+
770
+ assert_equal 1, parent_model_with_counter_cache_column.reload.related_models_count
771
+ related_model.destroy
772
+ assert_equal 0, parent_model_with_counter_cache_column.reload.related_models_count
773
+ end
774
+
775
+ def test_callbacks_for_counter_cache_column_update_on_destroy
776
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
777
+ related_model = parent_model_with_counter_cache_column.related_models.create
778
+
779
+ assert_equal nil, related_model.instance_variable_get(:@after_destroy_callback_called)
780
+ assert_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
781
+
782
+ related_model.destroy
783
+
784
+ assert related_model.instance_variable_get(:@after_destroy_callback_called)
785
+ # assert related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
786
+ end
787
+
788
+ # TODO: find a fix for Rails 4.1
789
+ if ActiveRecord::VERSION::STRING !~ /\A4\.1/
790
+ def test_counter_cache_column_update_on_really_destroy
791
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
792
+ related_model = parent_model_with_counter_cache_column.related_models.create
793
+
794
+ assert_equal 1, parent_model_with_counter_cache_column.reload.related_models_count
795
+ related_model.really_destroy!
796
+ assert_equal 0, parent_model_with_counter_cache_column.reload.related_models_count
797
+ end
798
+ end
799
+
800
+ # TODO: find a fix for Rails 4.0 and 4.1
801
+ if ActiveRecord::VERSION::STRING >= '4.2'
802
+ def test_callbacks_for_counter_cache_column_update_on_really_destroy!
803
+ parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
804
+ related_model = parent_model_with_counter_cache_column.related_models.create
805
+
806
+ assert_equal nil, related_model.instance_variable_get(:@after_destroy_callback_called)
807
+ assert_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
808
+
809
+ related_model.really_destroy!
810
+
811
+ assert related_model.instance_variable_get(:@after_destroy_callback_called)
812
+ assert related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
813
+ end
814
+ end
815
+
761
816
  private
762
817
  def get_featureful_model
763
818
  FeaturefulModel.new(:name => "not empty")
@@ -814,9 +869,27 @@ class ParentModel < ActiveRecord::Base
814
869
  has_one :polymorphic_model, as: :parent, dependent: :destroy
815
870
  end
816
871
 
872
+ class ParentModelWithCounterCacheColumn < ActiveRecord::Base
873
+ has_many :related_models
874
+ end
875
+
817
876
  class RelatedModel < ActiveRecord::Base
818
877
  acts_as_paranoid
819
878
  belongs_to :parent_model
879
+ belongs_to :parent_model_with_counter_cache_column, counter_cache: true
880
+
881
+ after_destroy do |model|
882
+ if parent_model_with_counter_cache_column && parent_model_with_counter_cache_column.reload.related_models_count == 0
883
+ model.instance_variable_set :@after_destroy_callback_called, true
884
+ end
885
+ end
886
+ after_commit :set_after_commit_on_destroy_callback_called, on: :destroy
887
+
888
+ def set_after_commit_on_destroy_callback_called
889
+ if parent_model_with_counter_cache_column && parent_model_with_counter_cache_column.reload.related_models_count == 0
890
+ self.instance_variable_set :@after_commit_on_destroy_callback_called, true
891
+ end
892
+ end
820
893
  end
821
894
 
822
895
  class Employer < ActiveRecord::Base
@@ -914,8 +987,6 @@ class FlaggedModelWithCustomIndex < PlainModel
914
987
  acts_as_paranoid :flag_column => :is_deleted, :indexed_column => :is_deleted
915
988
  end
916
989
 
917
-
918
-
919
990
  class AsplodeModel < ActiveRecord::Base
920
991
  acts_as_paranoid
921
992
  before_destroy do |r|
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.1.0
4
+ version: 2.1.1
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: 2015-01-24 00:00:00.000000000 Z
11
+ date: 2015-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -94,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
94
  version: 1.3.6
95
95
  requirements: []
96
96
  rubyforge_project: paranoia
97
- rubygems_version: 2.2.2
97
+ rubygems_version: 2.4.6
98
98
  signing_key:
99
99
  specification_version: 4
100
100
  summary: Paranoia is a re-implementation of acts_as_paranoid for Rails 3, using much,