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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/LICENSE.txt +20 -0
- data/README.md +49 -0
- data/Rakefile +31 -0
- data/lib/config/locales/en.yml +19 -0
- data/lib/mongoid/includes.rb +4 -0
- data/lib/mongoid/includes/criteria.rb +67 -0
- data/lib/mongoid/includes/eager_load.rb +50 -0
- data/lib/mongoid/includes/errors.rb +2 -0
- data/lib/mongoid/includes/errors/invalid_includes.rb +30 -0
- data/lib/mongoid/includes/errors/invalid_polymorphic_includes.rb +16 -0
- data/lib/mongoid/includes/inclusion.rb +64 -0
- data/lib/mongoid/includes/inclusions.rb +40 -0
- data/lib/mongoid/includes/relations/eager.rb +19 -0
- data/lib/mongoid/includes/version.rb +10 -0
- data/lib/mongoid_includes.rb +12 -0
- data/spec/mongoid/includes/criteria_spec.rb +25 -0
- data/spec/mongoid/includes/errors/invalid_includes_spec.rb +29 -0
- data/spec/mongoid/includes/errors/invalid_polymorphic_includes_spec.rb +29 -0
- data/spec/mongoid/includes/inclusions_spec.rb +23 -0
- data/spec/mongoid/includes/nested_inclusions_spec.rb +52 -0
- data/spec/mongoid/includes/simple_inclusions_spec.rb +1010 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/support/config/mongoid.yml +27 -0
- data/spec/support/helpers.rb +47 -0
- data/spec/support/models/address.rb +69 -0
- data/spec/support/models/album.rb +9 -0
- data/spec/support/models/artist.rb +8 -0
- data/spec/support/models/band.rb +25 -0
- data/spec/support/models/game.rb +18 -0
- data/spec/support/models/musician.rb +10 -0
- data/spec/support/models/person.rb +72 -0
- data/spec/support/models/post.rb +11 -0
- data/spec/support/models/preference.rb +11 -0
- data/spec/support/models/record.rb +50 -0
- data/spec/support/models/song.rb +7 -0
- 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
|