mongoid 7.1.1 → 7.1.2

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 (63) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  5. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  6. data/lib/mongoid/association/proxy.rb +1 -1
  7. data/lib/mongoid/atomic.rb +13 -3
  8. data/lib/mongoid/criteria.rb +7 -1
  9. data/lib/mongoid/criteria/modifiable.rb +2 -1
  10. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  11. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  12. data/lib/mongoid/criteria/queryable/mergeable.rb +75 -8
  13. data/lib/mongoid/criteria/queryable/selectable.rb +28 -8
  14. data/lib/mongoid/extensions/hash.rb +4 -2
  15. data/lib/mongoid/extensions/regexp.rb +1 -1
  16. data/lib/mongoid/fields.rb +2 -1
  17. data/lib/mongoid/matchable/regexp.rb +2 -2
  18. data/lib/mongoid/persistable/pushable.rb +4 -1
  19. data/lib/mongoid/persistence_context.rb +6 -6
  20. data/lib/mongoid/query_cache.rb +2 -1
  21. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  22. data/lib/mongoid/version.rb +1 -1
  23. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  24. data/spec/integration/app_spec.rb +192 -0
  25. data/spec/integration/associations/embedded_spec.rb +54 -0
  26. data/spec/integration/criteria/logical_spec.rb +13 -0
  27. data/spec/lite_spec_helper.rb +11 -4
  28. data/spec/mongoid/association/embedded/embeds_many_models.rb +19 -0
  29. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  30. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  31. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +2 -1
  32. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  33. data/spec/mongoid/clients/options_spec.rb +2 -2
  34. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  35. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  36. data/spec/mongoid/clients_spec.rb +2 -2
  37. data/spec/mongoid/contextual/atomic_spec.rb +22 -11
  38. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  39. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  40. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  41. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  42. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +45 -12
  43. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +480 -198
  44. data/spec/mongoid/criteria_spec.rb +4 -2
  45. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  46. data/spec/mongoid/indexable_spec.rb +6 -4
  47. data/spec/mongoid/matchable/default_spec.rb +1 -1
  48. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  49. data/spec/mongoid/matchable_spec.rb +2 -2
  50. data/spec/mongoid/query_cache_spec.rb +2 -1
  51. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  52. data/spec/mongoid/scopable_spec.rb +2 -1
  53. data/spec/mongoid/shardable_models.rb +1 -1
  54. data/spec/mongoid/shardable_spec.rb +2 -2
  55. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  56. data/spec/mongoid/tasks/database_spec.rb +1 -1
  57. data/spec/spec_helper.rb +0 -31
  58. data/spec/support/child_process_helper.rb +76 -0
  59. data/spec/support/cluster_config.rb +3 -3
  60. data/spec/support/constraints.rb +26 -10
  61. data/spec/support/spec_config.rb +12 -4
  62. metadata +8 -2
  63. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b90b3442fbb22055d692fca89eb33e90868e45588812f1975926dee5a85d8b10
4
- data.tar.gz: d9c2a5eaf30d5ca34a365be11b648efc8932a7cda1b29fd8982847cf9cb528cd
3
+ metadata.gz: fd29f8f1d696ba937654fcd72eb3669ac9ba501465828b7eba0f7b707e606f5b
4
+ data.tar.gz: c9f4a8dbb8a105a7e596019760d100913ddd008972b34f7595674ad7c49d4159
5
5
  SHA512:
6
- metadata.gz: ca1480e9cbd438a8905ef4d8f1e9ac59e66ee6436b0017f40c350de6421dd5f99a8a96c1bfa31bb0291738ad97bdad5ce18ee2ccc60a0cea2d91f1795abd5a8c
7
- data.tar.gz: a3f7da9b676f022dc9bf48199eeb34f68fc14e2c076b539a81149401a6b1b81914f96ba21daca7a392b40a5bf5ed8ef9b76d7499033b786b530d161a1e3bb29b
6
+ metadata.gz: 0e00e8d47e7af2fec23380035bcdbd0badc438552aed52aa0f462e246274e7f77220190d99bb6a83a1a90b8e0a41d19103c94fa454382635a2c193d52260c322
7
+ data.tar.gz: 709300f2570059e460cd341cbaffe7da6328df142825bb726b65cbc377d6ba5129cf7fc973a40fc2de10a50307f49ac0f71db98b0edbb8a65c9cb33910e9d66f
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -200,7 +200,8 @@ module Mongoid
200
200
  def determine_inverses(other)
201
201
  matches = relation_class.relations.values.select do |rel|
202
202
  relation_complements.include?(rel.class) &&
203
- rel.relation_class_name == inverse_class_name
203
+ # https://jira.mongodb.org/browse/MONGOID-4882
204
+ rel.relation_class_name.sub(/\A::/, '') == inverse_class_name
204
205
  end
205
206
  if matches.size > 1
206
207
  raise Errors::AmbiguousRelationship.new(relation_class, @owner_class, name, matches)
@@ -162,7 +162,8 @@ module Mongoid
162
162
  def determine_inverses(other)
163
163
  matches = relation_class.relations.values.select do |rel|
164
164
  relation_complements.include?(rel.class) &&
165
- rel.relation_class_name == inverse_class_name
165
+ # https://jira.mongodb.org/browse/MONGOID-4882
166
+ rel.relation_class_name.sub(/\A::/, '') == inverse_class_name
166
167
 
167
168
  end
168
169
  if matches.size > 1
@@ -16,7 +16,7 @@ module Mongoid
16
16
  # We undefine most methods to get them sent through to the target.
17
17
  instance_methods.each do |method|
18
18
  undef_method(method) unless
19
- method =~ /^(__.*|send|object_id|equal\?|respond_to\?|tap|public_send|extend_proxy|extend_proxies)$/
19
+ method =~ /\A(__.*|send|object_id|equal\?|respond_to\?|tap|public_send|extend_proxy|extend_proxies)\z/
20
20
  end
21
21
 
22
22
  include Threaded::Lifecycle
@@ -38,7 +38,9 @@ module Mongoid
38
38
  # @since 2.2.0
39
39
  def add_atomic_pull(document)
40
40
  document.flagged_for_destroy = true
41
- (delayed_atomic_pulls[document.association_name.to_s] ||= []).push(document)
41
+ key = document.association_name.to_s
42
+ delayed_atomic_pulls[key] ||= []
43
+ delayed_atomic_pulls[key] << document
42
44
  end
43
45
 
44
46
  # Add an atomic unset for the document.
@@ -53,7 +55,9 @@ module Mongoid
53
55
  # @since 3.0.0
54
56
  def add_atomic_unset(document)
55
57
  document.flagged_for_destroy = true
56
- (delayed_atomic_unsets[document.association_name.to_s] ||= []).push(document)
58
+ key = document.association_name.to_s
59
+ delayed_atomic_unsets[key] ||= []
60
+ delayed_atomic_unsets[key] << document
57
61
  end
58
62
 
59
63
  # Returns path of the attribute for modification
@@ -191,7 +195,13 @@ module Mongoid
191
195
  #
192
196
  # @since 2.1.0
193
197
  def atomic_paths
194
- @atomic_paths ||= _association ? _association.path(self) : Atomic::Paths::Root.new(self)
198
+ @atomic_paths ||= begin
199
+ if _association
200
+ _association.path(self)
201
+ else
202
+ Atomic::Paths::Root.new(self)
203
+ end
204
+ end
195
205
  end
196
206
 
197
207
  # Get all the attributes that need to be pulled.
@@ -450,7 +450,13 @@ module Mongoid
450
450
  #
451
451
  # @since 3.1.0
452
452
  def for_js(javascript, scope = {})
453
- js_query(BSON::CodeWithScope.new(javascript, scope))
453
+ code = if scope.empty?
454
+ # CodeWithScope is not supported for $where as of MongoDB 4.4
455
+ BSON::Code.new(javascript)
456
+ else
457
+ BSON::CodeWithScope.new(javascript, scope)
458
+ end
459
+ js_query(code)
454
460
  end
455
461
 
456
462
  private
@@ -74,7 +74,8 @@ module Mongoid
74
74
  # @since 5.1.0
75
75
  def create_with(attrs = {})
76
76
  tap do
77
- (@create_attrs ||= {}).merge!(attrs)
77
+ @create_attrs ||= {}
78
+ @create_attrs.update(attrs)
78
79
  end
79
80
  end
80
81
 
@@ -60,7 +60,7 @@ module Mongoid
60
60
  #
61
61
  # @since 1.0.0
62
62
  def __numeric__(object)
63
- object.to_s =~ /(^[-+]?[0-9]+$)|(\.0+$)|(\.$)/ ? object.to_i : Float(object)
63
+ object.to_s =~ /(\A[-+]?[0-9]+\z)|(\.0+\z)|(\.\z)/ ? object.to_i : Float(object)
64
64
  end
65
65
 
66
66
  # Evolve the object to an integer.
@@ -12,7 +12,7 @@ module Mongoid
12
12
  # Is the object a regexp?
13
13
  #
14
14
  # @example Is the object a regex?
15
- # /^[123]/.regexp?
15
+ # /\A[123]/.regexp?
16
16
  #
17
17
  # @return [ true ] Always true.
18
18
  #
@@ -24,7 +24,7 @@ module Mongoid
24
24
  # Evolve the object into a regex.
25
25
  #
26
26
  # @example Evolve the object to a regex.
27
- # Regexp.evolve("^[123]")
27
+ # Regexp.evolve("\A[123]")
28
28
  #
29
29
  # @param [ Regexp, String ] object The object to evolve.
30
30
  #
@@ -55,7 +55,7 @@ module Mongoid
55
55
  # Evolve the object into a raw bson regex.
56
56
  #
57
57
  # @example Evolve the object to a regex.
58
- # BSON::Regexp::Raw.evolve("^[123]")
58
+ # BSON::Regexp::Raw.evolve("\\A[123]")
59
59
  #
60
60
  # @param [ BSON::Regexp::Raw, String ] object The object to evolve.
61
61
  #
@@ -163,7 +163,7 @@ module Mongoid
163
163
  if expr.is_a?(Selectable)
164
164
  expr = expr.selector
165
165
  end
166
- normalized = _mongoid_normalize_expr(expr)
166
+ normalized = _mongoid_expand_keys(expr)
167
167
  sel.store(operator, result_criteria.push(normalized))
168
168
  end
169
169
  end
@@ -190,9 +190,9 @@ module Mongoid
190
190
  sel = query.selector
191
191
  _mongoid_flatten_arrays(criteria).each do |criterion|
192
192
  if criterion.is_a?(Selectable)
193
- expr = _mongoid_normalize_expr(criterion.selector)
193
+ expr = _mongoid_expand_keys(criterion.selector)
194
194
  else
195
- expr = criterion
195
+ expr = _mongoid_expand_keys(criterion)
196
196
  end
197
197
  if sel.empty?
198
198
  sel.store(operator, [expr])
@@ -212,7 +212,7 @@ module Mongoid
212
212
  # explicitly only expands Array objects and Array subclasses.
213
213
  private def _mongoid_flatten_arrays(array)
214
214
  out = []
215
- pending = array
215
+ pending = array.dup
216
216
  until pending.empty?
217
217
  item = pending.shift
218
218
  if item.nil?
@@ -226,11 +226,78 @@ module Mongoid
226
226
  out
227
227
  end
228
228
 
229
- # @api private
230
- private def _mongoid_normalize_expr(expr)
231
- expr.inject({}) do |hash, (field, value)|
232
- hash.merge!(field.__expr_part__(value.__expand_complex__))
229
+ # Takes a criteria hash and expands Key objects into hashes containing
230
+ # MQL corresponding to said key objects.
231
+ #
232
+ # Ruby does not permit multiple symbol operators. For example,
233
+ # {:foo.gt => 1, :foo.gt => 2} is collapsed to {:foo.gt => 2} by the
234
+ # language. Therefore this method never has to deal with multiple
235
+ # identical operators.
236
+ #
237
+ # Similarly, this method should never need to expand a literal value
238
+ # and an operator at the same time.
239
+ #
240
+ # @param [ Hash ] Criteria including Key instances.
241
+ #
242
+ # @return [ Hash ] Expanded criteria.
243
+ private def _mongoid_expand_keys(expr)
244
+ unless expr.is_a?(Hash)
245
+ raise ArgumentError, 'Argument must be a Hash'
246
+ end
247
+
248
+ result = {}
249
+ expr.each do |field, value|
250
+ field.__expr_part__(value.__expand_complex__).each do |k, v|
251
+ if result[k]
252
+ if result[k].is_a?(Hash)
253
+ # Existing value is an operator.
254
+ # If new value is also an operator, ensure there are no
255
+ # conflicts and add
256
+ if v.is_a?(Hash)
257
+ # The new value is also an operator.
258
+ # If there are no conflicts, combine the hashes, otherwise
259
+ # add new conditions to top level with $and.
260
+ if (v.keys & result[k].keys).empty?
261
+ result[k].update(v)
262
+ else
263
+ raise NotImplementedError, 'Ruby does not allow same symbol operator with different values'
264
+ result['$and'] ||= []
265
+ result['$and'] << {k => v}
266
+ end
267
+ else
268
+ # The new value is a simple value.
269
+ # If there isn't an $eq operator already in the query,
270
+ # transform the new value into an $eq operator and add it
271
+ # to the existing hash. Otherwise add the new condition
272
+ # with $and to the top level.
273
+ if result[k].key?('$eq')
274
+ raise NotImplementedError, 'Ruby does not allow same symbol operator with different values'
275
+ result['$and'] ||= []
276
+ result['$and'] << {k => v}
277
+ else
278
+ result[k].update('$eq' => v)
279
+ end
280
+ end
281
+ else
282
+ # Existing value is a simple value.
283
+ # If we are adding an operator, and the operator is not $eq,
284
+ # convert existing value into $eq and add the new operator
285
+ # to the same hash. Otherwise add the new condition with $and
286
+ # to the top level.
287
+ if v.is_a?(Hash) && !v.key?('$eq')
288
+ result[k] = {'$eq' => result[k]}.update(v)
289
+ else
290
+ raise NotImplementedError, 'Ruby does not allow same symbol operator with different values'
291
+ result['$and'] ||= []
292
+ result['$and'] << {k => v}
293
+ end
294
+ end
295
+ else
296
+ result[k] = v
297
+ end
298
+ end
233
299
  end
300
+ result
234
301
  end
235
302
 
236
303
  # Adds the criterion to the existing selection.
@@ -89,11 +89,27 @@ module Mongoid
89
89
  if new_s.is_a?(Selectable)
90
90
  new_s = new_s.selector
91
91
  end
92
- normalized = _mongoid_normalize_expr(new_s)
92
+ normalized = _mongoid_expand_keys(new_s)
93
93
  normalized.each do |k, v|
94
94
  k = k.to_s
95
95
  if c.selector[k]
96
- c = c.send(:__multi__, [k => v], '$and')
96
+ # There is already a condition on k.
97
+ # If v is an operator, and all existing conditions are
98
+ # also operators, and v isn't present in existing conditions,
99
+ # we can add to existing conditions.
100
+ # Otherwise use $and.
101
+ if v.is_a?(Hash) &&
102
+ v.length == 1 &&
103
+ (new_k = v.keys.first).start_with?('$') &&
104
+ (existing_kv = c.selector[k]).is_a?(Hash) &&
105
+ !existing_kv.key?(new_k) &&
106
+ existing_kv.keys.all? { |sub_k| sub_k.start_with?('$') }
107
+ then
108
+ merged_v = c.selector[k].merge(v)
109
+ c.selector.store(k, merged_v)
110
+ else
111
+ c = c.send(:__multi__, [k => v], '$and')
112
+ end
97
113
  else
98
114
  c.selector.store(k, v)
99
115
  end
@@ -567,17 +583,21 @@ module Mongoid
567
583
  if new_s.is_a?(Selectable)
568
584
  new_s = new_s.selector
569
585
  end
570
- new_s.each do |k, v|
586
+ _mongoid_expand_keys(new_s).each do |k, v|
571
587
  k = k.to_s
572
588
  if c.selector[k] || k[0] == ?$
573
589
  c = c.send(:__multi__, [{'$nor' => [{k => v}]}], '$and')
574
590
  else
575
- if v.is_a?(Regexp)
576
- negated_operator = '$not'
591
+ if v.is_a?(Hash)
592
+ c = c.send(:__multi__, [{'$nor' => [{k => v}]}], '$and')
577
593
  else
578
- negated_operator = '$ne'
594
+ if v.is_a?(Regexp)
595
+ negated_operator = '$not'
596
+ else
597
+ negated_operator = '$ne'
598
+ end
599
+ c = c.send(:__override__, {k => v}, negated_operator)
579
600
  end
580
- c = c.send(:__override__, {k => v}, negated_operator)
581
601
  end
582
602
  end
583
603
  c
@@ -665,7 +685,7 @@ module Mongoid
665
685
  # and add the result to self.
666
686
  exprs = criteria.map do |criterion|
667
687
  if criterion.is_a?(Selectable)
668
- _mongoid_normalize_expr(criterion.selector)
688
+ _mongoid_expand_keys(criterion.selector)
669
689
  else
670
690
  Hash[criterion.map do |k, v|
671
691
  if k.is_a?(Symbol)
@@ -48,9 +48,11 @@ module Mongoid
48
48
  value.each_pair do |_key, _value|
49
49
  value[_key] = (key == "$rename") ? _value.to_s : mongoize_for(key, klass, _key, _value)
50
50
  end
51
- (consolidated[key] ||= {}).merge!(value)
51
+ consolidated[key] ||= {}
52
+ consolidated[key].update(value)
52
53
  else
53
- (consolidated["$set"] ||= {}).merge!(key => mongoize_for(key, klass, key, value))
54
+ consolidated["$set"] ||= {}
55
+ consolidated["$set"].update(key => mongoize_for(key, klass, key, value))
54
56
  end
55
57
  end
56
58
  consolidated
@@ -11,7 +11,7 @@ module Mongoid
11
11
  # type.
12
12
  #
13
13
  # @example Mongoize the object.
14
- # Regexp.mongoize(/^[abc]/)
14
+ # Regexp.mongoize(/\A[abc]/)
15
15
  #
16
16
  # @param [ Regexp, String ] object The object to mongoize.
17
17
  #
@@ -500,7 +500,8 @@ module Mongoid
500
500
  def create_translations_getter(name, meth)
501
501
  generated_methods.module_eval do
502
502
  re_define_method("#{meth}_translations") do
503
- (attributes[name] ||= {}).with_indifferent_access
503
+ attributes[name] ||= {}
504
+ attributes[name].with_indifferent_access
504
505
  end
505
506
  alias_method :"#{meth}_t", :"#{meth}_translations"
506
507
  end
@@ -10,8 +10,8 @@ module Mongoid
10
10
  # Does the supplied query match the attribute?
11
11
  #
12
12
  # @example Does this match?
13
- # matcher._matches?(/^Em/)
14
- # matcher._matches?(BSON::Regex::Raw.new("^Em"))
13
+ # matcher._matches?(/\AEm/)
14
+ # matcher._matches?(BSON::Regex::Raw.new("\\AEm"))
15
15
  #
16
16
  # @param [ BSON::Regexp::Raw, Regexp ] regexp The regular expression object.
17
17
  #
@@ -57,7 +57,10 @@ module Mongoid
57
57
  def push(pushes)
58
58
  prepare_atomic_operation do |ops|
59
59
  process_atomic_operations(pushes) do |field, value|
60
- existing = send(field) || (attributes[field] ||= [])
60
+ existing = send(field) || begin
61
+ attributes[field] ||= []
62
+ attributes[field]
63
+ end
61
64
  values = [ value ].flatten(1)
62
65
  values.each{ |val| existing.push(val) }
63
66
  ops[atomic_attribute_name(field)] = { "$each" => values }
@@ -122,6 +122,12 @@ module Mongoid
122
122
  end
123
123
  end
124
124
 
125
+ def client_name
126
+ @client_name ||= options[:client] ||
127
+ Threaded.client_override ||
128
+ storage_options && __evaluate__(storage_options[:client])
129
+ end
130
+
125
131
  # Determine if this persistence context is equal to another.
126
132
  #
127
133
  # @example Compare two persistence contexts.
@@ -139,12 +145,6 @@ module Mongoid
139
145
 
140
146
  private
141
147
 
142
- def client_name
143
- @client_name ||= options[:client] ||
144
- Threaded.client_override ||
145
- storage_options && __evaluate__(storage_options[:client])
146
- end
147
-
148
148
  def set_options!(opts)
149
149
  @options ||= opts.each.reduce({}) do |_options, (key, value)|
150
150
  unless VALID_OPTIONS.include?(key.to_sym)
@@ -168,7 +168,8 @@ module Mongoid
168
168
  @coll_name ||= result.namespace.sub("#{database.name}.", '') if result.namespace
169
169
  documents = result.documents
170
170
  if @cursor_id.zero? && !@after_first_batch
171
- (@cached_documents ||= []).concat(documents)
171
+ @cached_documents ||= []
172
+ @cached_documents.concat(documents)
172
173
  end
173
174
  @after_first_batch = true
174
175
  documents
@@ -150,7 +150,7 @@ module Mongoid
150
150
  #
151
151
  # @since 2.3.0
152
152
  def filter(value)
153
- !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}$/i : value
153
+ !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}\z/i : value
154
154
  end
155
155
 
156
156
  # Scope the criteria to the scope options provided.
@@ -2,5 +2,5 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  module Mongoid
5
- VERSION = "7.1.1"
5
+ VERSION = "7.1.2"
6
6
  end
@@ -13,7 +13,7 @@ class <%= class_name %><%= " < #{options[:parent].classify}" if options[:parent]
13
13
  field :<%= attribute.name %>, type: <%= attribute.type_class %>
14
14
  <% end -%>
15
15
  <% attributes.select{|attr| attr.reference? }.each do |attribute| -%>
16
- embedded_in :<%= attribute.name%>
16
+ belongs_to :<%= attribute.name%>
17
17
  <% end -%>
18
18
  end
19
19
  <% end -%>