paranoia 1.3.3 → 1.3.4

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: 303721b73429fa8bacda1346701858f2439c8069
4
- data.tar.gz: 789252f10b981c0260f18f06adba4798fe7dbc99
3
+ metadata.gz: 1bf4edf194a7572da8b0edc79105f4c62a37bfd2
4
+ data.tar.gz: dd084504c6c843de8ab3f7970570db894feac1ca
5
5
  SHA512:
6
- metadata.gz: b9b84fe3194f415bf9871274646264d3aa68542310ee46e1abe9ff76f5c9c555cf6bf37f3e7aab09976c248b1456c9633af07c93a437b6e24f92faa29746198a
7
- data.tar.gz: 22d720ab4dded670a75abc090fc098755526c7cec719515f053f5e94881c3d519286a285d7cadf8f77833ddac34ec4b13316b4d38d6dffa989b691b470ed70bb
6
+ metadata.gz: 9162181a3e8261387eefdfa78a512c0eee6fef54d19e2455673cb4b96f1fd673b8531b3357a1501967360610ae7b0cefecbe75cee06ae732af331639e7012535
7
+ data.tar.gz: 66b30b12969246dc8c820c32bc4a4df0f817ffa00ab99a48fcd152e9e9fcf503f5d1a2dce271b154bd11a15d3c1a61a39a6678c520036f006d900e79215cc396
data/README.md CHANGED
@@ -4,9 +4,9 @@ Paranoia is a re-implementation of [acts\_as\_paranoid](http://github.com/techno
4
4
 
5
5
  You would use either plugin / gem if you wished that when you called `destroy` on an Active Record object that it didn't actually destroy it, but just "hid" the record. Paranoia does this by setting a `deleted_at` field to the current time when you `destroy` a record, and hides it by scoping all queries on your model to only include records which do not have a `deleted_at` field.
6
6
 
7
- If you wish to actually destroy an object you may call destroy! on it or simply call destroy twice on the same object.
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 they don't have that, then they will not be deleted.
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.
10
10
 
11
11
  ## Installation & Usage
12
12
 
@@ -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
  ```
@@ -43,7 +43,7 @@ Updating is as simple as `bundle update paranoia`.
43
43
  Run:
44
44
 
45
45
  ```shell
46
- rails generate migration AddDeletedAtToClients deleted_at:datetime
46
+ rails generate migration AddDeletedAtToClients deleted_at:datetime:index
47
47
  ```
48
48
 
49
49
  and now you have a migration
@@ -52,6 +52,7 @@ and now you have a migration
52
52
  class AddDeletedAtToClients < ActiveRecord::Migration
53
53
  def change
54
54
  add_column :clients, :deleted_at, :datetime
55
+ add_index :clients, :deleted_at
55
56
  end
56
57
  end
57
58
  ```
@@ -1,3 +1,5 @@
1
+ require 'active_record' unless defined? ActiveRecord
2
+
1
3
  module Paranoia
2
4
  def self.included(klazz)
3
5
  klazz.extend Query
@@ -47,7 +49,8 @@ module Paranoia
47
49
  end
48
50
 
49
51
  def destroy
50
- run_callbacks(:destroy) { touch_paranoia_column(true) }
52
+ callbacks_result = run_callbacks(:destroy) { touch_paranoia_column(true) }
53
+ callbacks_result ? self : false
51
54
  end
52
55
 
53
56
  def delete
@@ -77,10 +80,15 @@ module Paranoia
77
80
  # insert time to paranoia column.
78
81
  # @param with_transaction [Boolean] exec with ActiveRecord Transactions.
79
82
  def touch_paranoia_column(with_transaction=false)
80
- if with_transaction
81
- with_transaction_returning_status { touch(paranoia_column) }
82
- else
83
- touch(paranoia_column)
83
+ # This method is (potentially) called from really_destroy
84
+ # The object the method is being called on may be frozen
85
+ # Let's not touch it if it's frozen.
86
+ unless self.frozen?
87
+ if with_transaction
88
+ with_transaction_returning_status { touch(paranoia_column) }
89
+ else
90
+ touch(paranoia_column)
91
+ end
84
92
  end
85
93
  end
86
94
 
@@ -92,10 +100,16 @@ module Paranoia
92
100
  end
93
101
 
94
102
  destroyed_associations.each do |association|
95
- association = send(association.name)
96
-
97
- if association.paranoid?
98
- association.only_deleted.each { |record| record.restore(:recursive => true) }
103
+ association_data = send(association.name)
104
+
105
+ unless association_data.nil?
106
+ if association_data.paranoid?
107
+ if association.collection?
108
+ association_data.only_deleted.each { |record| record.restore(:recursive => true) }
109
+ else
110
+ association_data.restore(:recursive => true)
111
+ end
112
+ end
99
113
  end
100
114
  end
101
115
  end
@@ -103,9 +117,25 @@ end
103
117
 
104
118
  class ActiveRecord::Base
105
119
  def self.acts_as_paranoid(options={})
120
+ alias :really_destroyed? :destroyed?
106
121
  alias :ar_destroy :destroy
107
122
  alias :destroy! :ar_destroy
108
123
  alias :delete! :delete
124
+ def really_destroy!
125
+ dependent_reflections = self.reflections.select do |name, reflection|
126
+ reflection.options[:dependent] == :destroy
127
+ end
128
+ if dependent_reflections.any?
129
+ dependent_reflections.each do |name, _|
130
+ associated_records = self.send(name)
131
+ # Paranoid models will have this method, non-paranoid models will not
132
+ associated_records = associated_records.with_deleted if associated_records.respond_to?(:with_deleted)
133
+ associated_records.each(&:really_destroy!)
134
+ end
135
+ end
136
+ destroy!
137
+ end
138
+
109
139
  include Paranoia
110
140
  class_attribute :paranoia_column
111
141
 
@@ -141,3 +171,5 @@ class ActiveRecord::Base
141
171
  self.class.paranoia_column
142
172
  end
143
173
  end
174
+
175
+ require 'paranoia/rspec' if defined? RSpec
@@ -0,0 +1,13 @@
1
+ require 'rspec/expectations'
2
+
3
+ # Validate the subject's class did call "acts_as_paranoid"
4
+ RSpec::Matchers.define :act_as_paranoid do
5
+ match { |subject| subject.class.ancestors.include?(Paranoia) }
6
+
7
+ failure_message { "expected #{subject.class} to use `acts_as_paranoid`" }
8
+ failure_message_when_negated { "expected #{subject.class} not to use `acts_as_paranoid`" }
9
+
10
+ # RSpec 2 compatibility:
11
+ alias_method :failure_message_for_should, :failure_message
12
+ alias_method :failure_message_for_should_not, :failure_message_when_negated
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Paranoia
2
- VERSION = '1.3.3'
2
+ VERSION = '1.3.4'
3
3
  end
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.required_rubygems_version = '>= 1.3.6'
15
15
  s.rubyforge_project = 'paranoia'
16
16
 
17
- s.add_dependency 'activerecord', '~> 3.2'
17
+ s.add_dependency 'activerecord', '~> 3.1'
18
18
 
19
19
  s.add_development_dependency 'bundler', '>= 1.0.0'
20
20
  s.add_development_dependency 'rake'
@@ -10,9 +10,11 @@ FileUtils.rm_f DB_FILE
10
10
  ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => DB_FILE
11
11
  ActiveRecord::Base.connection.execute 'CREATE TABLE parent_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
12
12
  ActiveRecord::Base.connection.execute 'CREATE TABLE paranoid_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER, deleted_at DATETIME)'
13
+ 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)'
13
14
  ActiveRecord::Base.connection.execute 'CREATE TABLE featureful_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME, name VARCHAR(32))'
14
15
  ActiveRecord::Base.connection.execute 'CREATE TABLE plain_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
15
16
  ActiveRecord::Base.connection.execute 'CREATE TABLE callback_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
17
+ ActiveRecord::Base.connection.execute 'CREATE TABLE fail_callback_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
16
18
  ActiveRecord::Base.connection.execute 'CREATE TABLE related_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER NOT NULL, deleted_at DATETIME)'
17
19
  ActiveRecord::Base.connection.execute 'CREATE TABLE employers (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
18
20
  ActiveRecord::Base.connection.execute 'CREATE TABLE employees (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
@@ -100,6 +102,7 @@ class ParanoiaTest < Test::Unit::TestCase
100
102
  model.destroy
101
103
 
102
104
  assert_equal false, model.deleted_at.nil?
105
+ assert_equal false, model.really_destroyed?
103
106
 
104
107
  assert_equal 0, model.class.count
105
108
  assert_equal 1, model.class.unscoped.count
@@ -250,6 +253,20 @@ class ParanoiaTest < Test::Unit::TestCase
250
253
  assert_equal 1, ParanoidModel.unscoped.where(id: model.id).count
251
254
  end
252
255
 
256
+ def test_destroy_return_value_on_success
257
+ model = ParanoidModel.create
258
+ return_value = model.destroy
259
+
260
+ assert_equal(return_value, model)
261
+ end
262
+
263
+ def test_destroy_return_value_on_failure
264
+ model = FailCallbackModel.create
265
+ return_value = model.destroy
266
+
267
+ assert_equal(return_value, false)
268
+ end
269
+
253
270
  def test_restore_behavior_for_callbacks
254
271
  model = CallbackModel.new
255
272
  model.save
@@ -265,7 +282,7 @@ class ParanoiaTest < Test::Unit::TestCase
265
282
  assert model.instance_variable_get(:@restore_callback_called)
266
283
  end
267
284
 
268
- def test_real_destroy
285
+ def test_really_destroy
269
286
  model = ParanoidModel.new
270
287
  model.save
271
288
  model.destroy!
@@ -273,6 +290,40 @@ class ParanoiaTest < Test::Unit::TestCase
273
290
  assert_equal 0, ParanoidModel.unscoped.where(id: model.id).count
274
291
  end
275
292
 
293
+ def test_really_destroyed
294
+ model = ParanoidModel.new
295
+ model.save
296
+ model.destroy!
297
+
298
+ assert model.really_destroyed?
299
+ end
300
+
301
+ def test_real_destroy_dependent_destroy
302
+ parent = ParentModel.create
303
+ child = parent.very_related_models.create
304
+ parent.really_destroy!
305
+ refute RelatedModel.unscoped.exists?(child.id)
306
+ end
307
+
308
+ def test_real_destroy_dependent_destroy_after_normal_destroy
309
+ parent = ParentModel.create
310
+ child = parent.very_related_models.create
311
+ parent.destroy
312
+ parent.really_destroy!
313
+ refute RelatedModel.unscoped.exists?(child.id)
314
+ end
315
+
316
+ def test_real_destroy_dependent_destroy_after_normal_destroy_does_not_delete_other_children
317
+ parent_1 = ParentModel.create
318
+ child_1 = parent_1.very_related_models.create
319
+
320
+ parent_2 = ParentModel.create
321
+ child_2 = parent_2.very_related_models.create
322
+ parent_1.destroy
323
+ parent_1.really_destroy!
324
+ assert RelatedModel.unscoped.exists?(child_2.id)
325
+ end
326
+
276
327
  def test_real_delete
277
328
  model = ParanoidModel.new
278
329
  model.save
@@ -335,6 +386,39 @@ class ParanoiaTest < Test::Unit::TestCase
335
386
  assert_equal true, second_child.destroyed?
336
387
  end
337
388
 
389
+ # regression tests for #118
390
+ def test_restore_with_has_one_association
391
+ # setup and destroy test objects
392
+ hasOne = ParanoidModelWithHasOne.create
393
+ belongsTo = ParanoidModelWithBelong.create
394
+ hasOne.paranoid_model_with_belong = belongsTo
395
+ hasOne.save!
396
+
397
+ hasOne.destroy
398
+ assert_equal false, hasOne.deleted_at.nil?
399
+ assert_equal false, belongsTo.deleted_at.nil?
400
+
401
+ # Does it restore has_one associations?
402
+ hasOne.restore(:recursive => true)
403
+ hasOne.save!
404
+
405
+ assert_equal true, hasOne.reload.deleted_at.nil?
406
+ assert_equal true, belongsTo.reload.deleted_at.nil?, "#{belongsTo.deleted_at}"
407
+ assert ParanoidModelWithBelong.with_deleted.reload.count != 0, "There should be a record"
408
+ end
409
+
410
+ def test_restore_with_nil_has_one_association
411
+ # setup and destroy test object
412
+ hasOne = ParanoidModelWithHasOne.create
413
+ hasOne.destroy
414
+ assert_equal false, hasOne.reload.deleted_at.nil?
415
+
416
+ # Does it raise NoMethodException on restore of nil
417
+ hasOne.restore(:recursive => true)
418
+
419
+ assert hasOne.reload.deleted_at.nil?
420
+ end
421
+
338
422
  def test_observers_notified
339
423
  a = ParanoidModelWithObservers.create
340
424
  a.destroy
@@ -368,6 +452,13 @@ class ParanoidModel < ActiveRecord::Base
368
452
  acts_as_paranoid
369
453
  end
370
454
 
455
+ class FailCallbackModel < ActiveRecord::Base
456
+ belongs_to :parent_model
457
+ acts_as_paranoid
458
+
459
+ before_destroy { |_| false }
460
+ end
461
+
371
462
  class FeaturefulModel < ActiveRecord::Base
372
463
  acts_as_paranoid
373
464
  validates :name, :presence => true, :uniqueness => true
@@ -443,3 +534,13 @@ end
443
534
  class ParanoidModelWithoutObservers < ParanoidModel
444
535
  self.class.send(remove_method :notify_observers) if method_defined?(:notify_observers)
445
536
  end
537
+
538
+ # refer back to regression test for #118
539
+ class ParanoidModelWithHasOne < ParanoidModel
540
+ has_one :paranoid_model_with_belong, :dependent => :destroy
541
+ end
542
+
543
+ class ParanoidModelWithBelong < ActiveRecord::Base
544
+ acts_as_paranoid
545
+ belongs_to :paranoid_model_with_has_one
546
+ 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: 1.3.3
4
+ version: 1.3.4
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: 2014-01-16 00:00:00.000000000 Z
11
+ date: 2015-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.2'
19
+ version: '3.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '3.2'
26
+ version: '3.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -70,6 +70,7 @@ files:
70
70
  - README.md
71
71
  - Rakefile
72
72
  - lib/paranoia.rb
73
+ - lib/paranoia/rspec.rb
73
74
  - lib/paranoia/version.rb
74
75
  - paranoia.gemspec
75
76
  - test/paranoia_test.rb
@@ -92,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
93
  version: 1.3.6
93
94
  requirements: []
94
95
  rubyforge_project: paranoia
95
- rubygems_version: 2.2.0
96
+ rubygems_version: 2.2.2
96
97
  signing_key:
97
98
  specification_version: 4
98
99
  summary: Paranoia is a re-implementation of acts_as_paranoid for Rails 3, using much,