mongoid 8.0.13 → 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 (225) 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 +17 -66
  6. data/lib/config/locales/en.yml +46 -40
  7. data/lib/mongoid/association/accessors.rb +3 -7
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/depending.rb +4 -8
  10. data/lib/mongoid/association/eager_loadable.rb +0 -3
  11. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  12. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  13. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  14. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  15. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  16. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  17. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +2 -2
  18. data/lib/mongoid/association/macros.rb +0 -6
  19. data/lib/mongoid/association/nested/many.rb +3 -38
  20. data/lib/mongoid/association/nested/nested_buildable.rb +0 -14
  21. data/lib/mongoid/association/nested/one.rb +40 -2
  22. data/lib/mongoid/association/proxy.rb +1 -1
  23. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  24. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  25. data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
  26. data/lib/mongoid/association/referenced/has_many/proxy.rb +4 -103
  27. data/lib/mongoid/association/reflections.rb +2 -2
  28. data/lib/mongoid/atomic.rb +7 -16
  29. data/lib/mongoid/attributes/dynamic.rb +1 -1
  30. data/lib/mongoid/attributes/nested.rb +2 -2
  31. data/lib/mongoid/attributes/processing.rb +5 -29
  32. data/lib/mongoid/attributes/projector.rb +1 -1
  33. data/lib/mongoid/attributes/readonly.rb +1 -1
  34. data/lib/mongoid/attributes.rb +8 -2
  35. data/lib/mongoid/changeable.rb +107 -5
  36. data/lib/mongoid/clients/factory.rb +0 -4
  37. data/lib/mongoid/clients/storage_options.rb +2 -5
  38. data/lib/mongoid/clients/validators/storage.rb +1 -13
  39. data/lib/mongoid/collection_configurable.rb +58 -0
  40. data/lib/mongoid/composable.rb +2 -0
  41. data/lib/mongoid/config/defaults.rb +60 -0
  42. data/lib/mongoid/config/options.rb +0 -3
  43. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  44. data/lib/mongoid/config/validators.rb +1 -0
  45. data/lib/mongoid/config.rb +88 -89
  46. data/lib/mongoid/contextual/aggregable/memory.rb +2 -5
  47. data/lib/mongoid/contextual/atomic.rb +1 -1
  48. data/lib/mongoid/contextual/memory.rb +240 -51
  49. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  50. data/lib/mongoid/contextual/mongo.rb +370 -133
  51. data/lib/mongoid/contextual/none.rb +162 -7
  52. data/lib/mongoid/contextual.rb +12 -0
  53. data/lib/mongoid/criteria/findable.rb +2 -2
  54. data/lib/mongoid/criteria/includable.rb +4 -3
  55. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
  56. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  57. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -13
  58. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  59. data/lib/mongoid/criteria/queryable/selectable.rb +43 -121
  60. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  61. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  62. data/lib/mongoid/criteria.rb +6 -5
  63. data/lib/mongoid/deprecable.rb +1 -2
  64. data/lib/mongoid/deprecation.rb +3 -3
  65. data/lib/mongoid/document.rb +1 -8
  66. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  67. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  68. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  69. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  70. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  71. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  72. data/lib/mongoid/errors.rb +4 -2
  73. data/lib/mongoid/extensions/hash.rb +3 -51
  74. data/lib/mongoid/extensions/object.rb +2 -2
  75. data/lib/mongoid/extensions/time.rb +2 -0
  76. data/lib/mongoid/fields/localized.rb +10 -0
  77. data/lib/mongoid/fields/standard.rb +10 -0
  78. data/lib/mongoid/fields.rb +59 -35
  79. data/lib/mongoid/findable.rb +27 -3
  80. data/lib/mongoid/interceptable.rb +6 -116
  81. data/lib/mongoid/matchable.rb +1 -6
  82. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  83. data/lib/mongoid/matcher/eq_impl_with_regexp.rb +5 -3
  84. data/lib/mongoid/matcher/regex.rb +9 -8
  85. data/lib/mongoid/matcher/type.rb +1 -1
  86. data/lib/mongoid/matcher.rb +0 -1
  87. data/lib/mongoid/persistable/creatable.rb +1 -0
  88. data/lib/mongoid/persistable/deletable.rb +1 -1
  89. data/lib/mongoid/persistable/savable.rb +13 -1
  90. data/lib/mongoid/persistable/unsettable.rb +2 -2
  91. data/lib/mongoid/persistable/updatable.rb +51 -1
  92. data/lib/mongoid/persistable/upsertable.rb +20 -1
  93. data/lib/mongoid/persistable.rb +3 -0
  94. data/lib/mongoid/query_cache.rb +5 -1
  95. data/lib/mongoid/railties/database.rake +7 -2
  96. data/lib/mongoid/reloadable.rb +5 -3
  97. data/lib/mongoid/stateful.rb +22 -1
  98. data/lib/mongoid/tasks/database.rake +12 -0
  99. data/lib/mongoid/tasks/database.rb +20 -0
  100. data/lib/mongoid/threaded.rb +0 -3
  101. data/lib/mongoid/timestamps/created.rb +1 -8
  102. data/lib/mongoid/traversable.rb +0 -12
  103. data/lib/mongoid/utils.rb +22 -0
  104. data/lib/mongoid/validatable/associated.rb +17 -98
  105. data/lib/mongoid/validatable/macros.rb +5 -20
  106. data/lib/mongoid/validatable.rb +4 -10
  107. data/lib/mongoid/version.rb +1 -5
  108. data/lib/mongoid/warnings.rb +17 -2
  109. data/lib/mongoid.rb +16 -3
  110. data/spec/integration/app_spec.rb +2 -20
  111. data/spec/integration/associations/embeds_one_spec.rb +5 -25
  112. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -55
  113. data/spec/integration/callbacks_spec.rb +99 -12
  114. data/spec/integration/discriminator_key_spec.rb +4 -5
  115. data/spec/integration/dots_and_dollars_spec.rb +2 -12
  116. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  117. data/spec/integration/matcher_operator_data/regex.yml +0 -21
  118. data/spec/mongoid/association/eager_spec.rb +2 -24
  119. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  120. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  121. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  122. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
  123. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  124. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  125. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -103
  126. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -195
  127. data/spec/mongoid/association/syncable_spec.rb +1 -1
  128. data/spec/mongoid/attributes/nested_spec.rb +10 -204
  129. data/spec/mongoid/attributes_spec.rb +3 -33
  130. data/spec/mongoid/changeable_spec.rb +299 -24
  131. data/spec/mongoid/clients/factory_spec.rb +0 -30
  132. data/spec/mongoid/clients_spec.rb +122 -13
  133. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  134. data/spec/mongoid/config/defaults_spec.rb +160 -0
  135. data/spec/mongoid/config_spec.rb +154 -27
  136. data/spec/mongoid/contextual/aggregable/memory_spec.rb +0 -91
  137. data/spec/mongoid/contextual/memory_spec.rb +332 -253
  138. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  139. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  140. data/spec/mongoid/contextual/none_spec.rb +49 -2
  141. data/spec/mongoid/copyable_spec.rb +2 -10
  142. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  143. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  144. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -2
  145. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  146. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +0 -235
  147. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  148. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  149. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  150. data/spec/mongoid/criteria_spec.rb +5 -11
  151. data/spec/mongoid/document_spec.rb +0 -27
  152. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  153. data/spec/mongoid/extensions/hash_spec.rb +3 -239
  154. data/spec/mongoid/extensions/time_spec.rb +8 -43
  155. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  156. data/spec/mongoid/fields/localized_spec.rb +46 -28
  157. data/spec/mongoid/fields_spec.rb +136 -77
  158. data/spec/mongoid/findable_spec.rb +391 -34
  159. data/spec/mongoid/indexable_spec.rb +16 -10
  160. data/spec/mongoid/interceptable_spec.rb +153 -442
  161. data/spec/mongoid/interceptable_spec_models.rb +111 -51
  162. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  163. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  164. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  165. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  166. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  167. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  168. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  169. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  170. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  171. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  172. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  173. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  174. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  175. data/spec/mongoid/persistence_context_spec.rb +7 -57
  176. data/spec/mongoid/query_cache_spec.rb +56 -61
  177. data/spec/mongoid/reloadable_spec.rb +24 -28
  178. data/spec/mongoid/scopable_spec.rb +70 -0
  179. data/spec/mongoid/serializable_spec.rb +23 -44
  180. data/spec/mongoid/stateful_spec.rb +122 -8
  181. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  182. data/spec/mongoid/tasks/database_spec.rb +127 -0
  183. data/spec/mongoid/timestamps/created_spec.rb +0 -23
  184. data/spec/mongoid/timestamps_spec.rb +9 -11
  185. data/spec/mongoid/touchable_spec.rb +277 -5
  186. data/spec/mongoid/touchable_spec_models.rb +3 -1
  187. data/spec/mongoid/traversable_spec.rb +9 -24
  188. data/spec/mongoid/validatable/associated_spec.rb +34 -27
  189. data/spec/mongoid/validatable/numericality_spec.rb +0 -16
  190. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  191. data/spec/mongoid_spec.rb +36 -10
  192. data/spec/shared/lib/mrss/docker_runner.rb +1 -1
  193. data/spec/shared/lib/mrss/server_version_registry.rb +1 -1
  194. data/spec/shared/lib/mrss/spec_organizer.rb +3 -32
  195. data/spec/shared/share/Dockerfile.erb +85 -15
  196. data/spec/shared/shlib/distro.sh +0 -10
  197. data/spec/shared/shlib/server.sh +27 -34
  198. data/spec/shared/shlib/set_env.sh +68 -9
  199. data/spec/spec_helper.rb +5 -0
  200. data/spec/support/expectations.rb +17 -20
  201. data/spec/support/immutable_ids.rb +118 -0
  202. data/spec/support/macros.rb +47 -15
  203. data/spec/support/models/artist.rb +0 -1
  204. data/spec/support/models/band.rb +1 -0
  205. data/spec/support/models/building.rb +2 -0
  206. data/spec/support/models/name.rb +0 -10
  207. data/spec/support/models/person.rb +0 -1
  208. data/spec/support/models/product.rb +1 -0
  209. data.tar.gz.sig +0 -0
  210. metadata +715 -671
  211. metadata.gz.sig +2 -0
  212. data/lib/mongoid/errors/in_memory_regexp_timeout.rb +0 -26
  213. data/lib/mongoid/field_readable.rb +0 -70
  214. data/lib/mongoid/matcher/regexp_budget.rb +0 -389
  215. data/lib/mongoid/validatable/numericality.rb +0 -19
  216. data/spec/integration/matcher_regexp_timeout_spec.rb +0 -215
  217. data/spec/integration/query_operator_guard_spec.rb +0 -89
  218. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -61
  219. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -61
  220. data/spec/mongoid/matcher/regexp_budget_spec.rb +0 -570
  221. data/spec/shared/CANDIDATE.md +0 -28
  222. data/spec/shared/lib/mrss/release/candidate.rb +0 -281
  223. data/spec/shared/lib/mrss/release/product_data.rb +0 -144
  224. data/spec/shared/lib/tasks/candidate.rake +0 -64
  225. data/spec/support/models/purse.rb +0 -9
@@ -22,18 +22,24 @@ module Mongoid
22
22
  :each,
23
23
  :each_with_index,
24
24
  :extras,
25
+ :fifth,
26
+ :fifth!,
25
27
  :find_one_and_delete,
26
28
  :find_one_and_replace,
27
29
  :find_one_and_update,
28
30
  :find_or_create_by,
29
31
  :find_or_create_by!,
30
32
  :find_or_initialize_by,
33
+ :first!,
31
34
  :first_or_create,
32
35
  :first_or_create!,
33
36
  :first_or_initialize,
34
37
  :for_js,
38
+ :fourth,
39
+ :fourth!,
35
40
  :geo_near,
36
41
  :includes,
42
+ :last!,
37
43
  :map_reduce,
38
44
  :max,
39
45
  :min,
@@ -41,11 +47,19 @@ module Mongoid
41
47
  :pick,
42
48
  :pluck,
43
49
  :read,
50
+ :second,
51
+ :second!,
52
+ :second_to_last,
53
+ :second_to_last!,
44
54
  :sum,
45
55
  :take,
46
56
  :take!,
47
57
  :tally,
48
58
  :text_search,
59
+ :third,
60
+ :third!,
61
+ :third_to_last,
62
+ :third_to_last!,
49
63
  :update,
50
64
  :update_all,
51
65
 
@@ -87,9 +101,19 @@ module Mongoid
87
101
  # @example Do any documents exist for the conditions?
88
102
  # Person.exists?
89
103
  #
104
+ # @example Do any documents exist for given _id.
105
+ # Person.exists?(BSON::ObjectId(...))
106
+ #
107
+ # @example Do any documents exist for given conditions.
108
+ # Person.exists?(name: "...")
109
+ #
110
+ # @param [ Hash | Object | false ] id_or_conditions an _id to
111
+ # search for, a hash of conditions, nil or false.
112
+ #
90
113
  # @return [ true | false ] If any documents exist for the conditions.
91
- def exists?
92
- with_default_scope.exists?
114
+ # Always false if passed nil or false.
115
+ def exists?(id_or_conditions = :none)
116
+ with_default_scope.exists?(id_or_conditions)
93
117
  end
94
118
 
95
119
  # Finds a +Document+ or multiple documents by their _id values.
@@ -135,7 +159,7 @@ module Mongoid
135
159
  # @note Each argument can be an individual id, an array of ids or
136
160
  # a nested array. Each array will be flattened.
137
161
  #
138
- # @param [ Object | Array<Object> ] *args The _id value(s) to find.
162
+ # @param [ [ Object | Array<Object> ]... ] *args The id(s) to find.
139
163
  #
140
164
  # @return [ Document | Array<Document> | nil ] A document or matching documents.
141
165
  #
@@ -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
@@ -17,12 +17,7 @@ module Mongoid
17
17
  #
18
18
  # @return [ true | false ] True if matches, false if not.
19
19
  def _matches?(selector)
20
- # Opens a regexp budget for this document only. Callers that match many
21
- # documents against one selector open a budget of their own first, and
22
- # this one joins it rather than giving every document a fresh limit.
23
- Matcher::RegexpBudget.open(selector) do
24
- Matcher::Expression.matches?(self, selector)
25
- end
20
+ Matcher::Expression.matches?(self, selector)
26
21
  end
27
22
  end
28
23
  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
  #
@@ -6,10 +6,12 @@ module Mongoid
6
6
  #
7
7
  # @api private
8
8
  module EqImplWithRegexp
9
- module_function def matches?(_original_operator, value, condition)
9
+ module_function def matches?(original_operator, value, condition)
10
10
  case condition
11
- when Regexp, ::BSON::Regexp::Raw
12
- value.respond_to?(:=~) && RegexpBudget.match?(value, condition)
11
+ when Regexp
12
+ value =~ condition
13
+ when ::BSON::Regexp::Raw
14
+ value =~ condition.compile
13
15
  else
14
16
  if Mongoid.compare_time_by_ms &&
15
17
  value.kind_of?(Time) && condition.kind_of?(Time)
@@ -3,24 +3,25 @@ module Mongoid
3
3
 
4
4
  # @api private
5
5
  module Regex
6
- module_function def matches?(_exists, value, condition)
7
- unless condition.is_a?(Regexp) || condition.is_a?(BSON::Regexp::Raw)
6
+ module_function def matches?(exists, value, condition)
7
+ condition = case condition
8
+ when Regexp
9
+ condition
10
+ when BSON::Regexp::Raw
11
+ condition.compile
12
+ else
8
13
  # Note that strings must have been converted to a regular expression
9
14
  # instance already (with $options taken into account, if provided).
10
15
  raise Errors::InvalidQuery, "$regex requires a regular expression argument: #{Errors::InvalidQuery.truncate_expr(condition)}"
11
16
  end
12
17
 
13
- # The condition is compiled by RegexpBudget rather than here, so that
14
- # the budget's timeout can be baked into the pattern.
15
18
  case value
16
19
  when Array
17
- # Object#=~ is gone as of Ruby 3.2, so an element that cannot be
18
- # matched against has to be rejected rather than passed to =~.
19
20
  value.any? do |v|
20
- v.respond_to?(:=~) && RegexpBudget.match?(v, condition)
21
+ v =~ condition
21
22
  end
22
23
  when String
23
- RegexpBudget.match?(value, condition)
24
+ value =~ condition
24
25
  else
25
26
  false
26
27
  end
@@ -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
@@ -124,7 +124,6 @@ require 'mongoid/matcher/nor'
124
124
  require 'mongoid/matcher/not'
125
125
  require 'mongoid/matcher/or'
126
126
  require 'mongoid/matcher/regex'
127
- require 'mongoid/matcher/regexp_budget'
128
127
  require 'mongoid/matcher/size'
129
128
  require 'mongoid/matcher/type'
130
129
  require 'mongoid/matcher/expression_operator'
@@ -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.