acts_as_paranoid 0.4.2 → 0.4.3.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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