mongoid_paranoia 0.4.0 → 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/LICENSE +21 -21
- data/README.md +117 -117
- data/lib/mongoid/paranoia/configuration.rb +13 -11
- data/lib/mongoid/paranoia/monkey_patches.rb +114 -113
- data/lib/mongoid/paranoia/version.rb +7 -5
- data/lib/mongoid/paranoia.rb +196 -203
- data/lib/mongoid-paranoia.rb +3 -1
- data/lib/mongoid_paranoia.rb +3 -1
- data/perf/scope.rb +65 -64
- data/spec/app/models/address.rb +71 -71
- data/spec/app/models/appointment.rb +7 -7
- data/spec/app/models/author.rb +6 -6
- data/spec/app/models/fish.rb +8 -8
- data/spec/app/models/paranoid_phone.rb +25 -25
- data/spec/app/models/paranoid_post.rb +65 -65
- data/spec/app/models/person.rb +21 -21
- data/spec/app/models/phone.rb +11 -11
- data/spec/app/models/relations.rb +247 -247
- data/spec/app/models/tag.rb +6 -6
- data/spec/app/models/title.rb +4 -4
- data/spec/mongoid/configuration_spec.rb +19 -19
- data/spec/mongoid/document_spec.rb +21 -21
- data/spec/mongoid/nested_attributes_spec.rb +164 -164
- data/spec/mongoid/paranoia_spec.rb +887 -887
- data/spec/mongoid/scoping_spec.rb +55 -55
- data/spec/mongoid/validatable/uniqueness_spec.rb +74 -74
- data/spec/spec_helper.rb +43 -75
- metadata +36 -25
data/lib/mongoid/paranoia.rb
CHANGED
@@ -1,203 +1,196 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
require 'mongoid/paranoia/
|
4
|
-
require '
|
5
|
-
require 'active_support
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# Include this module to get soft deletion of root level documents.
|
10
|
-
# This will add a deleted_at field to the +Document+, managed automatically.
|
11
|
-
# Potentially incompatible with unique indices. (if collisions with deleted items)
|
12
|
-
#
|
13
|
-
# @example Make a document paranoid.
|
14
|
-
# class Person
|
15
|
-
# include Mongoid::Document
|
16
|
-
# include Mongoid::Paranoia
|
17
|
-
# end
|
18
|
-
module Paranoia
|
19
|
-
include Mongoid::Persistable::Deletable
|
20
|
-
extend ActiveSupport::Concern
|
21
|
-
|
22
|
-
class << self
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
# @
|
97
|
-
|
98
|
-
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
#
|
135
|
-
#
|
136
|
-
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
#
|
180
|
-
#
|
181
|
-
# @
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
#
|
191
|
-
#
|
192
|
-
def
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
# @return [ Object ] Update result.
|
198
|
-
#
|
199
|
-
def _paranoia_update(value)
|
200
|
-
paranoid_collection.find(atomic_selector).update_one(value)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mongoid/paranoia/monkey_patches'
|
4
|
+
require 'mongoid/paranoia/configuration'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/deprecation'
|
7
|
+
|
8
|
+
module Mongoid
|
9
|
+
# Include this module to get soft deletion of root level documents.
|
10
|
+
# This will add a deleted_at field to the +Document+, managed automatically.
|
11
|
+
# Potentially incompatible with unique indices. (if collisions with deleted items)
|
12
|
+
#
|
13
|
+
# @example Make a document paranoid.
|
14
|
+
# class Person
|
15
|
+
# include Mongoid::Document
|
16
|
+
# include Mongoid::Paranoia
|
17
|
+
# end
|
18
|
+
module Paranoia
|
19
|
+
include Mongoid::Persistable::Deletable
|
20
|
+
extend ActiveSupport::Concern
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def configuration
|
24
|
+
@configuration ||= Configuration.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def reset
|
28
|
+
@configuration = Configuration.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Allow the paranoid +Document+ to use an alternate field name for deleted_at.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# Mongoid::Paranoia.configure do |c|
|
35
|
+
# c.paranoid_field = :myFieldName
|
36
|
+
# end
|
37
|
+
def configure
|
38
|
+
yield(configuration)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
included do
|
43
|
+
field Paranoia.configuration.paranoid_field, as: :deleted_at, type: Time
|
44
|
+
|
45
|
+
self.paranoid = true
|
46
|
+
|
47
|
+
default_scope -> { where(deleted_at: nil) }
|
48
|
+
scope :deleted, -> { ne(deleted_at: nil) }
|
49
|
+
define_model_callbacks :restore
|
50
|
+
define_model_callbacks :remove
|
51
|
+
end
|
52
|
+
|
53
|
+
# Override the persisted method to allow for the paranoia gem.
|
54
|
+
# If a paranoid record is selected, then we only want to check
|
55
|
+
# if it's a new record, not if it is "destroyed"
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# document.persisted?
|
59
|
+
#
|
60
|
+
# @return [ true, false ] If the operation succeeded.
|
61
|
+
#
|
62
|
+
# @since 4.0.0
|
63
|
+
def persisted?
|
64
|
+
!new_record?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Delete the +Document+, will set the deleted_at timestamp and not actually
|
68
|
+
# delete it.
|
69
|
+
#
|
70
|
+
# @example Soft remove the document.
|
71
|
+
# document.remove
|
72
|
+
#
|
73
|
+
# @param [ Hash ] options The database options.
|
74
|
+
#
|
75
|
+
# @return [ true ] True.
|
76
|
+
#
|
77
|
+
# @since 1.0.0
|
78
|
+
alias :orig_delete :delete
|
79
|
+
|
80
|
+
def remove(_ = {})
|
81
|
+
time = self.deleted_at = Time.now
|
82
|
+
_paranoia_update('$set' => { paranoid_field => time })
|
83
|
+
@destroyed = true
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
alias :delete :remove
|
88
|
+
alias :delete! :orig_delete
|
89
|
+
|
90
|
+
# Delete the paranoid +Document+ from the database completely. This will
|
91
|
+
# run the destroy and remove callbacks.
|
92
|
+
#
|
93
|
+
# @example Hard destroy the document.
|
94
|
+
# document.destroy!
|
95
|
+
#
|
96
|
+
# @return [ true, false ] If the operation succeeded.
|
97
|
+
#
|
98
|
+
# @since 1.0.0
|
99
|
+
def destroy!(options = {})
|
100
|
+
raise Errors::ReadonlyDocument.new(self.class) if readonly?
|
101
|
+
self.flagged_for_destroy = true
|
102
|
+
result = run_callbacks(:destroy) do
|
103
|
+
run_callbacks(:remove) do
|
104
|
+
if catch(:abort) { apply_destroy_dependencies! }
|
105
|
+
delete!(options || {})
|
106
|
+
else
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
self.flagged_for_destroy = false
|
112
|
+
result
|
113
|
+
end
|
114
|
+
|
115
|
+
# Determines if this document is destroyed.
|
116
|
+
#
|
117
|
+
# @example Is the document destroyed?
|
118
|
+
# person.destroyed?
|
119
|
+
#
|
120
|
+
# @return [ true, false ] If the document is destroyed.
|
121
|
+
#
|
122
|
+
# @since 1.0.0
|
123
|
+
def destroyed?
|
124
|
+
(@destroyed ||= false) || !!deleted_at
|
125
|
+
end
|
126
|
+
alias deleted? destroyed?
|
127
|
+
|
128
|
+
# Restores a previously soft-deleted document. Handles this by removing the
|
129
|
+
# deleted_at flag.
|
130
|
+
#
|
131
|
+
# @example Restore the document from deleted state.
|
132
|
+
# document.restore
|
133
|
+
#
|
134
|
+
# For resoring associated documents use :recursive => true
|
135
|
+
# @example Restore the associated documents from deleted state.
|
136
|
+
# document.restore(:recursive => true)
|
137
|
+
#
|
138
|
+
# TODO: @return [ Time ] The time the document had been deleted.
|
139
|
+
#
|
140
|
+
# @since 1.0.0
|
141
|
+
def restore(opts = {})
|
142
|
+
run_callbacks(:restore) do
|
143
|
+
_paranoia_update('$unset' => { paranoid_field => true })
|
144
|
+
attributes.delete('deleted_at')
|
145
|
+
@destroyed = false
|
146
|
+
restore_relations if opts[:recursive]
|
147
|
+
true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns a string representing the documents's key suitable for use in URLs.
|
152
|
+
def to_param
|
153
|
+
new_record? ? nil : to_key.join('-')
|
154
|
+
end
|
155
|
+
|
156
|
+
def restore_relations
|
157
|
+
relations.each_pair do |name, association|
|
158
|
+
next unless association.dependent == :destroy
|
159
|
+
relation = send(name)
|
160
|
+
next unless relation.present? && relation.paranoid?
|
161
|
+
Array.wrap(relation).each do |doc|
|
162
|
+
doc.restore(recursive: true)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
# Get the collection to be used for paranoid operations.
|
170
|
+
#
|
171
|
+
# @example Get the paranoid collection.
|
172
|
+
# document.paranoid_collection
|
173
|
+
#
|
174
|
+
# @return [ Collection ] The root collection.
|
175
|
+
def paranoid_collection
|
176
|
+
embedded? ? _root.collection : collection
|
177
|
+
end
|
178
|
+
|
179
|
+
# Get the field to be used for paranoid operations.
|
180
|
+
#
|
181
|
+
# @example Get the paranoid field.
|
182
|
+
# document.paranoid_field
|
183
|
+
#
|
184
|
+
# @return [ String ] The deleted at field.
|
185
|
+
def paranoid_field
|
186
|
+
field = Paranoia.configuration.paranoid_field
|
187
|
+
embedded? ? "#{atomic_position}.#{field}" : field
|
188
|
+
end
|
189
|
+
|
190
|
+
# @return [ Object ] Update result.
|
191
|
+
#
|
192
|
+
def _paranoia_update(value)
|
193
|
+
paranoid_collection.find(atomic_selector).update_one(value)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
data/lib/mongoid-paranoia.rb
CHANGED
data/lib/mongoid_paranoia.rb
CHANGED
data/perf/scope.rb
CHANGED
@@ -1,64 +1,65 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
include Mongoid::
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
field :
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
n
|
41
|
-
n.times {|i|
|
42
|
-
n.times {|i|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
x.report(
|
51
|
-
x.report(
|
52
|
-
x.report(
|
53
|
-
x.report(
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
puts
|
58
|
-
|
59
|
-
|
60
|
-
x.report(
|
61
|
-
x.report(
|
62
|
-
x.report(
|
63
|
-
x.report(
|
64
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'mongoid'
|
5
|
+
require 'mongoid/paranoia'
|
6
|
+
require 'benchmark'
|
7
|
+
|
8
|
+
Mongoid.configure do |config|
|
9
|
+
config.connect_to('my_little_test')
|
10
|
+
end
|
11
|
+
|
12
|
+
class Model
|
13
|
+
include Mongoid::Document
|
14
|
+
field :text, type: String
|
15
|
+
|
16
|
+
index({ text: 'text' })
|
17
|
+
end
|
18
|
+
|
19
|
+
class ParanoidModel
|
20
|
+
include Mongoid::Document
|
21
|
+
include Mongoid::Paranoia
|
22
|
+
field :text, type: String
|
23
|
+
|
24
|
+
index({ text: 'text' })
|
25
|
+
end
|
26
|
+
|
27
|
+
class MetaParanoidModel
|
28
|
+
include Mongoid::Document
|
29
|
+
field :text, type: String
|
30
|
+
field :deleted_at, type: Time
|
31
|
+
default_scope -> { where(deleted_at: nil) }
|
32
|
+
|
33
|
+
index({ text: 'text' })
|
34
|
+
end
|
35
|
+
|
36
|
+
if ENV['FORCE']
|
37
|
+
Mongoid.purge!
|
38
|
+
::Mongoid::Tasks::Database.create_indexes
|
39
|
+
|
40
|
+
n = 50_000
|
41
|
+
n.times {|i| Model.create(text: "text #{i}") }
|
42
|
+
n.times {|i| ParanoidModel.create(text: "text #{i}") }
|
43
|
+
n.times {|i| MetaParanoidModel.create(text: "text #{i}") }
|
44
|
+
end
|
45
|
+
|
46
|
+
n = 100
|
47
|
+
|
48
|
+
puts 'text_search benchmark ***'
|
49
|
+
Benchmark.bm(20) do |x|
|
50
|
+
x.report('without') { n.times { Model.text_search('text').execute } }
|
51
|
+
x.report('with') { n.times { ParanoidModel.text_search('text').execute } }
|
52
|
+
x.report('meta') { n.times { MetaParanoidModel.text_search('text').execute } }
|
53
|
+
x.report('unscoped meta') { n.times { MetaParanoidModel.unscoped.text_search('text').execute } }
|
54
|
+
x.report('unscoped paranoid') { n.times { ParanoidModel.unscoped.text_search('text').execute } }
|
55
|
+
end
|
56
|
+
|
57
|
+
puts ''
|
58
|
+
puts 'Pluck all ids benchmark ***'
|
59
|
+
Benchmark.bm(20) do |x|
|
60
|
+
x.report('without') { n.times { Model.all.pluck(:id) } }
|
61
|
+
x.report('with') { n.times { ParanoidModel.all.pluck(:id) } }
|
62
|
+
x.report('meta') { n.times { MetaParanoidModel.all.pluck(:id) } }
|
63
|
+
x.report('unscoped meta') { n.times { MetaParanoidModel.unscoped.all.pluck(:id) } }
|
64
|
+
x.report('unscoped paranoid') { n.times { ParanoidModel.unscoped.all.pluck(:id) } }
|
65
|
+
end
|