mongoid 8.0.10 → 8.1.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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +3 -3
  5. data/Rakefile +18 -67
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +3 -7
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/eager_loadable.rb +0 -3
  10. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  12. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  13. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  14. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  15. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  16. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  17. data/lib/mongoid/association/macros.rb +0 -6
  18. data/lib/mongoid/association/nested/one.rb +40 -2
  19. data/lib/mongoid/association/proxy.rb +1 -1
  20. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  21. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  22. data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
  23. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  24. data/lib/mongoid/association/reflections.rb +2 -2
  25. data/lib/mongoid/atomic.rb +7 -16
  26. data/lib/mongoid/attributes/dynamic.rb +1 -1
  27. data/lib/mongoid/attributes/nested.rb +2 -2
  28. data/lib/mongoid/attributes/processing.rb +5 -29
  29. data/lib/mongoid/attributes/projector.rb +1 -1
  30. data/lib/mongoid/attributes/readonly.rb +1 -1
  31. data/lib/mongoid/attributes.rb +8 -2
  32. data/lib/mongoid/changeable.rb +107 -5
  33. data/lib/mongoid/clients/storage_options.rb +2 -5
  34. data/lib/mongoid/clients/validators/storage.rb +1 -13
  35. data/lib/mongoid/collection_configurable.rb +58 -0
  36. data/lib/mongoid/composable.rb +2 -0
  37. data/lib/mongoid/config/defaults.rb +60 -0
  38. data/lib/mongoid/config/options.rb +0 -3
  39. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  40. data/lib/mongoid/config/validators.rb +1 -0
  41. data/lib/mongoid/config.rb +88 -27
  42. data/lib/mongoid/contextual/atomic.rb +1 -1
  43. data/lib/mongoid/contextual/memory.rb +233 -33
  44. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  45. data/lib/mongoid/contextual/mongo.rb +370 -133
  46. data/lib/mongoid/contextual/none.rb +162 -7
  47. data/lib/mongoid/contextual.rb +12 -0
  48. data/lib/mongoid/criteria/findable.rb +2 -2
  49. data/lib/mongoid/criteria/includable.rb +4 -3
  50. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
  51. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  52. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  53. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  54. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  55. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  56. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  57. data/lib/mongoid/criteria.rb +6 -5
  58. data/lib/mongoid/deprecable.rb +1 -2
  59. data/lib/mongoid/deprecation.rb +3 -3
  60. data/lib/mongoid/document.rb +1 -8
  61. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  62. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  63. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  64. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  65. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  66. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  67. data/lib/mongoid/errors.rb +4 -1
  68. data/lib/mongoid/extensions/hash.rb +2 -24
  69. data/lib/mongoid/extensions/object.rb +2 -2
  70. data/lib/mongoid/extensions/time.rb +2 -0
  71. data/lib/mongoid/fields/localized.rb +10 -0
  72. data/lib/mongoid/fields/standard.rb +10 -0
  73. data/lib/mongoid/fields.rb +59 -35
  74. data/lib/mongoid/findable.rb +27 -3
  75. data/lib/mongoid/interceptable.rb +6 -116
  76. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  77. data/lib/mongoid/matcher/type.rb +1 -1
  78. data/lib/mongoid/persistable/creatable.rb +1 -0
  79. data/lib/mongoid/persistable/deletable.rb +1 -1
  80. data/lib/mongoid/persistable/savable.rb +13 -1
  81. data/lib/mongoid/persistable/unsettable.rb +2 -2
  82. data/lib/mongoid/persistable/updatable.rb +51 -1
  83. data/lib/mongoid/persistable/upsertable.rb +20 -1
  84. data/lib/mongoid/persistable.rb +3 -0
  85. data/lib/mongoid/query_cache.rb +5 -1
  86. data/lib/mongoid/railties/database.rake +7 -2
  87. data/lib/mongoid/reloadable.rb +5 -3
  88. data/lib/mongoid/stateful.rb +22 -1
  89. data/lib/mongoid/tasks/database.rake +12 -0
  90. data/lib/mongoid/tasks/database.rb +20 -0
  91. data/lib/mongoid/timestamps/created.rb +1 -8
  92. data/lib/mongoid/traversable.rb +0 -12
  93. data/lib/mongoid/utils.rb +22 -0
  94. data/lib/mongoid/validatable/associated.rb +17 -98
  95. data/lib/mongoid/validatable/macros.rb +5 -5
  96. data/lib/mongoid/validatable.rb +4 -9
  97. data/lib/mongoid/version.rb +1 -1
  98. data/lib/mongoid/warnings.rb +17 -1
  99. data/lib/mongoid.rb +16 -3
  100. data/spec/integration/app_spec.rb +2 -6
  101. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
  102. data/spec/integration/callbacks_spec.rb +99 -12
  103. data/spec/integration/discriminator_key_spec.rb +4 -5
  104. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  105. data/spec/mongoid/association/eager_spec.rb +2 -24
  106. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  107. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  108. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  109. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
  110. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  111. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  112. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -55
  113. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  114. data/spec/mongoid/association/syncable_spec.rb +1 -1
  115. data/spec/mongoid/association_spec.rb +0 -60
  116. data/spec/mongoid/attributes_spec.rb +3 -33
  117. data/spec/mongoid/changeable_spec.rb +299 -24
  118. data/spec/mongoid/clients_spec.rb +122 -13
  119. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  120. data/spec/mongoid/config/defaults_spec.rb +160 -0
  121. data/spec/mongoid/config_spec.rb +154 -27
  122. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  123. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  124. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  125. data/spec/mongoid/contextual/none_spec.rb +49 -2
  126. data/spec/mongoid/copyable_spec.rb +2 -10
  127. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  128. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  129. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  130. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  131. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  132. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  133. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  134. data/spec/mongoid/criteria_spec.rb +5 -9
  135. data/spec/mongoid/document_spec.rb +0 -27
  136. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  137. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  138. data/spec/mongoid/extensions/time_spec.rb +8 -43
  139. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  140. data/spec/mongoid/fields/localized_spec.rb +46 -28
  141. data/spec/mongoid/fields_spec.rb +136 -77
  142. data/spec/mongoid/findable_spec.rb +391 -34
  143. data/spec/mongoid/indexable_spec.rb +16 -10
  144. data/spec/mongoid/interceptable_spec.rb +153 -442
  145. data/spec/mongoid/interceptable_spec_models.rb +111 -51
  146. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  147. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  148. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  149. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  150. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  151. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  152. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  153. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  154. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  155. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  156. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  157. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  158. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  159. data/spec/mongoid/persistence_context_spec.rb +7 -57
  160. data/spec/mongoid/query_cache_spec.rb +56 -61
  161. data/spec/mongoid/reloadable_spec.rb +24 -28
  162. data/spec/mongoid/scopable_spec.rb +70 -0
  163. data/spec/mongoid/serializable_spec.rb +23 -44
  164. data/spec/mongoid/stateful_spec.rb +122 -8
  165. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  166. data/spec/mongoid/tasks/database_spec.rb +127 -0
  167. data/spec/mongoid/timestamps/created_spec.rb +0 -23
  168. data/spec/mongoid/timestamps_spec.rb +9 -11
  169. data/spec/mongoid/touchable_spec.rb +277 -5
  170. data/spec/mongoid/touchable_spec_models.rb +3 -1
  171. data/spec/mongoid/traversable_spec.rb +9 -24
  172. data/spec/mongoid/validatable/associated_spec.rb +34 -27
  173. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  174. data/spec/mongoid_spec.rb +36 -10
  175. data/spec/shared/LICENSE +20 -0
  176. data/spec/shared/bin/get-mongodb-download-url +17 -0
  177. data/spec/shared/bin/s3-copy +45 -0
  178. data/spec/shared/bin/s3-upload +69 -0
  179. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  180. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  181. data/spec/shared/lib/mrss/constraints.rb +378 -0
  182. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  183. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  184. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  185. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  186. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  187. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  188. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  189. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  190. data/spec/shared/lib/mrss/utils.rb +37 -0
  191. data/spec/shared/share/Dockerfile.erb +321 -0
  192. data/spec/shared/share/haproxy-1.conf +16 -0
  193. data/spec/shared/share/haproxy-2.conf +17 -0
  194. data/spec/shared/shlib/config.sh +27 -0
  195. data/spec/shared/shlib/distro.sh +74 -0
  196. data/spec/shared/shlib/server.sh +416 -0
  197. data/spec/shared/shlib/set_env.sh +169 -0
  198. data/spec/spec_helper.rb +5 -0
  199. data/spec/support/immutable_ids.rb +118 -0
  200. data/spec/support/macros.rb +47 -15
  201. data/spec/support/models/artist.rb +0 -1
  202. data/spec/support/models/band.rb +1 -0
  203. data/spec/support/models/building.rb +2 -0
  204. data/spec/support/models/name.rb +0 -10
  205. data/spec/support/models/person.rb +0 -1
  206. data/spec/support/models/product.rb +1 -0
  207. data.tar.gz.sig +0 -0
  208. metadata +745 -637
  209. metadata.gz.sig +2 -0
  210. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  211. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  212. data/spec/support/models/purse.rb +0 -9
@@ -140,32 +140,6 @@ module Mongoid
140
140
  #
141
141
  # @api private
142
142
  def _mongoid_run_child_callbacks(kind, children: nil, &block)
143
- if Mongoid::Config.around_callbacks_for_embeds
144
- _mongoid_run_child_callbacks_with_around(kind,
145
- children: children,
146
- &block)
147
- else
148
- _mongoid_run_child_callbacks_without_around(kind,
149
- children: children,
150
- &block)
151
- end
152
- end
153
-
154
- # Execute the callbacks of given kind for embedded documents including
155
- # around callbacks.
156
- #
157
- # @note This method is prone to stack overflow errors if the document
158
- # has a large number of embedded documents. It is recommended to avoid
159
- # using around callbacks for embedded documents until a proper solution
160
- # is implemented.
161
- #
162
- # @param [ Symbol ] kind The type of callback to execute.
163
- # @param [ Array<Document> ] children Children to execute callbacks on. If
164
- # nil, callbacks will be executed on all cascadable children of
165
- # the document.
166
- #
167
- # @api private
168
- def _mongoid_run_child_callbacks_with_around(kind, children: nil, &block)
169
143
  child, *tail = (children || cascadable_children(kind))
170
144
  with_children = !Mongoid::Config.prevent_multiple_calls_of_embedded_callbacks
171
145
  if child.nil?
@@ -179,83 +153,18 @@ module Mongoid
179
153
  end
180
154
  end
181
155
 
182
- # Execute the callbacks of given kind for embedded documents without
183
- # around callbacks.
184
- #
185
- # @param [ Symbol ] kind The type of callback to execute.
186
- # @param [ Array<Document> ] children Children to execute callbacks on. If
187
- # nil, callbacks will be executed on all cascadable children of
188
- # the document.
189
- #
190
- # @api private
191
- def _mongoid_run_child_callbacks_without_around(kind, children: nil, &block)
192
- children = (children || cascadable_children(kind))
193
- callback_list = _mongoid_run_child_before_callbacks(kind, children: children)
194
- return false if callback_list == false
195
- value = block&.call
196
- callback_list.each do |_next_sequence, env|
197
- env.value &&= value
198
- end
199
- return false if _mongoid_run_child_after_callbacks(callback_list: callback_list) == false
200
-
201
- value
202
- end
203
-
204
- # Execute the before callbacks of given kind for embedded documents.
205
- #
206
- # @param [ Symbol ] kind The type of callback to execute.
207
- # @param [ Array<Document> ] children Children to execute callbacks on.
208
- # @param [ Array<ActiveSupport::Callbacks::CallbackSequence, ActiveSupport::Callbacks::Filters::Environment> ] callback_list List of
209
- # pairs of callback sequence and environment. This list will be later used
210
- # to execute after callbacks in reverse order.
211
- #
212
- # @api private
213
- def _mongoid_run_child_before_callbacks(kind, children: [], callback_list: [])
214
- children.each do |child|
215
- chain = child.__callbacks[child_callback_type(kind, child)]
216
- env = ActiveSupport::Callbacks::Filters::Environment.new(child, false, nil)
217
- next_sequence = compile_callbacks(chain)
218
- unless next_sequence.final?
219
- Mongoid.logger.warn("Around callbacks are disabled for embedded documents. Skipping around callbacks for #{child.class.name}.")
220
- Mongoid.logger.warn("To enable around callbacks for embedded documents, set Mongoid::Config.around_callbacks_for_embeds to true.")
221
- end
222
- next_sequence.invoke_before(env)
223
- return false if env.halted
224
- env.value = !env.halted
225
- callback_list << [next_sequence, env]
226
- end
227
- callback_list
228
- end
229
-
230
- # Execute the after callbacks.
231
- #
232
- # @param [ Array<ActiveSupport::Callbacks::CallbackSequence, ActiveSupport::Callbacks::Filters::Environment> ] callback_list List of
233
- # pairs of callback sequence and environment.
234
- def _mongoid_run_child_after_callbacks(callback_list: [])
235
- callback_list.reverse_each do |next_sequence, env|
236
- next_sequence.invoke_after(env)
237
- return false if env.halted
238
- end
239
- end
240
-
241
- # Returns the stored callbacks to be executed later.
156
+ # This is used to store callbacks to be executed later. A good use case for
157
+ # this is delaying the after_find and after_initialize callbacks until the
158
+ # associations are set on the document. This can also be used to delay
159
+ # applying the defaults on a document.
242
160
  #
243
- # @return [ Array<Symbol> ] Method symbols of the stored pending callbacks.
161
+ # @return [ Array<Symbol> ] an array of symbols that represent the pending callbacks.
244
162
  #
245
163
  # @api private
246
164
  def pending_callbacks
247
165
  @pending_callbacks ||= [].to_set
248
166
  end
249
167
 
250
- # Stores callbacks to be executed later. A good use case for
251
- # this is delaying the after_find and after_initialize callbacks until the
252
- # associations are set on the document. This can also be used to delay
253
- # applying the defaults on a document.
254
- #
255
- # @param [ Array<Symbol> ] value Method symbols of the pending callbacks to store.
256
- #
257
- # @return [ Array<Symbol> ] Method symbols of the stored pending callbacks.
258
- #
259
168
  # @api private
260
169
  def pending_callbacks=(value)
261
170
  @pending_callbacks = value
@@ -390,7 +299,7 @@ module Mongoid
390
299
  end
391
300
  self.class.send :define_method, name do
392
301
  env = ActiveSupport::Callbacks::Filters::Environment.new(self, false, nil)
393
- sequence = compile_callbacks(chain)
302
+ sequence = chain.compile
394
303
  sequence.invoke_before(env)
395
304
  env.value = !env.halted
396
305
  sequence.invoke_after(env)
@@ -400,24 +309,5 @@ module Mongoid
400
309
  end
401
310
  send(name)
402
311
  end
403
-
404
- # Compile the callback chain.
405
- #
406
- # This method hides the differences between ActiveSupport implementations
407
- # before and after 7.1.
408
- #
409
- # @param [ ActiveSupport::Callbacks::CallbackChain ] chain The callback chain.
410
- # @param [ Symbol | nil ] type The type of callback chain to compile.
411
- #
412
- # @return [ ActiveSupport::Callbacks::CallbackSequence ] The compiled callback sequence.
413
- def compile_callbacks(chain, type = nil)
414
- if chain.method(:compile).arity == 0
415
- # ActiveSupport < 7.1
416
- chain.compile
417
- else
418
- # ActiveSupport >= 7.1
419
- chain.compile(type)
420
- end
421
- end
422
312
  end
423
313
  end
@@ -46,7 +46,7 @@ module Mongoid
46
46
  end
47
47
  end
48
48
 
49
- # Per https://docs.mongodb.com/ruby-driver/current/tutorials/bson-v4/#time-instances,
49
+ # Per https://www.mongodb.com/docs/ruby-driver/current/tutorials/bson-v4/#time-instances,
50
50
  # > Times in BSON (and MongoDB) can only have millisecond precision. When Ruby Time instances
51
51
  # are serialized to BSON or Extended JSON, the times are floored to the nearest millisecond.
52
52
  #
@@ -1,7 +1,7 @@
1
1
  module Mongoid
2
2
  module Matcher
3
3
 
4
- # @see https://docs.mongodb.com/manual/reference/operator/query/type/
4
+ # @see https://www.mongodb.com/docs/manual/reference/operator/query/type/
5
5
  #
6
6
  # @api private
7
7
  module Type
@@ -100,6 +100,7 @@ module Mongoid
100
100
  #
101
101
  # @return [ Document ] The document.
102
102
  def prepare_insert(options = {})
103
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
103
104
  return self if performing_validations?(options) &&
104
105
  invalid?(options[:context] || :create)
105
106
  run_callbacks(:save, with_children: false) do
@@ -16,7 +16,6 @@ module Mongoid
16
16
  #
17
17
  # @return [ TrueClass ] True.
18
18
  def delete(options = {})
19
- raise Errors::ReadonlyDocument.new(self.class) if readonly?
20
19
  prepare_delete do
21
20
  unless options[:persist] == false
22
21
  if embedded?
@@ -102,6 +101,7 @@ module Mongoid
102
101
  #
103
102
  # @return [ Object ] The result of the block.
104
103
  def prepare_delete
104
+ raise Errors::ReadonlyDocument.new(self.class) if readonly?
105
105
  yield(self)
106
106
  freeze
107
107
  self.destroyed = true
@@ -14,7 +14,13 @@ module Mongoid
14
14
  #
15
15
  # @param [ Hash ] options Options to pass to the save.
16
16
  #
17
- # @return [ true | false ] True is success, false if not.
17
+ # @option options [ true | false ] :touch Whether or not the updated_at
18
+ # attribute will be updated with the current time. When this option is
19
+ # false, none of the embedded documents will be touched. This option is
20
+ # ignored when saving a new document, and the created_at and updated_at
21
+ # will be set to the current time.
22
+ #
23
+ # @return [ true | false ] True if success, false if not.
18
24
  def save(options = {})
19
25
  if new_record?
20
26
  !insert(options).new_record?
@@ -31,6 +37,12 @@ module Mongoid
31
37
  #
32
38
  # @param [ Hash ] options Options to pass to the save.
33
39
  #
40
+ # @option options [ true | false ] :touch Whether or not the updated_at
41
+ # attribute will be updated with the current time. When this option is
42
+ # false, none of the embedded documents will be touched.This option is
43
+ # ignored when saving a new document, and the created_at and updated_at
44
+ # will be set to the current time.
45
+ #
34
46
  # @raise [ Errors::Validations ] If validation failed.
35
47
  # @raise [ Errors::Callback ] If a callback returns false.
36
48
  #
@@ -13,8 +13,8 @@ module Mongoid
13
13
  # @example Unset the values.
14
14
  # document.unset(:first_name, :last_name, :middle)
15
15
  #
16
- # @param [ Array<String | Symbol> ] fields The names of the fields to
17
- # unset.
16
+ # @param [ [ String | Symbol | Array<String | Symbol>]... ] *fields
17
+ # The names of the field(s) to unset.
18
18
  #
19
19
  # @return [ Document ] The document.
20
20
  def unset(*fields)
@@ -91,16 +91,23 @@ module Mongoid
91
91
  #
92
92
  # @param [ Hash ] options The options.
93
93
  #
94
+ # @option options [ true | false ] :touch Whether or not the updated_at
95
+ # attribute will be updated with the current time.
96
+ #
94
97
  # @return [ true | false ] The result of the update.
95
98
  def prepare_update(options = {})
99
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
100
+ enforce_immutability_of_id_field!
96
101
  return false if performing_validations?(options) &&
97
102
  invalid?(options[:context] || :update)
98
103
  process_flagged_destroys
104
+ update_children = cascadable_children(:update)
105
+ process_touch_option(options, update_children)
99
106
  run_callbacks(:save, with_children: false) do
100
107
  run_callbacks(:update, with_children: false) do
101
108
  run_callbacks(:persist_parent, with_children: false) do
102
109
  _mongoid_run_child_callbacks(:save) do
103
- _mongoid_run_child_callbacks(:update) do
110
+ _mongoid_run_child_callbacks(:update, children: update_children) do
104
111
  result = yield(self)
105
112
  self.previously_new_record = false
106
113
  post_process_persist(result, options)
@@ -160,6 +167,49 @@ module Mongoid
160
167
  end
161
168
  end
162
169
  end
170
+
171
+ # If there is a touch option and it is false, this method will call the
172
+ # timeless method so that the updated_at attribute is not updated. It
173
+ # will call the timeless method on all of the cascadable children as
174
+ # well. Note that timeless is cleared in the before_update callback.
175
+ #
176
+ # @param [ Hash ] options The options.
177
+ # @param [ Array<Document> ] children The children that the :update
178
+ # callbacks will be executed on.
179
+ #
180
+ # @option options [ true | false ] :touch Whether or not the updated_at
181
+ # attribute will be updated with the current time.
182
+ def process_touch_option(options, children)
183
+ unless options.fetch(:touch, true)
184
+ timeless
185
+ children.each(&:timeless)
186
+ end
187
+ end
188
+
189
+ # Checks to see if the _id field has been modified. If it has, and if
190
+ # the document has already been persisted, this is an error. Otherwise,
191
+ # returns without side-effects.
192
+ #
193
+ # Note that if `Mongoid::Config.immutable_ids` is false, this will do
194
+ # nothing.
195
+ #
196
+ # @raise [ Errors::ImmutableAttribute ] if _id has changed, and document
197
+ # has been persisted.
198
+ def enforce_immutability_of_id_field!
199
+ # special case here: we *do* allow the _id to be mutated if it was
200
+ # previously nil. This addresses an odd case exposed in
201
+ # has_one/proxy_spec.rb where `person.create_address` would
202
+ # (somehow?) create the address with a nil _id first, before then
203
+ # saving it *again* with the correct _id.
204
+
205
+ if _id_changed? && !_id_was.nil? && persisted?
206
+ if Mongoid::Config.immutable_ids
207
+ raise Errors::ImmutableAttribute.new(:_id, _id)
208
+ else
209
+ Mongoid::Warnings.warn_mutable_ids
210
+ end
211
+ end
212
+ end
163
213
  end
164
214
  end
165
215
  end
@@ -10,16 +10,32 @@ module Mongoid
10
10
  # database, then Mongo will insert a new one, otherwise the fields will get
11
11
  # overwritten with new values on the existing document.
12
12
  #
13
+ # If the replace option is true, unspecified attributes will be dropped,
14
+ # and if it is false, unspecified attributes will be maintained. The
15
+ # replace option defaults to true in Mongoid 8.1 and earlier. The default
16
+ # will be flipped to false in Mongoid 9.
17
+ #
13
18
  # @example Upsert the document.
14
19
  # document.upsert
15
20
  #
21
+ # @example Upsert the document without replace.
22
+ # document.upsert(replace: false)
23
+ #
16
24
  # @param [ Hash ] options The validation options.
17
25
  #
26
+ # @option options [ true | false ] :validate Whether or not to validate.
27
+ # @option options [ true | false ] :replace Whether or not to replace the document on upsert.
28
+ #
18
29
  # @return [ true ] True.
19
30
  def upsert(options = {})
20
31
  prepare_upsert(options) do
21
- collection.find(atomic_selector).replace_one(
32
+ if options.fetch(:replace, true)
33
+ collection.find(atomic_selector).replace_one(
22
34
  as_attributes, upsert: true, session: _session)
35
+ else
36
+ collection.find(atomic_selector).update_one(
37
+ { "$set" => as_attributes }, upsert: true, session: _session)
38
+ end
23
39
  end
24
40
  end
25
41
 
@@ -36,8 +52,11 @@ module Mongoid
36
52
  #
37
53
  # @param [ Hash ] options The options hash.
38
54
  #
55
+ # @option options [ true | false ] :validate Whether or not to validate.
56
+ #
39
57
  # @return [ true | false ] If the operation succeeded.
40
58
  def prepare_upsert(options = {})
59
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
41
60
  return false if performing_validations?(options) && invalid?(:upsert)
42
61
  result = run_callbacks(:upsert) do
43
62
  yield(self)
@@ -161,6 +161,8 @@ module Mongoid
161
161
  # @param [ Object ] result The result of the operation.
162
162
  # @param [ Hash ] options The options.
163
163
  #
164
+ # @option options [ true | false ] :validate Whether or not to validate.
165
+ #
164
166
  # @return [ true ] true.
165
167
  def post_process_persist(result, options = {})
166
168
  post_persist unless result == false
@@ -180,6 +182,7 @@ module Mongoid
180
182
  #
181
183
  # @return [ Object ] The result of the operation.
182
184
  def prepare_atomic_operation
185
+ raise Errors::ReadonlyDocument.new(self.class) if readonly? && !Mongoid.legacy_readonly
183
186
  operations = yield({})
184
187
  persist_or_delay_atomic_operation(operations)
185
188
  self
@@ -14,6 +14,7 @@ module Mongoid
14
14
  #
15
15
  # @return [ nil ] Always nil.
16
16
  def clear_cache
17
+ Mongoid::Warnings.warn_mongoid_query_cache_clear
17
18
  Mongo::QueryCache.clear
18
19
  end
19
20
 
@@ -24,6 +25,7 @@ module Mongoid
24
25
  #
25
26
  # @param [ true | false ] value The enabled value.
26
27
  def enabled=(value)
28
+ Mongoid::Warnings.warn_mongoid_query_cache
27
29
  Mongo::QueryCache.enabled = value
28
30
  end
29
31
 
@@ -34,6 +36,7 @@ module Mongoid
34
36
  #
35
37
  # @return [ true | false ] If the cache is enabled.
36
38
  def enabled?
39
+ Mongoid::Warnings.warn_mongoid_query_cache
37
40
  Mongo::QueryCache.enabled?
38
41
  end
39
42
 
@@ -44,6 +47,7 @@ module Mongoid
44
47
  #
45
48
  # @return [ Object ] The result of the block.
46
49
  def cache(&block)
50
+ Mongoid::Warnings.warn_mongoid_query_cache
47
51
  Mongo::QueryCache.cache(&block)
48
52
  end
49
53
 
@@ -54,6 +58,7 @@ module Mongoid
54
58
  #
55
59
  # @return [ Object ] The result of the block.
56
60
  def uncached(&block)
61
+ Mongoid::Warnings.warn_mongoid_query_cache
57
62
  Mongo::QueryCache.uncached(&block)
58
63
  end
59
64
  end
@@ -61,4 +66,3 @@ module Mongoid
61
66
  Middleware = Mongo::QueryCache::Middleware
62
67
  end
63
68
  end
64
-
@@ -25,7 +25,7 @@ namespace :db do
25
25
 
26
26
  unless Rake::Task.task_defined?("db:setup")
27
27
  desc "Create the database, and initialize with the seed data"
28
- task :setup => [ "db:create", "mongoid:create_indexes", "db:seed" ]
28
+ task :setup => [ "db:create", "mongoid:create_collections", "mongoid:create_indexes", "db:seed" ]
29
29
  end
30
30
 
31
31
  unless Rake::Task.task_defined?("db:reset")
@@ -55,10 +55,15 @@ namespace :db do
55
55
 
56
56
  unless Rake::Task.task_defined?("db:test:prepare")
57
57
  namespace :test do
58
- task :prepare => "mongoid:create_indexes"
58
+ task :prepare => ["mongoid:create_collections", "mongoid:create_indexes"]
59
59
  end
60
60
  end
61
61
 
62
+ unless Rake::Task.task_defined?("db:create_collections")
63
+ desc "Create collections specified in Mongoid models"
64
+ task :create_collections => "mongoid:create_collections"
65
+ end
66
+
62
67
  unless Rake::Task.task_defined?("db:create_indexes")
63
68
  desc "Create indexes specified in Mongoid models"
64
69
  task :create_indexes => "mongoid:create_indexes"
@@ -16,14 +16,16 @@ module Mongoid
16
16
  #
17
17
  # @return [ Document ] The document, reloaded.
18
18
  def reload
19
+ if @atomic_selector
20
+ # Clear atomic_selector cache for sharded clusters. MONGOID-5076
21
+ remove_instance_variable('@atomic_selector')
22
+ end
23
+
19
24
  reloaded = _reload
20
25
  if Mongoid.raise_not_found_error && (reloaded.nil? || reloaded.empty?)
21
26
  shard_keys = atomic_selector.with_indifferent_access.slice(*shard_key_fields, :_id)
22
27
  raise Errors::DocumentNotFound.new(self.class, _id, shard_keys)
23
28
  end
24
-
25
- reset_atomic_updates!
26
-
27
29
  @attributes = reloaded
28
30
  @attributes_before_type_cast = @attributes.dup
29
31
  @changed_attributes = {}
@@ -95,6 +95,23 @@ module Mongoid
95
95
  !_parent.delayed_atomic_sets[atomic_path]
96
96
  end
97
97
 
98
+ # Flags the document as readonly. Will cause a ReadonlyDocument error to be
99
+ # raised if the document is attempted to be saved, updated or destroyed.
100
+ #
101
+ # @example Flag the document as readonly.
102
+ # document.readonly!
103
+ #
104
+ # @return [ true | false ] true if the document was successfully marked
105
+ # readonly, false otherwise.
106
+ def readonly!
107
+ if Mongoid.legacy_readonly
108
+ Mongoid::Warnings.warn_legacy_readonly
109
+ false
110
+ else
111
+ @readonly = true
112
+ end
113
+ end
114
+
98
115
  # Is the document readonly?
99
116
  #
100
117
  # @example Is the document readonly?
@@ -102,7 +119,11 @@ module Mongoid
102
119
  #
103
120
  # @return [ true | false ] If the document is readonly.
104
121
  def readonly?
105
- __selected_fields != nil
122
+ if Mongoid.legacy_readonly
123
+ __selected_fields != nil
124
+ else
125
+ @readonly ||= false
126
+ end
106
127
  end
107
128
 
108
129
  # Determine if the document can be set.
@@ -5,6 +5,11 @@ namespace :db do
5
5
  task :load_models do
6
6
  end
7
7
 
8
+ desc "Create collections for Mongoid models"
9
+ task :create_collections => [:environment, :load_models] do
10
+ ::Mongoid::Tasks::Database.create_collections
11
+ end
12
+
8
13
  desc "Create indexes specified in Mongoid models"
9
14
  task :create_indexes => [:environment, :load_models] do
10
15
  ::Mongoid::Tasks::Database.create_indexes
@@ -34,5 +39,12 @@ namespace :db do
34
39
  task :purge => :environment do
35
40
  ::Mongoid.purge!
36
41
  end
42
+
43
+ namespace :create_collections do
44
+ desc "Drop and create collections for Mongoid models"
45
+ task :force => [:environment, :load_models] do
46
+ ::Mongoid::Tasks::Database.create_collections(force: true)
47
+ end
48
+ end
37
49
  end
38
50
  end
@@ -5,6 +5,26 @@ module Mongoid
5
5
  module Database
6
6
  extend self
7
7
 
8
+ # Create collections for each model given the provided globs and the class is
9
+ # not embedded.
10
+ #
11
+ # @param [ Array<Mongoid::Document> ] models. Array of document classes for
12
+ # which collections should be created. Defaulted to all document classes
13
+ # in the application.
14
+ # @param [ true | false ] force If true, the method will drop existing
15
+ # collections before creating new ones. If false, the method will create
16
+ # only new collection (that do not exist in the database).
17
+ def create_collections(models = ::Mongoid.models, force: false)
18
+ models.each do |model|
19
+ if !model.embedded? || model.cyclic?
20
+ model.create_collection(force: force)
21
+ logger.info("MONGOID: Created collection for #{model}:")
22
+ else
23
+ logger.info("MONGOID: collection options ignored on: #{model}, please define in the root model.")
24
+ end
25
+ end
26
+ end
27
+
8
28
  # Create indexes for each model given the provided globs and the class is
9
29
  # not embedded.
10
30
  #
@@ -22,20 +22,13 @@ module Mongoid
22
22
  # @example Set the created at time.
23
23
  # person.set_created_at
24
24
  def set_created_at
25
- if able_to_set_created_at?
25
+ if !timeless? && !created_at
26
26
  time = Time.configured.now
27
27
  self.updated_at = time if is_a?(Updated) && !updated_at_changed?
28
28
  self.created_at = time
29
29
  end
30
30
  clear_timeless_option
31
31
  end
32
-
33
- # Is the created timestamp able to be set?
34
- #
35
- # @return [ true, false ] If the timestamp can be set.
36
- def able_to_set_created_at?
37
- !frozen? && !timeless? && !created_at
38
- end
39
32
  end
40
33
  end
41
34
  end
@@ -300,18 +300,6 @@ module Mongoid
300
300
  !!(Mongoid::Document > superclass)
301
301
  end
302
302
 
303
- # Returns the root class of the STI tree that the current
304
- # class participates in. If the class is not an STI subclass, this
305
- # returns the class itself.
306
- #
307
- # @return [ Mongoid::Document ] the root of the STI tree
308
- def root_class
309
- root = self
310
- root = root.superclass while root.hereditary?
311
-
312
- root
313
- end
314
-
315
303
  # When inheriting, we want to copy the fields from the parent class and
316
304
  # set the on the child to start, mimicking the behavior of the old
317
305
  # class_inheritable_accessor that was deprecated in Rails edge.
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+
5
+ # @api private
6
+ module Utils
7
+
8
+ # This function should be used if you need to measure time.
9
+ # @example Calculate elapsed time.
10
+ # starting = Utils.monotonic_time
11
+ # # do something time consuming
12
+ # ending = Utils.monotonic_time
13
+ # puts "It took #{(ending - starting).to_i} seconds"
14
+ #
15
+ # @see https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/
16
+ #
17
+ # @return [Float] seconds according to monotonic clock
18
+ module_function def monotonic_time
19
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
20
+ end
21
+ end
22
+ end