mongoid 8.0.8 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) 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 +0 -25
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +2 -2
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  10. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  13. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  14. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  15. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  16. data/lib/mongoid/association/macros.rb +0 -6
  17. data/lib/mongoid/association/nested/one.rb +40 -2
  18. data/lib/mongoid/association/proxy.rb +1 -1
  19. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  20. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  21. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -2
  22. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  23. data/lib/mongoid/association/reflections.rb +2 -2
  24. data/lib/mongoid/atomic.rb +7 -16
  25. data/lib/mongoid/attributes/dynamic.rb +1 -1
  26. data/lib/mongoid/attributes/nested.rb +2 -2
  27. data/lib/mongoid/attributes/processing.rb +5 -29
  28. data/lib/mongoid/attributes/projector.rb +1 -1
  29. data/lib/mongoid/attributes/readonly.rb +1 -1
  30. data/lib/mongoid/attributes.rb +8 -2
  31. data/lib/mongoid/changeable.rb +107 -5
  32. data/lib/mongoid/clients/storage_options.rb +2 -5
  33. data/lib/mongoid/clients/validators/storage.rb +1 -13
  34. data/lib/mongoid/collection_configurable.rb +58 -0
  35. data/lib/mongoid/composable.rb +2 -0
  36. data/lib/mongoid/config/defaults.rb +60 -0
  37. data/lib/mongoid/config/options.rb +0 -3
  38. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  39. data/lib/mongoid/config/validators.rb +1 -0
  40. data/lib/mongoid/config.rb +99 -28
  41. data/lib/mongoid/contextual/atomic.rb +1 -1
  42. data/lib/mongoid/contextual/memory.rb +233 -33
  43. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  44. data/lib/mongoid/contextual/mongo.rb +370 -133
  45. data/lib/mongoid/contextual/none.rb +162 -7
  46. data/lib/mongoid/contextual.rb +12 -0
  47. data/lib/mongoid/criteria/findable.rb +2 -2
  48. data/lib/mongoid/criteria/includable.rb +4 -3
  49. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  50. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  51. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  52. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  53. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  54. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  55. data/lib/mongoid/criteria.rb +6 -5
  56. data/lib/mongoid/deprecable.rb +1 -2
  57. data/lib/mongoid/deprecation.rb +3 -3
  58. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  59. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  60. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  61. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  62. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  63. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  64. data/lib/mongoid/errors.rb +4 -1
  65. data/lib/mongoid/extensions/hash.rb +2 -24
  66. data/lib/mongoid/extensions/object.rb +2 -2
  67. data/lib/mongoid/extensions/time.rb +2 -0
  68. data/lib/mongoid/fields/localized.rb +10 -0
  69. data/lib/mongoid/fields/standard.rb +10 -0
  70. data/lib/mongoid/fields.rb +53 -24
  71. data/lib/mongoid/findable.rb +27 -3
  72. data/lib/mongoid/interceptable.rb +10 -118
  73. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  74. data/lib/mongoid/matcher/type.rb +1 -1
  75. data/lib/mongoid/persistable/creatable.rb +1 -0
  76. data/lib/mongoid/persistable/deletable.rb +1 -1
  77. data/lib/mongoid/persistable/savable.rb +13 -1
  78. data/lib/mongoid/persistable/unsettable.rb +2 -2
  79. data/lib/mongoid/persistable/updatable.rb +51 -1
  80. data/lib/mongoid/persistable/upsertable.rb +20 -1
  81. data/lib/mongoid/persistable.rb +3 -0
  82. data/lib/mongoid/query_cache.rb +5 -1
  83. data/lib/mongoid/railties/database.rake +7 -2
  84. data/lib/mongoid/reloadable.rb +5 -3
  85. data/lib/mongoid/stateful.rb +22 -1
  86. data/lib/mongoid/tasks/database.rake +12 -0
  87. data/lib/mongoid/tasks/database.rb +20 -0
  88. data/lib/mongoid/utils.rb +22 -0
  89. data/lib/mongoid/validatable/associated.rb +18 -96
  90. data/lib/mongoid/validatable/macros.rb +5 -5
  91. data/lib/mongoid/validatable.rb +4 -9
  92. data/lib/mongoid/version.rb +1 -1
  93. data/lib/mongoid/warnings.rb +17 -1
  94. data/lib/mongoid.rb +16 -3
  95. data/spec/integration/app_spec.rb +2 -2
  96. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
  97. data/spec/integration/callbacks_models.rb +37 -0
  98. data/spec/integration/callbacks_spec.rb +126 -12
  99. data/spec/integration/discriminator_key_spec.rb +4 -5
  100. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  101. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  102. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  103. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  104. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  105. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  106. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +5 -27
  107. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  108. data/spec/mongoid/association/syncable_spec.rb +1 -1
  109. data/spec/mongoid/attributes_spec.rb +3 -33
  110. data/spec/mongoid/changeable_spec.rb +299 -24
  111. data/spec/mongoid/clients_spec.rb +122 -13
  112. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  113. data/spec/mongoid/config/defaults_spec.rb +160 -0
  114. data/spec/mongoid/config_spec.rb +154 -27
  115. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  116. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  117. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  118. data/spec/mongoid/contextual/none_spec.rb +49 -2
  119. data/spec/mongoid/copyable_spec.rb +2 -10
  120. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  121. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  122. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  123. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  124. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  125. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  126. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  127. data/spec/mongoid/criteria_spec.rb +5 -9
  128. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  129. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  130. data/spec/mongoid/extensions/time_spec.rb +8 -43
  131. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  132. data/spec/mongoid/fields/localized_spec.rb +46 -28
  133. data/spec/mongoid/fields_spec.rb +136 -77
  134. data/spec/mongoid/findable_spec.rb +391 -34
  135. data/spec/mongoid/indexable_spec.rb +16 -10
  136. data/spec/mongoid/interceptable_spec.rb +173 -362
  137. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  138. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  139. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  140. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  141. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  142. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  143. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  144. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  145. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  146. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  147. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  148. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  149. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  150. data/spec/mongoid/persistence_context_spec.rb +7 -57
  151. data/spec/mongoid/query_cache_spec.rb +56 -61
  152. data/spec/mongoid/reloadable_spec.rb +24 -28
  153. data/spec/mongoid/scopable_spec.rb +70 -0
  154. data/spec/mongoid/serializable_spec.rb +9 -30
  155. data/spec/mongoid/stateful_spec.rb +122 -8
  156. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  157. data/spec/mongoid/tasks/database_spec.rb +127 -0
  158. data/spec/mongoid/timestamps_spec.rb +9 -11
  159. data/spec/mongoid/touchable_spec.rb +277 -5
  160. data/spec/mongoid/touchable_spec_models.rb +3 -1
  161. data/spec/mongoid/traversable_spec.rb +9 -24
  162. data/spec/mongoid/validatable/associated_spec.rb +30 -13
  163. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  164. data/spec/mongoid_spec.rb +36 -10
  165. data/spec/spec_helper.rb +5 -0
  166. data/spec/support/immutable_ids.rb +118 -0
  167. data/spec/support/macros.rb +47 -15
  168. data/spec/support/models/artist.rb +0 -1
  169. data/spec/support/models/band.rb +1 -0
  170. data/spec/support/models/book.rb +1 -0
  171. data/spec/support/models/building.rb +2 -0
  172. data/spec/support/models/cover.rb +10 -0
  173. data/spec/support/models/name.rb +0 -10
  174. data/spec/support/models/person.rb +0 -1
  175. data/spec/support/models/product.rb +1 -0
  176. data.tar.gz.sig +0 -0
  177. metadata +698 -664
  178. metadata.gz.sig +0 -0
  179. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  180. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  181. data/spec/support/models/purse.rb +0 -9
@@ -43,46 +43,22 @@ module Mongoid
43
43
  # @return [ true | false ] True if pending, false if not.
44
44
  def pending_attribute?(key, value)
45
45
  name = key.to_s
46
+
46
47
  aliased = if aliased_associations.key?(name)
47
48
  aliased_associations[name]
48
49
  else
49
50
  name
50
51
  end
52
+
51
53
  if relations.has_key?(aliased)
52
- set_pending_relation(name, aliased, value)
54
+ pending_relations[name] = value
53
55
  return true
54
56
  end
55
57
  if nested_attributes.has_key?(aliased)
56
- set_pending_nested(name, aliased, value)
57
- return true
58
- end
59
- false
60
- end
61
-
62
- # Set value of the pending relation.
63
- #
64
- # @param [ Symbol ] name The name of the relation.
65
- # @param [ Symbol ] aliased The aliased name of the relation.
66
- # @param [ Object ] value The value of the relation.
67
- def set_pending_relation(name, aliased, value)
68
- if stored_as_associations.include?(name)
69
- pending_relations[aliased] = value
70
- else
71
- pending_relations[name] = value
72
- end
73
- end
74
-
75
- # Set value of the pending nested attribute.
76
- #
77
- # @param [ Symbol ] name The name of the nested attribute.
78
- # @param [ Symbol ] aliased The aliased name of the nested attribute.
79
- # @param [ Object ] value The value of the nested attribute.
80
- def set_pending_nested(name, aliased, value)
81
- if stored_as_associations.include?(name)
82
- pending_nested[aliased] = value
83
- else
84
58
  pending_nested[name] = value
59
+ return true
85
60
  end
61
+ return false
86
62
  end
87
63
 
88
64
  # Get all the pending associations that need to be set.
@@ -6,7 +6,7 @@ module Mongoid
6
6
  # This module defines projection helpers.
7
7
  #
8
8
  # Projection rules are rather non-trivial. See
9
- # https://docs.mongodb.com/manual/reference/method/db.collection.find/#find-projection
9
+ # https://www.mongodb.com/docs/manual/reference/method/db.collection.find/#find-projection
10
10
  # for server documentation.
11
11
  # 4.4 server (and presumably all older ones) requires that a projection
12
12
  # for content fields is either exclusionary or inclusionary, i.e. one
@@ -61,7 +61,7 @@ module Mongoid
61
61
  # attr_readonly :name, :genre
62
62
  # end
63
63
  #
64
- # @param [ Array<Symbol> ] names The names of the fields.
64
+ # @param [ Symbol... ] *names The names of the fields.
65
65
  def attr_readonly(*names)
66
66
  names.each do |name|
67
67
  readonly_attributes << database_field_name(name)
@@ -177,8 +177,14 @@ module Mongoid
177
177
  attribute_will_change!(field_name)
178
178
  end
179
179
  if localized
180
- attributes[field_name] ||= {}
181
- attributes[field_name].merge!(typed_value)
180
+ present = fields[field_name].try(:localize_present?)
181
+ loc_key, loc_val = typed_value.first
182
+ if present && loc_val.blank?
183
+ attributes[field_name]&.delete(loc_key)
184
+ else
185
+ attributes[field_name] ||= {}
186
+ attributes[field_name].merge!(typed_value)
187
+ end
182
188
  else
183
189
  attributes[field_name] = typed_value
184
190
  end
@@ -68,9 +68,13 @@ module Mongoid
68
68
  # @example Move the changes to previous.
69
69
  # person.move_changes
70
70
  def move_changes
71
+ @changes_before_last_save = @previous_changes
71
72
  @previous_changes = changes
73
+ @attributes_before_last_save = @previous_attributes
72
74
  @previous_attributes = attributes.dup
73
- reset_atomic_updates!
75
+ Atomic::UPDATES.each do |update|
76
+ send(update).clear
77
+ end
74
78
  changed_attributes.clear
75
79
  end
76
80
 
@@ -131,6 +135,72 @@ module Mongoid
131
135
  mods
132
136
  end
133
137
 
138
+ # Returns the original value of an attribute before the last save.
139
+ #
140
+ # This method is useful in after callbacks to get the original value of
141
+ # an attribute before the save that triggered the callbacks to run.
142
+ #
143
+ # @param [ Symbol | String ] attr The name of the attribute.
144
+ #
145
+ # @return [ Object ] Value of the attribute before the last save.
146
+ def attribute_before_last_save(attr)
147
+ attr = database_field_name(attr)
148
+ attributes_before_last_save[attr]
149
+ end
150
+
151
+ # Returns the change to an attribute during the last save.
152
+ #
153
+ # @param [ Symbol | String ] attr The name of the attribute.
154
+ #
155
+ # @return [ Array<Object> | nil ] If the attribute was changed, returns
156
+ # an array containing the original value and the saved value, otherwise nil.
157
+ def saved_change_to_attribute(attr)
158
+ attr = database_field_name(attr)
159
+ previous_changes[attr]
160
+ end
161
+
162
+ # Returns whether this attribute changed during the last save.
163
+ #
164
+ # This method is useful in after callbacks, to see the change
165
+ # in an attribute during the save that triggered the callbacks to run.
166
+ #
167
+ # @param [ String ] attr The name of the attribute.
168
+ # @param **kwargs The optional keyword arguments.
169
+ #
170
+ # @option **kwargs [ Object ] :from The object the attribute was changed from.
171
+ # @option **kwargs [ Object ] :to The object the attribute was changed to.
172
+ #
173
+ # @return [ true | false ] Whether the attribute has changed during the last save.
174
+ def saved_change_to_attribute?(attr, **kwargs)
175
+ changes = saved_change_to_attribute(attr)
176
+ return false unless changes.is_a?(Array)
177
+ if kwargs.key?(:from) && kwargs.key?(:to)
178
+ changes.first == kwargs[:from] && changes.last == kwargs[:to]
179
+ elsif kwargs.key?(:from)
180
+ changes.first == kwargs[:from]
181
+ elsif kwargs.key?(:to)
182
+ changes.last == kwargs[:to]
183
+ else
184
+ true
185
+ end
186
+ end
187
+
188
+ # Returns whether this attribute change the next time we save.
189
+ #
190
+ # This method is useful in validations and before callbacks to determine
191
+ # if the next call to save will change a particular attribute.
192
+ #
193
+ # @param [ String ] attr The name of the attribute.
194
+ # @param **kwargs The optional keyword arguments.
195
+ #
196
+ # @option **kwargs [ Object ] :from The object the attribute was changed from.
197
+ # @option **kwargs [ Object ] :to The object the attribute was changed to.
198
+ #
199
+ # @return [ true | false ] Whether the attribute change the next time we save.
200
+ def will_save_change_to_attribute?(attr, **kwargs)
201
+ attribute_changed?(attr, **kwargs)
202
+ end
203
+
134
204
  private
135
205
 
136
206
  # Get attributes of the document before the document was saved.
@@ -140,6 +210,14 @@ module Mongoid
140
210
  @previous_attributes ||= {}
141
211
  end
142
212
 
213
+ def changes_before_last_save
214
+ @changes_before_last_save ||= {}
215
+ end
216
+
217
+ def attributes_before_last_save
218
+ @attributes_before_last_save ||= {}
219
+ end
220
+
143
221
  # Get the old and new value for the provided attribute.
144
222
  #
145
223
  # @example Get the attribute change.
@@ -159,12 +237,24 @@ module Mongoid
159
237
  # model.attribute_changed?("name")
160
238
  #
161
239
  # @param [ String ] attr The name of the attribute.
240
+ # @param **kwargs The optional keyword arguments.
241
+ #
242
+ # @option **kwargs [ Object ] :from The object the attribute was changed from.
243
+ # @option **kwargs [ Object ] :to The object the attribute was changed to.
162
244
  #
163
245
  # @return [ true | false ] Whether the attribute has changed.
164
- def attribute_changed?(attr)
246
+ def attribute_changed?(attr, **kwargs)
165
247
  attr = database_field_name(attr)
166
248
  return false unless changed_attributes.key?(attr)
167
- changed_attributes[attr] != attributes[attr]
249
+ return false if changed_attributes[attr] == attributes[attr]
250
+ if kwargs.key?(:from)
251
+ return false if changed_attributes[attr] != kwargs[:from]
252
+ end
253
+ if kwargs.key?(:to)
254
+ return false if attributes[attr] != kwargs[:to]
255
+ end
256
+
257
+ true
168
258
  end
169
259
 
170
260
  # Get whether or not the field has a different value from the default.
@@ -300,8 +390,11 @@ module Mongoid
300
390
  # @param [ String ] meth The name of the accessor.
301
391
  def create_dirty_change_check(name, meth)
302
392
  generated_methods.module_eval do
303
- re_define_method("#{meth}_changed?") do
304
- attribute_changed?(name)
393
+ re_define_method("#{meth}_changed?") do |**kwargs|
394
+ attribute_changed?(name, **kwargs)
395
+ end
396
+ re_define_method("will_save_change_to_#{meth}?") do |**kwargs|
397
+ will_save_change_to_attribute?(name, **kwargs)
305
398
  end
306
399
  end
307
400
  end
@@ -336,6 +429,15 @@ module Mongoid
336
429
  re_define_method("#{meth}_previously_was") do
337
430
  attribute_previously_was(name)
338
431
  end
432
+ re_define_method("#{meth}_before_last_save") do
433
+ attribute_before_last_save(name)
434
+ end
435
+ re_define_method("saved_change_to_#{meth}") do
436
+ saved_change_to_attribute(name)
437
+ end
438
+ re_define_method("saved_change_to_#{meth}?") do |**kwargs|
439
+ saved_change_to_attribute?(name, **kwargs)
440
+ end
339
441
  end
340
442
  end
341
443
 
@@ -6,10 +6,7 @@ module Mongoid
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
-
10
- cattr_accessor :storage_options, instance_writer: false do
11
- storage_options_defaults
12
- end
9
+ class_attribute :storage_options, instance_writer: false, default: storage_options_defaults
13
10
  end
14
11
 
15
12
  module ClassMethods
@@ -49,7 +46,7 @@ module Mongoid
49
46
  # @return [ Class ] The model class.
50
47
  def store_in(options)
51
48
  Validators::Storage.validate(self, options)
52
- storage_options.merge!(options)
49
+ self.storage_options = self.storage_options.merge(options)
53
50
  end
54
51
 
55
52
  # Reset the store_in options
@@ -9,7 +9,7 @@ module Mongoid
9
9
  extend self
10
10
 
11
11
  # The valid options for storage.
12
- VALID_OPTIONS = [ :collection, :database, :client ].freeze
12
+ VALID_OPTIONS = [ :collection, :collection_options, :database, :client ].freeze
13
13
 
14
14
  # Validate the options provided to :store_in.
15
15
  #
@@ -20,21 +20,9 @@ module Mongoid
20
20
  # @param [ Hash | String | Symbol ] options The provided options.
21
21
  def validate(klass, options)
22
22
  valid_keys?(options) or raise Errors::InvalidStorageOptions.new(klass, options)
23
- valid_parent?(klass) or raise Errors::InvalidStorageParent.new(klass)
24
23
  end
25
24
 
26
25
  private
27
- # Determine if the current klass is valid to change store_in
28
- # options
29
- #
30
- # @api private
31
- #
32
- # @param [ Class ] klass
33
- #
34
- # @return [ true | false ] If the class is valid.
35
- def valid_parent?(klass)
36
- !klass.superclass.include?(Mongoid::Document)
37
- end
38
26
 
39
27
  # Determine if all keys in the options hash are valid.
40
28
  #
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+
5
+ # Encapsulates behavior around defining collections.
6
+ module CollectionConfigurable
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ # Create the collection for the called upon Mongoid model.
11
+ #
12
+ # This method does not re-create existing collections.
13
+ #
14
+ # If the document includes `store_in` macro with `collection_options` key,
15
+ # these options are used when creating the collection.
16
+ #
17
+ # @param [ true | false ] force If true, the method will drop existing
18
+ # collections before creating new ones. If false, the method will create
19
+ # only new collection (that do not exist in the database).
20
+ #
21
+ # @raise [ Errors::CreateCollectionFailure ] If collection creation failed.
22
+ # @raise [ Errors::DropCollectionFailure ] If an attempt to drop collection failed.
23
+ def create_collection(force: false)
24
+ if collection_name.empty?
25
+ # This is most probably an anonymous class, we ignore them.
26
+ return
27
+ end
28
+ if collection_name.match(/^system\./)
29
+ # We do not do anything with system collections.
30
+ return
31
+ end
32
+ if force
33
+ collection.drop
34
+ end
35
+ if coll_options = collection.database.list_collections(filter: { name: collection_name.to_s }).first
36
+ if force
37
+ raise Errors::DropCollectionFailure.new(collection_name)
38
+ else
39
+ logger.debug(
40
+ "MONGOID: Collection '#{collection_name}' already exists " +
41
+ "in database '#{database_name}' with options '#{coll_options}'."
42
+ )
43
+ end
44
+ else
45
+ begin
46
+ collection.database[collection_name, storage_options.fetch(:collection_options, {})].create
47
+ rescue Mongo::Error::OperationFailure => e
48
+ raise Errors::CreateCollectionFailure.new(
49
+ collection_name,
50
+ storage_options[:collection_options],
51
+ e
52
+ )
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "mongoid/changeable"
4
+ require "mongoid/collection_configurable"
4
5
  require "mongoid/findable"
5
6
  require "mongoid/indexable"
6
7
  require "mongoid/inspectable"
@@ -36,6 +37,7 @@ module Mongoid
36
37
  include Atomic
37
38
  include Changeable
38
39
  include Clients
40
+ include CollectionConfigurable
39
41
  include Attributes
40
42
  include Evolvable
41
43
  include Fields
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Config
5
+
6
+ # Encapsulates logic for loading defaults.
7
+ module Defaults
8
+
9
+ # Load the defaults for the feature flags in the given Mongoid version.
10
+ # Note that this method will load the *new* functionality introduced in
11
+ # the given Mongoid version.
12
+ #
13
+ # @param [ String | Float ] The version number as X.y.
14
+ #
15
+ # raises [ ArgumentError ] if an invalid version is given.
16
+ def load_defaults(version)
17
+ # Note that for 7.x, since all of the feature flag defaults have been
18
+ # flipped to the new functionality, all of the settings for those
19
+ # versions are to give old functionality. Because of this, it is
20
+ # possible to recurse to later version to get all of the options to
21
+ # turn off. Note that this won't be true when adding feature flags to
22
+ # 9.x, since the default will be the old functionality until the next
23
+ # major version is released. More likely, the recursion will have to go
24
+ # in the other direction (towards earlier versions).
25
+
26
+ case version.to_s
27
+ when "7.3"
28
+ # flags introduced in 7.4 - old functionality
29
+ self.broken_aggregables = true
30
+ self.broken_alias_handling = true
31
+ self.broken_and = true
32
+ self.broken_scoping = true
33
+ self.broken_updates = true
34
+ self.compare_time_by_ms = false
35
+ self.legacy_pluck_distinct = true
36
+ self.legacy_triple_equals = true
37
+ self.object_id_as_json_oid = true
38
+
39
+ load_defaults "7.4"
40
+ when "7.4"
41
+ # flags introduced in 7.5 - old functionality
42
+ self.legacy_attributes = true
43
+ self.overwrite_chained_operators = true
44
+
45
+ load_defaults "7.5"
46
+ when "7.5"
47
+ # flags introduced in 8.0 - old functionality
48
+ self.map_big_decimal_to_decimal128 = false
49
+ when "8.0"
50
+ # All flag defaults currently reflect 8.0 behavior.
51
+ when "8.1"
52
+ # flags introduced in 8.1 - new functionality
53
+ self.legacy_readonly = false
54
+ else
55
+ raise ArgumentError, "Unknown version: #{version}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -25,8 +25,6 @@ module Mongoid
25
25
  # @param [ Hash ] options Extras for the option.
26
26
  #
27
27
  # @option options [ Object ] :default The default value.
28
- # @option options [ Proc | nil ] :on_change The callback to invoke when the
29
- # setter is invoked.
30
28
  def option(name, options = {})
31
29
  defaults[name] = settings[name] = options[:default]
32
30
 
@@ -40,7 +38,6 @@ module Mongoid
40
38
 
41
39
  define_method("#{name}=") do |value|
42
40
  settings[name] = value
43
- options[:on_change]&.call(value)
44
41
  end
45
42
 
46
43
  define_method("#{name}?") do
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongoid
4
+ module Config
5
+ module Validators
6
+
7
+ # Validator for async query executor configuration.
8
+ #
9
+ # @api private
10
+ module AsyncQueryExecutor
11
+ extend self
12
+
13
+
14
+ def validate(options)
15
+ if options.key?(:async_query_executor)
16
+ if options[:async_query_executor].to_sym == :immediate && !options[:global_executor_concurrency].nil?
17
+ raise Errors::InvalidGlobalExecutorConcurrency
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mongoid/config/validators/async_query_executor"
3
4
  require "mongoid/config/validators/option"
4
5
  require "mongoid/config/validators/client"
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mongoid/config/defaults"
3
4
  require "mongoid/config/environment"
4
5
  require "mongoid/config/options"
5
6
  require "mongoid/config/validators"
@@ -11,6 +12,7 @@ module Mongoid
11
12
  module Config
12
13
  extend Forwardable
13
14
  extend Options
15
+ extend Defaults
14
16
  extend self
15
17
 
16
18
  def_delegators ::Mongoid, :logger, :logger=
@@ -70,6 +72,7 @@ module Mongoid
70
72
 
71
73
  # Use ActiveSupport's time zone in time operations instead of the
72
74
  # Ruby default time zone.
75
+ # @deprecated
73
76
  option :use_activesupport_time_zone, default: true
74
77
 
75
78
  # Return stored times as UTC.
@@ -125,35 +128,52 @@ module Mongoid
125
128
  # always return a Hash.
126
129
  option :legacy_attributes, default: false
127
130
 
128
- # Allow BSON::Decimal128 to be parsed and returned directly in
129
- # field values. When BSON 5 is present and the this option is set to false
130
- # (the default), BSON::Decimal128 values in the database will be returned
131
- # as BigDecimal.
132
- #
133
- # @note this option only has effect when BSON 5+ is present. Otherwise,
134
- # the setting is ignored.
135
- option :allow_bson5_decimal128, default: false, on_change: -> (allow) do
136
- if BSON::VERSION >= '5.0.0'
137
- if allow
138
- BSON::Registry.register(BSON::Decimal128::BSON_TYPE, BSON::Decimal128)
139
- else
140
- BSON::Registry.register(BSON::Decimal128::BSON_TYPE, BigDecimal)
141
- end
142
- end
143
- end
144
-
145
- # When this flag is true, callbacks for embedded documents will not be
146
- # called. This is the default in 8.x, but will be changed to false in 9.0.
147
- #
148
- # Setting this flag to true (as it is in 8.x) may lead to stack
149
- # overflow errors if there are more than cicrca 1000 embedded
131
+ # Sets the async_query_executor for the application. By default the thread pool executor
132
+ # is set to `:immediate. Options are:
133
+ #
134
+ # - :immediate - Initializes a single +Concurrent::ImmediateExecutor+
135
+ # - :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+
136
+ # that uses the +async_query_concurrency+ for the +max_threads+ value.
137
+ option :async_query_executor, default: :immediate
138
+
139
+ # Defines how many asynchronous queries can be executed concurrently.
140
+ # This option should be set only if `async_query_executor` is set
141
+ # to `:global_thread_pool`.
142
+ option :global_executor_concurrency, default: nil
143
+
144
+ # When this flag is false, a document will become read-only only once the
145
+ # #readonly! method is called, and an error will be raised on attempting
146
+ # to save or update such documents, instead of just on delete. When this
147
+ # flag is true, a document is only read-only if it has been projected
148
+ # using #only or #without, and read-only documents will not be
149
+ # deletable/destroyable, but they will be savable/updatable.
150
+ # When this feature flag is turned on, the read-only state will be reset on
151
+ # reload, but when it is turned off, it won't be.
152
+ option :legacy_readonly, default: true
153
+
154
+ # When this flag is true, any attempt to change the _id of a persisted
155
+ # document will raise an exception (`Errors::ImmutableAttribute`).
156
+ # This will be the default in 9.0. When this flag is false (the default
157
+ # in 8.x), changing the _id of a persisted document might be ignored,
158
+ # or it might work, depending on the situation.
159
+ option :immutable_ids, default: false
160
+
161
+ # When this flag is true, callbacks for every embedded document will be
162
+ # called only once, even if the embedded document is embedded in multiple
150
163
  # documents in the root document's dependencies graph.
151
- #
152
- # It is strongly recommended to set this flag to false in 8.x, if you
153
- # are not using around callbacks for embedded documents.
154
- #
155
- # See https://jira.mongodb.org/browse/MONGOID-5658 for more details.
156
- option :around_callbacks_for_embeds, default: true
164
+ # This will be the default in 9.0. Setting this flag to false restores the
165
+ # pre-9.0 behavior, where callbacks are called for every occurrence of an
166
+ # embedded document. The pre-9.0 behavior leads to a problem that for multi
167
+ # level nested documents callbacks are called multiple times.
168
+ # See https://jira.mongodb.org/browse/MONGOID-5542
169
+ option :prevent_multiple_calls_of_embedded_callbacks, default: false
170
+
171
+ # Returns the Config singleton, for use in the configure DSL.
172
+ #
173
+ # @return [ self ] The Config singleton.
174
+ def config
175
+ self
176
+ end
157
177
 
158
178
  # Has Mongoid been configured? This is checking that at least a valid
159
179
  # client config exists.
@@ -236,6 +256,17 @@ module Mongoid
236
256
  end
237
257
  end
238
258
 
259
+ # Deregister a model in the application with Mongoid.
260
+ #
261
+ # @param [ Class ] klass The model to deregister.
262
+ #
263
+ # @api private
264
+ def deregister_model(klass)
265
+ LOCK.synchronize do
266
+ models.delete(klass)
267
+ end
268
+ end
269
+
239
270
  # From a hash of settings, load all the configuration.
240
271
  #
241
272
  # @example Load the configuration.
@@ -308,6 +339,7 @@ module Mongoid
308
339
  # @param [ Hash ] options The configuration options.
309
340
  def options=(options)
310
341
  if options
342
+ Validators::AsyncQueryExecutor.validate(options)
311
343
  options.each_pair do |option, value|
312
344
  Validators::Option.validate(option)
313
345
  send("#{option}=", value)
@@ -375,5 +407,44 @@ module Mongoid
375
407
  client
376
408
  end
377
409
  end
410
+
411
+ module DeprecatedOptions
412
+ OPTIONS = %i[ use_activesupport_time_zone
413
+ broken_aggregables
414
+ broken_alias_handling
415
+ broken_and
416
+ broken_scoping
417
+ broken_updates
418
+ compare_time_by_ms
419
+ legacy_attributes
420
+ legacy_pluck_distinct
421
+ legacy_triple_equals
422
+ object_id_as_json_oid
423
+ overwrite_chained_operators ]
424
+
425
+ if RUBY_VERSION < '3.0'
426
+ def self.prepended(klass)
427
+ klass.class_eval do
428
+ OPTIONS.each do |option|
429
+ alias_method :"#{option}_without_deprecation=", :"#{option}="
430
+
431
+ define_method(:"#{option}=") do |value|
432
+ Mongoid::Warnings.send(:"warn_#{option}_deprecated")
433
+ send(:"#{option}_without_deprecation=", value)
434
+ end
435
+ end
436
+ end
437
+ end
438
+ else
439
+ OPTIONS.each do |option|
440
+ define_method(:"#{option}=") do |value|
441
+ Mongoid::Warnings.send(:"warn_#{option}_deprecated")
442
+ super(value)
443
+ end
444
+ end
445
+ end
446
+ end
447
+
448
+ prepend DeprecatedOptions
378
449
  end
379
450
  end
@@ -150,7 +150,7 @@ module Mongoid
150
150
  # @example Unset the field on the matches.
151
151
  # context.unset(:name)
152
152
  #
153
- # @param [ String | Symbol | Array<String | Symbol> | Hash ] args
153
+ # @param [ [ String | Symbol | Array<String | Symbol> | Hash ]... ] *args
154
154
  # The name(s) of the field(s) to unset.
155
155
  # If a Hash is specified, its keys will be used irrespective of what
156
156
  # each key's value is, even if the value is nil or false.