mongoid 7.0.2 → 7.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/mongoid/association/referenced/eager.rb +4 -1
  4. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +3 -1
  5. data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
  6. data/lib/mongoid/association/relatable.rb +90 -10
  7. data/lib/mongoid/clients/options.rb +6 -4
  8. data/lib/mongoid/copyable.rb +2 -2
  9. data/lib/mongoid/criteria/options.rb +2 -2
  10. data/lib/mongoid/criteria/queryable/selectable.rb +33 -6
  11. data/lib/mongoid/document.rb +11 -4
  12. data/lib/mongoid/extensions/big_decimal.rb +1 -1
  13. data/lib/mongoid/extensions/regexp.rb +1 -0
  14. data/lib/mongoid/matchable.rb +3 -1
  15. data/lib/mongoid/matchable/eq.rb +22 -0
  16. data/lib/mongoid/matchable/ne.rb +1 -1
  17. data/lib/mongoid/persistence_context.rb +20 -5
  18. data/lib/mongoid/query_cache.rb +8 -4
  19. data/lib/mongoid/railtie.rb +17 -0
  20. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  21. data/lib/mongoid/scopable.rb +3 -3
  22. data/lib/mongoid/threaded.rb +36 -0
  23. data/lib/mongoid/version.rb +1 -1
  24. data/spec/app/models/minim.rb +7 -0
  25. data/spec/app/models/store_as_dup_test3.rb +7 -0
  26. data/spec/app/models/store_as_dup_test4.rb +7 -0
  27. data/spec/config/mongoid.yml +12 -3
  28. data/spec/integration/associations/belongs_to_spec.rb +13 -0
  29. data/spec/lite_spec_helper.rb +56 -0
  30. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +24 -5
  31. data/spec/mongoid/association/referenced/belongs_to_spec.rb +46 -3
  32. data/spec/mongoid/association/referenced/has_and_belongs_to_many/eager_spec.rb +21 -2
  33. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_persistence_spec.rb +56 -0
  34. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +26 -0
  35. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +3 -3
  36. data/spec/mongoid/association/referenced/has_many/proxy_query_spec.rb +23 -0
  37. data/spec/mongoid/association/referenced/has_many_models.rb +37 -0
  38. data/spec/mongoid/association/referenced/has_many_spec.rb +3 -3
  39. data/spec/mongoid/association/referenced/has_one_models.rb +48 -0
  40. data/spec/mongoid/association/referenced/has_one_spec.rb +51 -4
  41. data/spec/mongoid/clients/factory_spec.rb +24 -18
  42. data/spec/mongoid/clients/options_spec.rb +40 -37
  43. data/spec/mongoid/clients_spec.rb +68 -8
  44. data/spec/mongoid/config_spec.rb +3 -1
  45. data/spec/mongoid/contextual/mongo_spec.rb +5 -2
  46. data/spec/mongoid/copyable_spec.rb +40 -6
  47. data/spec/mongoid/copyable_spec_models.rb +17 -0
  48. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +3 -3
  49. data/spec/mongoid/criteria/queryable/selectable_spec.rb +42 -3
  50. data/spec/mongoid/criteria/queryable/selector_spec.rb +2 -2
  51. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  52. data/spec/mongoid/criteria_spec.rb +18 -3
  53. data/spec/mongoid/document_spec.rb +81 -2
  54. data/spec/mongoid/extensions/big_decimal_spec.rb +9 -9
  55. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  56. data/spec/mongoid/fields_spec.rb +1 -1
  57. data/spec/mongoid/findable_spec.rb +1 -1
  58. data/spec/mongoid/matchable/eq_spec.rb +48 -0
  59. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  60. data/spec/mongoid/persistence_context_spec.rb +1 -1
  61. data/spec/mongoid/query_cache_spec.rb +59 -6
  62. data/spec/mongoid/scopable_spec.rb +13 -0
  63. data/spec/mongoid/threaded_spec.rb +68 -0
  64. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  65. data/spec/spec_helper.rb +35 -25
  66. data/spec/support/constraints.rb +101 -0
  67. data/spec/support/macros.rb +20 -0
  68. data/spec/support/spec_config.rb +39 -0
  69. metadata +471 -460
  70. checksums.yaml.gz.sig +0 -0
  71. data.tar.gz.sig +0 -2
  72. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e3c2f9aa40e2f20c7620a2ae7684da4ebfc1699e32357dd4aa663d93b7ade33
4
- data.tar.gz: 9f0d2145026c3db222638b7d00d842a690294cc6101a582a14a75607c44474a1
3
+ metadata.gz: 6f9840f2a5bdcfd628358c56f70818857080118680059a6ca299c6de3b4554c2
4
+ data.tar.gz: f8700656e769546f2c8d2555013aa601d10552e9e48faf37f24c66fded700b1d
5
5
  SHA512:
6
- metadata.gz: 0f9b11ae060df18208b48b911ee39c2954a7855ac7ffc19b073aa21a707e51805bb465b73b7634abb425d983b3d57fbbab044a202853e532c0fc8331141b9734
7
- data.tar.gz: 24d37b3c3b77d110d45ec6df6209e8cf4a7821fb787c4d369cd87f01bcf66db0292e101928f8ab2a102f5020c6bb4d7b4b19b13fc2c7765096f5f409251825ff
6
+ metadata.gz: a05fb92510939cf2ccff55855219033db8a9a45399865f725e695af95a9501f72a0db72422766dd54fc9946854e0065b9beb870bfeb3a02b8ac9e605962419e0
7
+ data.tar.gz: 267ff19b100ca5ef83f383ddd8d4ece8ce0e122a16cbc6c1605bc872e391ad775bd6336beb53af733ba8d7b47d9ec01d9842f5502c7f0c135cdc2fa258b80b47
data/README.md CHANGED
@@ -21,7 +21,7 @@ Project Tracking
21
21
  Compatibility
22
22
  -------------
23
23
 
24
- Mongoid is tested against MRI 2.2, 2.3 and JRuby (9.1).
24
+ Mongoid is tested against MRI 2.2-2.5 and JRuby 9.1-9.2.
25
25
 
26
26
  Documentation
27
27
  -------------
@@ -64,7 +64,10 @@ module Mongoid
64
64
  #
65
65
  # @since 4.0.0
66
66
  def each_loaded_document
67
- criteria = @association.klass.any_in(key => keys_from_docs)
67
+ doc_keys = keys_from_docs
68
+ return @association.klass.none if doc_keys.all?(&:nil?)
69
+
70
+ criteria = @association.klass.any_in(key => doc_keys)
68
71
  criteria.inclusions = criteria.inclusions - [@association]
69
72
  criteria.each do |doc|
70
73
  yield doc
@@ -135,7 +135,9 @@ module Mongoid
135
135
  end
136
136
  unless _association.forced_nil_inverse?
137
137
  if replacement
138
- objects_to_clear = _base.send(foreign_key) - replacement.collect(&:id)
138
+ objects_to_clear = _base.send(foreign_key) - replacement.collect do |object|
139
+ object.send(_association.primary_key)
140
+ end
139
141
  criteria(objects_to_clear).pull(inverse_foreign_key => _base._id)
140
142
  else
141
143
  criteria.pull(inverse_foreign_key => _base._id)
@@ -284,7 +284,7 @@ module Mongoid
284
284
  new_ids = new_docs.map { |doc| doc._id }
285
285
  remove_not_in(new_ids)
286
286
  new_docs.each do |doc|
287
- docs.push(doc) if doc.send(foreign_key) != _base._id
287
+ docs.push(doc) if doc.send(foreign_key) != _base.send(_association.primary_key)
288
288
  end
289
289
  concat(docs)
290
290
  else
@@ -303,7 +303,7 @@ module Mongoid
303
303
  #
304
304
  # @since 2.4.0
305
305
  def unscoped
306
- klass.unscoped.where(foreign_key => _base._id)
306
+ klass.unscoped.where(foreign_key => _base.send(_association.primary_key))
307
307
  end
308
308
 
309
309
  private
@@ -147,23 +147,64 @@ module Mongoid
147
147
 
148
148
  # The class name of the relation object(s).
149
149
  #
150
- # @return [ String ] The relation objects' class name.
150
+ # The class name may be fully qualified or may be specified relative
151
+ # to the class on which the association is defined (this class is
152
+ # accessible as inverse_class). If :class_name option is given in the
153
+ # association, the exact value of that option is returned here for
154
+ # backwards compatibility reasons. If :class_name option is not given,
155
+ # the name of the class computed by Mongoid to be the association target
156
+ # is returned, and it will be fully qualified.
157
+ #
158
+ # The class name returned by this method may not correspond to a defined
159
+ # class. The return value of the method is the class name that Mongoid
160
+ # would reference, relative to the host document class, when it needs to
161
+ # perform operations on the association target.
162
+ #
163
+ # @note The return value of this method should not be used to determine
164
+ # whether two associations have the same target class, because the
165
+ # return value is not always a fully qualified class name. To compare
166
+ # classes, retrieve the class instance of the association target using
167
+ # the +relation_class+ method.
168
+ #
169
+ # @return [ String ] The association objects' class name.
151
170
  #
152
171
  # @since 7.0
153
172
  def relation_class_name
154
- @class_name ||= @options[:class_name] || ActiveSupport::Inflector.classify(name_with_module)
173
+ @class_name ||= @options[:class_name] || begin
174
+ cls_name = ActiveSupport::Inflector.classify(name)
175
+ begin
176
+ cls_name = resolve_name(inverse_class, cls_name).name
177
+ rescue NameError
178
+ # ignore
179
+ end
180
+ cls_name
181
+ end
155
182
  end
156
183
  alias :class_name :relation_class_name
157
184
 
158
185
  # The class of the relation object(s).
159
186
  #
160
- # @return [ String ] The relation objects' class.
187
+ # This method returns the class instance corresponding to
188
+ # +relation_class_name+, resolved relative to the host document class.
189
+ #
190
+ # If the class does not exist, this method raises NameError. This can
191
+ # happen because the target class has not yet been defined. Note that
192
+ # polymorphic associations generally do not have a well defined target
193
+ # class because the target class can change from one object to another,
194
+ # and calling this method on a polymorphic association will generally
195
+ # fail with a NameError or produce misleading results (if a class does
196
+ # happen to be defined with the same name as the association name).
197
+ #
198
+ # @return [ String ] The association objects' class.
161
199
  #
162
200
  # @since 7.0
163
- def klass
164
- @klass ||= relation_class_name.constantize
201
+ def relation_class
202
+ @klass ||= begin
203
+ cls_name = @options[:class_name] || ActiveSupport::Inflector.classify(name)
204
+ resolve_name(inverse_class, cls_name)
205
+ end
165
206
  end
166
- alias :relation_class :klass
207
+ alias :klass :relation_class
167
208
 
168
209
  # The class name of the object owning this relation.
169
210
  #
@@ -325,10 +366,6 @@ module Mongoid
325
366
 
326
367
  private
327
368
 
328
- def name_with_module
329
- @module_path + name.to_s.capitalize
330
- end
331
-
332
369
  # Gets the model classes with inverse associations of this model. This is used to determine
333
370
  # the classes on the other end of polymorphic relations with models.
334
371
  def inverse_association_classes
@@ -424,6 +461,49 @@ module Mongoid
424
461
  def default_inverse
425
462
  @default_inverse ||= klass.relations[inverse_klass.name.underscore]
426
463
  end
464
+
465
+ # Returns an array of classes/modules forming the namespace hierarchy
466
+ # where symbols referenced in the provided class/module would be looked
467
+ # up by Ruby. For example, if mod is Foo::Bar, this method would return
468
+ # [Foo::Bar, Foo, Object].
469
+ def namespace_hierarchy(mod)
470
+ parent = Object
471
+ hier = [parent]
472
+ mod.name.split('::').each do |part|
473
+ parent = parent.const_get(part)
474
+ hier << parent
475
+ end
476
+ hier.reverse
477
+ end
478
+
479
+ # Resolves the given class/module name in the context of the specified
480
+ # module, as Ruby would when a constant is referenced in the source.
481
+ def resolve_name(mod, name)
482
+ cls = exc = nil
483
+ parts = name.to_s.split('::')
484
+ namespace_hierarchy(mod).each do |ns|
485
+ begin
486
+ parts.each do |part|
487
+ # Simple const_get sometimes pulls names out of weird scopes,
488
+ # perhaps confusing the receiver (ns in this case) with the
489
+ # local scope. Walk the class hierarchy ourselves one node
490
+ # at a time by specifying false as the second argument.
491
+ ns = ns.const_get(part, false)
492
+ end
493
+ cls = ns
494
+ break
495
+ rescue NameError => e
496
+ if exc.nil?
497
+ exc = e
498
+ end
499
+ end
500
+ end
501
+ if cls.nil?
502
+ # Raise the first exception, this is from the most specific namespace
503
+ raise exc
504
+ end
505
+ cls
506
+ end
427
507
  end
428
508
  end
429
509
  end
@@ -20,11 +20,12 @@ module Mongoid
20
20
  #
21
21
  # @since 6.0.0
22
22
  def with(options_or_context, &block)
23
+ original_context = PersistenceContext.get(self)
23
24
  original_cluster = persistence_context.cluster
24
25
  set_persistence_context(options_or_context)
25
26
  yield self
26
27
  ensure
27
- clear_persistence_context(original_cluster)
28
+ clear_persistence_context(original_cluster, original_context)
28
29
  end
29
30
 
30
31
  def collection(parent = nil)
@@ -51,8 +52,8 @@ module Mongoid
51
52
  PersistenceContext.set(self, options_or_context)
52
53
  end
53
54
 
54
- def clear_persistence_context(original_cluster = nil)
55
- PersistenceContext.clear(self, original_cluster)
55
+ def clear_persistence_context(original_cluster = nil, context = nil)
56
+ PersistenceContext.clear(self, original_cluster, context)
56
57
  end
57
58
 
58
59
  module ClassMethods
@@ -92,11 +93,12 @@ module Mongoid
92
93
  #
93
94
  # @since 6.0.0
94
95
  def with(options, &block)
96
+ original_context = PersistenceContext.get(self)
95
97
  original_cluster = persistence_context.cluster
96
98
  PersistenceContext.set(self, options)
97
99
  yield self
98
100
  ensure
99
- PersistenceContext.clear(self, original_cluster)
101
+ PersistenceContext.clear(self, original_cluster, original_context)
100
102
  end
101
103
 
102
104
  def persistence_context
@@ -73,8 +73,8 @@ module Mongoid
73
73
  next unless attrs.present? && attrs[association.key].present?
74
74
 
75
75
  if association.is_a?(Association::Embedded::EmbedsMany)
76
- attrs[association.name.to_s].each do |attr|
77
- embedded_klass = attr.fetch('_type', association.class_name).constantize
76
+ attrs[association.key].each do |attr|
77
+ embedded_klass = attr.fetch('_type', association.relation_class_name).constantize
78
78
  process_localized_attributes(embedded_klass, attr)
79
79
  end
80
80
  else
@@ -17,8 +17,8 @@ module Mongoid
17
17
  PersistenceContext.set(klass, options)
18
18
  end
19
19
 
20
- def clear_persistence_context(original_cluster)
21
- PersistenceContext.clear(klass, original_cluster)
20
+ def clear_persistence_context(original_cluster, original_context)
21
+ PersistenceContext.clear(klass, original_cluster, original_context)
22
22
  end
23
23
  end
24
24
  end
@@ -134,13 +134,21 @@ module Mongoid
134
134
  ::Boolean.evolve(value)
135
135
  end
136
136
 
137
- # Add a $geoIntersects or $geoWithin selection. Symbol operators must be used as shown in
138
- # the examples to expand the criteria.
137
+ # Add a $geoIntersects or $geoWithin selection. Symbol operators must
138
+ # be used as shown in the examples to expand the criteria.
139
139
  #
140
140
  # @note The only valid geometry shapes for a $geoIntersects are:
141
141
  # :intersects_line, :intersects_point, and :intersects_polygon.
142
142
  #
143
- # @note The only valid geometry shape for a $geoWithin is :within_polygon
143
+ # @note The only valid options for a $geoWithin query are the geometry
144
+ # shape :within_polygon and the operator :within_box.
145
+ #
146
+ # @note The :within_box operator for the $geoWithin query expects the
147
+ # lower left (south west) coordinate pair as the first argument and
148
+ # the upper right (north east) as the second argument.
149
+ # Important: When latitude and longitude are passed, longitude is
150
+ # expected as the first element of the coordinate pair.
151
+ # Source: https://docs.mongodb.com/manual/reference/operator/query/box/
144
152
  #
145
153
  # @example Add a geo intersect criterion for a line.
146
154
  # query.geo_spacial(:location.intersects_line => [[ 1, 10 ], [ 2, 10 ]])
@@ -154,6 +162,9 @@ module Mongoid
154
162
  # @example Add a geo within criterion for a polygon.
155
163
  # query.geo_spacial(:location.within_polygon => [[ 1, 10 ], [ 2, 10 ], [ 1, 10 ]])
156
164
  #
165
+ # @example Add a geo within criterion for a box.
166
+ # query.geo_spacial(:location.within_box => [[ 1, 10 ], [ 2, 10 ])
167
+ #
157
168
  # @param [ Hash ] criterion The criterion.
158
169
  #
159
170
  # @return [ Selectable ] The cloned selectable.
@@ -174,6 +185,7 @@ module Mongoid
174
185
  key :within_polygon, :override, "$geoWithin", "$geometry" do |value|
175
186
  { "type" => POLYGON, "coordinates" => value }
176
187
  end
188
+ key :within_box, :override, "$geoWithin", "$box"
177
189
 
178
190
  # Add the $gt criterion to the selector.
179
191
  #
@@ -501,6 +513,11 @@ module Mongoid
501
513
  # @example Construct a text search selector with options.
502
514
  # selectable.text_search("testing", :$language => "fr")
503
515
  #
516
+ # @note Per https://docs.mongodb.com/manual/reference/operator/query/text/
517
+ # it is not currently possible to supply multiple text search
518
+ # conditions in a query. Mongoid will build such a query but the
519
+ # server will return an error when trying to execute it.
520
+ #
504
521
  # @param [ String, Symbol ] terms A string of terms that MongoDB parses
505
522
  # and uses to query the text index.
506
523
  # @param [ Hash ] opts Text search options. See MongoDB documentation
@@ -512,9 +529,19 @@ module Mongoid
512
529
  def text_search(terms, opts = nil)
513
530
  clone.tap do |query|
514
531
  if terms
515
- criterion = { :$text => { :$search => terms } }
516
- criterion[:$text].merge!(opts) if opts
517
- query.selector = criterion
532
+ criterion = {'$text' => { '$search' => terms }}
533
+ criterion['$text'].merge!(opts) if opts
534
+ if query.selector['$text']
535
+ # Per https://docs.mongodb.com/manual/reference/operator/query/text/
536
+ # multiple $text expressions are not currently supported by
537
+ # MongoDB server, but build the query correctly instead of
538
+ # overwriting previous text search condition with the currently
539
+ # given one.
540
+ Mongoid.logger.warn('Multiple $text expressions per query are not currently supported by the server')
541
+ query.selector = {'$and' => [query.selector]}.merge(criterion)
542
+ else
543
+ query.selector = query.selector.merge(criterion)
544
+ end
518
545
  end
519
546
  end
520
547
  end
@@ -180,6 +180,13 @@ module Mongoid
180
180
 
181
181
  # Calls #as_json on the document with additional, Mongoid-specific options.
182
182
  #
183
+ # @note Rails 6 changes return value of as_json for non-primitive types
184
+ # such as BSON::ObjectId. In Rails <= 5, as_json returned these as
185
+ # instances of the class. In Rails 6, these are returned serialized to
186
+ # primitive types (e.g. {"$oid"=>"5bcfc40bde340b37feda98e9"}).
187
+ # See https://github.com/rails/rails/commit/2e5cb980a448e7f4ab00df6e9ad4c1cc456616aa
188
+ # for more information.
189
+ #
183
190
  # @example Get the document as json.
184
191
  # document.as_json(compact: true)
185
192
  #
@@ -192,11 +199,11 @@ module Mongoid
192
199
  #
193
200
  # @since 5.1.0
194
201
  def as_json(options = nil)
195
- if options && (options[:compact] == true)
196
- super(options).reject! { |k,v| v.nil? }
197
- else
198
- super(options)
202
+ rv = super
203
+ if options && options[:compact]
204
+ rv = rv.compact
199
205
  end
206
+ rv
200
207
  end
201
208
 
202
209
  # Returns an instance of the specified class with the attributes,
@@ -53,7 +53,7 @@ module Mongoid
53
53
  #
54
54
  # @since 3.0.0
55
55
  def demongoize(object)
56
- object && object.numeric? ? ::BigDecimal.new(object.to_s) : nil
56
+ object && object.numeric? ? BigDecimal(object.to_s) : nil
57
57
  end
58
58
 
59
59
  # Mongoize an object of any type to how it's stored in the db as a String.
@@ -17,6 +17,7 @@ module Mongoid
17
17
  #
18
18
  # @since 3.0.0
19
19
  def mongoize(object)
20
+ return nil if object.nil?
20
21
  ::Regexp.new(object)
21
22
  end
22
23
  end
@@ -2,6 +2,7 @@
2
2
  require "mongoid/matchable/default"
3
3
  require "mongoid/matchable/all"
4
4
  require "mongoid/matchable/and"
5
+ require "mongoid/matchable/eq"
5
6
  require "mongoid/matchable/exists"
6
7
  require "mongoid/matchable/gt"
7
8
  require "mongoid/matchable/gte"
@@ -32,6 +33,7 @@ module Mongoid
32
33
  "$all" => All,
33
34
  "$elemMatch" => ElemMatch,
34
35
  "$and" => And,
36
+ "$eq" => Eq,
35
37
  "$exists" => Exists,
36
38
  "$gt" => Gt,
37
39
  "$gte" => Gte,
@@ -42,7 +44,7 @@ module Mongoid
42
44
  "$nin" => Nin,
43
45
  "$or" => Or,
44
46
  "$nor" => Nor,
45
- "$size" => Size
47
+ "$size" => Size,
46
48
  }.with_indifferent_access.freeze
47
49
 
48
50
  # Determines if this document has the attributes to match the supplied
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+ module Mongoid
4
+ module Matchable
5
+
6
+ # Performs equivalency checks.
7
+ class Eq < Default
8
+
9
+ # Return true if the attribute and first value are equal.
10
+ #
11
+ # @example Do the values match?
12
+ # matcher._matches?({ :key => 10 })
13
+ #
14
+ # @param [ Hash ] value The values to check.
15
+ #
16
+ # @return [ true, false ] True if matches, false if not.
17
+ def _matches?(value)
18
+ super(value.values.first)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -12,7 +12,7 @@ module Mongoid
12
12
  #
13
13
  # @param [ Hash ] value The values to check.
14
14
  #
15
- # @return [ true, false ] If a value exists.
15
+ # @return [ true, false ] True if matches, false if not.
16
16
  def _matches?(value)
17
17
  !super(value.values.first)
18
18
  end
@@ -169,6 +169,9 @@ module Mongoid
169
169
 
170
170
  # Set the persistence context for a particular class or model instance.
171
171
  #
172
+ # If there already is a persistence context set, options in the existing
173
+ # context are combined with options given to the set call.
174
+ #
172
175
  # @example Set the persistence context for a class or model instance.
173
176
  # PersistenceContext.set(model)
174
177
  #
@@ -180,9 +183,19 @@ module Mongoid
180
183
  #
181
184
  # @since 6.0.0
182
185
  def set(object, options_or_context)
183
- context = PersistenceContext.new(object, options_or_context.is_a?(PersistenceContext) ?
184
- options_or_context.options : options_or_context)
185
- Thread.current["[mongoid][#{object.object_id}]:context"] = context
186
+ key = "[mongoid][#{object.object_id}]:context"
187
+ existing_context = Thread.current[key]
188
+ existing_options = if existing_context
189
+ existing_context.options
190
+ else
191
+ {}
192
+ end
193
+ if options_or_context.is_a?(PersistenceContext)
194
+ options_or_context = options_or_context.options
195
+ end
196
+ new_options = existing_options.merge(options_or_context)
197
+ context = PersistenceContext.new(object, new_options)
198
+ Thread.current[key] = context
186
199
  end
187
200
 
188
201
  # Get the persistence context for a particular class or model instance.
@@ -206,14 +219,16 @@ module Mongoid
206
219
  #
207
220
  # @param [ Class, Object ] object The class or model instance.
208
221
  # @param [ Mongo::Cluster ] cluster The original cluster before this context was used.
222
+ # @param [ Mongoid::PersistenceContext ] original_context The original persistence
223
+ # context that was set before this context was used.
209
224
  #
210
225
  # @since 6.0.0
211
- def clear(object, cluster = nil)
226
+ def clear(object, cluster = nil, original_context = nil)
212
227
  if context = get(object)
213
228
  context.client.close unless (context.cluster.equal?(cluster) || cluster.nil?)
214
229
  end
215
230
  ensure
216
- Thread.current["[mongoid][#{object.object_id}]:context"] = nil
231
+ Thread.current["[mongoid][#{object.object_id}]:context"] = original_context
217
232
  end
218
233
  end
219
234
  end