mongoid_includes 1.0.5

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.
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