mongoid_paranoia 0.4.0 → 0.5.0

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