acts_as_paranoid 0.4.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +53 -49
- data/lib/acts_as_paranoid/associations.rb +9 -4
- data/lib/acts_as_paranoid/core.rb +39 -16
- data/lib/acts_as_paranoid/preloader_association.rb +15 -0
- data/lib/acts_as_paranoid/relation.rb +1 -1
- data/lib/acts_as_paranoid/validations.rb +58 -19
- data/lib/acts_as_paranoid/version.rb +1 -1
- data/lib/acts_as_paranoid.rb +6 -1
- data/test/test_associations.rb +21 -7
- data/test/test_core.rb +56 -7
- data/test/test_default_scopes.rb +1 -1
- data/test/test_helper.rb +80 -50
- data/test/test_preloader_association.rb +27 -0
- data/test/test_relations.rb +3 -3
- metadata +44 -13
- data/test/test_observers.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59510a8af45d737dccea2ee97aa62553790c49a2
|
4
|
+
data.tar.gz: d9b52994074e903a7b9d7f4d88f5113d37039f12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75b9853cab16dbce9e96a8976b0337e01fce18b38273e087ec68c39eae4fe3b4d2b347f1ead9f147226db2bced977eee5c4ae5a87658208737ae8ec9bd8e7d25
|
7
|
+
data.tar.gz: 3f6f6a1337fe18092b37f84de3bb08740ab971fa55cb7fb9cb5d7db5551275d498079f8ba38b6cf282e069d529fb0bd32e6765fc364b71dbca23dab90e542d42
|
data/README.md
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
# ActsAsParanoid
|
2
2
|
|
3
|
-
[](https://travis-ci.org/ActsAsParanoid/acts_as_paranoid)
|
4
4
|
|
5
|
-
A
|
5
|
+
A Rails plugin to add soft delete.
|
6
6
|
|
7
|
-
|
7
|
+
This gem can be used to hide records instead of deleting them, making them recoverable later.
|
8
|
+
|
9
|
+
## Support
|
10
|
+
|
11
|
+
**This branch targets Rails 4.x. and 5.x**
|
12
|
+
|
13
|
+
If you're working with another version, switch to the corresponding branch, or require an older version of the `acts_as_paranoid` gem.
|
8
14
|
|
9
15
|
## Usage
|
10
16
|
|
@@ -31,12 +37,18 @@ The values shown are the defaults. While *column* can be anything (as long as it
|
|
31
37
|
|
32
38
|
If your column type is a `string`, you can also specify which value to use when marking an object as deleted by passing `:deleted_value` (default is "deleted"). Any records with a non-matching value in this column will be treated normally (ie: not deleted).
|
33
39
|
|
40
|
+
If your column type is a `boolean`, it is possible to specify `allow_nulls` option which is `true` by default. When set to `false`, entities that have `false` value in this column will be considered not deleted, and those which have `true` will be considered deleted. When `true` everything that has a not-null value will be considered deleted.
|
41
|
+
|
34
42
|
### Filtering
|
35
43
|
|
36
|
-
If a record is deleted by ActsAsParanoid, it won't be retrieved when accessing the database.
|
44
|
+
If a record is deleted by ActsAsParanoid, it won't be retrieved when accessing the database.
|
45
|
+
|
46
|
+
So, `Paranoiac.all` will **not** include the **deleted records**.
|
47
|
+
|
48
|
+
When you want to access them, you have 2 choices:
|
37
49
|
|
38
50
|
```ruby
|
39
|
-
Paranoiac.only_deleted # retrieves the deleted records
|
51
|
+
Paranoiac.only_deleted # retrieves only the deleted records
|
40
52
|
Paranoiac.with_deleted # retrieves all records, deleted or not
|
41
53
|
```
|
42
54
|
|
@@ -61,7 +73,13 @@ paranoiac.destroy!
|
|
61
73
|
Paranoiac.delete_all!(conditions)
|
62
74
|
```
|
63
75
|
|
64
|
-
You can also permanently delete a record by calling `
|
76
|
+
You can also permanently delete a record by calling `destroy_fully!` on the object.
|
77
|
+
|
78
|
+
Alternatively you can permanently delete a record by calling `destroy` or `delete_all` on the object **twice**.
|
79
|
+
|
80
|
+
If a record was already deleted (hidden by `ActsAsParanoid`) and you delete it again, it will be removed from the database.
|
81
|
+
|
82
|
+
Take this example:
|
65
83
|
|
66
84
|
```ruby
|
67
85
|
p = Paranoiac.first
|
@@ -77,7 +95,9 @@ Recovery is easy. Just invoke `recover` on it, like this:
|
|
77
95
|
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover
|
78
96
|
```
|
79
97
|
|
80
|
-
All associations marked as `:dependent => :destroy` are also recursively recovered.
|
98
|
+
All associations marked as `:dependent => :destroy` are also recursively recovered.
|
99
|
+
|
100
|
+
If you would like to disable this behavior, you can call `recover` with the `recursive` option:
|
81
101
|
|
82
102
|
```ruby
|
83
103
|
Paranoiac.only_deleted.where("name = ?", "not dead yet").first.recover(:recursive => false)
|
@@ -91,7 +111,13 @@ class Paranoiac < ActiveRecord::Base
|
|
91
111
|
end
|
92
112
|
```
|
93
113
|
|
94
|
-
By default, dependent records will be recovered if they were deleted within 2 minutes of the object upon which they depend.
|
114
|
+
By default, dependent records will be recovered if they were deleted within 2 minutes of the object upon which they depend.
|
115
|
+
|
116
|
+
This restores the objects to the state before the recursive deletion without restoring other objects that were deleted earlier.
|
117
|
+
|
118
|
+
The behavior is only available when both parent and dependant are using timestamp fields to mark deletion, which is the default behavior.
|
119
|
+
|
120
|
+
This window can be changed with the `dependent_recovery_window` option:
|
95
121
|
|
96
122
|
```ruby
|
97
123
|
class Paranoiac < ActiveRecord::Base
|
@@ -119,9 +145,9 @@ ActiveRecord's built-in uniqueness validation does not account for records delet
|
|
119
145
|
|
120
146
|
```ruby
|
121
147
|
class Paranoiac < ActiveRecord::Base
|
122
|
-
|
123
|
-
|
124
|
-
|
148
|
+
acts_as_paranoid
|
149
|
+
validates_as_paranoid
|
150
|
+
validates_uniqueness_of_without_deleted :name
|
125
151
|
end
|
126
152
|
|
127
153
|
p1 = Paranoiac.create(:name => 'foo')
|
@@ -144,13 +170,15 @@ Paranoiac.with_deleted.first.deleted? #=> true
|
|
144
170
|
|
145
171
|
### Scopes
|
146
172
|
|
147
|
-
As you've probably guessed, `with_deleted` and `only_deleted` are scopes. You can, however, chain them freely with other scopes you might have.
|
173
|
+
As you've probably guessed, `with_deleted` and `only_deleted` are scopes. You can, however, chain them freely with other scopes you might have.
|
174
|
+
|
175
|
+
For example:
|
148
176
|
|
149
177
|
```ruby
|
150
178
|
Paranoiac.pretty.with_deleted
|
151
179
|
```
|
152
180
|
|
153
|
-
is exactly the same as
|
181
|
+
This is exactly the same as:
|
154
182
|
|
155
183
|
```ruby
|
156
184
|
Paranoiac.with_deleted.pretty
|
@@ -160,8 +188,8 @@ You can work freely with scopes and it will just work:
|
|
160
188
|
|
161
189
|
```ruby
|
162
190
|
class Paranoiac < ActiveRecord::Base
|
163
|
-
|
164
|
-
|
191
|
+
acts_as_paranoid
|
192
|
+
scope :pretty, where(:pretty => true)
|
165
193
|
end
|
166
194
|
|
167
195
|
Paranoiac.create(:pretty => true)
|
@@ -179,7 +207,9 @@ Paranoiac.pretty.only_deleted.count #=> 1
|
|
179
207
|
|
180
208
|
### Associations
|
181
209
|
|
182
|
-
Associations are also supported.
|
210
|
+
Associations are also supported.
|
211
|
+
|
212
|
+
From the simplest behaviors you'd expect to more nifty things like the ones mentioned previously or the usage of the `:with_deleted` option with `belongs_to`
|
183
213
|
|
184
214
|
```ruby
|
185
215
|
class Parent < ActiveRecord::Base
|
@@ -191,7 +221,7 @@ class ParanoiacChild < ActiveRecord::Base
|
|
191
221
|
belongs_to :parent
|
192
222
|
|
193
223
|
# You may need to provide a foreign_key like this
|
194
|
-
belongs_to :parent_including_deleted, :class_name => "Parent", foreign_key => 'parent_id', :with_deleted => true
|
224
|
+
belongs_to :parent_including_deleted, :class_name => "Parent", :foreign_key => 'parent_id', :with_deleted => true
|
195
225
|
end
|
196
226
|
|
197
227
|
parent = Parent.first
|
@@ -212,32 +242,9 @@ Watch out for these caveats:
|
|
212
242
|
- You cannot name association `*_with_deleted`
|
213
243
|
- `unscoped` will return all records, deleted or not
|
214
244
|
|
215
|
-
# Support
|
216
|
-
|
217
|
-
This gem supports the most recent versions of Rails and Ruby.
|
218
|
-
|
219
|
-
## Rails
|
220
|
-
|
221
|
-
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:
|
222
|
-
|
223
|
-
gem "acts_as_paranoid", "~> 0.4.0"
|
224
|
-
|
225
|
-
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:
|
226
|
-
|
227
|
-
gem "rails3_acts_as_paranoid", "~>0.1.4"
|
228
|
-
|
229
|
-
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:
|
230
|
-
|
231
|
-
gem "rails3_acts_as_paranoid", "~>0.0.9"
|
232
|
-
|
233
|
-
|
234
|
-
## Ruby
|
235
|
-
|
236
|
-
This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode).
|
237
|
-
|
238
|
-
|
239
245
|
# Acknowledgements
|
240
246
|
|
247
|
+
* To [Rick Olson](https://github.com/technoweenie) for creating acts_as_paranoid
|
241
248
|
* To [cheerfulstoic](https://github.com/cheerfulstoic) for adding recursive recovery
|
242
249
|
* To [Jonathan Vaught](https://github.com/gravelpup) for adding paranoid validations
|
243
250
|
* To [Geoffrey Hichborn](https://github.com/phene) for improving the overral code quality and adding support for after_commit
|
@@ -245,12 +252,9 @@ This gem is tested on Ruby 1.9, JRuby and Rubinius (both in 1.9 mode).
|
|
245
252
|
* To [vikramdhillon](https://github.com/vikramdhillon) for the idea and initial implementation of support for string column type
|
246
253
|
* To [Craig Walker](https://github.com/softcraft-development) for Rails 3.1 support and fixing various pending issues
|
247
254
|
* To [Charles G.](https://github.com/chuckg) for Rails 3.2 support and for making a desperately needed global code refactoring
|
255
|
+
* To [Gonçalo Silva](https://github.com/goncalossilva) for supporting this gem prior to v0.4.3
|
256
|
+
* To [Jean Boussier](https://github.com/byroot) for initial Rails 4.0.0 support
|
257
|
+
* To [Matijs van Zuijlen](https://github.com/mvz) for Rails 4.1 and 4.2 support
|
258
|
+
* To [Andrey Ponomarenko](https://github.com/sjke) for Rails 5 support
|
248
259
|
|
249
|
-
|
250
|
-
|
251
|
-
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).
|
252
|
-
|
253
|
-
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.
|
254
|
-
|
255
|
-
|
256
|
-
Copyright © 2014 Zachary Scott, Gonçalo Silva, Rick Olson, released under the MIT license
|
260
|
+
See `LICENSE`.
|
@@ -8,12 +8,17 @@ module ActsAsParanoid
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
-
def belongs_to_with_deleted(target, options = {})
|
12
|
-
with_deleted = options.delete(:with_deleted)
|
13
|
-
result = belongs_to_without_deleted(target, options)
|
11
|
+
def belongs_to_with_deleted(target, scope = nil, options = {})
|
12
|
+
with_deleted = (scope.is_a?(Hash) ? scope : options).delete(:with_deleted)
|
13
|
+
result = belongs_to_without_deleted(target, scope, options)
|
14
14
|
|
15
15
|
if with_deleted
|
16
|
-
result.
|
16
|
+
if result.is_a? Hash
|
17
|
+
result.values.last.options[:with_deleted] = with_deleted
|
18
|
+
else
|
19
|
+
result.options[:with_deleted] = with_deleted
|
20
|
+
end
|
21
|
+
|
17
22
|
unless method_defined? "#{target}_with_unscoped"
|
18
23
|
class_eval <<-RUBY, __FILE__, __LINE__
|
19
24
|
def #{target}_with_unscoped(*args)
|
@@ -23,9 +23,11 @@ module ActsAsParanoid
|
|
23
23
|
|
24
24
|
def only_deleted
|
25
25
|
if string_type_with_deleted_value?
|
26
|
-
without_paranoid_default_scope.where(
|
26
|
+
without_paranoid_default_scope.where(paranoid_column_reference => paranoid_configuration[:deleted_value])
|
27
|
+
elsif boolean_type_not_nullable?
|
28
|
+
without_paranoid_default_scope.where(paranoid_column_reference => true)
|
27
29
|
else
|
28
|
-
without_paranoid_default_scope.where(
|
30
|
+
without_paranoid_default_scope.where.not(paranoid_column_reference => nil)
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
@@ -34,16 +36,17 @@ module ActsAsParanoid
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def delete_all(conditions = nil)
|
37
|
-
update_all
|
39
|
+
where(conditions).update_all(["#{paranoid_configuration[:column]} = ?", delete_now_value])
|
38
40
|
end
|
39
41
|
|
40
|
-
def
|
42
|
+
def paranoid_default_scope
|
41
43
|
if string_type_with_deleted_value?
|
42
|
-
self.
|
43
|
-
or(self.
|
44
|
-
|
44
|
+
self.all.table[paranoid_column].eq(nil).
|
45
|
+
or(self.all.table[paranoid_column].not_eq(paranoid_configuration[:deleted_value]))
|
46
|
+
elsif boolean_type_not_nullable?
|
47
|
+
self.all.table[paranoid_column].eq(false)
|
45
48
|
else
|
46
|
-
self.
|
49
|
+
self.all.table[paranoid_column].eq(nil)
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -51,6 +54,10 @@ module ActsAsParanoid
|
|
51
54
|
paranoid_column_type == :string && !paranoid_configuration[:deleted_value].nil?
|
52
55
|
end
|
53
56
|
|
57
|
+
def boolean_type_not_nullable?
|
58
|
+
paranoid_column_type == :boolean && !paranoid_configuration[:allow_nulls]
|
59
|
+
end
|
60
|
+
|
54
61
|
def paranoid_column
|
55
62
|
paranoid_configuration[:column].to_sym
|
56
63
|
end
|
@@ -74,8 +81,17 @@ module ActsAsParanoid
|
|
74
81
|
protected
|
75
82
|
|
76
83
|
def without_paranoid_default_scope
|
77
|
-
scope = self.
|
78
|
-
|
84
|
+
scope = self.all
|
85
|
+
|
86
|
+
if ActiveRecord::VERSION::MAJOR < 5
|
87
|
+
# ActiveRecord 4.0.*
|
88
|
+
scope = scope.with_default_scope if ActiveRecord::VERSION::MINOR < 1
|
89
|
+
scope.where_values.delete(paranoid_default_scope)
|
90
|
+
else
|
91
|
+
scope = scope.unscope(where: paranoid_default_scope)
|
92
|
+
# Fix problems with unscope group chain
|
93
|
+
scope = scope.unscoped if scope.to_sql.include? paranoid_default_scope.to_sql
|
94
|
+
end
|
79
95
|
|
80
96
|
scope
|
81
97
|
end
|
@@ -89,7 +105,7 @@ module ActsAsParanoid
|
|
89
105
|
self.send(self.class.paranoid_column)
|
90
106
|
end
|
91
107
|
|
92
|
-
def
|
108
|
+
def destroy_fully!
|
93
109
|
with_transaction_returning_status do
|
94
110
|
run_callbacks :destroy do
|
95
111
|
destroy_dependent_associations!
|
@@ -101,7 +117,7 @@ module ActsAsParanoid
|
|
101
117
|
end
|
102
118
|
end
|
103
119
|
|
104
|
-
def destroy
|
120
|
+
def destroy!
|
105
121
|
if !deleted?
|
106
122
|
with_transaction_returning_status do
|
107
123
|
run_callbacks :destroy do
|
@@ -112,10 +128,12 @@ module ActsAsParanoid
|
|
112
128
|
end
|
113
129
|
end
|
114
130
|
else
|
115
|
-
|
131
|
+
destroy_fully!
|
116
132
|
end
|
117
133
|
end
|
118
134
|
|
135
|
+
alias_method :destroy, :destroy!
|
136
|
+
|
119
137
|
def recover(options={})
|
120
138
|
options = {
|
121
139
|
:recursive => self.class.paranoid_configuration[:recover_dependent_associations],
|
@@ -144,7 +162,7 @@ module ActsAsParanoid
|
|
144
162
|
# We can only recover by window if both parent and dependant have a
|
145
163
|
# paranoid column type of :time.
|
146
164
|
if self.class.paranoid_column_type == :time && klass.paranoid_column_type == :time
|
147
|
-
scope = scope.
|
165
|
+
scope = scope.deleted_inside_time_window(paranoid_value, window)
|
148
166
|
end
|
149
167
|
|
150
168
|
scope.each do |object|
|
@@ -169,8 +187,13 @@ module ActsAsParanoid
|
|
169
187
|
end
|
170
188
|
|
171
189
|
def deleted?
|
172
|
-
!
|
173
|
-
|
190
|
+
!if self.class.string_type_with_deleted_value?
|
191
|
+
paranoid_value != self.class.delete_now_value || paranoid_value.nil?
|
192
|
+
elsif self.class.boolean_type_not_nullable?
|
193
|
+
paranoid_value == false
|
194
|
+
else
|
195
|
+
paranoid_value.nil?
|
196
|
+
end
|
174
197
|
end
|
175
198
|
|
176
199
|
alias_method :destroyed?, :deleted?
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActsAsParanoid
|
2
|
+
module PreloaderAssociation
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
def build_scope_with_deleted
|
6
|
+
scope = build_scope_without_deleted
|
7
|
+
scope = scope.with_deleted if options[:with_deleted] && klass.respond_to?(:with_deleted)
|
8
|
+
scope
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method_chain :build_scope, :deleted
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -6,37 +6,76 @@ module ActsAsParanoid
|
|
6
6
|
base.extend ClassMethods
|
7
7
|
end
|
8
8
|
|
9
|
-
class UniquenessWithoutDeletedValidator
|
10
|
-
def
|
11
|
-
|
12
|
-
|
9
|
+
class UniquenessWithoutDeletedValidator
|
10
|
+
def self.[](version)
|
11
|
+
version = version.to_s
|
12
|
+
name = "V#{version.tr('.', '_')}"
|
13
|
+
unless constants.include? name.to_sym
|
14
|
+
raise "Unknown validator version #{version.inspect}; expected one of #{constants.sort.join(', ')}"
|
15
|
+
end
|
16
|
+
const_get name
|
17
|
+
end
|
13
18
|
|
14
|
-
|
19
|
+
class V5 < ActiveRecord::Validations::UniquenessValidator
|
20
|
+
def validate_each(record, attribute, value)
|
21
|
+
finder_class = find_finder_class_for(record)
|
22
|
+
table = finder_class.arel_table
|
15
23
|
|
16
|
-
|
17
|
-
value = coder.
|
18
|
-
end
|
24
|
+
coder = record.class.attribute_types[attribute.to_s]
|
25
|
+
value = coder.type_cast_for_schema value if value && coder
|
19
26
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
relation = build_relation(finder_class, table, attribute, value)
|
28
|
+
[Array(finder_class.primary_key), Array(record.send(:id))].transpose.each do |pk_key, pk_value|
|
29
|
+
relation = relation.where(table[pk_key.to_sym].not_eq(pk_value))
|
30
|
+
end if record.persisted?
|
24
31
|
|
25
|
-
|
26
|
-
|
27
|
-
|
32
|
+
Array.wrap(options[:scope]).each do |scope_item|
|
33
|
+
relation = relation.where(table[scope_item].eq(record.public_send(scope_item)))
|
34
|
+
end
|
35
|
+
|
36
|
+
if relation.where(finder_class.paranoid_default_scope).where(relation).exists?
|
37
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
38
|
+
end
|
28
39
|
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class V4 < ActiveRecord::Validations::UniquenessValidator
|
43
|
+
def validate_each(record, attribute, value)
|
44
|
+
finder_class = find_finder_class_for(record)
|
45
|
+
table = finder_class.arel_table
|
46
|
+
|
47
|
+
# TODO: Use record.class.column_types[attribute.to_s].coder ?
|
48
|
+
coder = record.class.column_types[attribute.to_s]
|
49
|
+
|
50
|
+
if value && coder
|
51
|
+
value = if coder.respond_to? :type_cast_for_database
|
52
|
+
coder.type_cast_for_database value
|
53
|
+
else
|
54
|
+
coder.type_cast_for_write value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
relation = build_relation(finder_class, table, attribute, value)
|
59
|
+
[Array(finder_class.primary_key), Array(record.send(:id))].transpose.each do |pk_key, pk_value|
|
60
|
+
relation = relation.and(table[pk_key.to_sym].not_eq(pk_value))
|
61
|
+
end if record.persisted?
|
62
|
+
|
63
|
+
Array.wrap(options[:scope]).each do |scope_item|
|
64
|
+
scope_value = record.send(scope_item)
|
65
|
+
relation = relation.and(table[scope_item].eq(scope_value))
|
66
|
+
end
|
29
67
|
|
30
|
-
|
31
|
-
|
32
|
-
|
68
|
+
# Re-add ActsAsParanoid default scope conditions manually.
|
69
|
+
if finder_class.unscoped.where(finder_class.paranoid_default_scope).where(relation).exists?
|
70
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
71
|
+
end
|
33
72
|
end
|
34
73
|
end
|
35
74
|
end
|
36
75
|
|
37
76
|
module ClassMethods
|
38
77
|
def validates_uniqueness_of_without_deleted(*attr_names)
|
39
|
-
validates_with UniquenessWithoutDeletedValidator, _merge_attributes(attr_names)
|
78
|
+
validates_with UniquenessWithoutDeletedValidator[ActiveRecord::VERSION::MAJOR], _merge_attributes(attr_names)
|
40
79
|
end
|
41
80
|
end
|
42
81
|
end
|
data/lib/acts_as_paranoid.rb
CHANGED
@@ -2,6 +2,7 @@ require 'acts_as_paranoid/core'
|
|
2
2
|
require 'acts_as_paranoid/associations'
|
3
3
|
require 'acts_as_paranoid/validations'
|
4
4
|
require 'acts_as_paranoid/relation'
|
5
|
+
require 'acts_as_paranoid/preloader_association'
|
5
6
|
|
6
7
|
module ActsAsParanoid
|
7
8
|
|
@@ -20,6 +21,7 @@ module ActsAsParanoid
|
|
20
21
|
|
21
22
|
self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes }
|
22
23
|
self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
|
24
|
+
self.paranoid_configuration.merge!({ :allow_nulls => true }) if options[:column_type] == "boolean"
|
23
25
|
self.paranoid_configuration.merge!(options) # user options
|
24
26
|
|
25
27
|
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]
|
@@ -31,7 +33,7 @@ module ActsAsParanoid
|
|
31
33
|
include ActsAsParanoid::Core
|
32
34
|
|
33
35
|
# Magic!
|
34
|
-
default_scope { where(
|
36
|
+
default_scope { where(paranoid_default_scope) }
|
35
37
|
|
36
38
|
if paranoid_configuration[:column_type] == 'time'
|
37
39
|
scope :deleted_inside_time_window, lambda {|time, window|
|
@@ -55,3 +57,6 @@ ActiveRecord::Relation.send :include, ActsAsParanoid::Relation
|
|
55
57
|
|
56
58
|
# Push the recover callback onto the activerecord callback list
|
57
59
|
ActiveRecord::Callbacks::CALLBACKS.push(:before_recover, :after_recover)
|
60
|
+
|
61
|
+
# Use with_deleted in preloader build_scope
|
62
|
+
ActiveRecord::Associations::Preloader::Association.send :include, ActsAsParanoid::PreloaderAssociation
|
data/test/test_associations.rb
CHANGED
@@ -2,8 +2,6 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class AssociationsTest < ParanoidBaseTest
|
4
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
5
|
paranoid_company_1 = ParanoidDestroyCompany.create! :name => "ParanoidDestroyCompany #1"
|
8
6
|
paranoid_company_2 = ParanoidDeleteCompany.create! :name => "ParanoidDestroyCompany #1"
|
9
7
|
paranoid_company_1.paranoid_products.create! :name => "ParanoidProduct #1"
|
@@ -19,19 +17,32 @@ class AssociationsTest < ParanoidBaseTest
|
|
19
17
|
assert_equal 1, ParanoidDestroyCompany.with_deleted.count
|
20
18
|
assert_equal 2, ParanoidProduct.with_deleted.count
|
21
19
|
|
22
|
-
ParanoidDestroyCompany.with_deleted.first.destroy
|
20
|
+
ParanoidDestroyCompany.with_deleted.first.destroy
|
23
21
|
assert_equal 0, ParanoidDestroyCompany.count
|
24
22
|
assert_equal 1, ParanoidProduct.count
|
25
23
|
assert_equal 0, ParanoidDestroyCompany.with_deleted.count
|
26
24
|
assert_equal 1, ParanoidProduct.with_deleted.count
|
27
25
|
|
28
|
-
ParanoidDeleteCompany.
|
26
|
+
ParanoidDeleteCompany.first.destroy
|
27
|
+
assert_equal 0, ParanoidDeleteCompany.count
|
28
|
+
assert_equal 0, ParanoidProduct.count
|
29
|
+
assert_equal 1, ParanoidDeleteCompany.with_deleted.count
|
30
|
+
assert_equal 1, ParanoidProduct.with_deleted.count
|
31
|
+
|
32
|
+
ParanoidDeleteCompany.with_deleted.first.destroy
|
29
33
|
assert_equal 0, ParanoidDeleteCompany.count
|
30
34
|
assert_equal 0, ParanoidProduct.count
|
31
35
|
assert_equal 0, ParanoidDeleteCompany.with_deleted.count
|
32
36
|
assert_equal 0, ParanoidProduct.with_deleted.count
|
33
37
|
end
|
34
38
|
|
39
|
+
def test_belongs_to_with_scope_option
|
40
|
+
paranoid_has_many_dependant = ParanoidHasManyDependant.new
|
41
|
+
includes_values = ParanoidTime.includes(:not_paranoid).includes_values
|
42
|
+
|
43
|
+
assert_equal includes_values, paranoid_has_many_dependant.association(:paranoid_time_with_scope).scope.includes_values
|
44
|
+
end
|
45
|
+
|
35
46
|
def test_belongs_to_with_deleted
|
36
47
|
paranoid_time = ParanoidTime.first
|
37
48
|
paranoid_has_many_dependant = paranoid_time.paranoid_has_many_dependants.create(:name => 'dependant!')
|
@@ -72,19 +83,19 @@ class AssociationsTest < ParanoidBaseTest
|
|
72
83
|
end
|
73
84
|
|
74
85
|
def test_belongs_to_options
|
75
|
-
paranoid_time = ParanoidHasManyDependant.reflections[:paranoid_time]
|
86
|
+
paranoid_time = ParanoidHasManyDependant.reflections.with_indifferent_access[:paranoid_time]
|
76
87
|
assert_equal :belongs_to, paranoid_time.macro
|
77
88
|
assert_nil paranoid_time.options[:with_deleted]
|
78
89
|
end
|
79
90
|
|
80
91
|
def test_belongs_to_with_deleted_options
|
81
|
-
paranoid_time_with_deleted = ParanoidHasManyDependant.reflections[:paranoid_time_with_deleted]
|
92
|
+
paranoid_time_with_deleted = ParanoidHasManyDependant.reflections.with_indifferent_access[:paranoid_time_with_deleted]
|
82
93
|
assert_equal :belongs_to, paranoid_time_with_deleted.macro
|
83
94
|
assert paranoid_time_with_deleted.options[:with_deleted]
|
84
95
|
end
|
85
96
|
|
86
97
|
def test_belongs_to_polymorphic_with_deleted_options
|
87
|
-
paranoid_time_polymorphic_with_deleted = ParanoidHasManyDependant.reflections[:paranoid_time_polymorphic_with_deleted]
|
98
|
+
paranoid_time_polymorphic_with_deleted = ParanoidHasManyDependant.reflections.with_indifferent_access[:paranoid_time_polymorphic_with_deleted]
|
88
99
|
assert_equal :belongs_to, paranoid_time_polymorphic_with_deleted.macro
|
89
100
|
assert paranoid_time_polymorphic_with_deleted.options[:with_deleted]
|
90
101
|
end
|
@@ -101,6 +112,9 @@ class AssociationsTest < ParanoidBaseTest
|
|
101
112
|
child.destroy
|
102
113
|
assert_paranoid_deletion(child)
|
103
114
|
|
115
|
+
parent.reload
|
116
|
+
|
117
|
+
assert_equal [], parent.paranoid_has_many_dependants.to_a
|
104
118
|
assert_equal [child], parent.paranoid_has_many_dependants.with_deleted.to_a
|
105
119
|
end
|
106
120
|
|
data/test/test_core.rb
CHANGED
@@ -4,7 +4,6 @@ class ParanoidTest < ParanoidBaseTest
|
|
4
4
|
def test_paranoid?
|
5
5
|
assert !NotParanoid.paranoid?
|
6
6
|
assert_raise(NoMethodError) { NotParanoid.delete_all! }
|
7
|
-
assert_raise(NoMethodError) { NotParanoid.first.destroy! }
|
8
7
|
assert_raise(NoMethodError) { NotParanoid.with_deleted }
|
9
8
|
assert_raise(NoMethodError) { NotParanoid.only_deleted }
|
10
9
|
|
@@ -41,9 +40,9 @@ class ParanoidTest < ParanoidBaseTest
|
|
41
40
|
end
|
42
41
|
|
43
42
|
def test_real_removal
|
44
|
-
ParanoidTime.first.
|
43
|
+
ParanoidTime.first.destroy_fully!
|
45
44
|
ParanoidBoolean.delete_all!("name = 'extremely paranoid' OR name = 'really paranoid'")
|
46
|
-
ParanoidString.first.
|
45
|
+
ParanoidString.first.destroy_fully!
|
47
46
|
assert_equal 2, ParanoidTime.count
|
48
47
|
assert_equal 1, ParanoidBoolean.count
|
49
48
|
assert_equal 0, ParanoidString.count
|
@@ -60,7 +59,7 @@ class ParanoidTest < ParanoidBaseTest
|
|
60
59
|
|
61
60
|
ParanoidTime.delete_all!
|
62
61
|
assert_empty ParanoidTime.all
|
63
|
-
assert_empty ParanoidTime.with_deleted
|
62
|
+
assert_empty ParanoidTime.with_deleted
|
64
63
|
end
|
65
64
|
|
66
65
|
def test_non_persisted_destroy
|
@@ -77,6 +76,10 @@ class ParanoidTest < ParanoidBaseTest
|
|
77
76
|
assert_not_nil pt.paranoid_value
|
78
77
|
end
|
79
78
|
|
79
|
+
def test_removal_not_persisted
|
80
|
+
assert ParanoidTime.new.destroy
|
81
|
+
end
|
82
|
+
|
80
83
|
def test_recovery
|
81
84
|
assert_equal 3, ParanoidBoolean.count
|
82
85
|
ParanoidBoolean.first.destroy
|
@@ -141,12 +144,18 @@ class ParanoidTest < ParanoidBaseTest
|
|
141
144
|
assert_equal 0, ParanoidHasOneDependant.count
|
142
145
|
assert_equal 1, NotParanoid.count
|
143
146
|
assert_equal 0, HasOneNotParanoid.count
|
147
|
+
|
148
|
+
assert_equal 3, ParanoidTime.with_deleted.count
|
149
|
+
assert_equal 4, ParanoidHasManyDependant.with_deleted.count
|
150
|
+
assert_equal 3, ParanoidBelongsDependant.with_deleted.count
|
151
|
+
assert_equal @paranoid_boolean_count + 3, ParanoidBoolean.with_deleted.count
|
152
|
+
assert_equal 3, ParanoidHasOneDependant.with_deleted.count
|
144
153
|
end
|
145
154
|
|
146
155
|
def test_recursive_real_removal
|
147
156
|
setup_recursive_tests
|
148
157
|
|
149
|
-
@paranoid_time_object.
|
158
|
+
@paranoid_time_object.destroy_fully!
|
150
159
|
|
151
160
|
assert_equal 0, ParanoidTime.only_deleted.count
|
152
161
|
assert_equal 1, ParanoidHasManyDependant.only_deleted.count
|
@@ -295,8 +304,8 @@ class ParanoidTest < ParanoidBaseTest
|
|
295
304
|
@paranoid_with_callback.recover
|
296
305
|
end
|
297
306
|
|
298
|
-
|
299
|
-
|
307
|
+
assert @paranoid_with_callback.called_before_recover
|
308
|
+
assert @paranoid_with_callback.called_after_recover
|
300
309
|
end
|
301
310
|
|
302
311
|
def test_delete_by_multiple_id_is_paranoid
|
@@ -384,4 +393,44 @@ class ParanoidTest < ParanoidBaseTest
|
|
384
393
|
2.times { ps.destroy }
|
385
394
|
assert_equal 0, ParanoidString.with_deleted.where(:id => ps).count
|
386
395
|
end
|
396
|
+
|
397
|
+
# Test boolean type columns, that are not nullable
|
398
|
+
def test_boolean_type_with_no_nil_value_before_destroy
|
399
|
+
ps = ParanoidBooleanNotNullable.create!()
|
400
|
+
assert_equal 1, ParanoidBooleanNotNullable.where(:id => ps).count
|
401
|
+
end
|
402
|
+
|
403
|
+
def test_boolean_type_with_no_nil_value_after_destroy
|
404
|
+
ps = ParanoidBooleanNotNullable.create!()
|
405
|
+
ps.destroy
|
406
|
+
assert_equal 0, ParanoidBooleanNotNullable.where(:id => ps).count
|
407
|
+
end
|
408
|
+
|
409
|
+
def test_boolean_type_with_no_nil_value_before_destroy_with_deleted
|
410
|
+
ps = ParanoidBooleanNotNullable.create!()
|
411
|
+
assert_equal 1, ParanoidBooleanNotNullable.with_deleted.where(:id => ps).count
|
412
|
+
end
|
413
|
+
|
414
|
+
def test_boolean_type_with_no_nil_value_after_destroy_with_deleted
|
415
|
+
ps = ParanoidBooleanNotNullable.create!()
|
416
|
+
ps.destroy
|
417
|
+
assert_equal 1, ParanoidBooleanNotNullable.with_deleted.where(:id => ps).count
|
418
|
+
end
|
419
|
+
|
420
|
+
def test_boolean_type_with_no_nil_value_before_destroy_only_deleted
|
421
|
+
ps = ParanoidBooleanNotNullable.create!()
|
422
|
+
assert_equal 0, ParanoidBooleanNotNullable.only_deleted.where(:id => ps).count
|
423
|
+
end
|
424
|
+
|
425
|
+
def test_boolean_type_with_no_nil_value_after_destroy_only_deleted
|
426
|
+
ps = ParanoidBooleanNotNullable.create!()
|
427
|
+
ps.destroy
|
428
|
+
assert_equal 1, ParanoidBooleanNotNullable.only_deleted.where(:id => ps).count
|
429
|
+
end
|
430
|
+
|
431
|
+
def test_boolean_type_with_no_nil_value_after_destroyed_twice
|
432
|
+
ps = ParanoidBooleanNotNullable.create!()
|
433
|
+
2.times { ps.destroy }
|
434
|
+
assert_equal 0, ParanoidBooleanNotNullable.with_deleted.where(:id => ps).count
|
435
|
+
end
|
387
436
|
end
|
data/test/test_default_scopes.rb
CHANGED
@@ -37,7 +37,7 @@ class MultipleDefaultScopesTest < ParanoidBaseTest
|
|
37
37
|
assert_equal 0, ParanoidHuman.only_deleted.count
|
38
38
|
assert_equal 3, ParanoidHuman.unscoped.count
|
39
39
|
|
40
|
-
ParanoidHuman.first.
|
40
|
+
ParanoidHuman.first.destroy_fully!
|
41
41
|
assert_equal 1, ParanoidHuman.count
|
42
42
|
assert_equal 1, ParanoidHuman.with_deleted.count
|
43
43
|
assert_equal 0, ParanoidHuman.only_deleted.count
|
data/test/test_helper.rb
CHANGED
@@ -24,7 +24,7 @@ def setup_db
|
|
24
24
|
t.integer :paranoid_belongs_dependant_id
|
25
25
|
t.integer :not_paranoid_id
|
26
26
|
|
27
|
-
t
|
27
|
+
timestamps t
|
28
28
|
end
|
29
29
|
|
30
30
|
create_table :paranoid_booleans do |t|
|
@@ -32,7 +32,7 @@ def setup_db
|
|
32
32
|
t.boolean :is_deleted
|
33
33
|
t.integer :paranoid_time_id
|
34
34
|
|
35
|
-
t
|
35
|
+
timestamps t
|
36
36
|
end
|
37
37
|
|
38
38
|
create_table :paranoid_strings do |t|
|
@@ -44,14 +44,14 @@ def setup_db
|
|
44
44
|
t.string :name
|
45
45
|
t.integer :paranoid_time_id
|
46
46
|
|
47
|
-
t
|
47
|
+
timestamps t
|
48
48
|
end
|
49
49
|
|
50
50
|
create_table :has_one_not_paranoids do |t|
|
51
51
|
t.string :name
|
52
52
|
t.integer :paranoid_time_id
|
53
53
|
|
54
|
-
t
|
54
|
+
timestamps t
|
55
55
|
end
|
56
56
|
|
57
57
|
create_table :paranoid_has_many_dependants do |t|
|
@@ -61,14 +61,14 @@ def setup_db
|
|
61
61
|
t.string :paranoid_time_polymorphic_with_deleted_type
|
62
62
|
t.integer :paranoid_belongs_dependant_id
|
63
63
|
|
64
|
-
t
|
64
|
+
timestamps t
|
65
65
|
end
|
66
66
|
|
67
67
|
create_table :paranoid_belongs_dependants do |t|
|
68
68
|
t.string :name
|
69
69
|
t.datetime :deleted_at
|
70
70
|
|
71
|
-
t
|
71
|
+
timestamps t
|
72
72
|
end
|
73
73
|
|
74
74
|
create_table :paranoid_has_one_dependants do |t|
|
@@ -76,28 +76,28 @@ def setup_db
|
|
76
76
|
t.datetime :deleted_at
|
77
77
|
t.integer :paranoid_boolean_id
|
78
78
|
|
79
|
-
t
|
79
|
+
timestamps t
|
80
80
|
end
|
81
81
|
|
82
82
|
create_table :paranoid_with_callbacks do |t|
|
83
83
|
t.string :name
|
84
84
|
t.datetime :deleted_at
|
85
85
|
|
86
|
-
t
|
86
|
+
timestamps t
|
87
87
|
end
|
88
88
|
|
89
89
|
create_table :paranoid_destroy_companies do |t|
|
90
90
|
t.string :name
|
91
91
|
t.datetime :deleted_at
|
92
92
|
|
93
|
-
t
|
93
|
+
timestamps t
|
94
94
|
end
|
95
95
|
|
96
96
|
create_table :paranoid_delete_companies do |t|
|
97
97
|
t.string :name
|
98
98
|
t.datetime :deleted_at
|
99
99
|
|
100
|
-
t
|
100
|
+
timestamps t
|
101
101
|
end
|
102
102
|
|
103
103
|
create_table :paranoid_products do |t|
|
@@ -106,46 +106,46 @@ def setup_db
|
|
106
106
|
t.string :name
|
107
107
|
t.datetime :deleted_at
|
108
108
|
|
109
|
-
t
|
109
|
+
timestamps t
|
110
110
|
end
|
111
111
|
|
112
112
|
create_table :super_paranoids do |t|
|
113
113
|
t.string :type
|
114
|
-
t.references :has_many_inherited_super_paranoidz
|
114
|
+
t.references :has_many_inherited_super_paranoidz, index: { name: 'index__sp_id_on_has_many_isp' }
|
115
115
|
t.datetime :deleted_at
|
116
116
|
|
117
|
-
t
|
117
|
+
timestamps t
|
118
118
|
end
|
119
119
|
|
120
120
|
create_table :has_many_inherited_super_paranoidzs do |t|
|
121
|
-
t.references :super_paranoidz
|
121
|
+
t.references :super_paranoidz, index: { name: 'index_has_many_isp_on_sp_id' }
|
122
122
|
t.datetime :deleted_at
|
123
123
|
|
124
|
-
t
|
124
|
+
timestamps t
|
125
125
|
end
|
126
126
|
|
127
127
|
create_table :paranoid_many_many_parent_lefts do |t|
|
128
128
|
t.string :name
|
129
|
-
t
|
129
|
+
timestamps t
|
130
130
|
end
|
131
131
|
|
132
132
|
create_table :paranoid_many_many_parent_rights do |t|
|
133
133
|
t.string :name
|
134
|
-
t
|
134
|
+
timestamps t
|
135
135
|
end
|
136
136
|
|
137
137
|
create_table :paranoid_many_many_children do |t|
|
138
138
|
t.integer :paranoid_many_many_parent_left_id
|
139
139
|
t.integer :paranoid_many_many_parent_right_id
|
140
140
|
t.datetime :deleted_at
|
141
|
-
t
|
141
|
+
timestamps t
|
142
142
|
end
|
143
143
|
|
144
144
|
create_table :paranoid_with_scoped_validations do |t|
|
145
145
|
t.string :name
|
146
146
|
t.string :category
|
147
147
|
t.datetime :deleted_at
|
148
|
-
t
|
148
|
+
timestamps t
|
149
149
|
end
|
150
150
|
|
151
151
|
create_table :paranoid_forests do |t|
|
@@ -153,7 +153,7 @@ def setup_db
|
|
153
153
|
t.boolean :rainforest
|
154
154
|
t.datetime :deleted_at
|
155
155
|
|
156
|
-
t
|
156
|
+
timestamps t
|
157
157
|
end
|
158
158
|
|
159
159
|
create_table :paranoid_trees do |t|
|
@@ -161,14 +161,14 @@ def setup_db
|
|
161
161
|
t.string :name
|
162
162
|
t.datetime :deleted_at
|
163
163
|
|
164
|
-
t
|
164
|
+
timestamps t
|
165
165
|
end
|
166
166
|
|
167
167
|
create_table :paranoid_humen do |t|
|
168
168
|
t.string :gender
|
169
169
|
t.datetime :deleted_at
|
170
170
|
|
171
|
-
t
|
171
|
+
timestamps t
|
172
172
|
end
|
173
173
|
|
174
174
|
create_table :paranoid_androids do |t|
|
@@ -181,13 +181,48 @@ def setup_db
|
|
181
181
|
t.string :paranoid_thing_type
|
182
182
|
t.datetime :deleted_at
|
183
183
|
end
|
184
|
+
|
185
|
+
create_table :paranoid_boolean_not_nullables do |t|
|
186
|
+
t.string :name
|
187
|
+
t.boolean :deleted, :boolean, :null => false, :default => false
|
188
|
+
end
|
189
|
+
|
190
|
+
create_table :paranoid_belongs_to_polymorphics do |t|
|
191
|
+
t.string :name
|
192
|
+
t.string :parent_type
|
193
|
+
t.integer :parent_id
|
194
|
+
t.datetime :deleted_at
|
195
|
+
|
196
|
+
t.timestamps
|
197
|
+
end
|
198
|
+
|
199
|
+
create_table :not_paranoid_has_many_as_parents do |t|
|
200
|
+
t.string :name
|
201
|
+
|
202
|
+
t.timestamps
|
203
|
+
end
|
204
|
+
|
205
|
+
create_table :paranoid_has_many_as_parents do |t|
|
206
|
+
t.string :name
|
207
|
+
t.datetime :deleted_at
|
208
|
+
|
209
|
+
t.timestamps
|
210
|
+
end
|
184
211
|
end
|
185
212
|
end
|
186
213
|
|
214
|
+
def timestamps(table)
|
215
|
+
table.column :created_at , :timestamp, :null => false
|
216
|
+
table.column :updated_at , :timestamp, :null => false
|
217
|
+
end
|
218
|
+
|
187
219
|
def teardown_db
|
188
|
-
ActiveRecord::
|
189
|
-
ActiveRecord::Base.connection.
|
220
|
+
tables = if ActiveRecord::VERSION::MAJOR < 5
|
221
|
+
ActiveRecord::Base.connection.tables
|
222
|
+
else
|
223
|
+
ActiveRecord::Base.connection.data_sources
|
190
224
|
end
|
225
|
+
tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
191
226
|
end
|
192
227
|
|
193
228
|
class ParanoidTime < ActiveRecord::Base
|
@@ -233,6 +268,7 @@ end
|
|
233
268
|
class ParanoidHasManyDependant < ActiveRecord::Base
|
234
269
|
acts_as_paranoid
|
235
270
|
belongs_to :paranoid_time
|
271
|
+
belongs_to :paranoid_time_with_scope, -> { includes(:not_paranoid) }, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id
|
236
272
|
belongs_to :paranoid_time_with_deleted, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :with_deleted => true
|
237
273
|
belongs_to :paranoid_time_polymorphic_with_deleted, :class_name => 'ParanoidTime', :foreign_key => :paranoid_time_id, :polymorphic => true, :with_deleted => true
|
238
274
|
|
@@ -323,25 +359,6 @@ class InheritedParanoid < SuperParanoid
|
|
323
359
|
acts_as_paranoid
|
324
360
|
end
|
325
361
|
|
326
|
-
class ParanoidObserver < ActiveRecord::Observer
|
327
|
-
observe :paranoid_with_callback
|
328
|
-
|
329
|
-
attr_accessor :called_before_recover, :called_after_recover
|
330
|
-
|
331
|
-
def before_recover(paranoid_object)
|
332
|
-
self.called_before_recover = paranoid_object
|
333
|
-
end
|
334
|
-
|
335
|
-
def after_recover(paranoid_object)
|
336
|
-
self.called_after_recover = paranoid_object
|
337
|
-
end
|
338
|
-
|
339
|
-
def reset
|
340
|
-
self.called_before_recover = nil
|
341
|
-
self.called_after_recover = nil
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
362
|
|
346
363
|
class ParanoidManyManyParentLeft < ActiveRecord::Base
|
347
364
|
has_many :paranoid_many_many_children
|
@@ -364,7 +381,19 @@ class ParanoidWithScopedValidation < ActiveRecord::Base
|
|
364
381
|
validates_uniqueness_of :name, :scope => :category
|
365
382
|
end
|
366
383
|
|
367
|
-
|
384
|
+
class ParanoidBelongsToPolymorphic < ActiveRecord::Base
|
385
|
+
acts_as_paranoid
|
386
|
+
belongs_to :parent, :polymorphic => true, :with_deleted => true
|
387
|
+
end
|
388
|
+
|
389
|
+
class NotParanoidHasManyAsParent < ActiveRecord::Base
|
390
|
+
has_many :paranoid_belongs_to_polymorphics, :as => :parent, :dependent => :destroy
|
391
|
+
end
|
392
|
+
|
393
|
+
class ParanoidHasManyAsParent < ActiveRecord::Base
|
394
|
+
acts_as_paranoid
|
395
|
+
has_many :paranoid_belongs_to_polymorphics, :as => :parent, :dependent => :destroy
|
396
|
+
end
|
368
397
|
|
369
398
|
class ParanoidBaseTest < ActiveSupport::TestCase
|
370
399
|
def setup
|
@@ -378,8 +407,6 @@ class ParanoidBaseTest < ActiveSupport::TestCase
|
|
378
407
|
ParanoidString.create! :name => "strings can be paranoid"
|
379
408
|
NotParanoid.create! :name => "no paranoid goals"
|
380
409
|
ParanoidWithCallback.create! :name => "paranoid with callbacks"
|
381
|
-
|
382
|
-
ParanoidObserver.instance.reset
|
383
410
|
end
|
384
411
|
|
385
412
|
def teardown
|
@@ -411,11 +438,9 @@ end
|
|
411
438
|
class ParanoidForest < ActiveRecord::Base
|
412
439
|
acts_as_paranoid
|
413
440
|
|
414
|
-
# HACK: scope throws an error on 1.8.7 because the logger isn't initialized (see https://github.com/Casecommons/pg_search/issues/26)
|
415
|
-
require "active_support/core_ext/logger.rb"
|
416
441
|
ActiveRecord::Base.logger = Logger.new(StringIO.new)
|
417
442
|
|
418
|
-
scope :rainforest, where(:rainforest => true)
|
443
|
+
scope :rainforest, lambda{ where(:rainforest => true) }
|
419
444
|
|
420
445
|
has_many :paranoid_trees, :dependent => :destroy
|
421
446
|
end
|
@@ -428,7 +453,7 @@ end
|
|
428
453
|
|
429
454
|
class ParanoidHuman < ActiveRecord::Base
|
430
455
|
acts_as_paranoid
|
431
|
-
default_scope where('gender = ?', 'male')
|
456
|
+
default_scope { where('gender = ?', 'male') }
|
432
457
|
end
|
433
458
|
|
434
459
|
class ParanoidAndroid < ActiveRecord::Base
|
@@ -440,3 +465,8 @@ class ParanoidSection < ActiveRecord::Base
|
|
440
465
|
belongs_to :paranoid_time
|
441
466
|
belongs_to :paranoid_thing, :polymorphic => true, :dependent => :destroy
|
442
467
|
end
|
468
|
+
|
469
|
+
class ParanoidBooleanNotNullable < ActiveRecord::Base
|
470
|
+
acts_as_paranoid column: 'deleted', column_type: 'boolean', allow_nulls: false
|
471
|
+
end
|
472
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PreloaderAssociationTest < ParanoidBaseTest
|
4
|
+
def test_includes_with_deleted
|
5
|
+
paranoid_time = ParanoidTime.first
|
6
|
+
paranoid_has_many_dependant = paranoid_time.paranoid_has_many_dependants.create(:name => 'dependant!')
|
7
|
+
|
8
|
+
paranoid_time.destroy
|
9
|
+
|
10
|
+
ParanoidHasManyDependant.with_deleted.includes(:paranoid_time_with_deleted).each do |hasmany|
|
11
|
+
assert_not_nil hasmany.paranoid_time_with_deleted
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_includes_with_deleted_with_polymorphic_parent
|
16
|
+
not_paranoid_parent = NotParanoidHasManyAsParent.create(name: 'not paranoid parent')
|
17
|
+
paranoid_parent = ParanoidHasManyAsParent.create(name: 'paranoid parent')
|
18
|
+
ParanoidBelongsToPolymorphic.create(:name => 'belongs_to', :parent => not_paranoid_parent)
|
19
|
+
ParanoidBelongsToPolymorphic.create(:name => 'belongs_to', :parent => paranoid_parent)
|
20
|
+
|
21
|
+
paranoid_parent.destroy
|
22
|
+
|
23
|
+
ParanoidBelongsToPolymorphic.with_deleted.includes(:parent).each do |hasmany|
|
24
|
+
assert_not_nil hasmany.parent
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/test/test_relations.rb
CHANGED
@@ -75,7 +75,7 @@ class RelationsTest < ParanoidBaseTest
|
|
75
75
|
|
76
76
|
def test_fake_removal_through_relation
|
77
77
|
# destroy: through a relation.
|
78
|
-
ParanoidForest.rainforest.destroy(@paranoid_forest_3)
|
78
|
+
ParanoidForest.rainforest.destroy(@paranoid_forest_3.id)
|
79
79
|
assert_equal 1, ParanoidForest.rainforest.count
|
80
80
|
assert_equal 2, ParanoidForest.rainforest.with_deleted.count
|
81
81
|
assert_equal 1, ParanoidForest.rainforest.only_deleted.count
|
@@ -95,8 +95,8 @@ class RelationsTest < ParanoidBaseTest
|
|
95
95
|
|
96
96
|
# destroy: two-step through a relation
|
97
97
|
paranoid_tree = @paranoid_forest_1.paranoid_trees.first
|
98
|
-
@paranoid_forest_1.paranoid_trees.order(:id).destroy(paranoid_tree)
|
99
|
-
@paranoid_forest_1.paranoid_trees.only_deleted.destroy(paranoid_tree)
|
98
|
+
@paranoid_forest_1.paranoid_trees.order(:id).destroy(paranoid_tree.id)
|
99
|
+
@paranoid_forest_1.paranoid_trees.only_deleted.destroy(paranoid_tree.id)
|
100
100
|
assert_equal 1, @paranoid_forest_1.paranoid_trees(true).count
|
101
101
|
assert_equal 1, @paranoid_forest_1.paranoid_trees(true).with_deleted.count
|
102
102
|
assert_equal 0, @paranoid_forest_1.paranoid_trees(true).only_deleted.count
|
metadata
CHANGED
@@ -1,33 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_paranoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zachary Scott
|
8
|
-
- André Medeiros
|
9
8
|
- Goncalo Silva
|
10
|
-
- Charles G.
|
11
9
|
- Rick Olson
|
12
10
|
autorequire:
|
13
11
|
bindir: bin
|
14
12
|
cert_chain: []
|
15
|
-
date: 2016-
|
13
|
+
date: 2016-08-09 00:00:00.000000000 Z
|
16
14
|
dependencies:
|
17
15
|
- !ruby/object:Gem::Dependency
|
18
16
|
name: activerecord
|
19
17
|
requirement: !ruby/object:Gem::Requirement
|
20
18
|
requirements:
|
21
|
-
- - "
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '4.0'
|
22
|
+
- - "<"
|
22
23
|
- !ruby/object:Gem::Version
|
23
|
-
version: '
|
24
|
+
version: '5.1'
|
24
25
|
type: :runtime
|
25
26
|
prerelease: false
|
26
27
|
version_requirements: !ruby/object:Gem::Requirement
|
27
28
|
requirements:
|
28
|
-
- - "
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '4.0'
|
32
|
+
- - "<"
|
29
33
|
- !ruby/object:Gem::Version
|
30
|
-
version: '
|
34
|
+
version: '5.1'
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: activesupport
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '4.0'
|
42
|
+
- - "<"
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '5.1'
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '4.0'
|
52
|
+
- - "<"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.1'
|
31
55
|
- !ruby/object:Gem::Dependency
|
32
56
|
name: bundler
|
33
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,16 +98,22 @@ dependencies:
|
|
74
98
|
name: minitest
|
75
99
|
requirement: !ruby/object:Gem::Requirement
|
76
100
|
requirements:
|
77
|
-
- - "
|
101
|
+
- - ">="
|
78
102
|
- !ruby/object:Gem::Version
|
79
103
|
version: '4.0'
|
104
|
+
- - "<="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '6.0'
|
80
107
|
type: :development
|
81
108
|
prerelease: false
|
82
109
|
version_requirements: !ruby/object:Gem::Requirement
|
83
110
|
requirements:
|
84
|
-
- - "
|
111
|
+
- - ">="
|
85
112
|
- !ruby/object:Gem::Version
|
86
113
|
version: '4.0'
|
114
|
+
- - "<="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '6.0'
|
87
117
|
description: Check the home page for more in-depth information.
|
88
118
|
email:
|
89
119
|
- e@zzak.io
|
@@ -96,6 +126,7 @@ files:
|
|
96
126
|
- lib/acts_as_paranoid.rb
|
97
127
|
- lib/acts_as_paranoid/associations.rb
|
98
128
|
- lib/acts_as_paranoid/core.rb
|
129
|
+
- lib/acts_as_paranoid/preloader_association.rb
|
99
130
|
- lib/acts_as_paranoid/relation.rb
|
100
131
|
- lib/acts_as_paranoid/validations.rb
|
101
132
|
- lib/acts_as_paranoid/version.rb
|
@@ -104,10 +135,10 @@ files:
|
|
104
135
|
- test/test_default_scopes.rb
|
105
136
|
- test/test_helper.rb
|
106
137
|
- test/test_inheritance.rb
|
107
|
-
- test/
|
138
|
+
- test/test_preloader_association.rb
|
108
139
|
- test/test_relations.rb
|
109
140
|
- test/test_validations.rb
|
110
|
-
homepage: https://github.com/
|
141
|
+
homepage: https://github.com/ActsAsParanoid/acts_as_paranoid
|
111
142
|
licenses:
|
112
143
|
- MIT
|
113
144
|
metadata: {}
|
@@ -134,7 +165,7 @@ summary: Active Record plugin which allows you to hide and restore records witho
|
|
134
165
|
actually deleting them.
|
135
166
|
test_files:
|
136
167
|
- test/test_inheritance.rb
|
137
|
-
- test/
|
168
|
+
- test/test_preloader_association.rb
|
138
169
|
- test/test_relations.rb
|
139
170
|
- test/test_core.rb
|
140
171
|
- test/test_validations.rb
|
data/test/test_observers.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class ParanoidObserverTest < ParanoidBaseTest
|
4
|
-
def test_called_observer_methods
|
5
|
-
@subject = ParanoidWithCallback.new
|
6
|
-
@subject.save
|
7
|
-
|
8
|
-
assert_nil ParanoidObserver.instance.called_before_recover
|
9
|
-
assert_nil ParanoidObserver.instance.called_after_recover
|
10
|
-
|
11
|
-
ParanoidWithCallback.find(@subject.id).recover
|
12
|
-
|
13
|
-
assert_equal @subject, ParanoidObserver.instance.called_before_recover
|
14
|
-
assert_equal @subject, ParanoidObserver.instance.called_after_recover
|
15
|
-
end
|
16
|
-
end
|