mongoid 7.1.1 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.
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 -%>