mongoid 7.0.1 → 7.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongoid/association.rb +0 -1
- data/lib/mongoid/association/depending.rb +22 -1
- data/lib/mongoid/association/embedded/embeds_one/proxy.rb +3 -3
- data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
- data/lib/mongoid/association/relatable.rb +16 -2
- data/lib/mongoid/attributes/nested.rb +14 -3
- data/lib/mongoid/contextual/map_reduce.rb +1 -1
- data/lib/mongoid/copyable.rb +3 -2
- data/lib/mongoid/document.rb +2 -0
- data/lib/mongoid/matchable.rb +3 -0
- data/lib/mongoid/matchable/nor.rb +37 -0
- data/lib/mongoid/persistable/settable.rb +58 -13
- data/lib/mongoid/persistence_context.rb +5 -1
- data/lib/mongoid/touchable.rb +102 -0
- data/lib/mongoid/version.rb +1 -1
- data/spec/app/models/array_field.rb +7 -0
- data/spec/app/models/updatable.rb +7 -0
- data/spec/mongoid/association/accessors_spec.rb +39 -0
- data/spec/mongoid/association/depending_spec.rb +253 -0
- data/spec/mongoid/association/polymorphic_spec.rb +59 -0
- data/spec/mongoid/association/referenced/belongs_to_spec.rb +3 -3
- data/spec/mongoid/association/referenced/has_one_spec.rb +59 -0
- data/spec/mongoid/attributes/nested_spec.rb +18 -2
- data/spec/mongoid/clients/factory_spec.rb +3 -3
- data/spec/mongoid/clients/options_spec.rb +28 -13
- data/spec/mongoid/clients/sessions_spec.rb +3 -3
- data/spec/mongoid/clients/transactions_spec.rb +369 -0
- data/spec/mongoid/copyable_spec.rb +16 -0
- data/spec/mongoid/matchable/nor_spec.rb +209 -0
- data/spec/mongoid/matchable_spec.rb +26 -1
- data/spec/mongoid/persistable/settable_spec.rb +128 -9
- data/spec/mongoid/{association/touchable_spec.rb → touchable_spec.rb} +28 -7
- data/spec/spec_helper.rb +8 -0
- metadata +457 -448
- metadata.gz.sig +0 -0
- data/lib/mongoid/association/touchable.rb +0 -97
@@ -35,6 +35,10 @@ describe Mongoid::Copyable do
|
|
35
35
|
person.build_game(name: "Tron")
|
36
36
|
end
|
37
37
|
|
38
|
+
let!(:name_translations) do
|
39
|
+
person.name.translations.build(language: 'en')
|
40
|
+
end
|
41
|
+
|
38
42
|
context "when the document has an id field in the database" do
|
39
43
|
|
40
44
|
let!(:band) do
|
@@ -245,14 +249,26 @@ describe Mongoid::Copyable do
|
|
245
249
|
expect(copy.addresses).to eq(person.addresses)
|
246
250
|
end
|
247
251
|
|
252
|
+
it "copys deep embeds many documents" do
|
253
|
+
expect(copy.name.translations).to eq(person.name.translations)
|
254
|
+
end
|
255
|
+
|
248
256
|
it "sets the embedded many documents as new" do
|
249
257
|
expect(copy.addresses.first).to be_new_record
|
250
258
|
end
|
251
259
|
|
260
|
+
it "sets the deep embedded many documents as new" do
|
261
|
+
expect(copy.name.translations.first).to be_new_record
|
262
|
+
end
|
263
|
+
|
252
264
|
it "creates new embeds many instances" do
|
253
265
|
expect(copy.addresses).to_not equal(person.addresses)
|
254
266
|
end
|
255
267
|
|
268
|
+
it "creates new deep embeds many instances" do
|
269
|
+
expect(copy.name.translations).to_not equal(person.name.translations)
|
270
|
+
end
|
271
|
+
|
256
272
|
it "copys embeds one documents" do
|
257
273
|
expect(copy.name).to eq(person.name)
|
258
274
|
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Mongoid::Matchable::Nor do
|
6
|
+
|
7
|
+
let(:target) do
|
8
|
+
Person.new
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:matcher) do
|
12
|
+
described_class.new("value", target)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#_matches?" do
|
16
|
+
|
17
|
+
context "when provided a simple expression" do
|
18
|
+
|
19
|
+
context "when one of the hashes does not match model" do
|
20
|
+
|
21
|
+
let(:matches) do
|
22
|
+
matcher._matches?(
|
23
|
+
[ { title: "Sir" }, { title: "King" } ]
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:target) do
|
28
|
+
Person.new(title: 'Queen')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns true" do
|
32
|
+
expect(matches).to be true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when all of the hashes match different fields in model" do
|
37
|
+
let(:matches) do
|
38
|
+
matcher._matches?(
|
39
|
+
[ { age: 10 }, { title: "King" } ]
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:target) do
|
44
|
+
Person.new(title: 'King', age: 10)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns false" do
|
48
|
+
expect(matches).to be false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when one of the hashes matches an array field in model" do
|
53
|
+
let(:matches) do
|
54
|
+
matcher._matches?(
|
55
|
+
[ { af: "Sir" }, { af: "King" } ]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
let(:target) do
|
60
|
+
ArrayField.new(af: ['King'])
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns false" do
|
64
|
+
expect(matches).to be false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when none of the hashes matches an array field in model" do
|
69
|
+
let(:matches) do
|
70
|
+
matcher._matches?(
|
71
|
+
[ { af: "Sir" }, { af: "King" } ]
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
let(:target) do
|
76
|
+
ArrayField.new(af: ['Boo'])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns true" do
|
80
|
+
expect(matches).to be true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when there are no criteria" do
|
85
|
+
|
86
|
+
it "returns false" do
|
87
|
+
expect(matcher._matches?([])).to be false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# $nor with $not is a double negation.
|
92
|
+
# Whatever the argument of $not is is what the overall condition
|
93
|
+
# is looking for.
|
94
|
+
context "when the expression is a $not" do
|
95
|
+
|
96
|
+
let(:matches) do
|
97
|
+
matcher._matches?([ { title: {:$not => /Foobar/ } }])
|
98
|
+
end
|
99
|
+
|
100
|
+
context "when the value does not match $not argument" do
|
101
|
+
|
102
|
+
let(:target) do
|
103
|
+
Person.new(title: 'test')
|
104
|
+
end
|
105
|
+
|
106
|
+
it "returns false" do
|
107
|
+
expect(matches).to be false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when the value matches $not argument" do
|
112
|
+
|
113
|
+
let(:target) do
|
114
|
+
Person.new(title: 'Foobar baz')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "returns true" do
|
118
|
+
expect(matches).to be true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "when provided a complex expression" do
|
125
|
+
|
126
|
+
context "when none of the model values match criteria values" do
|
127
|
+
|
128
|
+
let(:matches) do
|
129
|
+
matcher._matches?(
|
130
|
+
[
|
131
|
+
{ title: { "$in" => [ "Sir", "Madam" ] } },
|
132
|
+
{ title: "King" }
|
133
|
+
]
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
let(:target) do
|
138
|
+
Person.new(title: 'Queen')
|
139
|
+
end
|
140
|
+
|
141
|
+
it "returns true" do
|
142
|
+
expect(matches).to be true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "when there is a matching value" do
|
147
|
+
|
148
|
+
let(:matches) do
|
149
|
+
matcher._matches?(
|
150
|
+
[
|
151
|
+
{ title: { "$in" => [ "Prince", "Madam" ] } },
|
152
|
+
{ title: "King" }
|
153
|
+
]
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
let(:target) do
|
158
|
+
Person.new(title: 'Prince')
|
159
|
+
end
|
160
|
+
|
161
|
+
it "returns false" do
|
162
|
+
expect(matches).to be false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context "when expression contain multiple fields" do
|
167
|
+
|
168
|
+
let(:matches) do
|
169
|
+
matcher._matches?(
|
170
|
+
[
|
171
|
+
{ title: "Sir", age: 23 },
|
172
|
+
{ title: "King", age: 100 }
|
173
|
+
]
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'and model has different values in all of the fields' do
|
178
|
+
let(:target) do
|
179
|
+
Person.new(title: 'Queen', age: 10)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "returns true" do
|
183
|
+
expect(matches).to be true
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'and model has identical value in one of the fields' do
|
188
|
+
let(:target) do
|
189
|
+
Person.new(title: 'Queen', age: 23)
|
190
|
+
end
|
191
|
+
|
192
|
+
it "returns true" do
|
193
|
+
expect(matches).to be true
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'and model has identical values in all of the fields' do
|
198
|
+
let(:target) do
|
199
|
+
Person.new(title: 'Sir', age: 23)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "returns false" do
|
203
|
+
expect(matches).to be false
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -86,7 +86,7 @@ describe Mongoid::Matchable do
|
|
86
86
|
let(:selector) do
|
87
87
|
{ "occupants.0" => "Tim" }
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
it "returns true" do
|
91
91
|
expect(document.locations.first._matches?(selector)).to be true
|
92
92
|
end
|
@@ -774,6 +774,31 @@ describe Mongoid::Matchable do
|
|
774
774
|
end
|
775
775
|
end
|
776
776
|
|
777
|
+
context "with an $nor selector" do
|
778
|
+
|
779
|
+
context "when the attributes match" do
|
780
|
+
|
781
|
+
let(:selector) do
|
782
|
+
{ "$nor" => [ { number: 10 }, { number: { "$gt" => 199 } } ] }
|
783
|
+
end
|
784
|
+
|
785
|
+
it "returns true" do
|
786
|
+
expect(document._matches?(selector)).to be true
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
context "when the attributes do not match" do
|
791
|
+
|
792
|
+
let(:selector) do
|
793
|
+
{ "$nor" => [ { number: 10 }, { number: { "$gt" => 99 } } ] }
|
794
|
+
end
|
795
|
+
|
796
|
+
it "returns false" do
|
797
|
+
expect(document._matches?(selector)).to be false
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
777
802
|
context "with a $size selector" do
|
778
803
|
|
779
804
|
context "when the attributes match" do
|
@@ -211,7 +211,7 @@ describe Mongoid::Persistable::Settable do
|
|
211
211
|
Church.new.tap do |a|
|
212
212
|
a.location = { 'city' => 'Berlin' }
|
213
213
|
a.name = 'Church1'
|
214
|
-
a.save
|
214
|
+
a.save!
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
@@ -278,7 +278,22 @@ describe Mongoid::Persistable::Settable do
|
|
278
278
|
end
|
279
279
|
end
|
280
280
|
|
281
|
-
context 'when the field is a
|
281
|
+
context 'when the field is a nested hash' do
|
282
|
+
|
283
|
+
context 'when the field is set to an empty hash' do
|
284
|
+
|
285
|
+
before do
|
286
|
+
church.set('location' => {})
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'updates the field locally' do
|
290
|
+
expect(church.location).to eq({})
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'updates the field in the database' do
|
294
|
+
expect(church.reload.location).to eq({})
|
295
|
+
end
|
296
|
+
end
|
282
297
|
|
283
298
|
context 'when a leaf value in the nested hash is updated' do
|
284
299
|
|
@@ -286,7 +301,7 @@ describe Mongoid::Persistable::Settable do
|
|
286
301
|
Church.new.tap do |a|
|
287
302
|
a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
|
288
303
|
a.name = 'Church1'
|
289
|
-
a.save
|
304
|
+
a.save!
|
290
305
|
end
|
291
306
|
end
|
292
307
|
|
@@ -300,6 +315,25 @@ describe Mongoid::Persistable::Settable do
|
|
300
315
|
end
|
301
316
|
end
|
302
317
|
|
318
|
+
context 'when a leaf value in the nested hash is updated to a number' do
|
319
|
+
|
320
|
+
let(:church) do
|
321
|
+
Church.new.tap do |a|
|
322
|
+
a.location = {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}
|
323
|
+
a.name = 'Church1'
|
324
|
+
a.save!
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
before do
|
329
|
+
church.set('location.address.city' => 12345)
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'updates the nested value to the correct value' do
|
333
|
+
expect(church.name).to eq('Church1')
|
334
|
+
expect(church.location).to eql({'address' => {'city' => 12345, 'street' => 'Yorckstr'}})
|
335
|
+
end
|
336
|
+
end
|
303
337
|
|
304
338
|
context 'when the nested hash is many levels deep' do
|
305
339
|
|
@@ -307,20 +341,105 @@ describe Mongoid::Persistable::Settable do
|
|
307
341
|
Church.new.tap do |a|
|
308
342
|
a.location = {'address' => {'state' => {'address' => {'city' => 'Berlin', 'street' => 'Yorckstr'}}}}
|
309
343
|
a.name = 'Church1'
|
310
|
-
a.save
|
344
|
+
a.save!
|
311
345
|
end
|
312
346
|
end
|
313
347
|
|
314
|
-
|
315
|
-
|
348
|
+
context 'setting value to a string' do
|
349
|
+
it 'keeps peer attributes of the nested hash' do
|
350
|
+
church.set('location.address.state.address.city' => 'Munich')
|
351
|
+
|
352
|
+
expect(church.name).to eq('Church1')
|
353
|
+
expect(church.location).to eql({'address' => {'state' => {'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}}}})
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'removes lower level attributes of the nested hash' do
|
357
|
+
church.set('location.address.state.address' => 'hello')
|
358
|
+
|
359
|
+
expect(church.name).to eq('Church1')
|
360
|
+
expect(church.location).to eql({'address' => {'state' => {'address' => 'hello'}}})
|
361
|
+
end
|
316
362
|
end
|
317
363
|
|
318
|
-
|
319
|
-
|
320
|
-
|
364
|
+
context 'setting value to a hash' do
|
365
|
+
it 'keeps peer attributes of the nested hash' do
|
366
|
+
church.set('location.address.state.address.city' => {'hello' => 'world'})
|
367
|
+
|
368
|
+
expect(church.name).to eq('Church1')
|
369
|
+
expect(church.location).to eql({'address' => {'state' => {'address' => {'city' => {'hello' => 'world'}, 'street' => 'Yorckstr'}}}})
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'removes lower level attributes of the nested hash' do
|
373
|
+
church.set('location.address.state.address' => {'hello' => 'world'})
|
374
|
+
|
375
|
+
expect(church.name).to eq('Church1')
|
376
|
+
expect(church.location).to eql({'address' => {'state' => {'address' => {'hello' => 'world'}}}})
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
context 'when nested field is an array' do
|
383
|
+
let(:church) do
|
384
|
+
Church.create!(
|
385
|
+
location: {'address' => ['one', 'two']}
|
386
|
+
)
|
387
|
+
end
|
388
|
+
|
389
|
+
context 'setting to a different array' do
|
390
|
+
it 'sets values to new array discarding old values' do
|
391
|
+
church.set('location.address' => ['three'])
|
392
|
+
|
393
|
+
expect(church.location).to eq('address' => ['three'])
|
394
|
+
church.reload
|
395
|
+
expect(church.location).to eq('address' => ['three'])
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
context 'changing from an array to a number' do
|
400
|
+
it 'sets value to the number' do
|
401
|
+
church.set('location.address' => 5)
|
402
|
+
|
403
|
+
expect(church.location).to eq('address' => 5)
|
404
|
+
church.reload
|
405
|
+
expect(church.location).to eq('address' => 5)
|
321
406
|
end
|
322
407
|
end
|
323
408
|
end
|
409
|
+
|
410
|
+
context 'when nested field is not an array' do
|
411
|
+
let(:church) do
|
412
|
+
Church.create!(
|
413
|
+
location: {'address' => 5}
|
414
|
+
)
|
415
|
+
end
|
416
|
+
|
417
|
+
context 'setting to an array' do
|
418
|
+
it 'sets values to the array' do
|
419
|
+
church.set('location.address' => ['three'])
|
420
|
+
|
421
|
+
expect(church.location).to eq('address' => ['three'])
|
422
|
+
church.reload
|
423
|
+
expect(church.location).to eq('address' => ['three'])
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
context 'when nesting into a field that is not a hash' do
|
429
|
+
let(:church) do
|
430
|
+
Church.create!(
|
431
|
+
location: {'address' => 5}
|
432
|
+
)
|
433
|
+
end
|
434
|
+
|
435
|
+
it 'sets field to new hash value discarding original value' do
|
436
|
+
church.set('location.address.a' => 'test')
|
437
|
+
|
438
|
+
expect(church.location).to eq('address' => {'a' => 'test'})
|
439
|
+
church.reload
|
440
|
+
expect(church.location).to eq('address' => {'a' => 'test'})
|
441
|
+
end
|
442
|
+
end
|
324
443
|
end
|
325
444
|
|
326
445
|
context 'when the field is not already set locally' do
|