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 +4 -4
- data/Rakefile +1 -1
- data/lib/mongoid/association/eager_loadable.rb +3 -0
- data/lib/mongoid/attributes/readonly.rb +8 -3
- data/lib/mongoid/criteria/queryable/selectable.rb +1 -1
- data/lib/mongoid/document.rb +8 -1
- data/lib/mongoid/equality.rb +1 -0
- data/lib/mongoid/interceptable.rb +6 -5
- data/lib/mongoid/timestamps/created.rb +8 -1
- data/lib/mongoid/traversable.rb +36 -5
- data/lib/mongoid/validatable/associated.rb +1 -1
- data/lib/mongoid/version.rb +1 -1
- data/spec/mongoid/association/eager_spec.rb +24 -2
- data/spec/mongoid/association_spec.rb +60 -0
- data/spec/mongoid/attributes/readonly_spec.rb +19 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +29 -0
- data/spec/mongoid/document_spec.rb +27 -0
- data/spec/mongoid/equality_spec.rb +6 -0
- data/spec/mongoid/interceptable_spec.rb +80 -0
- data/spec/mongoid/interceptable_spec_models.rb +47 -111
- data/spec/mongoid/timestamps/created_spec.rb +23 -0
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3a2e1b9f4abdd261c6d18c42a1c107089ae415a57c9dcb0978c14891e78330b
|
4
|
+
data.tar.gz: 9c09793f70f1dd4ad367e2abcac271245c86f8d6414f262e7643d09538b3bce4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0b89ea745ddafadbe1d1cf17c467ee7413eaf94d341bbb209df4db0eeb7cdff6e1fd8b01bc2a85016c47bb830e93ccbcdb0317578c32ee8bbb3dc06eb7db54e
|
7
|
+
data.tar.gz: 51627fd03388cbaeef60014b77e2a15202f06f2dffebd69d7ff0cdf5840885a5cdbacb2bc34794eb04c72595c675d5edb55e42b9aa4079dbb5de7d24f454eb8a
|
data/Rakefile
CHANGED
@@ -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 =
|
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)
|
data/lib/mongoid/document.rb
CHANGED
@@ -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
|
-
|
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.
|
data/lib/mongoid/equality.rb
CHANGED
@@ -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,
|
144
|
+
_mongoid_run_child_callbacks_with_around(kind,
|
145
|
+
children: children,
|
146
|
+
&block)
|
145
147
|
else
|
146
|
-
_mongoid_run_child_callbacks_without_around(kind,
|
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
|
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
|
data/lib/mongoid/traversable.rb
CHANGED
@@ -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
|
-
|
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?
|
76
|
+
if value && !value.flagged_for_destroy?
|
77
77
|
value.validated? ? true : value.valid?
|
78
78
|
else
|
79
79
|
true
|
data/lib/mongoid/version.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
220
|
-
@callback_registry = callback_registry
|
221
|
-
super(options)
|
222
|
-
end
|
143
|
+
before_save :test_mongoid_state
|
223
144
|
|
224
|
-
|
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.
|
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:
|
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.
|
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:
|