mongoid 8.0.9 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (214) 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 +2 -2
  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 -2
  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 +5 -27
  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/book.rb +1 -0
  204. data/spec/support/models/building.rb +2 -0
  205. data/spec/support/models/cover.rb +10 -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 +746 -636
  211. metadata.gz.sig +2 -0
  212. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  213. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  214. data/spec/support/models/purse.rb +0 -9
@@ -25,7 +25,7 @@ module Mongoid
25
25
  # @example Concat with other documents.
26
26
  # person.posts.concat([ post_one, post_two ])
27
27
  #
28
- # @param [ Document | Array<Document> ] args Any number of documents.
28
+ # @param [ Document... ] *args Any number of documents.
29
29
  #
30
30
  # @return [ Array<Document> ] The loaded docs.
31
31
  def <<(*args)
@@ -203,7 +203,7 @@ module Mongoid
203
203
  # @note This will keep matching documents in memory for iteration
204
204
  # later.
205
205
  #
206
- # @param [ Object | Array<Object> ] *args The ids.
206
+ # @param [ [ Object | Array<Object> ]... ] *args The ids.
207
207
  # @param [ Proc ] block Optional block to pass.
208
208
  #
209
209
  # @return [ Document | Array<Document> | nil ] A document or matching documents.
@@ -418,7 +418,7 @@ module Mongoid
418
418
  # If the method exists on the array, use the default proxy behavior.
419
419
  #
420
420
  # @param [ Symbol | String ] name The name of the method.
421
- # @param [ Array ] args The method args
421
+ # @param [ Object... ] *args The method args
422
422
  # @param [ Proc ] block Optional block to pass.
423
423
  #
424
424
  # @return [ Criteria | Object ] A Criteria or return value from the target.
@@ -25,7 +25,7 @@ module Mongoid
25
25
  # @example Find multiple association metadata by macro.
26
26
  # person.reflect_on_all_associations(:embeds_many)
27
27
  #
28
- # @param [ Array<Symbol> ] macros The association macros.
28
+ # @param [ Symbol... ] *macros The association macros.
29
29
  #
30
30
  # @return [ Array<Association> ] The matching association metadata.
31
31
  def reflect_on_all_association(*macros)
@@ -51,7 +51,7 @@ module Mongoid
51
51
  # @example Find multiple association metadata by macro.
52
52
  # Person.reflect_on_all_associations(:embeds_many)
53
53
  #
54
- # @param [ Array<Symbol> ] macros The association macros.
54
+ # @param [ Symbol... ] *macros The association macros.
55
55
  #
56
56
  # @return [ Array<Association> ] The matching association metadata.
57
57
  def reflect_on_all_associations(*macros)
@@ -178,15 +178,13 @@ module Mongoid
178
178
  #
179
179
  # @return [ Object ] The associated path.
180
180
  def atomic_paths
181
- return @atomic_paths if @atomic_paths
182
-
183
- paths = if _association
184
- _association.path(self)
185
- else
186
- Atomic::Paths::Root.new(self)
187
- end
188
-
189
- paths.tap { @atomic_paths = paths unless new_record? }
181
+ @atomic_paths ||= begin
182
+ if _association
183
+ _association.path(self)
184
+ else
185
+ Atomic::Paths::Root.new(self)
186
+ end
187
+ end
190
188
  end
191
189
 
192
190
  # Get all the attributes that need to be pulled.
@@ -313,13 +311,6 @@ module Mongoid
313
311
 
314
312
  private
315
313
 
316
- # Clears all pending atomic updates.
317
- def reset_atomic_updates!
318
- Atomic::UPDATES.each do |update|
319
- send(update).clear
320
- end
321
- end
322
-
323
314
  # Generates the atomic updates in the correct order.
324
315
  #
325
316
  # @example Generate the updates.
@@ -115,7 +115,7 @@ module Mongoid
115
115
  # document.method_missing(:test)
116
116
  #
117
117
  # @param [ String | Symbol ] name The name of the method.
118
- # @param [ Array ] args The arguments to the method.
118
+ # @param [ Object... ] *args The arguments to the method.
119
119
  #
120
120
  # @return [ Object ] The result of the method call.
121
121
  def method_missing(name, *args)
@@ -33,8 +33,8 @@ module Mongoid
33
33
  # accepts_nested_attributes_for :addresses, :game, :posts
34
34
  # end
35
35
  #
36
- # @param [ Array<Symbol> | Hash ] args A list of association names, followed
37
- # by a hash of options.
36
+ # @param [ Symbol..., Hash ] *args A list of association names, followed
37
+ # by an optional hash of options.
38
38
  #
39
39
  # @option *args [ true | false ] :allow_destroy Can deletion occur?
40
40
  # @option *args [ Proc | Symbol ] :reject_if Block or symbol pointing
@@ -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"