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 +4 -4
- data/README.md +5 -4
- data/lib/paranoia.rb +41 -9
- data/lib/paranoia/rspec.rb +13 -0
- data/lib/paranoia/version.rb +1 -1
- data/paranoia.gemspec +1 -1
- data/test/paranoia_test.rb +102 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bf4edf194a7572da8b0edc79105f4c62a37bfd2
|
4
|
+
data.tar.gz: dd084504c6c843de8ab3f7970570db894feac1ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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 => '
|
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
|
```
|
data/lib/paranoia.rb
CHANGED
@@ -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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
data/lib/paranoia/version.rb
CHANGED
data/paranoia.gemspec
CHANGED
@@ -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.
|
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'
|
data/test/paranoia_test.rb
CHANGED
@@ -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
|
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.
|
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:
|
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.
|
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.
|
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.
|
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,
|