mongoid_includes 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +17 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.md +49 -0
  5. data/Rakefile +31 -0
  6. data/lib/config/locales/en.yml +19 -0
  7. data/lib/mongoid/includes.rb +4 -0
  8. data/lib/mongoid/includes/criteria.rb +67 -0
  9. data/lib/mongoid/includes/eager_load.rb +50 -0
  10. data/lib/mongoid/includes/errors.rb +2 -0
  11. data/lib/mongoid/includes/errors/invalid_includes.rb +30 -0
  12. data/lib/mongoid/includes/errors/invalid_polymorphic_includes.rb +16 -0
  13. data/lib/mongoid/includes/inclusion.rb +64 -0
  14. data/lib/mongoid/includes/inclusions.rb +40 -0
  15. data/lib/mongoid/includes/relations/eager.rb +19 -0
  16. data/lib/mongoid/includes/version.rb +10 -0
  17. data/lib/mongoid_includes.rb +12 -0
  18. data/spec/mongoid/includes/criteria_spec.rb +25 -0
  19. data/spec/mongoid/includes/errors/invalid_includes_spec.rb +29 -0
  20. data/spec/mongoid/includes/errors/invalid_polymorphic_includes_spec.rb +29 -0
  21. data/spec/mongoid/includes/inclusions_spec.rb +23 -0
  22. data/spec/mongoid/includes/nested_inclusions_spec.rb +52 -0
  23. data/spec/mongoid/includes/simple_inclusions_spec.rb +1010 -0
  24. data/spec/spec_helper.rb +60 -0
  25. data/spec/support/config/mongoid.yml +27 -0
  26. data/spec/support/helpers.rb +47 -0
  27. data/spec/support/models/address.rb +69 -0
  28. data/spec/support/models/album.rb +9 -0
  29. data/spec/support/models/artist.rb +8 -0
  30. data/spec/support/models/band.rb +25 -0
  31. data/spec/support/models/game.rb +18 -0
  32. data/spec/support/models/musician.rb +10 -0
  33. data/spec/support/models/person.rb +72 -0
  34. data/spec/support/models/post.rb +11 -0
  35. data/spec/support/models/preference.rb +11 -0
  36. data/spec/support/models/record.rb +50 -0
  37. data/spec/support/models/song.rb +7 -0
  38. metadata +123 -0
@@ -0,0 +1,10 @@
1
+ # Mongoid is an ODM (Object Document Mapper) Framework for MongoDB, written in Ruby.
2
+ module Mongoid
3
+ # Improves eager loading in Mongoid, supporting polymorphic associations,
4
+ # and up to two-levels of eager loading.
5
+ module Includes
6
+
7
+ # Public: This library will attempt to follow semantic versioning (whatever that's supposed to be).
8
+ VERSION = '1.0.5'
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ require 'mongoid'
2
+
3
+ require 'mongoid/includes'
4
+
5
+ # add english load path by default
6
+ I18n.load_path << File.join(File.dirname(__FILE__), 'config', 'locales', 'en.yml')
7
+
8
+ Mongoid::Contextual::Mongo.send :prepend, Mongoid::Includes::EagerLoad
9
+ Mongoid::Contextual::Memory.send :prepend, Mongoid::Includes::EagerLoad
10
+
11
+ Mongoid::Criteria.send :prepend, Mongoid::Includes::Criteria
12
+ Mongoid::Relations::Eager::Base.send :prepend, Mongoid::Includes::Relations::Eager
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Criteria do
4
+ Given(:criteria) { Band.includes(:songs, :owner, from: :albums) }
5
+ Given(:other_criteria) { Band.includes(:musicians, :albums) }
6
+
7
+ describe '#merge' do
8
+ context 'inclusions are combined properly' do
9
+ When(:new_criteria) { criteria.merge(other_criteria) }
10
+ Then { new_criteria.inclusions.size == 4 }
11
+ And { new_criteria.inclusions.class == Mongoid::Includes::Inclusions }
12
+ And { criteria.inclusions.size == 3 }
13
+ And { other_criteria.inclusions.size == 2 }
14
+ end
15
+ end
16
+
17
+ describe '#merge!' do
18
+ context 'inclusions are merged properly' do
19
+ When { criteria.merge!(other_criteria) }
20
+ Then { criteria.inclusions.size == 4 }
21
+ And { criteria.inclusions.class == Mongoid::Includes::Inclusions }
22
+ And { other_criteria.inclusions.size == 2 }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Includes::Errors::InvalidIncludes do
4
+
5
+ describe "#message" do
6
+
7
+ Given(:error) {
8
+ described_class.new(Album, :something, {})
9
+ }
10
+
11
+ it "contains the problem in the message" do
12
+ expect(error.message).to include(
13
+ "Invalid includes directive: Album.includes(:something)"
14
+ )
15
+ end
16
+
17
+ it "contains the summary in the message" do
18
+ expect(error.message).to include(
19
+ "that are the names of relations on the Album model"
20
+ )
21
+ end
22
+
23
+ it "contains the resolution in the message" do
24
+ expect(error.message).to include(
25
+ "is a valid name of a relation on the Album model"
26
+ )
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Includes::Errors::InvalidPolymorphicIncludes do
4
+
5
+ describe "#message" do
6
+
7
+ Given(:error) {
8
+ described_class.new(Album, :something, from: :artist)
9
+ }
10
+
11
+ it "contains the problem in the message" do
12
+ expect(error.message).to include(
13
+ ":artist is a polymorphic relation"
14
+ )
15
+ end
16
+
17
+ it "contains the summary in the message" do
18
+ expect(error.message).to include(
19
+ "specify :from_class when calling the method to resolve the ambiguity"
20
+ )
21
+ end
22
+
23
+ it "contains the resolution in the message" do
24
+ expect(error.message).to include(
25
+ "non-polymorphic relation in the Album model, or a explicit class in :from_class"
26
+ )
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Includes::Inclusions do
4
+
5
+ describe '#inclusions' do
6
+ Given(:criteria) { Band.includes(:songs, :owner, from: :albums) }
7
+ Given(:inclusions) { criteria.inclusions }
8
+
9
+ context 'adding two different inclusions' do
10
+ Given(:other_criteria) { Band.includes(:musicians, :albums) }
11
+ When(:result) { inclusions + other_criteria.inclusions }
12
+ Then { result.size == 4 }
13
+ And { result.class == Mongoid::Includes::Inclusions }
14
+ end
15
+
16
+ context 'when removing duplicates' do
17
+ Given(:inclusions) { Mongoid::Includes::Inclusions.new(criteria.inclusions) }
18
+ When(:result) { inclusions.uniq }
19
+ Then { result.size == 3 }
20
+ And { result.class == Mongoid::Includes::Inclusions }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Includes::Criteria do
4
+
5
+ describe '#includes' do
6
+
7
+ context 'inclusion has additional options' do
8
+ Given(:band) { Band.create!(name: 'Pink Floyd') }
9
+ Given {
10
+ Album.create!(name: 'Wish You Were Here', release: Date.new(1975), owner: band)
11
+ Album.create!(name: 'The Dark Side of the Moon', release: Date.new(1973), owner: band)
12
+ }
13
+ Given(:criteria) {
14
+ Band.includes(:albums, with: ->(albums) { albums.lt(release: Date.new(1974)) })
15
+ }
16
+ When(:result) { criteria.entries.first }
17
+ Then { expect(result).to eq band }
18
+ And { result.albums.first.name == 'The Dark Side of the Moon' }
19
+ And { result.albums.size == 1 }
20
+ And { band.albums.size == 2 }
21
+ end
22
+ end
23
+
24
+ describe '#inclusions' do
25
+ Given(:inclusions) { criteria.inclusions }
26
+
27
+ context 'does not duplicate inclusions' do
28
+ When(:criteria) {
29
+ Band.includes(:songs, :owner, from: :albums)
30
+ .includes(:musicians, :albums)
31
+ .includes(:songs, :owner, from: :albums)
32
+ }
33
+ Then { inclusions.size == 4 }
34
+ end
35
+
36
+ context 'each inclusion has valid properties' do
37
+ Given(:metadata) { Band.relations['albums'] }
38
+ Given(:nested_metadata) { Album.relations['songs'] }
39
+ Given(:polymorphic_metadata) { Album.relations['owner'] }
40
+
41
+ When(:criteria) { Band.includes(:songs, :owner, from: :albums) }
42
+
43
+ Then { inclusions.size == 3 }
44
+ And { expect(inclusions).to include(metadata) }
45
+ And { expect(inclusions).to include(nested_metadata) }
46
+ And { expect(inclusions).to include(polymorphic_metadata) }
47
+ And { !inclusions.to_a[0].nested? && !inclusions.to_a[0].polymorphic_belongs_to? }
48
+ And { inclusions.to_a[1].nested? && !inclusions.to_a[1].polymorphic_belongs_to? }
49
+ And { inclusions.to_a[2].nested? && inclusions.to_a[2].polymorphic_belongs_to? }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,1010 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Includes::Criteria do
4
+
5
+ describe '#includes' do
6
+
7
+ let!(:person) do
8
+ Person.create(age: 1)
9
+ end
10
+
11
+ context 'when providing a name that is not a relation' do
12
+
13
+ it 'raises an error' do
14
+ expect {
15
+ Person.includes(:members)
16
+ }.to raise_error(Mongoid::Includes::Errors::InvalidIncludes)
17
+ end
18
+ end
19
+
20
+ context 'when the models are inherited' do
21
+
22
+ before(:all) do
23
+ class A
24
+ include Mongoid::Document
25
+ end
26
+
27
+ class B < A
28
+ belongs_to :c
29
+ end
30
+
31
+ class C
32
+ include Mongoid::Document
33
+ has_one :b
34
+ end
35
+ end
36
+
37
+ after(:all) do
38
+ Object.send(:remove_const, :A)
39
+ Object.send(:remove_const, :B)
40
+ Object.send(:remove_const, :C)
41
+ end
42
+
43
+ context 'when the includes is on the subclass' do
44
+
45
+ let!(:c_one) do
46
+ C.create
47
+ end
48
+
49
+ let!(:c_two) do
50
+ C.create
51
+ end
52
+
53
+ let!(:b) do
54
+ B.create(c: c_two)
55
+ end
56
+
57
+ let!(:results) do
58
+ C.includes(:b).to_a.detect do |c|
59
+ c.id == c_two.id
60
+ end
61
+ end
62
+
63
+ it 'returns the correct documents' do
64
+ expect(results).to eq(c_two)
65
+ end
66
+
67
+ it 'does not query the db' do
68
+ expect_query(0) do
69
+ results.b
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'when the models are inherited from another one model' do
76
+
77
+ context 'when the relation is a has_one' do
78
+
79
+ before(:all) do
80
+ class A
81
+ include Mongoid::Document
82
+ end
83
+
84
+ class B < A
85
+ belongs_to :d
86
+ end
87
+
88
+ class C < A
89
+ belongs_to :d
90
+ end
91
+
92
+ class D
93
+ include Mongoid::Document
94
+ has_one :b
95
+ has_one :c
96
+ end
97
+ end
98
+
99
+ after(:all) do
100
+ Object.send(:remove_const, :A)
101
+ Object.send(:remove_const, :B)
102
+ Object.send(:remove_const, :C)
103
+ Object.send(:remove_const, :D)
104
+ end
105
+
106
+ context 'when the includes is on the several relations' do
107
+
108
+ let!(:d_one) do
109
+ D.create
110
+ end
111
+
112
+ let!(:d_two) do
113
+ D.create
114
+ end
115
+
116
+ let!(:b) do
117
+ B.create(d: d_two)
118
+ end
119
+
120
+ let!(:c) do
121
+ C.create(d: d_two)
122
+ end
123
+
124
+ let!(:results) do
125
+ D.includes(:b, :c).entries.detect do |d|
126
+ d.id == d_two.id
127
+ end
128
+ end
129
+
130
+ it 'returns the correct documents' do
131
+ expect(results).to eq(d_two)
132
+ end
133
+
134
+ it 'does not query the db on b' do
135
+ expect_query(0) do
136
+ results.b
137
+ end
138
+ end
139
+
140
+ it 'does not query the db on c' do
141
+ expect_query(0) do
142
+ results.b
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ context 'when the relation is a has_many' do
149
+
150
+ before(:all) do
151
+ class A
152
+ include Mongoid::Document
153
+ end
154
+
155
+ class B < A
156
+ belongs_to :d
157
+ end
158
+
159
+ class C < A
160
+ belongs_to :d
161
+ end
162
+
163
+ class D
164
+ include Mongoid::Document
165
+ has_many :b
166
+ has_many :c
167
+ end
168
+ end
169
+
170
+ after(:all) do
171
+ Object.send(:remove_const, :A)
172
+ Object.send(:remove_const, :B)
173
+ Object.send(:remove_const, :C)
174
+ Object.send(:remove_const, :D)
175
+ end
176
+
177
+ context 'when the includes is on the several relations' do
178
+
179
+ let!(:d_one) do
180
+ D.create
181
+ end
182
+
183
+ let!(:d_two) do
184
+ D.create
185
+ end
186
+
187
+ let!(:bs) do
188
+ 2.times.map { B.create(d: d_two) }
189
+ end
190
+
191
+ let!(:cs) do
192
+ 2.times.map { C.create(d: d_two) }
193
+ end
194
+
195
+ let!(:results) do
196
+ D.includes(:b, :c).entries.detect do |d|
197
+ d.id == d_two.id
198
+ end
199
+ end
200
+
201
+ it 'returns the correct documents' do
202
+ expect(results).to eq(d_two)
203
+ end
204
+
205
+ it 'does not query the db on b' do
206
+ expect_query(0) do
207
+ results.b
208
+ end
209
+ end
210
+
211
+ it 'does not query the db on c' do
212
+ expect_query(0) do
213
+ results.b
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ context 'when including the same metadata multiple times' do
221
+
222
+ let(:criteria) do
223
+ Person.all.includes(:posts, :posts).includes(:posts)
224
+ end
225
+
226
+ let(:metadata) do
227
+ Person.reflect_on_association(:posts)
228
+ end
229
+
230
+ it 'does not duplicate the metadata in the inclusions' do
231
+ expect(criteria.inclusions.size).to eq 1
232
+ expect(criteria.inclusions.first).to eq metadata
233
+ end
234
+ end
235
+
236
+ context 'when mapping the results more than once' do
237
+
238
+ let!(:post) do
239
+ person.posts.create(title: 'one')
240
+ end
241
+
242
+ let(:criteria) do
243
+ Post.includes(:person)
244
+ end
245
+
246
+ let!(:results) do
247
+ criteria.map { |doc| doc }
248
+ criteria.map { |doc| doc }
249
+ end
250
+
251
+ it 'returns the proper results' do
252
+ expect(results.first.title).to eq('one')
253
+ end
254
+ end
255
+
256
+ context 'when including a belongs to relation' do
257
+
258
+ context 'when the criteria is from the root' do
259
+
260
+ let!(:person_two) do
261
+ Person.create(age: 2)
262
+ end
263
+
264
+ let!(:post_one) do
265
+ person.posts.create(title: 'one')
266
+ end
267
+
268
+ let!(:post_two) do
269
+ person_two.posts.create(title: 'two')
270
+ end
271
+
272
+ context 'when calling first' do
273
+
274
+ let(:criteria) do
275
+ Post.includes(:person)
276
+ end
277
+
278
+ let!(:document) do
279
+ criteria.first
280
+ end
281
+
282
+ it 'eager loads the first document' do
283
+ expect_query(0) do
284
+ expect(document.person).to eq(person)
285
+ end
286
+ end
287
+
288
+ it 'returns the first document' do
289
+ expect(document).to eq(post_one)
290
+ end
291
+ end
292
+
293
+ context 'when calling last' do
294
+
295
+ let!(:criteria) do
296
+ Post.asc(:_id).includes(:person)
297
+ end
298
+
299
+ let!(:document) do
300
+ criteria.last
301
+ end
302
+
303
+ it 'eager loads the last document' do
304
+ expect_query(0) do
305
+ expect(document.person).to eq(person_two)
306
+ end
307
+ end
308
+
309
+ it 'does not eager load the first document' do
310
+ doc = criteria.first
311
+ expect_query(1) do
312
+ expect(doc.person).to eq(person)
313
+ end
314
+ end
315
+
316
+ it 'returns the last document' do
317
+ expect(document).to eq(post_two)
318
+ end
319
+ end
320
+ end
321
+
322
+ context 'when the criteria is from an embedded relation' do
323
+
324
+ let(:peep) do
325
+ Person.create
326
+ end
327
+
328
+ let!(:address_one) do
329
+ peep.addresses.create(street: 'rosenthaler')
330
+ end
331
+
332
+ let!(:address_two) do
333
+ peep.addresses.create(street: 'weinmeister')
334
+ end
335
+
336
+ let!(:depeche) do
337
+ Band.create!(name: 'Depeche Mode')
338
+ end
339
+
340
+ let!(:tool) do
341
+ Band.create!(name: 'Tool')
342
+ end
343
+
344
+ before do
345
+ address_one.band = depeche
346
+ address_two.band = tool
347
+ address_one.save
348
+ address_two.save
349
+ end
350
+
351
+ context 'when calling first' do
352
+
353
+ let(:criteria) do
354
+ peep.reload.addresses.includes(:band)
355
+ end
356
+
357
+ let(:context) do
358
+ criteria.context
359
+ end
360
+
361
+ let!(:document) do
362
+ criteria.first
363
+ end
364
+
365
+ it 'eager loads the first document' do
366
+ expect_query(0) do
367
+ expect(document.band).to eq(depeche)
368
+ end
369
+ end
370
+
371
+ it 'does not eager load the last document' do
372
+ doc = criteria.last
373
+ expect_query(1) do
374
+ expect(doc.band).to eq(tool)
375
+ end
376
+ end
377
+
378
+ it 'returns the document' do
379
+ expect(document).to eq(address_one)
380
+ end
381
+ end
382
+
383
+ context 'when calling last' do
384
+
385
+ let(:criteria) do
386
+ peep.reload.addresses.includes(:band)
387
+ end
388
+
389
+ let(:context) do
390
+ criteria.context
391
+ end
392
+
393
+ let!(:document) do
394
+ criteria.last
395
+ end
396
+
397
+ it 'eager loads the last document' do
398
+ expect_query(0) do
399
+ expect(document.band).to eq(tool)
400
+ end
401
+ end
402
+
403
+ it 'does not eager load the first document' do
404
+ doc = criteria.first
405
+ expect_query(1) do
406
+ expect(doc.band).to eq(depeche)
407
+ end
408
+ end
409
+
410
+ it 'returns the document' do
411
+ expect(document).to eq(address_two)
412
+ end
413
+ end
414
+
415
+ context 'when iterating all documents' do
416
+
417
+ let(:criteria) do
418
+ peep.reload.addresses.includes(:band)
419
+ end
420
+
421
+ let(:context) do
422
+ criteria.context
423
+ end
424
+
425
+ let!(:documents) do
426
+ criteria.to_a
427
+ end
428
+
429
+ it 'eager loads the first document' do
430
+ expect_query(0) do
431
+ expect(documents.first.band).to eq(depeche)
432
+ end
433
+ end
434
+
435
+ it 'eager loads the last document' do
436
+ expect_query(0) do
437
+ expect(documents.last.band).to eq(tool)
438
+ end
439
+ end
440
+
441
+ it 'returns the documents' do
442
+ expect(documents).to eq([ address_one, address_two ])
443
+ end
444
+ end
445
+ end
446
+ end
447
+
448
+ context 'when providing inclusions to the default scope' do
449
+
450
+ before do
451
+ Person.default_scope(->{ Person.includes(:posts) })
452
+ end
453
+
454
+ after do
455
+ Person.default_scoping = nil
456
+ end
457
+
458
+ let!(:post_one) do
459
+ person.posts.create(title: 'one')
460
+ end
461
+
462
+ let!(:post_two) do
463
+ person.posts.create(title: 'two')
464
+ end
465
+
466
+ context 'when the criteria has no options' do
467
+
468
+ let!(:criteria) do
469
+ Person.asc(:age).all
470
+ end
471
+
472
+ let!(:documents) do
473
+ criteria.entries
474
+ end
475
+
476
+ it 'returns the correct documents' do
477
+ expect(documents).to eq([ person ])
478
+ end
479
+
480
+ it 'eager loads the first document' do
481
+ expect_query(0) do
482
+ expect(documents.first.posts.first).to eq(post_one)
483
+ end
484
+ end
485
+
486
+ it 'eager loads the last document' do
487
+ expect_query(0) do
488
+ expect(documents.first.posts.last).to eq(post_two)
489
+ end
490
+ end
491
+
492
+ context 'when executing the query twice' do
493
+
494
+ let!(:new_criteria) do
495
+ Person.where(id: person.id)
496
+ end
497
+
498
+ let!(:new_context) do
499
+ new_criteria.context
500
+ end
501
+
502
+ before do
503
+ expect(new_context).to receive(:eager_load_one).with(person).once.and_call_original
504
+ end
505
+
506
+ let!(:from_db) do
507
+ new_criteria.first
508
+ end
509
+
510
+ it 'does not duplicate documents in the relation' do
511
+ expect(person.posts.size).to eq(2)
512
+ end
513
+ end
514
+ end
515
+
516
+ context 'when calling first on the criteria' do
517
+
518
+ let(:criteria) do
519
+ Person.asc(:age).all
520
+ end
521
+
522
+ let!(:from_db) do
523
+ criteria.first
524
+ end
525
+
526
+ it 'returns the correct documents' do
527
+ expect(from_db).to eq(person)
528
+ end
529
+
530
+ it 'eager loads the first document' do
531
+ expect_query(0) do
532
+ expect(from_db.posts.first).to eq(post_one)
533
+ end
534
+ end
535
+
536
+ it 'eager loads the last document' do
537
+ expect_query(0) do
538
+ expect(from_db.posts.last).to eq(post_two)
539
+ end
540
+ end
541
+ end
542
+
543
+ context 'when calling last on the criteria' do
544
+
545
+ let(:criteria) do
546
+ Person.asc(:age).all
547
+ end
548
+
549
+ let!(:context) do
550
+ criteria.context
551
+ end
552
+
553
+ before do
554
+ expect(context).to receive(:eager_load_one).with(person).once.and_call_original
555
+ end
556
+
557
+ let!(:from_db) do
558
+ criteria.last
559
+ end
560
+
561
+ it 'returns the correct documents' do
562
+ expect(from_db).to eq(person)
563
+ end
564
+
565
+ it 'eager loads the first document' do
566
+ expect_query(0) do
567
+ expect(from_db.posts.first).to eq(post_one)
568
+ end
569
+ end
570
+
571
+ it 'eager loads the last document' do
572
+ expect_query(0) do
573
+ expect(from_db.posts.last).to eq(post_two)
574
+ end
575
+ end
576
+ end
577
+
578
+ context 'when the criteria has limiting options' do
579
+
580
+ let!(:person_two) do
581
+ Person.create
582
+ end
583
+
584
+ let!(:post_three) do
585
+ person_two.posts.create(title: 'three')
586
+ end
587
+
588
+ let!(:criteria) do
589
+ Person.asc(:age).limit(1)
590
+ end
591
+
592
+ let!(:documents) do
593
+ criteria.entries
594
+ end
595
+
596
+ it 'returns the correct documents' do
597
+ expect(criteria).to eq([ person ])
598
+ end
599
+
600
+ it 'eager loads the first document' do
601
+ expect_query(0) do
602
+ expect(documents.first.posts.first).to eq(post_one)
603
+ end
604
+ end
605
+
606
+ it 'eager loads the second document' do
607
+ expect_query(0) do
608
+ expect(documents.first.posts.last).to eq(post_two)
609
+ end
610
+ end
611
+ end
612
+ end
613
+
614
+ context 'when including a has and belongs to many' do
615
+
616
+ let!(:preference_one) do
617
+ person.preferences.create(name: 'one')
618
+ end
619
+
620
+ let!(:preference_two) do
621
+ person.preferences.create(name: 'two')
622
+ end
623
+
624
+ context 'when the criteria has no options' do
625
+
626
+ let!(:criteria) do
627
+ Person.asc(:age).includes(:preferences)
628
+ end
629
+
630
+ let!(:documents) do
631
+ criteria.entries
632
+ end
633
+
634
+ it 'returns the correct documents' do
635
+ expect(documents).to eq([ person ])
636
+ end
637
+
638
+ it 'eager loads the first document' do
639
+ expect_query(0) do
640
+ expect(documents.first.preferences.first).to eq(preference_one)
641
+ end
642
+ end
643
+
644
+ it 'eager loads the last document' do
645
+ expect_query(0) do
646
+ expect(documents.first.preferences.last).to eq(preference_two)
647
+ end
648
+ end
649
+ end
650
+
651
+ context 'when calling first on the criteria' do
652
+
653
+ let!(:criteria) do
654
+ Person.asc(:age).includes(:preferences)
655
+ end
656
+
657
+ let!(:from_db) do
658
+ criteria.first
659
+ end
660
+
661
+ it 'returns the correct documents' do
662
+ expect(from_db).to eq(person)
663
+ end
664
+
665
+ it 'eager loads the first document' do
666
+ expect_query(0) do
667
+ expect(from_db.preferences.first).to eq(preference_one)
668
+ end
669
+ end
670
+
671
+ it 'eager loads the last document' do
672
+ expect_query(0) do
673
+ expect(from_db.preferences.last).to eq(preference_two)
674
+ end
675
+ end
676
+ end
677
+
678
+ context 'when calling last on the criteria' do
679
+
680
+ let!(:criteria) do
681
+ Person.asc(:age).includes(:preferences)
682
+ end
683
+
684
+ let!(:from_db) do
685
+ criteria.last
686
+ end
687
+
688
+ it 'returns the correct documents' do
689
+ expect(from_db).to eq(person)
690
+ end
691
+
692
+ it 'eager loads the first document' do
693
+ expect_query(0) do
694
+ expect(from_db.preferences.first).to eq(preference_one)
695
+ end
696
+ end
697
+
698
+ it 'eager loads the last document' do
699
+ expect_query(0) do
700
+ expect(from_db.preferences.last).to eq(preference_two)
701
+ end
702
+ end
703
+ end
704
+ end
705
+
706
+ context 'when including a has many' do
707
+
708
+ let!(:post_one) do
709
+ person.posts.create(title: 'one')
710
+ end
711
+
712
+ let!(:post_two) do
713
+ person.posts.create(title: 'two')
714
+ end
715
+
716
+ context 'when the criteria has no options' do
717
+
718
+ let!(:criteria) do
719
+ Person.asc(:age).includes(:posts)
720
+ end
721
+
722
+ let!(:documents) do
723
+ criteria.entries
724
+ end
725
+
726
+ it 'returns the correct documents' do
727
+ expect(documents).to eq([ person ])
728
+ end
729
+
730
+ it 'eager loads the first document' do
731
+ expect_query(0) do
732
+ expect(documents.first.posts.first).to eq(post_one)
733
+ end
734
+ end
735
+
736
+ it 'eager loads the last document' do
737
+ expect_query(0) do
738
+ expect(documents.first.posts.last).to eq(post_two)
739
+ end
740
+ end
741
+ end
742
+
743
+ context 'when calling first on the criteria' do
744
+
745
+ let!(:criteria) do
746
+ Person.asc(:age).includes(:posts)
747
+ end
748
+
749
+ let!(:from_db) do
750
+ criteria.first
751
+ end
752
+
753
+ it 'returns the correct documents' do
754
+ expect(from_db).to eq(person)
755
+ end
756
+
757
+ context 'when subsequently getting all documents' do
758
+
759
+ let!(:documents) do
760
+ criteria.entries
761
+ end
762
+
763
+ it 'returns the correct documents' do
764
+ expect(documents).to eq([ person ])
765
+ end
766
+ end
767
+ end
768
+
769
+ context 'when calling last on the criteria' do
770
+
771
+ let!(:criteria) do
772
+ Person.asc(:age).includes(:posts)
773
+ end
774
+
775
+ let!(:from_db) do
776
+ criteria.last
777
+ end
778
+
779
+ it 'returns the correct documents' do
780
+ expect(from_db).to eq(person)
781
+ end
782
+
783
+ context 'when subsequently getting all documents' do
784
+
785
+ let!(:documents) do
786
+ criteria.entries
787
+ end
788
+
789
+ it 'returns the correct documents' do
790
+ expect(documents).to eq([ person ])
791
+ end
792
+ end
793
+ end
794
+
795
+ context 'when the criteria has limiting options' do
796
+
797
+ let!(:person_two) do
798
+ Person.create
799
+ end
800
+
801
+ let!(:post_three) do
802
+ person_two.posts.create(title: 'three')
803
+ end
804
+
805
+ let!(:criteria) do
806
+ Person.includes(:posts).asc(:age).limit(1)
807
+ end
808
+
809
+ let(:context) do
810
+ criteria.context
811
+ end
812
+
813
+ before do
814
+ expect(context).to receive(:eager_load).with([ person ]).once.and_call_original
815
+ end
816
+
817
+ let!(:documents) do
818
+ criteria.entries
819
+ end
820
+
821
+ it 'returns the correct documents' do
822
+ expect(documents).to eq([ person ])
823
+ end
824
+ end
825
+ end
826
+
827
+ context 'when including a has one' do
828
+
829
+ let!(:game_one) do
830
+ person.create_game(name: 'one')
831
+ end
832
+
833
+ let!(:game_two) do
834
+ person.create_game(name: 'two')
835
+ end
836
+
837
+ context 'when the criteria has no options' do
838
+
839
+ let!(:criteria) do
840
+ Person.asc(:age).includes(:game)
841
+ end
842
+
843
+ let(:context) do
844
+ criteria.context
845
+ end
846
+
847
+ before do
848
+ expect(context).to receive(:eager_load).with([ person ]).once.and_call_original
849
+ end
850
+
851
+ let!(:documents) do
852
+ criteria.entries
853
+ end
854
+
855
+ it 'returns the correct documents' do
856
+ expect(documents).to eq([ person ])
857
+ end
858
+ end
859
+
860
+ context 'when the criteria has limiting options' do
861
+
862
+ let!(:person_two) do
863
+ Person.create(age: 2)
864
+ end
865
+
866
+ let!(:game_three) do
867
+ person_two.create_game(name: 'Skyrim')
868
+ end
869
+
870
+ let!(:criteria) do
871
+ Person.where(id: person.id).includes(:game).asc(:age).limit(1)
872
+ end
873
+
874
+ let(:context) do
875
+ criteria.context
876
+ end
877
+
878
+ before do
879
+ expect(context).to receive(:eager_load).with([ person ]).once.and_call_original
880
+ end
881
+
882
+ let!(:documents) do
883
+ criteria.entries
884
+ end
885
+
886
+ it 'returns the correct documents' do
887
+ expect(documents).to eq([ person ])
888
+ end
889
+ end
890
+ end
891
+
892
+ context 'when including a belongs to' do
893
+
894
+ let(:person_two) do
895
+ Person.create(age: 2)
896
+ end
897
+
898
+ let!(:game_one) do
899
+ person.create_game(name: 'one')
900
+ end
901
+
902
+ let!(:game_two) do
903
+ person_two.create_game(name: 'two')
904
+ end
905
+
906
+ context 'when providing no options' do
907
+
908
+ let!(:criteria) do
909
+ Game.includes(:person)
910
+ end
911
+
912
+ let(:context) do
913
+ criteria.context
914
+ end
915
+
916
+ before do
917
+ expect(context).to receive(:eager_load).with([ game_one, game_two ]).once.and_call_original
918
+ end
919
+
920
+ let!(:documents) do
921
+ criteria.entries
922
+ end
923
+
924
+ it 'returns the correct documents' do
925
+ expect(criteria).to eq([ game_one, game_two ])
926
+ end
927
+ end
928
+
929
+ context 'when the criteria has limiting options' do
930
+
931
+ let!(:criteria) do
932
+ Game.where(id: game_one.id).includes(:person).asc(:_id).limit(1)
933
+ end
934
+
935
+ let(:context) do
936
+ criteria.context
937
+ end
938
+
939
+ before do
940
+ expect(context).to receive(:eager_load).with([ game_one ]).once.and_call_original
941
+ end
942
+
943
+ let!(:documents) do
944
+ criteria.entries
945
+ end
946
+
947
+ it 'returns the correct documents' do
948
+ expect(documents).to eq([ game_one ])
949
+ end
950
+ end
951
+ end
952
+
953
+ context 'when including multiples in the same criteria' do
954
+
955
+ let!(:post_one) do
956
+ person.posts.create(title: 'one')
957
+ end
958
+
959
+ let!(:post_two) do
960
+ person.posts.create(title: 'two')
961
+ end
962
+
963
+ let!(:game_one) do
964
+ person.create_game(name: 'one')
965
+ end
966
+
967
+ let!(:game_two) do
968
+ person.create_game(name: 'two')
969
+ end
970
+
971
+ let!(:criteria) do
972
+ Person.includes(:posts, :game).asc(:age)
973
+ end
974
+
975
+ let(:context) do
976
+ criteria.context
977
+ end
978
+
979
+ before do
980
+ expect(context).to receive(:eager_load).with([ person ]).once.and_call_original
981
+ end
982
+
983
+ let!(:documents) do
984
+ criteria.entries
985
+ end
986
+
987
+ it 'returns the correct documents' do
988
+ expect(criteria).to eq([ person ])
989
+ end
990
+ end
991
+ end
992
+
993
+ describe '#inclusions' do
994
+
995
+ Given(:criteria) { Band.includes(:albums) }
996
+ Given(:metadata) { Band.relations['albums'] }
997
+ Then { expect(criteria.inclusions).to include(metadata) }
998
+ And { criteria.inclusions.none?(&:nested?) }
999
+ end
1000
+
1001
+ describe '#inclusions=' do
1002
+ Given(:criteria) { Band.all }
1003
+ Given(:metadata) { Band.relations['records'] }
1004
+
1005
+ When { criteria.inclusions = [ Mongoid::Includes::Inclusion.new(metadata) ] }
1006
+
1007
+ Then { expect(criteria.inclusions).to include(metadata) }
1008
+ And { criteria.inclusions.first.is_a?(Mongoid::Includes::Inclusion) }
1009
+ end
1010
+ end