paranoia 2.2.0 → 2.2.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: e4840070148649d6ad07f0342b48a031fdf25827
4
- data.tar.gz: 25ae25c52215d9cc78ab55756baa09f79255b283
3
+ metadata.gz: a081d1169b0d0b18a5815aafe4bafdeab81e62fd
4
+ data.tar.gz: 4b38ec1db06051b0c5ada74f9405f935ba39624b
5
5
  SHA512:
6
- metadata.gz: ea4f3ab508171f21e047ded352fabb8b0f6f5821746054d00dcc5b733f3150f069086ccdbf892ad3c79aec071609839dba4676eaab73941f7b2b23b49639b46c
7
- data.tar.gz: 732a2fe402b9d718cecf2968068adf2a83c7486cac87127684d92736b0d39b9352a433ae69e062c90665fd4b93a5cb4cbfe40d38d3f46a1efaf4b9374185bb2c
6
+ metadata.gz: e80496f3e472a7ecef7abee20f49d394fb884bb8e81313c92e6f1915b2f805d9f14ef1ef930f47ccdf97300df51f25458881d465df4355f85d2a6066c0b92671
7
+ data.tar.gz: 205530a0638b6d1d1f455cfae490e2c9e77f080ef4fdfd63d6fdafba849ebc7615ff34f091479b011a02cd9bd7919f27061a0f608be33be22b9801041d8277b7
@@ -3,20 +3,24 @@ language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
5
  - 2.1.10
6
- - 2.2.5
7
- - 2.3.1
8
- - jruby-9.1.0.0
6
+ - 2.2.6
7
+ - 2.3.3
8
+ - jruby-9.1.6.0
9
9
 
10
10
  env:
11
11
  matrix:
12
- - RAILS='~> 4.1.15'
13
- - RAILS='~> 4.2.6'
14
- - RAILS='~> 5.0.0'
12
+ - RAILS='~> 4.1.16'
13
+ - RAILS='~> 4.2.7.1'
14
+ - RAILS='~> 5.0.0.1'
15
15
 
16
16
  matrix:
17
17
  exclude:
18
- - env: RAILS='~> 5.0.0'
18
+ - env: RAILS='~> 5.0.0.1'
19
19
  rvm: 2.1.10
20
20
  allow_failures:
21
- - env: RAILS='~> 5.0.0'
22
- rvm: jruby-9.1.0.0
21
+ - env: RAILS='~> 4.1.16'
22
+ rvm: jruby-9.1.6.0
23
+ - env: RAILS='~> 4.2.7.1'
24
+ rvm: jruby-9.1.6.0
25
+ - env: RAILS='~> 5.0.0.1'
26
+ rvm: jruby-9.1.6.0
@@ -1,6 +1,28 @@
1
1
  # paranoia Changelog
2
2
 
3
- ## 2.2.0 (unreleased)
3
+ ## 2.2.2 (Unreleased)
4
+
5
+ ## 2.2.1 (2017-02-15)
6
+
7
+ * [#371](https://github.com/rubysherpas/paranoia/pull/371) Use ActiveSupport.on_load to correctly re-open ActiveRecord::Base
8
+
9
+ _Fixes [#335](https://github.com/rubysherpas/paranoia/issues/335) and [#381](https://github.com/rubysherpas/paranoia/issues/381)._
10
+
11
+ [Iaan Krynauw (@iaankrynauw)](https://github.com/iaankrynauw)
12
+
13
+ * [#377](https://github.com/rubysherpas/paranoia/pull/377) Touch record on paranoia-destroy.
14
+
15
+ _Fixes [#296](https://github.com/rubysherpas/paranoia/issues/296)._
16
+
17
+ [René (@rbr)](https://github.com/rbr)
18
+
19
+ * [#379](https://github.com/rubysherpas/paranoia/pull/379) Fixes a problem of ambiguous table names when using only_deleted method.
20
+
21
+ _Fixes [#26](https://github.com/rubysherpas/paranoia/issues/26) and [#27](https://github.com/rubysherpas/paranoia/pull/27)._
22
+
23
+ [Thomas Romera (@Erowlin)](https://github.com/Erowlin)
24
+
25
+ ## 2.2.0 (2016-10-21)
4
26
 
5
27
  * Ruby 2.0 or greater is required
6
28
  * Rails 5.0.0.beta1.1 support [@pigeonworks](https://github.com/pigeonworks) [@halostatue](https://github.com/halostatue) and [@gagalago](https://github.com/gagalago)
data/README.md CHANGED
@@ -6,7 +6,7 @@ When your app is using Paranoia, calling `destroy` on an ActiveRecord object doe
6
6
 
7
7
  If you wish to actually destroy an object you may call `really_destroy!`. **WARNING**: This will also *really destroy* all `dependent: :destroy` records, so please aim this method away from face when using.
8
8
 
9
- If a record has `has_many` associations defined AND those associations have `dependent: :destroy` set on them, then they will also be soft-deleted if `acts_as_paranoid` is set, otherwise the normal destroy will be called.
9
+ If a record has `has_many` associations defined AND those associations have `dependent: :destroy` set on them, then they will also be soft-deleted if `acts_as_paranoid` is set, otherwise the normal destroy will be called. ***See [Destroying through association callbacks](#destroying-through-association-callbacks) for clarifying examples.***
10
10
 
11
11
  ## Getting Started Video
12
12
  Setup and basic usage of the paranoia gem
@@ -245,6 +245,77 @@ class Client < ActiveRecord::Base
245
245
  end
246
246
  ```
247
247
 
248
+ ##### Destroying through association callbacks
249
+
250
+ When dealing with `dependent: :destroy` associations and `acts_as_paranoid`, it's important to remember that whatever method is called on the parent model will be called on the child model. For example, given both models of an association have `acts_as_paranoid` defined:
251
+
252
+ ``` ruby
253
+ class Client < ActiveRecord::Base
254
+ acts_as_paranoid
255
+
256
+ has_many :emails, dependent: :destroy
257
+ end
258
+
259
+ class Email < ActiveRecord::Base
260
+ acts_as_paranoid
261
+
262
+ belongs_to :client
263
+ end
264
+ ```
265
+
266
+ When we call `destroy` on the parent `client`, it will call `destroy` on all of its associated children `emails`:
267
+
268
+ ``` ruby
269
+ >> client.emails.count
270
+ # => 5
271
+ >> client.destroy
272
+ # => client
273
+ >> client.deleted_at
274
+ # => [current timestamp]
275
+ >> Email.where(client_id: client.id).count
276
+ # => 0
277
+ >> Email.with_deleted.where(client_id: client.id).count
278
+ # => 5
279
+ ```
280
+
281
+ Similarly, when we call `really_destroy!` on the parent `client`, then each child `email` will also have `really_destroy!` called:
282
+
283
+ ``` ruby
284
+ >> client.emails.count
285
+ # => 5
286
+ >> client.id
287
+ # => 12345
288
+ >> client.really_destroy!
289
+ # => client
290
+ >> Client.find 12345
291
+ # => ActiveRecord::RecordNotFound
292
+ >> Email.with_deleted.where(client_id: client.id).count
293
+ # => 0
294
+ ```
295
+
296
+ However, if the child model `Email` does not have `acts_as_paranoid` set, then calling `destroy` on the parent `client` will also call `destroy` on each child `email`, thereby actually destroying them:
297
+
298
+ ``` ruby
299
+ class Client < ActiveRecord::Base
300
+ acts_as_paranoid
301
+
302
+ has_many :emails, dependent: :destroy
303
+ end
304
+
305
+ class Email < ActiveRecord::Base
306
+ belongs_to :client
307
+ end
308
+
309
+ >> client.emails.count
310
+ # => 5
311
+ >> client.destroy
312
+ # => client
313
+ >> Email.where(client_id: client.id).count
314
+ # => 0
315
+ >> Email.with_deleted.where(client_id: client.id).count
316
+ # => NoMethodError: undefined method `with_deleted' for #<Class:0x0123456>
317
+ ```
318
+
248
319
  ## Acts As Paranoid Migration
249
320
 
250
321
  You can replace the older `acts_as_paranoid` methods as follows:
@@ -35,8 +35,9 @@ module Paranoia
35
35
  # some deleted rows will hold a null value in the paranoia column
36
36
  # these will not match != sentinel value because "NULL != value" is
37
37
  # NULL under the sql standard
38
- quoted_paranoia_column = connection.quote_column_name(paranoia_column)
39
- with_deleted.where("#{quoted_paranoia_column} IS NULL OR #{quoted_paranoia_column} != ?", paranoia_sentinel_value)
38
+ # Scoping with the table_name is mandatory to avoid ambiguous errors when joining tables.
39
+ scoped_quoted_paranoia_column = "#{self.table_name}.#{connection.quote_column_name(paranoia_column)}"
40
+ with_deleted.where("#{scoped_quoted_paranoia_column} IS NULL OR #{scoped_quoted_paranoia_column} != ?", paranoia_sentinel_value)
40
41
  end
41
42
  alias_method :deleted, :only_deleted
42
43
 
@@ -110,7 +111,6 @@ module Paranoia
110
111
  if (noop_if_frozen && !@attributes.frozen?) || !noop_if_frozen
111
112
  write_attribute paranoia_column, paranoia_sentinel_value
112
113
  update_columns(paranoia_restore_attributes)
113
- touch
114
114
  end
115
115
  restore_associated_records if opts[:recursive]
116
116
  end
@@ -154,13 +154,17 @@ module Paranoia
154
154
  def paranoia_restore_attributes
155
155
  {
156
156
  paranoia_column => paranoia_sentinel_value
157
- }
157
+ }.merge(timestamp_attributes_with_current_time)
158
158
  end
159
159
 
160
160
  def paranoia_destroy_attributes
161
161
  {
162
162
  paranoia_column => current_time_from_proper_timezone
163
- }
163
+ }.merge(timestamp_attributes_with_current_time)
164
+ end
165
+
166
+ def timestamp_attributes_with_current_time
167
+ timestamp_attributes_for_update_in_model.each_with_object({}) { |attr,hash| hash[attr] = current_time_from_proper_timezone }
164
168
  end
165
169
 
166
170
  # restore associated records that have been soft deleted when
@@ -205,56 +209,58 @@ module Paranoia
205
209
  end
206
210
  end
207
211
 
208
- class ActiveRecord::Base
209
- def self.acts_as_paranoid(options={})
210
- alias_method :really_destroyed?, :destroyed?
211
- alias_method :really_delete, :delete
212
- alias_method :destroy_without_paranoia, :destroy
212
+ ActiveSupport.on_load(:active_record) do
213
+ class ActiveRecord::Base
214
+ def self.acts_as_paranoid(options={})
215
+ alias_method :really_destroyed?, :destroyed?
216
+ alias_method :really_delete, :delete
217
+ alias_method :destroy_without_paranoia, :destroy
213
218
 
214
- include Paranoia
215
- class_attribute :paranoia_column, :paranoia_sentinel_value
219
+ include Paranoia
220
+ class_attribute :paranoia_column, :paranoia_sentinel_value
216
221
 
217
- self.paranoia_column = (options[:column] || :deleted_at).to_s
218
- self.paranoia_sentinel_value = options.fetch(:sentinel_value) { Paranoia.default_sentinel_value }
219
- def self.paranoia_scope
220
- where(paranoia_column => paranoia_sentinel_value)
221
- end
222
- class << self; alias_method :without_deleted, :paranoia_scope end
222
+ self.paranoia_column = (options[:column] || :deleted_at).to_s
223
+ self.paranoia_sentinel_value = options.fetch(:sentinel_value) { Paranoia.default_sentinel_value }
224
+ def self.paranoia_scope
225
+ where(paranoia_column => paranoia_sentinel_value)
226
+ end
227
+ class << self; alias_method :without_deleted, :paranoia_scope end
223
228
 
224
- unless options[:without_default_scope]
225
- default_scope { paranoia_scope }
226
- end
229
+ unless options[:without_default_scope]
230
+ default_scope { paranoia_scope }
231
+ end
227
232
 
228
- before_restore {
229
- self.class.notify_observers(:before_restore, self) if self.class.respond_to?(:notify_observers)
230
- }
231
- after_restore {
232
- self.class.notify_observers(:after_restore, self) if self.class.respond_to?(:notify_observers)
233
- }
234
- end
233
+ before_restore {
234
+ self.class.notify_observers(:before_restore, self) if self.class.respond_to?(:notify_observers)
235
+ }
236
+ after_restore {
237
+ self.class.notify_observers(:after_restore, self) if self.class.respond_to?(:notify_observers)
238
+ }
239
+ end
235
240
 
236
- # Please do not use this method in production.
237
- # Pretty please.
238
- def self.I_AM_THE_DESTROYER!
239
- # TODO: actually implement spelling error fixes
241
+ # Please do not use this method in production.
242
+ # Pretty please.
243
+ def self.I_AM_THE_DESTROYER!
244
+ # TODO: actually implement spelling error fixes
240
245
  puts %Q{
241
246
  Sharon: "There should be a method called I_AM_THE_DESTROYER!"
242
247
  Ryan: "What should this method do?"
243
248
  Sharon: "It should fix all the spelling errors on the page!"
244
249
  }
245
- end
250
+ end
246
251
 
247
- def self.paranoid? ; false ; end
248
- def paranoid? ; self.class.paranoid? ; end
252
+ def self.paranoid? ; false ; end
253
+ def paranoid? ; self.class.paranoid? ; end
249
254
 
250
- private
255
+ private
251
256
 
252
- def paranoia_column
253
- self.class.paranoia_column
254
- end
257
+ def paranoia_column
258
+ self.class.paranoia_column
259
+ end
255
260
 
256
- def paranoia_sentinel_value
257
- self.class.paranoia_sentinel_value
261
+ def paranoia_sentinel_value
262
+ self.class.paranoia_sentinel_value
263
+ end
258
264
  end
259
265
  end
260
266
 
@@ -1,3 +1,3 @@
1
1
  module Paranoia
2
- VERSION = "2.2.0"
2
+ VERSION = '2.2.1'.freeze
3
3
  end
@@ -40,6 +40,8 @@ def setup!
40
40
  'unparanoid_unique_models' => 'name VARCHAR(32), paranoid_with_unparanoids_id INTEGER',
41
41
  'active_column_models' => 'deleted_at DATETIME, active BOOLEAN',
42
42
  'active_column_model_with_uniqueness_validations' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
43
+ 'paranoid_model_with_belongs_to_active_column_model_with_has_many_relationships' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN, active_column_model_with_has_many_relationship_id INTEGER',
44
+ 'active_column_model_with_has_many_relationships' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
43
45
  'without_default_scope_models' => 'deleted_at DATETIME'
44
46
  }.each do |table_name, columns_as_sql_string|
45
47
  ActiveRecord::Base.connection.execute "CREATE TABLE #{table_name} (id INTEGER NOT NULL PRIMARY KEY, #{columns_as_sql_string})"
@@ -182,8 +184,11 @@ class ParanoiaTest < test_framework
182
184
  p2 = ParanoidModel.create(:parent_model => parent2)
183
185
  p1.destroy
184
186
  p2.destroy
187
+
185
188
  assert_equal 0, parent1.paranoid_models.count
186
189
  assert_equal 1, parent1.paranoid_models.only_deleted.count
190
+
191
+ assert_equal 2, ParanoidModel.only_deleted.joins(:parent_model).count
187
192
  assert_equal 1, parent1.paranoid_models.deleted.count
188
193
  assert_equal 0, parent1.paranoid_models.without_deleted.count
189
194
  p3 = ParanoidModel.create(:parent_model => parent1)
@@ -192,6 +197,17 @@ class ParanoiaTest < test_framework
192
197
  assert_equal [p1,p3], parent1.paranoid_models.with_deleted
193
198
  end
194
199
 
200
+ def test_only_deleted_with_joins
201
+ c1 = ActiveColumnModelWithHasManyRelationship.create(name: 'Jacky')
202
+ c2 = ActiveColumnModelWithHasManyRelationship.create(name: 'Thomas')
203
+ p1 = ParanoidModelWithBelongsToActiveColumnModelWithHasManyRelationship.create(name: 'Hello', active_column_model_with_has_many_relationship: c1)
204
+
205
+ c1.destroy
206
+ assert_equal 1, ActiveColumnModelWithHasManyRelationship.count
207
+ assert_equal 1, ActiveColumnModelWithHasManyRelationship.only_deleted.count
208
+ assert_equal 1, ActiveColumnModelWithHasManyRelationship.only_deleted.joins(:paranoid_model_with_belongs_to_active_column_model_with_has_many_relationships).count
209
+ end
210
+
195
211
  def test_destroy_behavior_for_custom_column_models
196
212
  model = CustomColumnModel.new
197
213
  assert_equal 0, model.class.count
@@ -774,6 +790,13 @@ class ParanoiaTest < test_framework
774
790
  refute b.valid?
775
791
  end
776
792
 
793
+ def test_updated_at_modification_on_destroy
794
+ paranoid_model = ParanoidModelWithTimestamp.create(:parent_model => ParentModel.create, :updated_at => 1.day.ago)
795
+ assert paranoid_model.updated_at < 10.minutes.ago
796
+ paranoid_model.destroy
797
+ assert paranoid_model.updated_at > 10.minutes.ago
798
+ end
799
+
777
800
  def test_updated_at_modification_on_restore
778
801
  parent1 = ParentModel.create
779
802
  pt1 = ParanoidModelWithTimestamp.create(:parent_model => parent1)
@@ -1105,6 +1128,45 @@ class ActiveColumnModelWithUniquenessValidation < ActiveRecord::Base
1105
1128
  end
1106
1129
  end
1107
1130
 
1131
+ class ActiveColumnModelWithHasManyRelationship < ActiveRecord::Base
1132
+ has_many :paranoid_model_with_belongs_to_active_column_model_with_has_many_relationships
1133
+ acts_as_paranoid column: :active, sentinel_value: true
1134
+
1135
+ def paranoia_restore_attributes
1136
+ {
1137
+ deleted_at: nil,
1138
+ active: true
1139
+ }
1140
+ end
1141
+
1142
+ def paranoia_destroy_attributes
1143
+ {
1144
+ deleted_at: current_time_from_proper_timezone,
1145
+ active: nil
1146
+ }
1147
+ end
1148
+ end
1149
+
1150
+ class ParanoidModelWithBelongsToActiveColumnModelWithHasManyRelationship < ActiveRecord::Base
1151
+ belongs_to :active_column_model_with_has_many_relationship
1152
+
1153
+ acts_as_paranoid column: :active, sentinel_value: true
1154
+
1155
+ def paranoia_restore_attributes
1156
+ {
1157
+ deleted_at: nil,
1158
+ active: true
1159
+ }
1160
+ end
1161
+
1162
+ def paranoia_destroy_attributes
1163
+ {
1164
+ deleted_at: current_time_from_proper_timezone,
1165
+ active: nil
1166
+ }
1167
+ end
1168
+ end
1169
+
1108
1170
  class NonParanoidModel < ActiveRecord::Base
1109
1171
  end
1110
1172
 
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.0
4
+ version: 2.2.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: 2016-10-20 00:00:00.000000000 Z
11
+ date: 2017-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  version: 1.3.6
107
107
  requirements: []
108
108
  rubyforge_project:
109
- rubygems_version: 2.5.1
109
+ rubygems_version: 2.6.9
110
110
  signing_key:
111
111
  specification_version: 4
112
112
  summary: Paranoia is a re-implementation of acts_as_paranoid for Rails 3, 4, and 5,