acts_as_paranoid 0.4.2 → 0.4.3.pre

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4045fd67f66b6cbbaae31fce07b7e6f5cadafd77
4
+ data.tar.gz: e97fdbb51832ea05c1704627a4ce43cce1d2a824
5
+ SHA512:
6
+ metadata.gz: 01ff43ed7501da46e4c2dcd0f595ac689f26a578a8e11fdaa250f393befb9ac51ec07117ff9b7970ef67ef050c9212d504702be63bb7b9558c320fe3fcb942ca
7
+ data.tar.gz: 6075e2f197eb0c8545c3eb3e772079976f4485b34c1b3e1f65ae496da85dde0c5b275c0b2c3812918e59e2a58b0cfad3d0759675164852165a81b113eb9d8d1c
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Gonçalo Silva
1
+ Copyright (c) 2014 Zachary Scott, Gonçalo Silva, Rick Olson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -1,15 +1,11 @@
1
1
  # ActsAsParanoid
2
2
 
3
+ [![Build Status](https://travis-ci.org/zzak/acts_as_paranoid.png?branch=master)](https://travis-ci.org/zzak/acts_as_paranoid)
4
+
3
5
  A simple plugin which hides records instead of deleting them, being able to recover them.
4
6
 
5
7
  **This branch targets Rails 3.2.** If you're working with another version, switch to the corresponding branch.
6
8
 
7
- ## Credits
8
-
9
- This plugin was inspired by [acts_as_paranoid](http://github.com/technoweenie/acts_as_paranoid) and [acts_as_active](http://github.com/fernandoluizao/acts_as_active).
10
-
11
- While porting it to Rails 3, I decided to apply the ideas behind those plugins to an unified solution while removing a **lot** of the complexity found in them. I eventually ended up writing a new plugin from scratch.
12
-
13
9
  ## Usage
14
10
 
15
11
  You can enable ActsAsParanoid like this:
@@ -222,20 +218,21 @@ This gem supports the most recent versions of Rails and Ruby.
222
218
 
223
219
  For Rails 3.2 check the README at the [rails3.2](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.2) branch and add this to your Gemfile:
224
220
 
225
- gem "acts_as_paranoid", "~>0.4.0"
221
+ gem "acts_as_paranoid", "~> 0.4.0"
226
222
 
227
223
  For Rails 3.1 check the README at the [rails3.1](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.1) branch and add this to your Gemfile:
228
224
 
229
- gem "rails3_acts_as_paranoid", "~>0.1.4"
225
+ gem "rails3_acts_as_paranoid", "~>0.1.4"
230
226
 
231
227
  For Rails 3.0 check the README at the [rails3.0](https://github.com/goncalossilva/rails3_acts_as_paranoid/tree/rails3.0) branch and add this to your Gemfile:
232
228
 
233
- gem "rails3_acts_as_paranoid", "~>0.0.9"
229
+ gem "rails3_acts_as_paranoid", "~>0.0.9"
234
230
 
235
231
 
236
232
  ## Ruby
237
233
 
238
- This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode). It *might* work fine in 1.8, but it's not officially supported.
234
+ This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode).
235
+
239
236
 
240
237
  # Acknowledgements
241
238
 
@@ -247,4 +244,11 @@ This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode). It *might
247
244
  * To [Craig Walker](https://github.com/softcraft-development) for Rails 3.1 support and fixing various pending issues
248
245
  * To [Charles G.](https://github.com/chuckg) for Rails 3.2 support and for making a desperately needed global code refactoring
249
246
 
250
- Copyright © 2010 Gonçalo Silva, released under the MIT license
247
+ ## Credits
248
+
249
+ This plugin was inspired by [acts_as_paranoid](http://github.com/technoweenie/acts_as_paranoid) and [acts_as_active](http://github.com/fernandoluizao/acts_as_active).
250
+
251
+ While porting it to Rails 3, I decided to apply the ideas behind those plugins to an unified solution while removing a **lot** of the complexity found in them. I eventually ended up writing a new plugin from scratch.
252
+
253
+
254
+ Copyright © 2014 Zachary Scott, Gonçalo Silva, Rick Olson, released under the MIT license
@@ -1,27 +1,23 @@
1
- require 'active_record/base'
2
- require 'active_record/relation'
3
- require 'active_record/callbacks'
4
1
  require 'acts_as_paranoid/core'
5
2
  require 'acts_as_paranoid/associations'
6
3
  require 'acts_as_paranoid/validations'
7
4
  require 'acts_as_paranoid/relation'
8
5
 
9
-
10
6
  module ActsAsParanoid
11
-
7
+
12
8
  def paranoid?
13
9
  self.included_modules.include?(ActsAsParanoid::Core)
14
10
  end
15
-
11
+
16
12
  def validates_as_paranoid
17
13
  include ActsAsParanoid::Validations
18
14
  end
19
-
15
+
20
16
  def acts_as_paranoid(options = {})
21
17
  raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
22
-
18
+
23
19
  class_attribute :paranoid_configuration, :paranoid_column_reference
24
-
20
+
25
21
  self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes }
26
22
  self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
27
23
  self.paranoid_configuration.merge!(options) # user options
@@ -29,17 +25,14 @@ module ActsAsParanoid
29
25
  raise ArgumentError, "'time', 'boolean' or 'string' expected for :column_type option, got #{paranoid_configuration[:column_type]}" unless ['time', 'boolean', 'string'].include? paranoid_configuration[:column_type]
30
26
 
31
27
  self.paranoid_column_reference = "#{self.table_name}.#{paranoid_configuration[:column]}"
32
-
28
+
33
29
  return if paranoid?
34
-
30
+
35
31
  include ActsAsParanoid::Core
36
-
32
+
37
33
  # Magic!
38
34
  default_scope { where(paranoid_default_scope_sql) }
39
35
 
40
- # The paranoid column should not be mass-assignable
41
- attr_protected paranoid_configuration[:column]
42
-
43
36
  if paranoid_configuration[:column_type] == 'time'
44
37
  scope :deleted_inside_time_window, lambda {|time, window|
45
38
  deleted_after_time((time - window)).deleted_before_time((time + window))
@@ -134,17 +134,17 @@ module ActsAsParanoid
134
134
 
135
135
  def recover_dependent_associations(window, options)
136
136
  self.class.dependent_associations.each do |reflection|
137
- next unless reflection.klass.paranoid?
137
+ next unless (klass = get_reflection_class(reflection)).paranoid?
138
138
 
139
- scope = reflection.klass.only_deleted
139
+ scope = klass.only_deleted
140
140
 
141
141
  # Merge in the association's scope
142
142
  scope = scope.merge(association(reflection.name).association_scope)
143
143
 
144
144
  # We can only recover by window if both parent and dependant have a
145
145
  # paranoid column type of :time.
146
- if self.class.paranoid_column_type == :time && reflection.klass.paranoid_column_type == :time
147
- scope = scope.merge(reflection.klass.deleted_inside_time_window(paranoid_value, window))
146
+ if self.class.paranoid_column_type == :time && klass.paranoid_column_type == :time
147
+ scope = scope.merge(klass.deleted_inside_time_window(paranoid_value, window))
148
148
  end
149
149
 
150
150
  scope.each do |object|
@@ -155,9 +155,9 @@ module ActsAsParanoid
155
155
 
156
156
  def destroy_dependent_associations!
157
157
  self.class.dependent_associations.each do |reflection|
158
- next unless reflection.klass.paranoid?
158
+ next unless (klass = get_reflection_class(reflection)).paranoid?
159
159
 
160
- scope = reflection.klass.only_deleted
160
+ scope = klass.only_deleted
161
161
 
162
162
  # Merge in the association's scope
163
163
  scope = scope.merge(association(reflection.name).association_scope)
@@ -177,6 +177,14 @@ module ActsAsParanoid
177
177
 
178
178
  private
179
179
 
180
+ def get_reflection_class(reflection)
181
+ if reflection.macro == :belongs_to && reflection.options.include?(:polymorphic)
182
+ self.send(reflection.foreign_type).constantize
183
+ else
184
+ reflection.klass
185
+ end
186
+ end
187
+
180
188
  def paranoid_value=(value)
181
189
  self.send("#{self.class.paranoid_column}=", value)
182
190
  end
@@ -5,7 +5,7 @@ module ActsAsParanoid
5
5
  def paranoid?
6
6
  klass.try(:paranoid?) ? true : false
7
7
  end
8
-
8
+
9
9
  def paranoid_deletion_attributes
10
10
  { klass.paranoid_column => klass.delete_now_value }
11
11
  end
@@ -18,7 +18,7 @@ module ActsAsParanoid
18
18
  orig_delete_all
19
19
  end
20
20
  end
21
-
21
+
22
22
  def delete_all(conditions = nil)
23
23
  if paranoid?
24
24
  update_all(paranoid_deletion_attributes, conditions)
@@ -0,0 +1,3 @@
1
+ module ActsAsParanoid
2
+ VERSION = "0.4.3.pre"
3
+ end
@@ -0,0 +1,222 @@
1
+ require 'test_helper'
2
+
3
+ class AssociationsTest < ParanoidBaseTest
4
+ def test_removal_with_associations
5
+ # This test shows that the current implementation doesn't handle
6
+ # assciation deletion correctly (when hard deleting via parent-object)
7
+ paranoid_company_1 = ParanoidDestroyCompany.create! :name => "ParanoidDestroyCompany #1"
8
+ paranoid_company_2 = ParanoidDeleteCompany.create! :name => "ParanoidDestroyCompany #1"
9
+ paranoid_company_1.paranoid_products.create! :name => "ParanoidProduct #1"
10
+ paranoid_company_2.paranoid_products.create! :name => "ParanoidProduct #2"
11
+
12
+ assert_equal 1, ParanoidDestroyCompany.count
13
+ assert_equal 1, ParanoidDeleteCompany.count
14
+ assert_equal 2, ParanoidProduct.count
15
+
16
+ ParanoidDestroyCompany.first.destroy
17
+ assert_equal 0, ParanoidDestroyCompany.count
18
+ assert_equal 1, ParanoidProduct.count
19
+ assert_equal 1, ParanoidDestroyCompany.with_deleted.count
20
+ assert_equal 2, ParanoidProduct.with_deleted.count
21
+
22
+ ParanoidDestroyCompany.with_deleted.first.destroy!
23
+ assert_equal 0, ParanoidDestroyCompany.count
24
+ assert_equal 1, ParanoidProduct.count
25
+ assert_equal 0, ParanoidDestroyCompany.with_deleted.count
26
+ assert_equal 1, ParanoidProduct.with_deleted.count
27
+
28
+ ParanoidDeleteCompany.with_deleted.first.destroy!
29
+ assert_equal 0, ParanoidDeleteCompany.count
30
+ assert_equal 0, ParanoidProduct.count
31
+ assert_equal 0, ParanoidDeleteCompany.with_deleted.count
32
+ assert_equal 0, ParanoidProduct.with_deleted.count
33
+ end
34
+
35
+ def test_belongs_to_with_deleted
36
+ paranoid_time = ParanoidTime.first
37
+ paranoid_has_many_dependant = paranoid_time.paranoid_has_many_dependants.create(:name => 'dependant!')
38
+
39
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time
40
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted
41
+
42
+ paranoid_time.destroy
43
+
44
+ assert_nil paranoid_has_many_dependant.paranoid_time(true)
45
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted(true)
46
+ end
47
+
48
+ def test_belongs_to_polymorphic_with_deleted
49
+ paranoid_time = ParanoidTime.first
50
+ paranoid_has_many_dependant = ParanoidHasManyDependant.create!(:name => 'dependant!', :paranoid_time_polymorphic_with_deleted => paranoid_time)
51
+
52
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time
53
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted
54
+
55
+ paranoid_time.destroy
56
+
57
+ assert_nil paranoid_has_many_dependant.paranoid_time(true)
58
+ assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted(true)
59
+ end
60
+
61
+ def test_belongs_to_nil_polymorphic_with_deleted
62
+ paranoid_time = ParanoidTime.first
63
+ paranoid_has_many_dependant = ParanoidHasManyDependant.create!(:name => 'dependant!', :paranoid_time_polymorphic_with_deleted => nil)
64
+
65
+ assert_nil paranoid_has_many_dependant.paranoid_time
66
+ assert_nil paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted
67
+
68
+ paranoid_time.destroy
69
+
70
+ assert_nil paranoid_has_many_dependant.paranoid_time(true)
71
+ assert_nil paranoid_has_many_dependant.paranoid_time_polymorphic_with_deleted(true)
72
+ end
73
+
74
+ def test_belongs_to_options
75
+ paranoid_time = ParanoidHasManyDependant.reflections[:paranoid_time]
76
+ assert_equal :belongs_to, paranoid_time.macro
77
+ assert_nil paranoid_time.options[:with_deleted]
78
+ end
79
+
80
+ def test_belongs_to_with_deleted_options
81
+ paranoid_time_with_deleted = ParanoidHasManyDependant.reflections[:paranoid_time_with_deleted]
82
+ assert_equal :belongs_to, paranoid_time_with_deleted.macro
83
+ assert paranoid_time_with_deleted.options[:with_deleted]
84
+ end
85
+
86
+ def test_belongs_to_polymorphic_with_deleted_options
87
+ paranoid_time_polymorphic_with_deleted = ParanoidHasManyDependant.reflections[:paranoid_time_polymorphic_with_deleted]
88
+ assert_equal :belongs_to, paranoid_time_polymorphic_with_deleted.macro
89
+ assert paranoid_time_polymorphic_with_deleted.options[:with_deleted]
90
+ end
91
+
92
+ def test_only_find_associated_records_when_finding_with_paranoid_deleted
93
+ parent = ParanoidBelongsDependant.create
94
+ child = ParanoidHasManyDependant.create
95
+ parent.paranoid_has_many_dependants << child
96
+
97
+ unrelated_parent = ParanoidBelongsDependant.create
98
+ unrelated_child = ParanoidHasManyDependant.create
99
+ unrelated_parent.paranoid_has_many_dependants << unrelated_child
100
+
101
+ child.destroy
102
+ assert_paranoid_deletion(child)
103
+
104
+ assert_equal [child], parent.paranoid_has_many_dependants.with_deleted.to_a
105
+ end
106
+
107
+ def test_cannot_find_a_paranoid_deleted_many_many_association
108
+ left = ParanoidManyManyParentLeft.create
109
+ right = ParanoidManyManyParentRight.create
110
+ left.paranoid_many_many_parent_rights << right
111
+
112
+ left.paranoid_many_many_parent_rights.delete(right)
113
+
114
+ left.reload
115
+
116
+ assert_equal [], left.paranoid_many_many_children, "Linking objects not deleted"
117
+ assert_equal [], left.paranoid_many_many_parent_rights, "Associated objects not unlinked"
118
+ assert_equal right, ParanoidManyManyParentRight.find(right.id), "Associated object deleted"
119
+ end
120
+
121
+ def test_cannot_find_a_paranoid_destroyed_many_many_association
122
+ left = ParanoidManyManyParentLeft.create
123
+ right = ParanoidManyManyParentRight.create
124
+ left.paranoid_many_many_parent_rights << right
125
+
126
+ left.paranoid_many_many_parent_rights.destroy(right)
127
+
128
+ left.reload
129
+
130
+ assert_equal [], left.paranoid_many_many_children, "Linking objects not deleted"
131
+ assert_equal [], left.paranoid_many_many_parent_rights, "Associated objects not unlinked"
132
+ assert_equal right, ParanoidManyManyParentRight.find(right.id), "Associated object deleted"
133
+ end
134
+
135
+ def test_cannot_find_a_has_many_through_object_when_its_linking_object_is_paranoid_destroyed
136
+ left = ParanoidManyManyParentLeft.create
137
+ right = ParanoidManyManyParentRight.create
138
+ left.paranoid_many_many_parent_rights << right
139
+
140
+ child = left.paranoid_many_many_children.first
141
+
142
+ child.destroy
143
+
144
+ left.reload
145
+
146
+ assert_equal [], left.paranoid_many_many_parent_rights, "Associated objects not deleted"
147
+ end
148
+
149
+ def test_cannot_find_a_paranoid_deleted_model
150
+ model = ParanoidBelongsDependant.create
151
+ model.destroy
152
+
153
+ assert_raises ActiveRecord::RecordNotFound do
154
+ ParanoidBelongsDependant.find(model.id)
155
+ end
156
+ end
157
+
158
+ def test_bidirectional_has_many_through_association_clear_is_paranoid
159
+ left = ParanoidManyManyParentLeft.create
160
+ right = ParanoidManyManyParentRight.create
161
+ left.paranoid_many_many_parent_rights << right
162
+
163
+ child = left.paranoid_many_many_children.first
164
+ assert_equal left, child.paranoid_many_many_parent_left, "Child's left parent is incorrect"
165
+ assert_equal right, child.paranoid_many_many_parent_right, "Child's right parent is incorrect"
166
+
167
+ left.paranoid_many_many_parent_rights.clear
168
+
169
+ assert_paranoid_deletion(child)
170
+ end
171
+
172
+ def test_bidirectional_has_many_through_association_destroy_is_paranoid
173
+ left = ParanoidManyManyParentLeft.create
174
+ right = ParanoidManyManyParentRight.create
175
+ left.paranoid_many_many_parent_rights << right
176
+
177
+ child = left.paranoid_many_many_children.first
178
+ assert_equal left, child.paranoid_many_many_parent_left, "Child's left parent is incorrect"
179
+ assert_equal right, child.paranoid_many_many_parent_right, "Child's right parent is incorrect"
180
+
181
+ left.paranoid_many_many_parent_rights.destroy(right)
182
+
183
+ assert_paranoid_deletion(child)
184
+ end
185
+
186
+ def test_bidirectional_has_many_through_association_delete_is_paranoid
187
+ left = ParanoidManyManyParentLeft.create
188
+ right = ParanoidManyManyParentRight.create
189
+ left.paranoid_many_many_parent_rights << right
190
+
191
+ child = left.paranoid_many_many_children.first
192
+ assert_equal left, child.paranoid_many_many_parent_left, "Child's left parent is incorrect"
193
+ assert_equal right, child.paranoid_many_many_parent_right, "Child's right parent is incorrect"
194
+
195
+ left.paranoid_many_many_parent_rights.delete(right)
196
+
197
+ assert_paranoid_deletion(child)
198
+ end
199
+
200
+ def test_belongs_to_on_normal_model_is_paranoid
201
+ not_paranoid = HasOneNotParanoid.create
202
+ not_paranoid.paranoid_time = ParanoidTime.create
203
+
204
+ assert not_paranoid.save
205
+ assert_not_nil not_paranoid.paranoid_time
206
+ end
207
+
208
+ def test_double_belongs_to_with_deleted
209
+ not_paranoid = DoubleHasOneNotParanoid.create
210
+ not_paranoid.paranoid_time = ParanoidTime.create
211
+
212
+ assert not_paranoid.save
213
+ assert_not_nil not_paranoid.paranoid_time
214
+ end
215
+
216
+ def test_mass_assignment_of_paranoid_column_enabled
217
+ now = Time.now
218
+ record = ParanoidTime.create! :name => 'Foo', :deleted_at => now
219
+ assert_equal 'Foo', record.name
220
+ assert_equal now, record.deleted_at
221
+ end
222
+ end
@@ -0,0 +1,387 @@
1
+ require 'test_helper'
2
+
3
+ class ParanoidTest < ParanoidBaseTest
4
+ def test_paranoid?
5
+ assert !NotParanoid.paranoid?
6
+ assert_raise(NoMethodError) { NotParanoid.delete_all! }
7
+ assert_raise(NoMethodError) { NotParanoid.first.destroy! }
8
+ assert_raise(NoMethodError) { NotParanoid.with_deleted }
9
+ assert_raise(NoMethodError) { NotParanoid.only_deleted }
10
+
11
+ assert ParanoidTime.paranoid?
12
+ end
13
+
14
+ def test_scope_inclusion_with_time_column_type
15
+ assert ParanoidTime.respond_to?(:deleted_inside_time_window)
16
+ assert ParanoidTime.respond_to?(:deleted_before_time)
17
+ assert ParanoidTime.respond_to?(:deleted_after_time)
18
+
19
+ assert !ParanoidBoolean.respond_to?(:deleted_inside_time_window)
20
+ assert !ParanoidBoolean.respond_to?(:deleted_before_time)
21
+ assert !ParanoidBoolean.respond_to?(:deleted_after_time)
22
+ end
23
+
24
+ def test_fake_removal
25
+ assert_equal 3, ParanoidTime.count
26
+ assert_equal 3, ParanoidBoolean.count
27
+ assert_equal 1, ParanoidString.count
28
+
29
+ ParanoidTime.first.destroy
30
+ ParanoidBoolean.delete_all("name = 'paranoid' OR name = 'really paranoid'")
31
+ ParanoidString.first.destroy
32
+ assert_equal 2, ParanoidTime.count
33
+ assert_equal 1, ParanoidBoolean.count
34
+ assert_equal 0, ParanoidString.count
35
+ assert_equal 1, ParanoidTime.only_deleted.count
36
+ assert_equal 2, ParanoidBoolean.only_deleted.count
37
+ assert_equal 1, ParanoidString.only_deleted.count
38
+ assert_equal 3, ParanoidTime.with_deleted.count
39
+ assert_equal 3, ParanoidBoolean.with_deleted.count
40
+ assert_equal 1, ParanoidString.with_deleted.count
41
+ end
42
+
43
+ def test_real_removal
44
+ ParanoidTime.first.destroy!
45
+ ParanoidBoolean.delete_all!("name = 'extremely paranoid' OR name = 'really paranoid'")
46
+ ParanoidString.first.destroy!
47
+ assert_equal 2, ParanoidTime.count
48
+ assert_equal 1, ParanoidBoolean.count
49
+ assert_equal 0, ParanoidString.count
50
+ assert_equal 2, ParanoidTime.with_deleted.count
51
+ assert_equal 1, ParanoidBoolean.with_deleted.count
52
+ assert_equal 0, ParanoidString.with_deleted.count
53
+ assert_equal 0, ParanoidTime.only_deleted.count
54
+ assert_equal 0, ParanoidBoolean.only_deleted.count
55
+ assert_equal 0, ParanoidString.only_deleted.count
56
+
57
+ ParanoidTime.first.destroy
58
+ ParanoidTime.only_deleted.first.destroy
59
+ assert_equal 0, ParanoidTime.only_deleted.count
60
+
61
+ ParanoidTime.delete_all!
62
+ assert_empty ParanoidTime.all
63
+ assert_empty ParanoidTime.with_deleted.all
64
+ end
65
+
66
+ def test_non_persisted_destroy
67
+ pt = ParanoidTime.new
68
+ assert_nil pt.paranoid_value
69
+ pt.destroy
70
+ assert_not_nil pt.paranoid_value
71
+ end
72
+
73
+ def test_non_persisted_destroy!
74
+ pt = ParanoidTime.new
75
+ assert_nil pt.paranoid_value
76
+ pt.destroy!
77
+ assert_not_nil pt.paranoid_value
78
+ end
79
+
80
+ def test_recovery
81
+ assert_equal 3, ParanoidBoolean.count
82
+ ParanoidBoolean.first.destroy
83
+ assert_equal 2, ParanoidBoolean.count
84
+ ParanoidBoolean.only_deleted.first.recover
85
+ assert_equal 3, ParanoidBoolean.count
86
+
87
+ assert_equal 1, ParanoidString.count
88
+ ParanoidString.first.destroy
89
+ assert_equal 0, ParanoidString.count
90
+ ParanoidString.with_deleted.first.recover
91
+ assert_equal 1, ParanoidString.count
92
+ end
93
+
94
+ def setup_recursive_tests
95
+ @paranoid_time_object = ParanoidTime.first
96
+
97
+ # Create one extra ParanoidHasManyDependant record so that we can validate
98
+ # the correct dependants are recovered.
99
+ ParanoidTime.where('id <> ?', @paranoid_time_object.id).first.paranoid_has_many_dependants.create(:name => "should not be recovered").destroy
100
+
101
+ @paranoid_boolean_count = ParanoidBoolean.count
102
+
103
+ assert_equal 0, ParanoidHasManyDependant.count
104
+ assert_equal 0, ParanoidBelongsDependant.count
105
+ assert_equal 1, NotParanoid.count
106
+
107
+ (1..3).each do |i|
108
+ has_many_object = @paranoid_time_object.paranoid_has_many_dependants.create(:name => "has_many_#{i}")
109
+ has_many_object.create_paranoid_belongs_dependant(:name => "belongs_to_#{i}")
110
+ has_many_object.save
111
+
112
+ paranoid_boolean = @paranoid_time_object.paranoid_booleans.create(:name => "boolean_#{i}")
113
+ paranoid_boolean.create_paranoid_has_one_dependant(:name => "has_one_#{i}")
114
+ paranoid_boolean.save
115
+
116
+ @paranoid_time_object.not_paranoids.create(:name => "not_paranoid_a#{i}")
117
+
118
+ end
119
+
120
+ @paranoid_time_object.create_not_paranoid(:name => "not_paranoid_belongs_to")
121
+ @paranoid_time_object.create_has_one_not_paranoid(:name => "has_one_not_paranoid")
122
+
123
+ assert_equal 3, ParanoidTime.count
124
+ assert_equal 3, ParanoidHasManyDependant.count
125
+ assert_equal 3, ParanoidBelongsDependant.count
126
+ assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.count
127
+ assert_equal 3, ParanoidHasOneDependant.count
128
+ assert_equal 5, NotParanoid.count
129
+ assert_equal 1, HasOneNotParanoid.count
130
+ end
131
+
132
+ def test_recursive_fake_removal
133
+ setup_recursive_tests
134
+
135
+ @paranoid_time_object.destroy
136
+
137
+ assert_equal 2, ParanoidTime.count
138
+ assert_equal 0, ParanoidHasManyDependant.count
139
+ assert_equal 0, ParanoidBelongsDependant.count
140
+ assert_equal @paranoid_boolean_count, ParanoidBoolean.count
141
+ assert_equal 0, ParanoidHasOneDependant.count
142
+ assert_equal 1, NotParanoid.count
143
+ assert_equal 0, HasOneNotParanoid.count
144
+ end
145
+
146
+ def test_recursive_real_removal
147
+ setup_recursive_tests
148
+
149
+ @paranoid_time_object.destroy!
150
+
151
+ assert_equal 0, ParanoidTime.only_deleted.count
152
+ assert_equal 1, ParanoidHasManyDependant.only_deleted.count
153
+ assert_equal 0, ParanoidBelongsDependant.only_deleted.count
154
+ assert_equal 0, ParanoidBoolean.only_deleted.count
155
+ assert_equal 0, ParanoidHasOneDependant.only_deleted.count
156
+ assert_equal 1, NotParanoid.count
157
+ assert_equal 0, HasOneNotParanoid.count
158
+ end
159
+
160
+ def test_recursive_recovery
161
+ setup_recursive_tests
162
+
163
+ @paranoid_time_object.destroy
164
+ @paranoid_time_object.reload
165
+
166
+ @paranoid_time_object.recover(:recursive => true)
167
+
168
+ assert_equal 3, ParanoidTime.count
169
+ assert_equal 3, ParanoidHasManyDependant.count
170
+ assert_equal 3, ParanoidBelongsDependant.count
171
+ assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.count
172
+ assert_equal 3, ParanoidHasOneDependant.count
173
+ assert_equal 1, NotParanoid.count
174
+ assert_equal 0, HasOneNotParanoid.count
175
+ end
176
+
177
+ def test_recursive_recovery_dependant_window
178
+ setup_recursive_tests
179
+
180
+ @paranoid_time_object.destroy
181
+ @paranoid_time_object.reload
182
+
183
+ # Stop the following from recovering:
184
+ # - ParanoidHasManyDependant and its ParanoidBelongsDependant
185
+ # - A single ParanoidBelongsDependant, but not its parent
186
+ dependants = @paranoid_time_object.paranoid_has_many_dependants.with_deleted
187
+ dependants.first.update_attribute(:deleted_at, 2.days.ago)
188
+ ParanoidBelongsDependant.with_deleted.where(:id => dependants.last.paranoid_belongs_dependant_id).first.update_attribute(:deleted_at, 1.hour.ago)
189
+
190
+ @paranoid_time_object.recover(:recursive => true)
191
+
192
+ assert_equal 3, ParanoidTime.count
193
+ assert_equal 2, ParanoidHasManyDependant.count
194
+ assert_equal 1, ParanoidBelongsDependant.count
195
+ assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.count
196
+ assert_equal 3, ParanoidHasOneDependant.count
197
+ assert_equal 1, NotParanoid.count
198
+ assert_equal 0, HasOneNotParanoid.count
199
+ end
200
+
201
+ def test_recursive_recovery_for_belongs_to_polymorphic
202
+ child_1 = ParanoidAndroid.create
203
+ section_1 = ParanoidSection.create(:paranoid_thing => child_1)
204
+
205
+ child_2 = ParanoidHuman.create(:gender => 'male')
206
+ section_2 = ParanoidSection.create(:paranoid_thing => child_2)
207
+
208
+ assert_equal section_1.paranoid_thing, child_1
209
+ assert_equal section_1.paranoid_thing.class, ParanoidAndroid
210
+ assert_equal section_2.paranoid_thing, child_2
211
+ assert_equal section_2.paranoid_thing.class, ParanoidHuman
212
+
213
+ parent = ParanoidTime.create(:name => "paranoid_parent")
214
+ parent.paranoid_sections << section_1
215
+ parent.paranoid_sections << section_2
216
+
217
+ assert_equal 4, ParanoidTime.count
218
+ assert_equal 2, ParanoidSection.count
219
+ assert_equal 1, ParanoidAndroid.count
220
+ assert_equal 1, ParanoidHuman.count
221
+
222
+ parent.destroy
223
+
224
+ assert_equal 3, ParanoidTime.count
225
+ assert_equal 0, ParanoidSection.count
226
+ assert_equal 0, ParanoidAndroid.count
227
+ assert_equal 0, ParanoidHuman.count
228
+
229
+ parent.reload
230
+ parent.recover
231
+
232
+ assert_equal 4, ParanoidTime.count
233
+ assert_equal 2, ParanoidSection.count
234
+ assert_equal 1, ParanoidAndroid.count
235
+ assert_equal 1, ParanoidHuman.count
236
+ end
237
+
238
+ def test_non_recursive_recovery
239
+ setup_recursive_tests
240
+
241
+ @paranoid_time_object.destroy
242
+ @paranoid_time_object.reload
243
+
244
+ @paranoid_time_object.recover(:recursive => false)
245
+
246
+ assert_equal 3, ParanoidTime.count
247
+ assert_equal 0, ParanoidHasManyDependant.count
248
+ assert_equal 0, ParanoidBelongsDependant.count
249
+ assert_equal @paranoid_boolean_count, ParanoidBoolean.count
250
+ assert_equal 0, ParanoidHasOneDependant.count
251
+ assert_equal 1, NotParanoid.count
252
+ assert_equal 0, HasOneNotParanoid.count
253
+ end
254
+
255
+ def test_deleted?
256
+ ParanoidTime.first.destroy
257
+ assert ParanoidTime.with_deleted.first.deleted?
258
+
259
+ ParanoidString.first.destroy
260
+ assert ParanoidString.with_deleted.first.deleted?
261
+ end
262
+
263
+ def test_paranoid_destroy_callbacks
264
+ @paranoid_with_callback = ParanoidWithCallback.first
265
+ ParanoidWithCallback.transaction do
266
+ @paranoid_with_callback.destroy
267
+ end
268
+
269
+ assert @paranoid_with_callback.called_before_destroy
270
+ assert @paranoid_with_callback.called_after_destroy
271
+ assert @paranoid_with_callback.called_after_commit_on_destroy
272
+ end
273
+
274
+ def test_hard_destroy_callbacks
275
+ @paranoid_with_callback = ParanoidWithCallback.first
276
+
277
+ ParanoidWithCallback.transaction do
278
+ @paranoid_with_callback.destroy!
279
+ end
280
+
281
+ assert @paranoid_with_callback.called_before_destroy
282
+ assert @paranoid_with_callback.called_after_destroy
283
+ assert @paranoid_with_callback.called_after_commit_on_destroy
284
+ end
285
+
286
+ def test_recovery_callbacks
287
+ @paranoid_with_callback = ParanoidWithCallback.first
288
+
289
+ ParanoidWithCallback.transaction do
290
+ @paranoid_with_callback.destroy
291
+
292
+ assert_nil @paranoid_with_callback.called_before_recover
293
+ assert_nil @paranoid_with_callback.called_after_recover
294
+
295
+ @paranoid_with_callback.recover
296
+ end
297
+
298
+ assert @paranoid_with_callback.called_before_recover
299
+ assert @paranoid_with_callback.called_after_recover
300
+ end
301
+
302
+ def test_delete_by_multiple_id_is_paranoid
303
+ model_a = ParanoidBelongsDependant.create
304
+ model_b = ParanoidBelongsDependant.create
305
+ ParanoidBelongsDependant.delete([model_a.id, model_b.id])
306
+
307
+ assert_paranoid_deletion(model_a)
308
+ assert_paranoid_deletion(model_b)
309
+ end
310
+
311
+ def test_destroy_by_multiple_id_is_paranoid
312
+ model_a = ParanoidBelongsDependant.create
313
+ model_b = ParanoidBelongsDependant.create
314
+ ParanoidBelongsDependant.destroy([model_a.id, model_b.id])
315
+
316
+ assert_paranoid_deletion(model_a)
317
+ assert_paranoid_deletion(model_b)
318
+ end
319
+
320
+ def test_delete_by_single_id_is_paranoid
321
+ model = ParanoidBelongsDependant.create
322
+ ParanoidBelongsDependant.delete(model.id)
323
+
324
+ assert_paranoid_deletion(model)
325
+ end
326
+
327
+ def test_destroy_by_single_id_is_paranoid
328
+ model = ParanoidBelongsDependant.create
329
+ ParanoidBelongsDependant.destroy(model.id)
330
+
331
+ assert_paranoid_deletion(model)
332
+ end
333
+
334
+ def test_instance_delete_is_paranoid
335
+ model = ParanoidBelongsDependant.create
336
+ model.delete
337
+
338
+ assert_paranoid_deletion(model)
339
+ end
340
+
341
+ def test_instance_destroy_is_paranoid
342
+ model = ParanoidBelongsDependant.create
343
+ model.destroy
344
+
345
+ assert_paranoid_deletion(model)
346
+ end
347
+
348
+ # Test string type columns that don't have a nil value when not deleted (Y/N for example)
349
+ def test_string_type_with_no_nil_value_before_destroy
350
+ ps = ParanoidString.create!(:deleted => 'not dead')
351
+ assert_equal 1, ParanoidString.where(:id => ps).count
352
+ end
353
+
354
+ def test_string_type_with_no_nil_value_after_destroy
355
+ ps = ParanoidString.create!(:deleted => 'not dead')
356
+ ps.destroy
357
+ assert_equal 0, ParanoidString.where(:id => ps).count
358
+ end
359
+
360
+ def test_string_type_with_no_nil_value_before_destroy_with_deleted
361
+ ps = ParanoidString.create!(:deleted => 'not dead')
362
+ assert_equal 1, ParanoidString.with_deleted.where(:id => ps).count
363
+ end
364
+
365
+ def test_string_type_with_no_nil_value_after_destroy_with_deleted
366
+ ps = ParanoidString.create!(:deleted => 'not dead')
367
+ ps.destroy
368
+ assert_equal 1, ParanoidString.with_deleted.where(:id => ps).count
369
+ end
370
+
371
+ def test_string_type_with_no_nil_value_before_destroy_only_deleted
372
+ ps = ParanoidString.create!(:deleted => 'not dead')
373
+ assert_equal 0, ParanoidString.only_deleted.where(:id => ps).count
374
+ end
375
+
376
+ def test_string_type_with_no_nil_value_after_destroy_only_deleted
377
+ ps = ParanoidString.create!(:deleted => 'not dead')
378
+ ps.destroy
379
+ assert_equal 1, ParanoidString.only_deleted.where(:id => ps).count
380
+ end
381
+
382
+ def test_string_type_with_no_nil_value_after_destroyed_twice
383
+ ps = ParanoidString.create!(:deleted => 'not dead')
384
+ 2.times { ps.destroy }
385
+ assert_equal 0, ParanoidString.with_deleted.where(:id => ps).count
386
+ end
387
+ end