mongoid 7.2.6 → 7.3.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 (200) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1 -1
  4. data/Rakefile +16 -0
  5. data/lib/config/locales/en.yml +2 -15
  6. data/lib/mongoid/association/accessors.rb +1 -1
  7. data/lib/mongoid/association/constrainable.rb +1 -1
  8. data/lib/mongoid/association/depending.rb +4 -4
  9. data/lib/mongoid/association/embedded/batchable.rb +1 -1
  10. data/lib/mongoid/association/embedded/embedded_in.rb +1 -1
  11. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +11 -4
  12. data/lib/mongoid/association/nested/many.rb +1 -1
  13. data/lib/mongoid/association/nested/one.rb +4 -2
  14. data/lib/mongoid/association/proxy.rb +7 -2
  15. data/lib/mongoid/association/referenced/auto_save.rb +2 -2
  16. data/lib/mongoid/association/referenced/has_many/enumerable.rb +493 -495
  17. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  18. data/lib/mongoid/association/referenced/has_one/nested_builder.rb +2 -2
  19. data/lib/mongoid/association/relatable.rb +0 -2
  20. data/lib/mongoid/attributes/projector.rb +120 -0
  21. data/lib/mongoid/attributes.rb +24 -13
  22. data/lib/mongoid/cacheable.rb +2 -2
  23. data/lib/mongoid/clients/factory.rb +22 -8
  24. data/lib/mongoid/clients.rb +1 -1
  25. data/lib/mongoid/config/environment.rb +1 -9
  26. data/lib/mongoid/config.rb +19 -2
  27. data/lib/mongoid/contextual/aggregable/mongo.rb +10 -8
  28. data/lib/mongoid/contextual/atomic.rb +2 -7
  29. data/lib/mongoid/contextual/none.rb +0 -3
  30. data/lib/mongoid/copyable.rb +1 -1
  31. data/lib/mongoid/criteria/findable.rb +1 -1
  32. data/lib/mongoid/criteria/queryable/expandable.rb +0 -24
  33. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  34. data/lib/mongoid/criteria/queryable/extensions.rb +0 -4
  35. data/lib/mongoid/criteria/queryable/mergeable.rb +46 -20
  36. data/lib/mongoid/criteria/queryable/selectable.rb +10 -10
  37. data/lib/mongoid/criteria/queryable/storable.rb +4 -4
  38. data/lib/mongoid/criteria.rb +5 -6
  39. data/lib/mongoid/document.rb +3 -18
  40. data/lib/mongoid/errors/delete_restriction.rb +8 -9
  41. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  42. data/lib/mongoid/errors.rb +0 -2
  43. data/lib/mongoid/evolvable.rb +1 -1
  44. data/lib/mongoid/extensions/boolean.rb +1 -2
  45. data/lib/mongoid/extensions/false_class.rb +1 -1
  46. data/lib/mongoid/extensions/hash.rb +2 -2
  47. data/lib/mongoid/extensions/true_class.rb +1 -1
  48. data/lib/mongoid/fields.rb +43 -5
  49. data/lib/mongoid/inspectable.rb +1 -1
  50. data/lib/mongoid/interceptable.rb +1 -1
  51. data/lib/mongoid/matcher/bits.rb +41 -0
  52. data/lib/mongoid/matcher/bits_all_clear.rb +20 -0
  53. data/lib/mongoid/matcher/bits_all_set.rb +20 -0
  54. data/lib/mongoid/matcher/bits_any_clear.rb +20 -0
  55. data/lib/mongoid/matcher/bits_any_set.rb +20 -0
  56. data/lib/mongoid/matcher/expression.rb +4 -0
  57. data/lib/mongoid/matcher/field_operator.rb +6 -0
  58. data/lib/mongoid/matcher/mod.rb +17 -0
  59. data/lib/mongoid/matcher/type.rb +99 -0
  60. data/lib/mongoid/matcher.rb +7 -0
  61. data/lib/mongoid/persistable/deletable.rb +1 -2
  62. data/lib/mongoid/persistable/destroyable.rb +8 -2
  63. data/lib/mongoid/persistable/updatable.rb +27 -2
  64. data/lib/mongoid/persistence_context.rb +1 -3
  65. data/lib/mongoid/query_cache.rb +36 -40
  66. data/lib/mongoid/selectable.rb +5 -7
  67. data/lib/mongoid/shardable.rb +21 -5
  68. data/lib/mongoid/tasks/database.rb +1 -1
  69. data/lib/mongoid/touchable.rb +23 -4
  70. data/lib/mongoid/validatable/associated.rb +1 -1
  71. data/lib/mongoid/validatable/presence.rb +3 -3
  72. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  73. data/lib/mongoid/version.rb +1 -1
  74. data/lib/mongoid.rb +0 -1
  75. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
  76. data/spec/integration/app_spec.rb +0 -3
  77. data/spec/integration/associations/embeds_many_spec.rb +44 -0
  78. data/spec/integration/associations/has_one_spec.rb +48 -0
  79. data/spec/integration/criteria/date_field_spec.rb +1 -1
  80. data/spec/integration/document_spec.rb +9 -0
  81. data/spec/integration/matcher_operator_data/bits_all_clear.yml +159 -0
  82. data/spec/integration/matcher_operator_data/bits_all_set.yml +159 -0
  83. data/spec/integration/matcher_operator_data/bits_any_clear.yml +159 -0
  84. data/spec/integration/matcher_operator_data/bits_any_set.yml +159 -0
  85. data/spec/integration/matcher_operator_data/comment.yml +22 -0
  86. data/spec/integration/matcher_operator_data/in.yml +16 -0
  87. data/spec/integration/matcher_operator_data/mod.yml +55 -0
  88. data/spec/integration/matcher_operator_data/type.yml +70 -0
  89. data/spec/integration/matcher_operator_data/type_array.yml +16 -0
  90. data/spec/integration/matcher_operator_data/type_binary.yml +18 -0
  91. data/spec/integration/matcher_operator_data/type_boolean.yml +39 -0
  92. data/spec/integration/matcher_operator_data/type_code.yml +26 -0
  93. data/spec/integration/matcher_operator_data/type_code_with_scope.yml +26 -0
  94. data/spec/integration/matcher_operator_data/type_date.yml +39 -0
  95. data/spec/integration/matcher_operator_data/type_db_pointer.yml +19 -0
  96. data/spec/integration/matcher_operator_data/type_decimal.yml +40 -0
  97. data/spec/integration/matcher_operator_data/type_double.yml +15 -0
  98. data/spec/integration/matcher_operator_data/type_int32.yml +33 -0
  99. data/spec/integration/matcher_operator_data/type_int64.yml +33 -0
  100. data/spec/integration/matcher_operator_data/type_max_key.yml +17 -0
  101. data/spec/integration/matcher_operator_data/type_min_key.yml +17 -0
  102. data/spec/integration/matcher_operator_data/type_null.yml +23 -0
  103. data/spec/integration/matcher_operator_data/type_object.yml +23 -0
  104. data/spec/integration/matcher_operator_data/type_object_id.yml +25 -0
  105. data/spec/integration/matcher_operator_data/type_regex.yml +44 -0
  106. data/spec/integration/matcher_operator_data/type_string.yml +15 -0
  107. data/spec/integration/matcher_operator_data/type_symbol.yml +32 -0
  108. data/spec/integration/matcher_operator_data/type_timestamp.yml +25 -0
  109. data/spec/integration/matcher_operator_data/type_undefined.yml +17 -0
  110. data/spec/integration/stringified_symbol_field_spec.rb +2 -2
  111. data/spec/lite_spec_helper.rb +2 -0
  112. data/spec/mongoid/association/depending_spec.rb +391 -352
  113. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +4 -17
  114. data/spec/mongoid/association/nested/one_spec.rb +18 -14
  115. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +25 -25
  116. data/spec/mongoid/association/referenced/belongs_to_query_spec.rb +0 -20
  117. data/spec/mongoid/association/referenced/has_and_belongs_to_many/binding_spec.rb +1 -1
  118. data/spec/mongoid/association/referenced/has_many/binding_spec.rb +1 -1
  119. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +1 -1
  120. data/spec/mongoid/association/referenced/has_many_models.rb +0 -17
  121. data/spec/mongoid/association/referenced/has_one_models.rb +8 -0
  122. data/spec/mongoid/atomic/paths_spec.rb +64 -12
  123. data/spec/mongoid/attributes/projector_data/embedded.yml +105 -0
  124. data/spec/mongoid/attributes/projector_data/fields.yml +93 -0
  125. data/spec/mongoid/attributes/projector_spec.rb +41 -0
  126. data/spec/mongoid/attributes_spec.rb +98 -6
  127. data/spec/mongoid/clients/factory_spec.rb +51 -9
  128. data/spec/mongoid/clients/options_spec.rb +3 -11
  129. data/spec/mongoid/config/environment_spec.rb +8 -86
  130. data/spec/mongoid/config_spec.rb +32 -0
  131. data/spec/mongoid/contextual/atomic_spec.rb +25 -64
  132. data/spec/mongoid/contextual/geo_near_spec.rb +1 -1
  133. data/spec/mongoid/contextual/mongo_spec.rb +2 -2
  134. data/spec/mongoid/criteria/modifiable_spec.rb +1 -1
  135. data/spec/mongoid/criteria/queryable/expandable_spec.rb +0 -73
  136. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +1 -1
  137. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +105 -7
  138. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +229 -24
  139. data/spec/mongoid/criteria/queryable/selectable_shared_examples.rb +39 -0
  140. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -565
  141. data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +590 -0
  142. data/spec/mongoid/criteria_projection_spec.rb +411 -0
  143. data/spec/mongoid/criteria_spec.rb +0 -279
  144. data/spec/mongoid/document_query_spec.rb +0 -51
  145. data/spec/mongoid/document_spec.rb +14 -34
  146. data/spec/mongoid/errors/delete_restriction_spec.rb +1 -1
  147. data/spec/mongoid/errors/mongoid_error_spec.rb +8 -20
  148. data/spec/mongoid/extensions/false_class_spec.rb +1 -1
  149. data/spec/mongoid/extensions/string_spec.rb +5 -5
  150. data/spec/mongoid/extensions/true_class_spec.rb +1 -1
  151. data/spec/mongoid/fields/localized_spec.rb +4 -4
  152. data/spec/mongoid/fields_spec.rb +4 -4
  153. data/spec/mongoid/inspectable_spec.rb +12 -4
  154. data/spec/mongoid/persistable/deletable_spec.rb +175 -1
  155. data/spec/mongoid/persistable/destroyable_spec.rb +191 -3
  156. data/spec/mongoid/persistable/savable_spec.rb +3 -5
  157. data/spec/mongoid/persistable/updatable_spec.rb +0 -2
  158. data/spec/mongoid/persistable/upsertable_spec.rb +1 -1
  159. data/spec/mongoid/persistable_spec.rb +2 -2
  160. data/spec/mongoid/query_cache_middleware_spec.rb +8 -0
  161. data/spec/mongoid/query_cache_spec.rb +0 -24
  162. data/spec/mongoid/reloadable_spec.rb +18 -1
  163. data/spec/mongoid/shardable_spec.rb +44 -0
  164. data/spec/mongoid/touchable_spec.rb +104 -16
  165. data/spec/mongoid/touchable_spec_models.rb +52 -0
  166. data/spec/mongoid/validatable_spec.rb +1 -1
  167. data/spec/shared/lib/mrss/cluster_config.rb +3 -8
  168. data/spec/shared/lib/mrss/constraints.rb +10 -41
  169. data/spec/shared/lib/mrss/docker_runner.rb +1 -7
  170. data/spec/shared/lib/mrss/server_version_registry.rb +12 -17
  171. data/spec/shared/lib/mrss/spec_organizer.rb +1 -18
  172. data/spec/shared/share/Dockerfile.erb +33 -125
  173. data/spec/shared/shlib/server.sh +23 -100
  174. data/spec/shared/shlib/set_env.sh +1 -4
  175. data/spec/spec_helper.rb +7 -3
  176. data/spec/support/client_registry.rb +9 -0
  177. data/spec/support/models/address.rb +0 -4
  178. data/spec/support/models/bolt.rb +8 -0
  179. data/spec/support/models/hole.rb +13 -0
  180. data/spec/support/models/mop.rb +0 -1
  181. data/spec/support/models/nut.rb +8 -0
  182. data/spec/support/models/person.rb +6 -9
  183. data/spec/support/models/sealer.rb +8 -0
  184. data/spec/support/models/shirt.rb +12 -0
  185. data/spec/support/models/spacer.rb +8 -0
  186. data/spec/support/models/threadlocker.rb +8 -0
  187. data/spec/support/models/washer.rb +8 -0
  188. data.tar.gz.sig +0 -0
  189. metadata +609 -545
  190. metadata.gz.sig +0 -0
  191. data/lib/mongoid/errors/empty_config_file.rb +0 -26
  192. data/lib/mongoid/errors/invalid_config_file.rb +0 -26
  193. data/spec/integration/contextual/empty_spec.rb +0 -142
  194. data/spec/mongoid/errors/invalid_config_file_spec.rb +0 -32
  195. data/spec/shared/bin/s3-copy +0 -45
  196. data/spec/shared/bin/s3-upload +0 -69
  197. data/spec/shared/lib/mrss/event_subscriber.rb +0 -200
  198. data/spec/shared/share/haproxy-1.conf +0 -16
  199. data/spec/shared/share/haproxy-2.conf +0 -17
  200. data/spec/support/cluster_config.rb +0 -158
@@ -79,6 +79,11 @@ end
79
79
 
80
80
  require 'mongoid/matcher/all'
81
81
  require 'mongoid/matcher/and'
82
+ require 'mongoid/matcher/bits'
83
+ require 'mongoid/matcher/bits_all_clear'
84
+ require 'mongoid/matcher/bits_all_set'
85
+ require 'mongoid/matcher/bits_any_clear'
86
+ require 'mongoid/matcher/bits_any_set'
82
87
  require 'mongoid/matcher/elem_match'
83
88
  require 'mongoid/matcher/elem_match_expression'
84
89
  require 'mongoid/matcher/eq'
@@ -92,6 +97,7 @@ require 'mongoid/matcher/gte'
92
97
  require 'mongoid/matcher/in'
93
98
  require 'mongoid/matcher/lt'
94
99
  require 'mongoid/matcher/lte'
100
+ require 'mongoid/matcher/mod'
95
101
  require 'mongoid/matcher/ne'
96
102
  require 'mongoid/matcher/nin'
97
103
  require 'mongoid/matcher/nor'
@@ -99,5 +105,6 @@ require 'mongoid/matcher/not'
99
105
  require 'mongoid/matcher/or'
100
106
  require 'mongoid/matcher/regex'
101
107
  require 'mongoid/matcher/size'
108
+ require 'mongoid/matcher/type'
102
109
  require 'mongoid/matcher/expression_operator'
103
110
  require 'mongoid/matcher/field_operator'
@@ -47,7 +47,7 @@ module Mongoid
47
47
  #
48
48
  # @since 4.0.0
49
49
  def atomic_deletes
50
- { atomic_delete_modifier => { atomic_path => _index ? { "_id" => id } : true }}
50
+ { atomic_delete_modifier => { atomic_path => _index ? { "_id" => _id } : true }}
51
51
  end
52
52
 
53
53
  # Delete the embedded document.
@@ -117,7 +117,6 @@ module Mongoid
117
117
  #
118
118
  # @since 4.0.0
119
119
  def prepare_delete
120
- return false unless catch(:abort) { apply_delete_dependencies! }
121
120
  yield(self)
122
121
  freeze
123
122
  self.destroyed = true
@@ -23,13 +23,19 @@ module Mongoid
23
23
  def destroy(options = nil)
24
24
  raise Errors::ReadonlyDocument.new(self.class) if readonly?
25
25
  self.flagged_for_destroy = true
26
- result = run_callbacks(:destroy) { delete(options || {}) }
26
+ result = run_callbacks(:destroy) do
27
+ if catch(:abort) { apply_destroy_dependencies! }
28
+ delete(options || {})
29
+ else
30
+ false
31
+ end
32
+ end
27
33
  self.flagged_for_destroy = false
28
34
  result
29
35
  end
30
36
 
31
37
  def destroy!(options = {})
32
- destroy || raise(Errors::DocumentNotDestroyed.new(id, self.class))
38
+ destroy || raise(Errors::DocumentNotDestroyed.new(_id, self.class))
33
39
  end
34
40
 
35
41
  module ClassMethods
@@ -137,8 +137,33 @@ module Mongoid
137
137
  coll = collection(_root)
138
138
  selector = atomic_selector
139
139
  coll.find(selector).update_one(positionally(selector, updates), session: _session)
140
- conflicts.each_pair do |key, value|
141
- coll.find(selector).update_one(positionally(selector, { key => value }), session: _session)
140
+
141
+ # The following code applies updates which would cause
142
+ # path conflicts in MongoDB, for example when changing attributes
143
+ # of foo.0.bars while adding another foo. Each conflicting update
144
+ # is applied using its own write.
145
+ #
146
+ # TODO: MONGOID-5026: reduce the number of writes performed by
147
+ # more intelligently combining the writes such that there are
148
+ # fewer conflicts.
149
+ conflicts.each_pair do |modifier, changes|
150
+
151
+ # Group the changes according to their root key which is
152
+ # the top-level association name.
153
+ # This handles at least the cases described in MONGOID-4982.
154
+ conflicting_change_groups = changes.group_by do |key, _|
155
+ key.split(".", 2).first
156
+ end.values
157
+
158
+ # Apply changes in batches. Pop one change from each
159
+ # field-conflict group round-robin until all changes
160
+ # have been applied.
161
+ while batched_changes = conflicting_change_groups.map(&:pop).compact.to_h.presence
162
+ coll.find(selector).update_one(
163
+ positionally(selector, modifier => batched_changes),
164
+ session: _session,
165
+ )
166
+ end
142
167
  end
143
168
  end
144
169
  end
@@ -237,9 +237,7 @@ module Mongoid
237
237
  # @since 6.0.0
238
238
  def clear(object, cluster = nil, original_context = nil)
239
239
  if context = get(object)
240
- unless cluster.nil? || context.cluster.equal?(cluster)
241
- context.client.close
242
- end
240
+ context.client.close unless (context.cluster.equal?(cluster) || cluster.nil?)
243
241
  end
244
242
  ensure
245
243
  Thread.current["[mongoid][#{object.object_id}]:context"] = original_context
@@ -7,14 +7,8 @@ module Mongoid
7
7
  #
8
8
  # @since 4.0.0
9
9
  module QueryCache
10
- # @api private
11
- LEGACY_WARNING = <<~DOC
12
- You are using the legacy Mongoid query cache which has known issues.
13
- Please upgrade the `mongo' gem to at least 2.14.0 to use the improved driver query cache.
14
- Refer to: https://docs.mongodb.com/mongoid/current/tutorials/mongoid-queries/#the-improved-driver-query-cache
15
- DOC
16
-
17
10
  class << self
11
+
18
12
  # Get the cached queries.
19
13
  #
20
14
  # @example Get the cached queries from the current thread.
@@ -92,10 +86,6 @@ module Mongoid
92
86
  if defined?(Mongo::QueryCache)
93
87
  Mongo::QueryCache.cache(&block)
94
88
  else
95
- @legacy_query_cache_warned ||= begin
96
- Mongoid.logger.warn(LEGACY_WARNING)
97
- true
98
- end
99
89
  enabled = QueryCache.enabled?
100
90
  QueryCache.enabled = true
101
91
  begin
@@ -127,38 +117,44 @@ module Mongoid
127
117
  end
128
118
  end
129
119
 
130
- # The middleware to be added to a rack application in order to activate the
131
- # query cache.
132
- #
133
- # @since 4.0.0
134
- class Middleware
135
-
136
- # Instantiate the middleware.
137
- #
138
- # @example Create the new middleware.
139
- # Middleware.new(app)
140
- #
141
- # @param [ Object ] app The rack applciation stack.
120
+ if defined?(Mongo::QueryCache::Middleware)
121
+ Middleware = Mongo::QueryCache::Middleware
122
+ else
123
+ # The middleware to be added to a rack application in order to activate the
124
+ # query cache.
142
125
  #
143
126
  # @since 4.0.0
144
- def initialize(app)
145
- @app = app
146
- end
127
+ class Middleware
147
128
 
148
- # Execute the request, wrapping in a query cache.
149
- #
150
- # @example Execute the request.
151
- # middleware.call(env)
152
- #
153
- # @param [ Object ] env The environment.
154
- #
155
- # @return [ Object ] The result of the call.
156
- #
157
- # @since 4.0.0
158
- def call(env)
159
- QueryCache.cache { @app.call(env) }
160
- ensure
161
- QueryCache.clear_cache
129
+ # Instantiate the middleware.
130
+ #
131
+ # @example Create the new middleware.
132
+ # Middleware.new(app)
133
+ #
134
+ # @param [ Object ] app The rack application stack.
135
+ #
136
+ # @since 4.0.0
137
+ def initialize(app)
138
+ @app = app
139
+ end
140
+
141
+ # Execute the request, wrapping in a query cache.
142
+ #
143
+ # @example Execute the request.
144
+ # middleware.call(env)
145
+ #
146
+ # @param [ Object ] env The environment.
147
+ #
148
+ # @return [ Object ] The result of the call.
149
+ #
150
+ # @since 4.0.0
151
+ def call(env)
152
+ QueryCache.cache do
153
+ @app.call(env)
154
+ end
155
+ ensure
156
+ QueryCache.clear_cache
157
+ end
162
158
  end
163
159
  end
164
160
 
@@ -21,7 +21,7 @@ module Mongoid
21
21
  # @since 1.0.0
22
22
  def atomic_selector
23
23
  @atomic_selector ||=
24
- (embedded? ? embedded_atomic_selector : root_atomic_selector)
24
+ (embedded? ? embedded_atomic_selector : root_atomic_selector_in_db)
25
25
  end
26
26
 
27
27
  private
@@ -44,18 +44,16 @@ module Mongoid
44
44
  end
45
45
  end
46
46
 
47
- # Get the atomic selector for a root document.
47
+ # Get the atomic selector that would match the existing version of the
48
+ # root document.
48
49
  #
49
50
  # @api private
50
51
  #
51
- # @example Get the root atomic selector.
52
- # document.root_atomic_selector
53
- #
54
52
  # @return [ Hash ] The root document selector.
55
53
  #
56
54
  # @since 4.0.0
57
- def root_atomic_selector
58
- { "_id" => _id }.merge!(shard_key_selector)
55
+ def root_atomic_selector_in_db
56
+ { "_id" => _id }.merge!(shard_key_selector_in_db)
59
57
  end
60
58
  end
61
59
  end
@@ -52,15 +52,31 @@ module Mongoid
52
52
  self.class.shard_key_fields
53
53
  end
54
54
 
55
- # Get the document selector with the defined shard keys.
56
- #
57
- # @example Get the selector for the shard keys.
58
- # person.shard_key_selector
55
+ # Returns the selector that would match the current version of this
56
+ # document.
59
57
  #
60
58
  # @return [ Hash ] The shard key selector.
61
59
  #
62
- # @since 2.0.0
60
+ # @api private
63
61
  def shard_key_selector
62
+ selector = {}
63
+ shard_key_fields.each do |field|
64
+ selector[field.to_s] = send(field)
65
+ end
66
+ selector
67
+ end
68
+
69
+ # Returns the selector that would match the existing version of this
70
+ # document in the database.
71
+ #
72
+ # If the document is not persisted, this method uses the current values
73
+ # of the shard key fields. If the document is persisted, this method
74
+ # uses the values retrieved from the database.
75
+ #
76
+ # @return [ Hash ] The shard key selector.
77
+ #
78
+ # @api private
79
+ def shard_key_selector_in_db
64
80
  selector = {}
65
81
  shard_key_fields.each do |field|
66
82
  selector[field.to_s] = new_record? ? send(field) : attribute_was(field)
@@ -123,7 +123,7 @@ module Mongoid
123
123
  next if model.shard_config.nil?
124
124
 
125
125
  if model.embedded? && !model.cyclic?
126
- logger.warn("MONGOID: #{model} has shard config but is embedded")
126
+ logger.warn("MONGOID: #{model} has shard config but is emdedded")
127
127
  next
128
128
  end
129
129
 
@@ -30,11 +30,30 @@ module Mongoid
30
30
  write_attribute(:updated_at, current) if respond_to?("updated_at=")
31
31
  write_attribute(field, current) if field
32
32
 
33
- touches = touch_atomic_updates(field)
34
- unless touches["$set"].blank?
35
- selector = atomic_selector
36
- _root.collection.find(selector).update_one(positionally(selector, touches), session: _session)
33
+ # If the document being touched is embedded, touch its parents
34
+ # all the way through the composition hierarchy to the root object,
35
+ # because when an embedded document is changed the write is actually
36
+ # performed by the composition root. See MONGOID-3468.
37
+ if _parent
38
+ # This will persist updated_at on this document as well as parents.
39
+ # TODO support passing the field name to the parent's touch method;
40
+ # I believe it should be read out of
41
+ # _association.inverse_association.options but inverse_association
42
+ # seems to not always/ever be set here. See MONGOID-5014.
43
+ _parent.touch
44
+ else
45
+ # If the current document is not embedded, it is composition root
46
+ # and we need to persist the write here.
47
+ touches = touch_atomic_updates(field)
48
+ unless touches["$set"].blank?
49
+ selector = atomic_selector
50
+ _root.collection.find(selector).update_one(positionally(selector, touches), session: _session)
51
+ end
37
52
  end
53
+
54
+ # Callbacks are invoked on the composition root first and on the
55
+ # leaf-most embedded document last.
56
+ # TODO add tests, see MONGOID-5015.
38
57
  run_callbacks(:touch)
39
58
  true
40
59
  end
@@ -43,7 +43,7 @@ module Mongoid
43
43
  ensure
44
44
  document.exit_validate
45
45
  end
46
- document.errors.add(attribute, :invalid, **options) unless valid
46
+ document.errors.add(attribute, :invalid, options) unless valid
47
47
  end
48
48
  end
49
49
  end
@@ -34,15 +34,15 @@ module Mongoid
34
34
  document.errors.add(
35
35
  attribute,
36
36
  :blank_in_locale,
37
- **options.merge(location: _locale)
37
+ options.merge(location: _locale)
38
38
  ) if not_present?(_value)
39
39
  end
40
40
  elsif document.relations.has_key?(attribute.to_s)
41
41
  if relation_or_fk_missing?(document, attribute, value)
42
- document.errors.add(attribute, :blank, **options)
42
+ document.errors.add(attribute, :blank, options)
43
43
  end
44
44
  else
45
- document.errors.add(attribute, :blank, **options) if not_present?(value)
45
+ document.errors.add(attribute, :blank, options) if not_present?(value)
46
46
  end
47
47
  end
48
48
 
@@ -68,7 +68,7 @@ module Mongoid
68
68
  # @since 2.4.10
69
69
  def add_error(document, attribute, value)
70
70
  document.errors.add(
71
- attribute, :taken, **options.except(:case_sensitive, :scope).merge(value: value)
71
+ attribute, :taken, options.except(:case_sensitive, :scope).merge(value: value)
72
72
  )
73
73
  end
74
74
 
@@ -2,5 +2,5 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  module Mongoid
5
- VERSION = "7.2.6"
5
+ VERSION = "7.3.0"
6
6
  end
data/lib/mongoid.rb CHANGED
@@ -6,7 +6,6 @@ require "support/ruby_version"
6
6
  require "forwardable"
7
7
  require "time"
8
8
  require "set"
9
- require "ruby2_keywords"
10
9
 
11
10
  require "active_support"
12
11
  require "active_support/core_ext"
@@ -57,7 +57,7 @@ development:
57
57
  # connect: :direct
58
58
 
59
59
  # Changes the default time in seconds the server monitors refresh their status
60
- # via hello commands. (default: 10)
60
+ # via ismaster commands. (default: 10)
61
61
  # heartbeat_frequency: 10
62
62
 
63
63
  # The time in seconds for selecting servers for a near read preference. (default: 0.015)
@@ -297,9 +297,6 @@ describe 'Mongoid application tests' do
297
297
  end
298
298
 
299
299
  def remove_bundler_req
300
- return unless File.file?('Gemfile.lock')
301
- # TODO: Remove this method completely when we get rid of .lock files in
302
- # mongoid-demo apps.
303
300
  lock_lines = IO.readlines('Gemfile.lock')
304
301
  # Get rid of the bundled with line so that whatever bundler is installed
305
302
  # on the system is usable with the application.
@@ -21,4 +21,48 @@ describe 'embeds_many associations' do
21
21
  end
22
22
  end
23
23
  end
24
+
25
+ context 'clearing association when parent is not saved' do
26
+ let!(:parent) { Canvas.create!(shapes: [Shape.new]) }
27
+
28
+ let(:unsaved_parent) { Canvas.new(id: parent.id, shapes: [Shape.new]) }
29
+
30
+ context "using #clear" do
31
+ it 'deletes the target from the database' do
32
+ unsaved_parent.shapes.clear
33
+
34
+ unsaved_parent.shapes.should be_empty
35
+
36
+ unsaved_parent.new_record?.should be true
37
+ parent.reload
38
+ parent.shapes.should be_empty
39
+ end
40
+ end
41
+
42
+ shared_examples 'does not delete the target from the database' do
43
+ it 'does not delete the target from the database' do
44
+ unsaved_parent.shapes.should be_empty
45
+
46
+ unsaved_parent.new_record?.should be true
47
+ parent.reload
48
+ parent.shapes.length.should == 1
49
+ end
50
+ end
51
+
52
+ context "using #delete_all" do
53
+ before do
54
+ unsaved_parent.shapes.delete_all
55
+ end
56
+
57
+ include_examples 'does not delete the target from the database'
58
+ end
59
+
60
+ context "using #destroy_all" do
61
+ before do
62
+ unsaved_parent.shapes.destroy_all
63
+ end
64
+
65
+ include_examples 'does not delete the target from the database'
66
+ end
67
+ end
24
68
  end
@@ -67,6 +67,54 @@ describe 'has_one associations' do
67
67
  end
68
68
  end
69
69
 
70
+ context 'when calling methods on target' do
71
+ let(:parent) do
72
+ HomCollege.create!.tap do |college|
73
+ HomAccreditation.create!(college: college)
74
+ end
75
+ end
76
+
77
+ shared_examples 'delegates to the field' do |reloaded: false|
78
+ context 'non-conflicting field name' do
79
+ it 'delegates to the field' do
80
+ parent.accreditation.price.should == 42
81
+ end
82
+
83
+ context 'using send' do
84
+ it 'delegates to the field' do
85
+ parent.accreditation.send(:price).should == 42
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'field name that conflicts with Kernel' do
91
+ it 'delegates to the field' do
92
+ parent.accreditation.format.should == 'fmt'
93
+ end
94
+
95
+ context 'using send' do
96
+ it 'delegates to the field' do
97
+ if reloaded
98
+ pending 'MONGOID-4018'
99
+ end
100
+
101
+ parent.accreditation.send(:format).should == 'fmt'
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ include_examples 'delegates to the field'
108
+
109
+ context 'after reloading parent' do
110
+ before do
111
+ parent.reload
112
+ end
113
+
114
+ include_examples 'delegates to the field', reloaded: true
115
+ end
116
+ end
117
+
70
118
  context 'when child does not have parent association' do
71
119
  context 'Child.new' do
72
120
  it 'creates a child instance' do
@@ -12,7 +12,7 @@ describe 'Queries on Date fields' do
12
12
 
13
13
  shared_examples 'converts to beginning of day in UTC' do
14
14
  it 'converts to beginning of day in UTC' do
15
- selector['founded'].should == arg.dup.beginning_of_day.utc.beginning_of_day
15
+ selector['founded'].should == Time.utc(arg.year, arg.month, arg.day)
16
16
  end
17
17
  end
18
18
 
@@ -20,6 +20,15 @@ describe Mongoid::Document do
20
20
  end
21
21
  end
22
22
 
23
+ context 'when id is unaliased' do
24
+ it 'persists separate id and _id values' do
25
+ shirt = Shirt.create!(id: 'hello', _id: 'foo')
26
+ shirt = Shirt.find(shirt._id)
27
+ shirt.id.should == 'hello'
28
+ shirt._id.should == 'foo'
29
+ end
30
+ end
31
+
23
32
  describe '#reload' do
24
33
  context 'when changing shard key value' do
25
34
  require_topology :sharded