Skip to content

Commit e631de2

Browse files
authored
MONGOID-5839 Fix eager-loading from STI subclasses (backport to 8.0) (#5942)
(backport to 8.0) If the root of a query is an STI subclass (e.g. Subclass.all) AND the query tries to eager load (includes) another association, the eager load was failing because it was looking for inverse associations using the STI subclass name, instead of the class at the root of the hierarchy.
1 parent ed8bc62 commit e631de2

File tree

3 files changed

+39
-2
lines changed

3 files changed

+39
-2
lines changed

lib/mongoid/association/eager_loadable.rb

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ def preload(associations, docs)
3131
docs_map = {}
3232
queue = [ klass.to_s ]
3333

34+
# account for single-collection inheritance
35+
queue.push(klass.root_class.to_s) if klass != klass.root_class
36+
3437
while klass = queue.shift
3538
if as = assoc_map.delete(klass)
3639
as.each do |assoc|

lib/mongoid/traversable.rb

+12
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,18 @@ def hereditary?
300300
!!(Mongoid::Document > superclass)
301301
end
302302

303+
# Returns the root class of the STI tree that the current
304+
# class participates in. If the class is not an STI subclass, this
305+
# returns the class itself.
306+
#
307+
# @return [ Mongoid::Document ] the root of the STI tree
308+
def root_class
309+
root = self
310+
root = root.superclass while root.hereditary?
311+
312+
root
313+
end
314+
303315
# When inheriting, we want to copy the fields from the parent class and
304316
# set the on the child to start, mimicking the behavior of the old
305317
# class_inheritable_accessor that was deprecated in Rails edge.

spec/mongoid/association/eager_spec.rb

+24-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,36 @@
1414
Mongoid::Contextual::Mongo.new(criteria)
1515
end
1616

17+
let(:association_host) { Account }
18+
1719
let(:inclusions) do
1820
includes.map do |key|
19-
Account.reflect_on_association(key)
21+
association_host.reflect_on_association(key)
2022
end
2123
end
2224

2325
let(:doc) { criteria.first }
2426

27+
context 'when root is an STI subclass' do
28+
# Driver has_one Vehicle
29+
# Vehicle belongs_to Driver
30+
# Truck is a Vehicle
31+
32+
before do
33+
Driver.create!(vehicle: Truck.new)
34+
end
35+
36+
let(:criteria) { Truck.all }
37+
let(:includes) { %i[ driver ] }
38+
let(:association_host) { Truck }
39+
40+
it 'preloads the driver' do
41+
expect(doc.ivar(:driver)).to be false
42+
context.preload(inclusions, [ doc ])
43+
expect(doc.ivar(:driver)).to be == Driver.first
44+
end
45+
end
46+
2547
context "when belongs_to" do
2648

2749
let!(:account) do
@@ -42,7 +64,7 @@
4264
it "preloads the parent" do
4365
expect(doc.ivar(:person)).to be false
4466
context.preload(inclusions, [doc])
45-
expect(doc.ivar(:person)).to eq(doc.person)
67+
expect(doc.ivar(:person)).to be == person
4668
end
4769
end
4870

0 commit comments

Comments
 (0)