mongoid 8.1.7 → 8.1.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 965ef60c6694e5d48b7edd1f16c9718b975f3b3969e27a3557e9913a87791e00
4
- data.tar.gz: 28ea0535a075f4759ff40bb11dca76481b77e9d9cd00ce611b6e4d162e191532
3
+ metadata.gz: d3a2e1b9f4abdd261c6d18c42a1c107089ae415a57c9dcb0978c14891e78330b
4
+ data.tar.gz: 9c09793f70f1dd4ad367e2abcac271245c86f8d6414f262e7643d09538b3bce4
5
5
  SHA512:
6
- metadata.gz: bd40308e934c907c54d05a8644ac104e1236887e80eaba812939ad9c2a9dca622a306be12a7cf35b9af217c6c9fe894d5794053970ab89c18f21933f59da2197
7
- data.tar.gz: ba2aa5da4708302723c9170b57d6cfd9d760b63c2e1f3c33efcdcef33971c5720f07cd2f08d160160be675125981b8cab2b529b0a39d2100723de485e869b12f
6
+ metadata.gz: b0b89ea745ddafadbe1d1cf17c467ee7413eaf94d341bbb209df4db0eeb7cdff6e1fd8b01bc2a85016c47bb830e93ccbcdb0317578c32ee8bbb3dc06eb7db54e
7
+ data.tar.gz: 51627fd03388cbaeef60014b77e2a15202f06f2dffebd69d7ff0cdf5840885a5cdbacb2bc34794eb04c72595c675d5edb55e42b9aa4079dbb5de7d24f454eb8a
data/Rakefile CHANGED
@@ -55,7 +55,7 @@ task :release do
55
55
  WARNING
56
56
  end
57
57
 
58
- system 'gem', 'push', "mongoid-#{Mongoid::VERSION}.gem"
58
+ system 'bundle', 'exec', 'gem', 'push', "mongoid-#{Mongoid::VERSION}.gem"
59
59
  end
60
60
 
61
61
  RSpec::Core::RakeTask.new("spec") do |spec|
@@ -31,6 +31,9 @@ module Mongoid
31
31
  docs_map = {}
32
32
  queue = [ klass.to_s ]
33
33
 
34
+ # account for single-collection inheritance
35
+ queue.push(klass.root_class.to_s) if klass != klass.root_class
36
+
34
37
  while klass = queue.shift
35
38
  if as = assoc_map.delete(klass)
36
39
  as.each do |assoc|
@@ -22,7 +22,7 @@ module Mongoid
22
22
  # @return [ true | false ] If the document is new, or if the field is not
23
23
  # readonly.
24
24
  def attribute_writable?(name)
25
- new_record? || (!readonly_attributes.include?(name) && _loaded?(name))
25
+ new_record? || (!self.class.readonly_attributes.include?(name) && _loaded?(name))
26
26
  end
27
27
 
28
28
  private
@@ -62,12 +62,17 @@ module Mongoid
62
62
  # end
63
63
  #
64
64
  # @param [ Symbol... ] *names The names of the fields.
65
+ # @note When a parent class contains readonly attributes and is then
66
+ # inherited by a child class, the child class will inherit the
67
+ # parent's readonly attributes at the time of its creation.
68
+ # Updating the parent does not propagate down to child classes after wards.
65
69
  def attr_readonly(*names)
70
+ self.readonly_attributes = self.readonly_attributes.dup
66
71
  names.each do |name|
67
- readonly_attributes << database_field_name(name)
72
+ self.readonly_attributes << database_field_name(name)
68
73
  end
69
74
  end
70
75
  end
71
76
  end
72
77
  end
73
- end
78
+ end
@@ -552,7 +552,7 @@ module Mongoid
552
552
  # @return [ Selectable ] The new selectable.
553
553
  def not(*criteria)
554
554
  if criteria.empty?
555
- dup.tap { |query| query.negating = true }
555
+ dup.tap { |query| query.negating = !query.negating }
556
556
  else
557
557
  criteria.compact.inject(self.clone) do |c, new_s|
558
558
  if new_s.is_a?(Selectable)
@@ -133,7 +133,14 @@ module Mongoid
133
133
  #
134
134
  # @return [ Hash ] A hash of all attributes in the hierarchy.
135
135
  def as_document
136
- BSON::Document.new(as_attributes)
136
+ attrs = as_attributes
137
+
138
+ # legacy attributes have a tendency to leak internal state via
139
+ # `as_document`; we have to deep_dup the attributes here to prevent
140
+ # that.
141
+ attrs = attrs.deep_dup if Mongoid.legacy_attributes
142
+
143
+ BSON::Document.new(attrs)
137
144
  end
138
145
 
139
146
  # Calls #as_json on the document with additional, Mongoid-specific options.
@@ -17,6 +17,7 @@ module Mongoid
17
17
  #
18
18
  # @return [ Integer ] -1, 0, 1.
19
19
  def <=>(other)
20
+ return super unless other.is_a?(Mongoid::Equality)
20
21
  attributes["_id"].to_s <=> other.attributes["_id"].to_s
21
22
  end
22
23
 
@@ -141,9 +141,13 @@ module Mongoid
141
141
  # @api private
142
142
  def _mongoid_run_child_callbacks(kind, children: nil, &block)
143
143
  if Mongoid::Config.around_callbacks_for_embeds
144
- _mongoid_run_child_callbacks_with_around(kind, children: children, &block)
144
+ _mongoid_run_child_callbacks_with_around(kind,
145
+ children: children,
146
+ &block)
145
147
  else
146
- _mongoid_run_child_callbacks_without_around(kind, children: children, &block)
148
+ _mongoid_run_child_callbacks_without_around(kind,
149
+ children: children,
150
+ &block)
147
151
  end
148
152
  end
149
153
 
@@ -219,9 +223,6 @@ module Mongoid
219
223
  return false if env.halted
220
224
  env.value = !env.halted
221
225
  callback_list << [next_sequence, env]
222
- if (grandchildren = child.send(:cascadable_children, kind))
223
- _mongoid_run_child_before_callbacks(kind, children: grandchildren, callback_list: callback_list)
224
- end
225
226
  end
226
227
  callback_list
227
228
  end
@@ -22,13 +22,20 @@ module Mongoid
22
22
  # @example Set the created at time.
23
23
  # person.set_created_at
24
24
  def set_created_at
25
- if !timeless? && !created_at
25
+ if able_to_set_created_at?
26
26
  time = Time.configured.now
27
27
  self.updated_at = time if is_a?(Updated) && !updated_at_changed?
28
28
  self.created_at = time
29
29
  end
30
30
  clear_timeless_option
31
31
  end
32
+
33
+ # Is the created timestamp able to be set?
34
+ #
35
+ # @return [ true, false ] If the timestamp can be set.
36
+ def able_to_set_created_at?
37
+ !frozen? && !timeless? && !created_at
38
+ end
32
39
  end
33
40
  end
34
41
  end
@@ -7,6 +7,29 @@ module Mongoid
7
7
  # Provides behavior around traversing the document graph.
8
8
  module Traversable
9
9
  extend ActiveSupport::Concern
10
+ # This code is extracted from ActiveSupport so that we do not depend on
11
+ # their private API that may change at any time.
12
+ # This code should be reviewed and maybe removed when implementing
13
+ # https://jira.mongodb.org/browse/MONGOID-5832
14
+ class << self
15
+ # @api private
16
+ def __redefine(owner, name, value)
17
+ if owner.singleton_class?
18
+ owner.redefine_method(name) { value }
19
+ owner.send(:public, name)
20
+ end
21
+ owner.redefine_singleton_method(name) { value }
22
+ owner.singleton_class.send(:public, name)
23
+ owner.redefine_singleton_method("#{name}=") do |new_value|
24
+ if owner.equal?(self)
25
+ value = new_value
26
+ else
27
+ ::Mongoid::Traversable.redefine(self, name, new_value)
28
+ end
29
+ end
30
+ owner.singleton_class.send(:public, "#{name}=")
31
+ end
32
+ end
10
33
 
11
34
  def _parent
12
35
  @__parent ||= nil
@@ -30,11 +53,7 @@ module Mongoid
30
53
  if value
31
54
  Mongoid::Fields::Validators::Macro.validate_field_name(self, value)
32
55
  value = value.to_s
33
- if defined?(::ActiveSupport::ClassAttribute)
34
- ::ActiveSupport::ClassAttribute.redefine(self, 'discriminator_key', value)
35
- else
36
- super
37
- end
56
+ ::Mongoid::Traversable.__redefine(self, 'discriminator_key', value)
38
57
  else
39
58
  # When discriminator key is set to nil, replace the class's definition
40
59
  # of the discriminator key reader (provided by class_attribute earlier)
@@ -304,6 +323,18 @@ module Mongoid
304
323
  !!(Mongoid::Document > superclass)
305
324
  end
306
325
 
326
+ # Returns the root class of the STI tree that the current
327
+ # class participates in. If the class is not an STI subclass, this
328
+ # returns the class itself.
329
+ #
330
+ # @return [ Mongoid::Document ] the root of the STI tree
331
+ def root_class
332
+ root = self
333
+ root = root.superclass while root.hereditary?
334
+
335
+ root
336
+ end
337
+
307
338
  # When inheriting, we want to copy the fields from the parent class and
308
339
  # set the on the child to start, mimicking the behavior of the old
309
340
  # class_inheritable_accessor that was deprecated in Rails edge.
@@ -73,7 +73,7 @@ module Mongoid
73
73
  # use map.all? instead of just all?, because all? will do short-circuit
74
74
  # evaluation and terminate on the first failed validation.
75
75
  list.map do |value|
76
- if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
76
+ if value && !value.flagged_for_destroy?
77
77
  value.validated? ? true : value.valid?
78
78
  else
79
79
  true
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongoid
4
- VERSION = "8.1.7"
4
+ VERSION = "8.1.9"
5
5
  end
@@ -14,14 +14,36 @@ describe Mongoid::Association::EagerLoadable do
14
14
  Mongoid::Contextual::Mongo.new(criteria)
15
15
  end
16
16
 
17
+ let(:association_host) { Account }
18
+
17
19
  let(:inclusions) do
18
20
  includes.map do |key|
19
- Account.reflect_on_association(key)
21
+ association_host.reflect_on_association(key)
20
22
  end
21
23
  end
22
24
 
23
25
  let(:doc) { criteria.first }
24
26
 
27
+ context 'when root is an STI subclass' do
28
+ # Driver has_one Vehicle
29
+ # Vehicle belongs_to Driver
30
+ # Truck is a Vehicle
31
+
32
+ before do
33
+ Driver.create!(vehicle: Truck.new)
34
+ end
35
+
36
+ let(:criteria) { Truck.all }
37
+ let(:includes) { %i[ driver ] }
38
+ let(:association_host) { Truck }
39
+
40
+ it 'preloads the driver' do
41
+ expect(doc.ivar(:driver)).to be false
42
+ context.preload(inclusions, [ doc ])
43
+ expect(doc.ivar(:driver)).to be == Driver.first
44
+ end
45
+ end
46
+
25
47
  context "when belongs_to" do
26
48
 
27
49
  let!(:account) do
@@ -42,7 +64,7 @@ describe Mongoid::Association::EagerLoadable do
42
64
  it "preloads the parent" do
43
65
  expect(doc.ivar(:person)).to be false
44
66
  context.preload(inclusions, [doc])
45
- expect(doc.ivar(:person)).to eq(doc.person)
67
+ expect(doc.ivar(:person)).to be == person
46
68
  end
47
69
  end
48
70
 
@@ -100,6 +100,66 @@ describe Mongoid::Association do
100
100
  expect(name).to_not be_an_embedded_many
101
101
  end
102
102
  end
103
+
104
+ context "when validation depends on association" do
105
+ before(:all) do
106
+ class Author
107
+ include Mongoid::Document
108
+ embeds_many :books, cascade_callbacks: true
109
+ field :condition, type: Boolean
110
+ end
111
+
112
+ class Book
113
+ include Mongoid::Document
114
+ embedded_in :author
115
+ validate :parent_condition_is_not_true
116
+
117
+ def parent_condition_is_not_true
118
+ return unless author&.condition
119
+ errors.add :base, "Author condition is true."
120
+ end
121
+ end
122
+
123
+ Author.delete_all
124
+ Book.delete_all
125
+ end
126
+
127
+ let(:author) { Author.new }
128
+ let(:book) { Book.new }
129
+
130
+ context "when author is not persisted" do
131
+ it "is valid without books" do
132
+ expect(author.valid?).to be true
133
+ end
134
+
135
+ it "is valid with a book" do
136
+ author.books << book
137
+ expect(author.valid?).to be true
138
+ end
139
+
140
+ it "is not valid when condition is true with a book" do
141
+ author.condition = true
142
+ author.books << book
143
+ expect(author.valid?).to be false
144
+ end
145
+ end
146
+
147
+ context "when author is persisted" do
148
+ before do
149
+ author.books << book
150
+ author.save
151
+ end
152
+
153
+ it "remains valid initially" do
154
+ expect(author.valid?).to be true
155
+ end
156
+
157
+ it "becomes invalid when condition is set to true" do
158
+ author.update_attributes(condition: true)
159
+ expect(author.valid?).to be false
160
+ end
161
+ end
162
+ end
103
163
  end
104
164
 
105
165
  describe "#embedded_one?" do
@@ -265,7 +265,26 @@ describe Mongoid::Attributes::Readonly do
265
265
  expect(child.mother).to be_nil
266
266
  end
267
267
  end
268
+ end
269
+
270
+ context "when a subclass inherits readonly fields" do
271
+ let(:attributes) do
272
+ [:title, :terms]
273
+ end
274
+
275
+ before do
276
+ class OldPerson < Person
277
+ attr_readonly :age
278
+ end
279
+ end
268
280
 
281
+ it "ensures subclass inherits the readonly attributes from parent" do
282
+ expect(OldPerson.readonly_attributes.to_a).to include("title","terms")
283
+ end
284
+
285
+ it "ensures subclass does not modify parent's readonly attributes" do
286
+ expect(Person.readonly_attributes.to_a).not_to include("age")
287
+ end
269
288
  end
270
289
  end
271
290
  end
@@ -2059,6 +2059,35 @@ describe Mongoid::Criteria::Queryable::Selectable do
2059
2059
  end
2060
2060
  end
2061
2061
 
2062
+ describe "#not" do
2063
+ context "when negating a criterion" do
2064
+ let(:selection) do
2065
+ query.not(field: /value/)
2066
+ end
2067
+
2068
+ it "adds the $not selector" do
2069
+ expect(selection.selector).to eq({
2070
+ "field" => { "$not" => /value/ }
2071
+ })
2072
+ end
2073
+
2074
+ it "returns a cloned query" do
2075
+ expect(selection).to_not equal(query)
2076
+ end
2077
+
2078
+ context "when toggling negation state" do
2079
+ it "negates the negating value" do
2080
+ expect(query.negating).to be_nil
2081
+ negated_query = query.not
2082
+ expect(negated_query.negating).to be true
2083
+ double_negated_query = negated_query.not
2084
+ expect(double_negated_query.negating).to be false
2085
+ end
2086
+ end
2087
+ end
2088
+ end
2089
+
2090
+
2062
2091
  describe Symbol do
2063
2092
 
2064
2093
  describe "#all" do
@@ -591,6 +591,33 @@ describe Mongoid::Document do
591
591
  expect(person.as_document["addresses"].first).to have_key(:locations)
592
592
  end
593
593
 
594
+ context 'when modifying the returned object' do
595
+ let(:record) do
596
+ RootCategory.create(categories: [{ name: 'tests' }]).reload
597
+ end
598
+
599
+ shared_examples_for 'an object with protected internal state' do
600
+ it 'does not expose internal state' do
601
+ before_change = record.as_document.dup
602
+ record.categories.first.name = 'things'
603
+ after_change = record.as_document
604
+ expect(before_change['categories'].first['name']).not_to eq('things')
605
+ end
606
+ end
607
+
608
+ context 'when legacy_attributes is true' do
609
+ config_override :legacy_attributes, true
610
+
611
+ it_behaves_like 'an object with protected internal state'
612
+ end
613
+
614
+ context 'when legacy_attributes is false' do
615
+ config_override :legacy_attributes, false
616
+
617
+ it_behaves_like 'an object with protected internal state'
618
+ end
619
+ end
620
+
594
621
  context "with relation define store_as option in embeded_many" do
595
622
 
596
623
  let!(:phone) do
@@ -291,6 +291,12 @@ describe Mongoid::Equality do
291
291
  it "compares based on the document id" do
292
292
  expect(first <=> second).to eq(-1)
293
293
  end
294
+
295
+ it "doesn't break if one isn't a document" do
296
+ expect do
297
+ first <=> "Foo"
298
+ end.to_not raise_error
299
+ end
294
300
  end
295
301
 
296
302
  describe "#eql?" do
@@ -388,6 +388,86 @@ describe Mongoid::Interceptable do
388
388
  end
389
389
  end
390
390
  end
391
+
392
+ context 'with embedded grandchildren' do
393
+ IS = InterceptableSpec
394
+
395
+ config_override :prevent_multiple_calls_of_embedded_callbacks, true
396
+
397
+ context 'when creating' do
398
+ let(:registry) { IS::CallbackRegistry.new(only: %i[ before_save ]) }
399
+
400
+ let(:expected_calls) do
401
+ [
402
+ # the parent
403
+ [ IS::CbParent, :before_save ],
404
+
405
+ # the immediate child of the parent
406
+ [ IS::CbCascadedNode, :before_save ],
407
+
408
+ # the grandchild of the parent
409
+ [ IS::CbCascadedNode, :before_save ],
410
+ ]
411
+ end
412
+
413
+ let!(:parent) do
414
+ parent = IS::CbParent.new(registry)
415
+ child = IS::CbCascadedNode.new(registry)
416
+ grandchild = IS::CbCascadedNode.new(registry)
417
+
418
+ child.cb_cascaded_nodes = [ grandchild ]
419
+ parent.cb_cascaded_nodes = [ child ]
420
+
421
+ parent.tap(&:save)
422
+ end
423
+
424
+ it 'should cascade callbacks to grandchildren' do
425
+ expect(registry.calls).to be == expected_calls
426
+ end
427
+ end
428
+
429
+ context 'when updating' do
430
+ let(:registry) { IS::CallbackRegistry.new(only: %i[ before_update ]) }
431
+
432
+ let(:expected_calls) do
433
+ [
434
+ # the parent
435
+ [ IS::CbParent, :before_update ],
436
+
437
+ # the immediate child of the parent
438
+ [ IS::CbCascadedNode, :before_update ],
439
+
440
+ # the grandchild of the parent
441
+ [ IS::CbCascadedNode, :before_update ],
442
+ ]
443
+ end
444
+
445
+ let!(:parent) do
446
+ parent = IS::CbParent.new(nil)
447
+ child = IS::CbCascadedNode.new(nil)
448
+ grandchild = IS::CbCascadedNode.new(nil)
449
+
450
+ child.cb_cascaded_nodes = [ grandchild ]
451
+ parent.cb_cascaded_nodes = [ child ]
452
+
453
+ parent.save
454
+
455
+ parent.callback_registry = registry
456
+ child.callback_registry = registry
457
+ grandchild.callback_registry = registry
458
+
459
+ parent.name = 'updated'
460
+ child.name = 'updated'
461
+ grandchild.name = 'updated'
462
+
463
+ parent.tap(&:save)
464
+ end
465
+
466
+ it 'should cascade callbacks to grandchildren' do
467
+ expect(registry.calls).to be == expected_calls
468
+ end
469
+ end
470
+ end
391
471
  end
392
472
 
393
473
  describe ".before_destroy" do
@@ -1,10 +1,12 @@
1
1
  module InterceptableSpec
2
2
  class CallbackRegistry
3
- def initialize
3
+ def initialize(only: [])
4
4
  @calls = []
5
+ @only = only
5
6
  end
6
7
 
7
8
  def record_call(cls, cb)
9
+ return unless @only.empty? || @only.any? { |pat| pat == cb }
8
10
  @calls << [cls, cb]
9
11
  end
10
12
 
@@ -15,6 +17,8 @@ module InterceptableSpec
15
17
  extend ActiveSupport::Concern
16
18
 
17
19
  included do
20
+ field :name, type: String
21
+
18
22
  %i(
19
23
  validation save create update
20
24
  ).each do |what|
@@ -34,196 +38,128 @@ module InterceptableSpec
34
38
  end
35
39
  end
36
40
  end
37
- end
38
41
 
39
- class CbHasOneParent
40
- include Mongoid::Document
41
-
42
- has_one :child, autosave: true, class_name: "CbHasOneChild", inverse_of: :parent
42
+ attr_accessor :callback_registry
43
43
 
44
- def initialize(callback_registry)
44
+ def initialize(callback_registry, *args, **kwargs)
45
45
  @callback_registry = callback_registry
46
- super()
46
+ super(*args, **kwargs)
47
47
  end
48
+ end
48
49
 
49
- attr_accessor :callback_registry
50
-
50
+ module RootInsertable
51
51
  def insert_as_root
52
52
  @callback_registry&.record_call(self.class, :insert_into_database)
53
53
  super
54
54
  end
55
+ end
55
56
 
57
+ class CbHasOneParent
58
+ include Mongoid::Document
56
59
  include CallbackTracking
60
+ include RootInsertable
61
+
62
+ has_one :child, autosave: true, class_name: "CbHasOneChild", inverse_of: :parent
57
63
  end
58
64
 
59
65
  class CbHasOneChild
60
66
  include Mongoid::Document
67
+ include CallbackTracking
61
68
 
62
69
  belongs_to :parent, class_name: "CbHasOneParent", inverse_of: :child
63
-
64
- def initialize(callback_registry)
65
- @callback_registry = callback_registry
66
- super()
67
- end
68
-
69
- attr_accessor :callback_registry
70
-
71
- include CallbackTracking
72
70
  end
73
71
 
74
72
  class CbHasManyParent
75
73
  include Mongoid::Document
74
+ include CallbackTracking
75
+ include RootInsertable
76
76
 
77
77
  has_many :children, autosave: true, class_name: "CbHasManyChild", inverse_of: :parent
78
-
79
- def initialize(callback_registry)
80
- @callback_registry = callback_registry
81
- super()
82
- end
83
-
84
- attr_accessor :callback_registry
85
-
86
- def insert_as_root
87
- @callback_registry&.record_call(self.class, :insert_into_database)
88
- super
89
- end
90
-
91
- include CallbackTracking
92
78
  end
93
79
 
94
80
  class CbHasManyChild
95
81
  include Mongoid::Document
82
+ include CallbackTracking
96
83
 
97
84
  belongs_to :parent, class_name: "CbHasManyParent", inverse_of: :children
98
-
99
- def initialize(callback_registry)
100
- @callback_registry = callback_registry
101
- super()
102
- end
103
-
104
- attr_accessor :callback_registry
105
-
106
- include CallbackTracking
107
85
  end
108
86
 
109
87
  class CbEmbedsOneParent
110
88
  include Mongoid::Document
89
+ include CallbackTracking
90
+ include RootInsertable
111
91
 
112
92
  field :name
113
93
 
114
94
  embeds_one :child, cascade_callbacks: true, class_name: "CbEmbedsOneChild", inverse_of: :parent
115
-
116
- def initialize(callback_registry)
117
- @callback_registry = callback_registry
118
- super()
119
- end
120
-
121
- attr_accessor :callback_registry
122
-
123
- def insert_as_root
124
- @callback_registry&.record_call(self.class, :insert_into_database)
125
- super
126
- end
127
-
128
- include CallbackTracking
129
95
  end
130
96
 
131
97
  class CbEmbedsOneChild
132
98
  include Mongoid::Document
99
+ include CallbackTracking
133
100
 
134
101
  field :age
135
102
 
136
103
  embedded_in :parent, class_name: "CbEmbedsOneParent", inverse_of: :child
137
-
138
- def initialize(callback_registry)
139
- @callback_registry = callback_registry
140
- super()
141
- end
142
-
143
- attr_accessor :callback_registry
144
-
145
- include CallbackTracking
146
104
  end
147
105
 
148
106
  class CbEmbedsManyParent
149
107
  include Mongoid::Document
108
+ include CallbackTracking
109
+ include RootInsertable
150
110
 
151
111
  embeds_many :children, cascade_callbacks: true, class_name: "CbEmbedsManyChild", inverse_of: :parent
152
-
153
- def initialize(callback_registry)
154
- @callback_registry = callback_registry
155
- super()
156
- end
157
-
158
- attr_accessor :callback_registry
159
-
160
- def insert_as_root
161
- @callback_registry&.record_call(self.class, :insert_into_database)
162
- super
163
- end
164
-
165
- include CallbackTracking
166
112
  end
167
113
 
168
114
  class CbEmbedsManyChild
169
115
  include Mongoid::Document
116
+ include CallbackTracking
170
117
 
171
118
  embedded_in :parent, class_name: "CbEmbedsManyParent", inverse_of: :children
172
-
173
- def initialize(callback_registry)
174
- @callback_registry = callback_registry
175
- super()
176
- end
177
-
178
- attr_accessor :callback_registry
179
-
180
- include CallbackTracking
181
119
  end
182
120
 
183
121
  class CbParent
184
122
  include Mongoid::Document
185
-
186
- def initialize(callback_registry)
187
- @callback_registry = callback_registry
188
- super()
189
- end
190
-
191
- attr_accessor :callback_registry
123
+ include CallbackTracking
192
124
 
193
125
  embeds_many :cb_children
194
126
  embeds_many :cb_cascaded_children, cascade_callbacks: true
195
-
196
- include CallbackTracking
127
+ embeds_many :cb_cascaded_nodes, cascade_callbacks: true, as: :parent
197
128
  end
198
129
 
199
130
  class CbChild
200
131
  include Mongoid::Document
132
+ include CallbackTracking
201
133
 
202
134
  embedded_in :cb_parent
203
-
204
- def initialize(callback_registry, options)
205
- @callback_registry = callback_registry
206
- super(options)
207
- end
208
-
209
- attr_accessor :callback_registry
210
-
211
- include CallbackTracking
212
135
  end
213
136
 
214
137
  class CbCascadedChild
215
138
  include Mongoid::Document
139
+ include CallbackTracking
216
140
 
217
141
  embedded_in :cb_parent
218
142
 
219
- def initialize(callback_registry, options)
220
- @callback_registry = callback_registry
221
- super(options)
222
- end
143
+ before_save :test_mongoid_state
223
144
 
224
- attr_accessor :callback_registry
145
+ private
146
+
147
+ # Helps test that cascading child callbacks have access to the Mongoid
148
+ # state objects; if the implementation uses fiber-local (instead of truly
149
+ # thread-local) variables, the related tests will fail because the
150
+ # cascading child callbacks use fibers to linearize the recursion.
151
+ def test_mongoid_state
152
+ Mongoid::Threaded.stack('interceptable').push(self)
153
+ end
154
+ end
225
155
 
156
+ class CbCascadedNode
157
+ include Mongoid::Document
226
158
  include CallbackTracking
159
+
160
+ embedded_in :parent, polymorphic: true
161
+
162
+ embeds_many :cb_cascaded_nodes, cascade_callbacks: true, as: :parent
227
163
  end
228
164
  end
229
165
 
@@ -43,4 +43,27 @@ describe Mongoid::Timestamps::Created do
43
43
  expect(quiz.created_at).to be_within(10).of(Time.now.utc)
44
44
  end
45
45
  end
46
+
47
+ context "when the document is destroyed" do
48
+ let(:book) do
49
+ Book.create!
50
+ end
51
+
52
+ before do
53
+ Cover.before_save do
54
+ destroy if title == "delete me"
55
+ end
56
+ end
57
+
58
+ after do
59
+ Cover.reset_callbacks(:save)
60
+ end
61
+
62
+ it "does not set the created_at timestamp" do
63
+ book.covers << Cover.new(title: "delete me")
64
+ expect {
65
+ book.save
66
+ }.not_to raise_error
67
+ end
68
+ end
46
69
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.7
4
+ version: 8.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MongoDB Ruby Team
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-12 00:00:00.000000000 Z
10
+ date: 2025-01-30 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activemodel
@@ -1146,7 +1145,6 @@ metadata:
1146
1145
  documentation_uri: https://www.mongodb.com/docs/mongoid/
1147
1146
  homepage_uri: https://mongoid.org/
1148
1147
  source_code_uri: https://github.com/mongodb/mongoid
1149
- post_install_message:
1150
1148
  rdoc_options: []
1151
1149
  require_paths:
1152
1150
  - lib
@@ -1161,8 +1159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1161
1159
  - !ruby/object:Gem::Version
1162
1160
  version: 1.3.6
1163
1161
  requirements: []
1164
- rubygems_version: 3.4.19
1165
- signing_key:
1162
+ rubygems_version: 3.6.3
1166
1163
  specification_version: 4
1167
1164
  summary: Elegant Persistence in Ruby for MongoDB.
1168
1165
  test_files: