Skip to content

Commit 9ad4d91

Browse files
nagirrablgebhardt
authored andcommitted
Fix has_one polymorphism (#945)
* Fix has_one polymorphism * Add tests for has one polymorphic serialization
1 parent 9503063 commit 9ad4d91

8 files changed

Lines changed: 211 additions & 4 deletions

File tree

lib/jsonapi/relationship_builder.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,16 @@ def build_has_one(relationship, foreign_key, associated_records_method_name, rel
120120
define_on_resource relationship_name do |options = {}|
121121
relationship = self.class._relationships[relationship_name]
122122

123-
resource_klass = relationship.resource_klass
124-
if resource_klass
123+
if relationship.polymorphic?
125124
associated_model = public_send(associated_records_method_name)
126-
return associated_model ? resource_klass.new(associated_model, @context) : nil
125+
resource_klass = self.class.resource_for_model(associated_model) if associated_model
126+
return resource_klass.new(associated_model, @context) if resource_klass && associated_model
127+
else
128+
resource_klass = relationship.resource_klass
129+
if resource_klass
130+
associated_model = public_send(associated_records_method_name)
131+
return associated_model ? resource_klass.new(associated_model, @context) : nil
132+
end
127133
end
128134
end
129135
end

test/fixtures/active_record.rb

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,25 @@
291291
t.timestamps null: false
292292
end
293293

294+
create_table :questions, force: true do |t|
295+
t.string :text
296+
end
297+
298+
create_table :answers, force: true do |t|
299+
t.references :question
300+
t.integer :respondent_id
301+
t.string :respondent_type
302+
t.string :text
303+
end
304+
305+
create_table :patients, force: true do |t|
306+
t.string :name
307+
end
308+
309+
create_table :doctors, force: true do |t|
310+
t.string :name
311+
end
312+
294313
# special cases
295314
end
296315

@@ -606,6 +625,25 @@ class RelatedThing < ActiveRecord::Base
606625
belongs_to :to, class_name: Thing, foreign_key: :to_id
607626
end
608627

628+
class Question < ActiveRecord::Base
629+
has_one :answer
630+
631+
def respondent
632+
answer.try(:respondent)
633+
end
634+
end
635+
636+
class Answer < ActiveRecord::Base
637+
belongs_to :question
638+
belongs_to :respondent, polymorphic: true
639+
end
640+
641+
class Patient < ActiveRecord::Base
642+
end
643+
644+
class Doctor < ActiveRecord::Base
645+
end
646+
609647
module Api
610648
module V7
611649
class Client < Customer
@@ -882,6 +920,21 @@ class BoxesController < JSONAPI::ResourceController
882920
end
883921
end
884922

923+
class QuestionsController < JSONAPI::ResourceController
924+
end
925+
926+
class AnswersController < JSONAPI::ResourceController
927+
end
928+
929+
class PatientsController < JSONAPI::ResourceController
930+
end
931+
932+
class DoctorsController < JSONAPI::ResourceController
933+
end
934+
935+
class RespondentController < JSONAPI::ResourceController
936+
end
937+
885938
### RESOURCES
886939
class BaseResource < JSONAPI::Resource
887940
abstract
@@ -1795,6 +1848,30 @@ class UserResource < JSONAPI::Resource
17951848
end
17961849
end
17971850

1851+
class QuestionResource < JSONAPI::Resource
1852+
has_one :answer
1853+
has_one :respondent, polymorphic: true, class_name: "Respondent", foreign_key_on: :related
1854+
1855+
attributes :text
1856+
end
1857+
1858+
class AnswerResource < JSONAPI::Resource
1859+
has_one :question
1860+
has_one :respondent, polymorphic: true
1861+
end
1862+
1863+
class PatientResource < JSONAPI::Resource
1864+
attributes :name
1865+
end
1866+
1867+
class DoctorResource < JSONAPI::Resource
1868+
attributes :name
1869+
end
1870+
1871+
class RespondentResource < JSONAPI::Resource
1872+
abstract
1873+
end
1874+
17981875
### PORO Data - don't do this in a production app
17991876
$breed_data = BreedData.new
18001877
$breed_data.add(Breed.new(0, 'persian'))

test/fixtures/answers.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
answer1:
2+
id: 1
3+
question_id: 1
4+
text: Great thanks
5+
respondent_id: 1
6+
respondent_type: Patient
7+
answer2:
8+
id: 2
9+
question_id: 2
10+
text: Better than last week
11+
respondent_id: 1
12+
respondent_type: Doctor

test/fixtures/doctors.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
doctor1:
2+
id: 1
3+
name: Henry Jones Jr

test/fixtures/patients.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
patient1:
2+
id: 1
3+
name: Bob Smith

test/fixtures/questions.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
question1:
2+
id: 1
3+
text: How are you feeling today?
4+
question2:
5+
id: 2
6+
text: How does the patient look today?

test/test_helper.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ class CatResource < JSONAPI::Resource
255255
jsonapi_resources :books
256256
jsonapi_resources :authors
257257

258+
jsonapi_resources :questions
259+
jsonapi_resources :answers
260+
jsonapi_resources :doctors
261+
jsonapi_resources :patients
262+
258263
namespace :api do
259264
jsonapi_resources :boxes
260265

test/unit/serializer/polymorphic_serializer_test.rb

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ def setup
77
@pictures = Picture.all
88
@person = Person.find(1)
99

10+
@questions = Question.all
11+
1012
JSONAPI.configuration.json_key_format = :camelized_key
1113
JSONAPI.configuration.route_format = :camelized_route
1214
end
@@ -128,7 +130,7 @@ def test_sti_polymorphic_to_many_serialization
128130
)
129131
end
130132

131-
def test_polymorphic_to_one_serialization
133+
def test_polymorphic_belongs_to_serialization
132134
serialized_data = JSONAPI::ResourceSerializer.new(
133135
PictureResource,
134136
include: %w(imageable)
@@ -249,6 +251,99 @@ def test_polymorphic_to_one_serialization
249251
)
250252
end
251253

254+
def test_polymorphic_has_one_serialization
255+
serialized_data = JSONAPI::ResourceSerializer.new(
256+
QuestionResource,
257+
include: %w(respondent)
258+
).serialize_to_hash(@questions.map { |p| QuestionResource.new p, nil })
259+
260+
assert_hash_equals(
261+
{
262+
data: [
263+
{
264+
id: '1',
265+
type: 'questions',
266+
links: {
267+
self: '/questions/1'
268+
},
269+
attributes: {
270+
text: 'How are you feeling today?'
271+
},
272+
relationships: {
273+
answer: {
274+
links: {
275+
self: '/questions/1/relationships/answer',
276+
related: '/questions/1/answer'
277+
}
278+
},
279+
respondent: {
280+
links: {
281+
self: '/questions/1/relationships/respondent',
282+
related: '/questions/1/respondent'
283+
},
284+
data: {
285+
type: 'patients',
286+
id: '1'
287+
}
288+
}
289+
}
290+
},
291+
{
292+
id: '2',
293+
type: 'questions',
294+
links: {
295+
self: '/questions/2'
296+
},
297+
attributes: {
298+
text: 'How does the patient look today?'
299+
},
300+
relationships: {
301+
answer: {
302+
links: {
303+
self: '/questions/2/relationships/answer',
304+
related: '/questions/2/answer'
305+
}
306+
},
307+
respondent: {
308+
links: {
309+
self: '/questions/2/relationships/respondent',
310+
related: '/questions/2/respondent'
311+
},
312+
data: {
313+
type: 'doctors',
314+
id: '1'
315+
}
316+
}
317+
}
318+
}
319+
],
320+
:included => [
321+
{
322+
id: '1',
323+
type: 'patients',
324+
links: {
325+
self: '/patients/1'
326+
},
327+
attributes: {
328+
name: 'Bob Smith'
329+
},
330+
},
331+
{
332+
id: '1',
333+
type: 'doctors',
334+
links: {
335+
self: '/doctors/1'
336+
},
337+
attributes: {
338+
name: 'Henry Jones Jr'
339+
},
340+
}
341+
]
342+
},
343+
serialized_data
344+
)
345+
end
346+
252347
def test_polymorphic_get_related_resource
253348
get '/pictures/1/imageable', headers: { 'Accept' => JSONAPI::MEDIA_TYPE }
254349
serialized_data = JSON.parse(response.body)

0 commit comments

Comments
 (0)