mongoid 7.0.1 → 7.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongoid/association.rb +0 -1
  5. data/lib/mongoid/association/depending.rb +22 -1
  6. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +3 -3
  7. data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
  8. data/lib/mongoid/association/relatable.rb +16 -2
  9. data/lib/mongoid/attributes/nested.rb +14 -3
  10. data/lib/mongoid/contextual/map_reduce.rb +1 -1
  11. data/lib/mongoid/copyable.rb +3 -2
  12. data/lib/mongoid/document.rb +2 -0
  13. data/lib/mongoid/matchable.rb +3 -0
  14. data/lib/mongoid/matchable/nor.rb +37 -0
  15. data/lib/mongoid/persistable/settable.rb +58 -13
  16. data/lib/mongoid/persistence_context.rb +5 -1
  17. data/lib/mongoid/touchable.rb +102 -0
  18. data/lib/mongoid/version.rb +1 -1
  19. data/spec/app/models/array_field.rb +7 -0
  20. data/spec/app/models/updatable.rb +7 -0
  21. data/spec/mongoid/association/accessors_spec.rb +39 -0
  22. data/spec/mongoid/association/depending_spec.rb +253 -0
  23. data/spec/mongoid/association/polymorphic_spec.rb +59 -0
  24. data/spec/mongoid/association/referenced/belongs_to_spec.rb +3 -3
  25. data/spec/mongoid/association/referenced/has_one_spec.rb +59 -0
  26. data/spec/mongoid/attributes/nested_spec.rb +18 -2
  27. data/spec/mongoid/clients/factory_spec.rb +3 -3
  28. data/spec/mongoid/clients/options_spec.rb +28 -13
  29. data/spec/mongoid/clients/sessions_spec.rb +3 -3
  30. data/spec/mongoid/clients/transactions_spec.rb +369 -0
  31. data/spec/mongoid/copyable_spec.rb +16 -0
  32. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  33. data/spec/mongoid/matchable_spec.rb +26 -1
  34. data/spec/mongoid/persistable/settable_spec.rb +128 -9
  35. data/spec/mongoid/{association/touchable_spec.rb → touchable_spec.rb} +28 -7
  36. data/spec/spec_helper.rb +8 -0
  37. metadata +457 -448
  38. metadata.gz.sig +0 -0
  39. 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 bested hash' do
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
- before do
315
- church.set('location.address.state.address.city' => 'Munich')
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
- it 'does not reset the nested hash' do
319
- expect(church.name).to eq('Church1')
320
- expect(church.location).to eql({'address' => {'state' => {'address' => {'city' => 'Munich', 'street' => 'Yorckstr'}}}})
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