paranoia 1.3.3 → 1.3.4

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
  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,