acts_as_paranoid 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://travis-ci.org/ActsAsParanoid/acts_as_paranoid.png?branch=
|
3
|
+
[![Build Status](https://travis-ci.org/ActsAsParanoid/acts_as_paranoid.png?branch=master)](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
|