Skip to content

Commit da14366

Browse files
authored
fix(has_many_getter): add has_one support (#752)
1 parent d23ea0b commit da14366

File tree

2 files changed

+100
-10
lines changed

2 files changed

+100
-10
lines changed

app/services/forest_liana/has_many_getter.rb

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ class HasManyGetter < BaseGetter
44
attr_reader :includes
55
attr_reader :records_count
66

7+
SUPPORTED_ASSOCIATION_MACROS = [:belongs_to, :has_one, :has_and_belongs_to_many].freeze
8+
79
def initialize(resource, association, params, forest_user)
810
@resource = resource
911
@association = association
@@ -43,22 +45,21 @@ def compute_includes
4345
.reflect_on_all_associations
4446
.select do |association|
4547

48+
next false unless SUPPORTED_ASSOCIATION_MACROS.include?(association.macro)
49+
4650
if SchemaUtils.polymorphic?(association)
4751
inclusion = SchemaUtils.polymorphic_models(association)
48-
.all? { |model| SchemaUtils.model_included?(model) } &&
49-
[:belongs_to, :has_and_belongs_to_many].include?(association.macro)
52+
.all? { |model| SchemaUtils.model_included?(model) }
5053
else
51-
inclusion = SchemaUtils.model_included?(association.klass) &&
52-
[:belongs_to, :has_and_belongs_to_many].include?(association.macro)
54+
inclusion = SchemaUtils.model_included?(association.klass)
5355
end
5456

55-
if @field_names_requested
56-
inclusion && @field_names_requested.include?(association.name)
57-
else
58-
inclusion
59-
end
57+
if @field_names_requested.any?
58+
inclusion && @field_names_requested.include?(association.name)
59+
else
60+
inclusion
6061
end
61-
.map { |association| association.name }
62+
end.map(&:name)
6263
end
6364

6465
def field_names_requested

spec/services/forest_liana/has_many_getter_spec.rb

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,95 @@ module ForestLiana
113113
end
114114
end
115115
end
116+
117+
describe 'compute_includes' do
118+
it 'should include has_one relation from association' do
119+
expect(subject.includes).to include(:location)
120+
end
121+
122+
it 'should include belongs_to relations from association' do
123+
expect(subject.includes).to include(:owner, :cutter, :island, :eponymous_island)
124+
end
125+
126+
it 'should exclude has_many relations' do
127+
has_many_associations = Tree.reflect_on_all_associations
128+
.select { |a| a.macro == :has_many }
129+
.map(&:name)
130+
131+
has_many_associations.each do |assoc|
132+
expect(subject.includes).not_to include(assoc)
133+
end
134+
end
135+
136+
it 'should include all supported associations from association by default' do
137+
expected_associations = Tree.reflect_on_all_associations
138+
.select { |a| [:belongs_to, :has_one, :has_and_belongs_to_many].include?(a.macro) }
139+
.map(&:name)
140+
141+
expect(subject.includes).to match_array(expected_associations)
142+
end
143+
144+
it 'should respect fields filter for associations' do
145+
params[:fields] = { 'Tree' => 'owner,island' }
146+
getter = described_class.new(Island, association, params, user)
147+
148+
expect(getter.includes).to include(:owner, :island)
149+
expect(getter.includes).not_to include(:cutter, :eponymous_island, :location)
150+
end
151+
152+
it 'should exclude Tree associations when models not included' do
153+
allow(SchemaUtils).to receive(:model_included?).and_return(false)
154+
expect(subject.includes).to be_empty
155+
end
156+
157+
context 'on polymorphic associations' do
158+
let(:base_params) do
159+
{
160+
id: Island.first&.id || 1,
161+
association_name: 'trees',
162+
page: { size: 15, number: 1 },
163+
timezone: 'UTC'
164+
}
165+
end
166+
167+
before do
168+
# temporarily add a polymorphic association on Tree
169+
Tree.class_eval { belongs_to :addressable, polymorphic: true, optional: true }
170+
171+
allow_any_instance_of(described_class).to receive(:prepare_query).and_return(nil)
172+
allow(ForestLiana).to receive(:name_for).and_return('trees')
173+
end
174+
175+
after do
176+
%w[addressable].each do |name|
177+
Tree._reflections.delete(name)
178+
Tree.reflections.delete(name)
179+
end
180+
%w[addressable addressable= addressable_id addressable_type].each do |m|
181+
Tree.undef_method(m) rescue nil
182+
end
183+
end
184+
185+
it 'should exclude the polymorphic association when not all target models are includable' do
186+
params = base_params.merge(fields: { 'trees' => 'addressable' })
187+
188+
allow(SchemaUtils).to receive(:model_included?).and_return(true, false)
189+
190+
getter = described_class.new(Island, association, params, user)
191+
expect(getter.includes).to eq([])
192+
end
193+
194+
it 'should include the polymorphic association only when all target models are includable' do
195+
params = base_params.merge(fields: { 'trees' => 'addressable' })
196+
197+
allow(SchemaUtils).to receive(:model_included?).and_return(true, true)
198+
199+
getter = described_class.new(Island, association, params, user)
200+
expect(getter.includes).to contain_exactly(:addressable)
201+
end
202+
203+
end
204+
end
116205
end
117206
end
118207
end

0 commit comments

Comments
 (0)