Skip to content

Commit 1ad20d6

Browse files
committed
Merge branch 'upstream-dev' into add-links-option-to-relationship-serializer
2 parents 85b41c4 + 11b5255 commit 1ad20d6

7 files changed

Lines changed: 160 additions & 17 deletions

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ end
221221
```
222222

223223
### Links Per Object
224-
Links are defined in FastJsonapi using the `link` method. By default, link are read directly from the model property of the same name.In this example, `public_url` is expected to be a property of the object being serialized.
224+
Links are defined in FastJsonapi using the `link` method. By default, link are read directly from the model property of the same name. In this example, `public_url` is expected to be a property of the object being serialized.
225225

226226
You can configure the method to use on the object for example a link with key `self` will get set to the value returned by a method called `url` on the movie object.
227227

@@ -449,6 +449,7 @@ serializer.serializable_hash
449449
Option | Purpose | Example
450450
------------ | ------------- | -------------
451451
set_type | Type name of Object | ```set_type :movie ```
452+
key | Key of Object | ```belongs_to :owner, key: :user ```
452453
set_id | ID of Object | ```set_id :owner_id ```
453454
cache_options | Hash to enable caching and set cache length | ```cache_options enabled: true, cache_length: 12.hours, race_condition_ttl: 10.seconds```
454455
id_method_name | Set custom method name to get ID of an object | ```has_many :locations, id_method_name: :place_ids ```

lib/fast_jsonapi/object_serializer.rb

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22

3-
require 'active_support/core_ext/object'
3+
require 'active_support/json'
44
require 'active_support/concern'
55
require 'active_support/inflector'
66
require 'fast_jsonapi/attribute'
@@ -65,7 +65,7 @@ def hash_for_collection
6565
end
6666

6767
def serialized_json
68-
self.class.to_json(serializable_hash)
68+
ActiveSupport::JSON.encode(serializable_hash)
6969
end
7070

7171
private
@@ -143,7 +143,11 @@ def set_key_transform(transform_name)
143143
self.transform_method = mapping[transform_name.to_sym]
144144

145145
# ensure that the record type is correctly transformed
146-
set_type(reflected_record_type) if reflected_record_type
146+
if record_type
147+
set_type(record_type)
148+
elsif reflected_record_type
149+
set_type(reflected_record_type)
150+
end
147151
end
148152

149153
def run_key_transform(input)
@@ -250,6 +254,7 @@ def create_relationship(base_key, relationship_type, options, block)
250254
cached: options[:cached],
251255
polymorphic: fetch_polymorphic_option(options),
252256
conditional_proc: options[:if],
257+
transform_method: @transform_method,
253258
links: options[:links],
254259
lazy_load_data: options[:lazy_load_data]
255260
)
@@ -294,10 +299,10 @@ def validate_includes!(includes)
294299
includes.detect do |include_item|
295300
klass = self
296301
parse_include_item(include_item).each do |parsed_include|
297-
relationship_to_include = klass.relationships_to_serialize[parsed_include]
302+
relationships_to_serialize = klass.relationships_to_serialize || {}
303+
relationship_to_include = relationships_to_serialize[parsed_include]
298304
raise ArgumentError, "#{parsed_include} is not specified as a relationship on #{klass.name}" unless relationship_to_include
299-
raise NotImplementedError if relationship_to_include.polymorphic.is_a?(Hash)
300-
klass = relationship_to_include.serializer.to_s.constantize
305+
klass = relationship_to_include.serializer.to_s.constantize unless relationship_to_include.polymorphic.is_a?(Hash)
301306
end
302307
end
303308
end

lib/fast_jsonapi/relationship.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module FastJsonapi
22
class Relationship
3-
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc, :links, :lazy_load_data
3+
attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc, :transform_method, :links, :lazy_load_data
44

55
def initialize(
66
key:,
@@ -14,6 +14,7 @@ def initialize(
1414
cached: false,
1515
polymorphic:,
1616
conditional_proc:,
17+
transform_method:,
1718
links:,
1819
lazy_load_data: false
1920
)
@@ -28,6 +29,7 @@ def initialize(
2829
@cached = cached
2930
@polymorphic = polymorphic
3031
@conditional_proc = conditional_proc
32+
@transform_method = transform_method
3133
@links = links || {}
3234
@lazy_load_data = lazy_load_data
3335
end
@@ -75,7 +77,7 @@ def ids_hash_from_record_and_relationship(record, params = {})
7577

7678
def id_hash_from_record(record, record_types)
7779
# memoize the record type within the record_types dictionary, then assigning to record_type:
78-
associated_record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
80+
associated_record_type = record_types[record.class] ||= run_key_transform(record.class.name.demodulize.underscore)
7981
id_hash(record.id, associated_record_type)
8082
end
8183

@@ -103,8 +105,16 @@ def fetch_id(record, params)
103105

104106
def add_links_hash(record, params, output_hash)
105107
output_hash[key][:links] = links.each_with_object({}) do |(key, method), hash|
106-
Link.new(key: key, method: method).serialize(record, params, hash)
108+
Link.new(key: key, method: method).serialize(record, params, hash)\
109+
end
110+
end
111+
112+
def run_key_transform(input)
113+
if self.transform_method.present?
114+
input.to_s.send(*self.transform_method).to_sym
115+
else
116+
input.to_sym
107117
end
108118
end
109119
end
110-
end
120+
end

lib/fast_jsonapi/serialization_core.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,28 @@ def get_included_records(record, includes_list, known_included_objects, fieldset
119119
next unless relationships_to_serialize && relationships_to_serialize[item]
120120
relationship_item = relationships_to_serialize[item]
121121
next unless relationship_item.include_relationship?(record, params)
122-
raise NotImplementedError if relationship_item.polymorphic.is_a?(Hash)
123-
record_type = relationship_item.record_type
124-
serializer = relationship_item.serializer.to_s.constantize
122+
unless relationship_item.polymorphic.is_a?(Hash)
123+
record_type = relationship_item.record_type
124+
serializer = relationship_item.serializer.to_s.constantize
125+
end
125126
relationship_type = relationship_item.relationship_type
126127

127128
included_objects = relationship_item.fetch_associated_object(record, params)
128129
next if included_objects.blank?
129130
included_objects = [included_objects] unless relationship_type == :has_many
130131

131132
included_objects.each do |inc_obj|
133+
if relationship_item.polymorphic.is_a?(Hash)
134+
record_type = inc_obj.class.name.demodulize.underscore
135+
serializer = self.compute_serializer_name(inc_obj.class.name.demodulize.to_sym).to_s.constantize
136+
end
137+
132138
if remaining_items(items)
133139
serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets, params)
134140
included_records.concat(serializer_records) unless serializer_records.empty?
135141
end
136142

137-
code = "#{record_type}_#{inc_obj.id}"
143+
code = "#{record_type}_#{serializer.id_from_record(inc_obj)}"
138144
next if known_included_objects.key?(code)
139145

140146
known_included_objects[code] = inc_obj

spec/lib/object_serializer_class_methods_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,4 +411,34 @@ def year_since_release_calculator(release_year)
411411
it_behaves_like 'returning key transformed hash', :movie_type, :underscore_movie_type, :release_year
412412
end
413413
end
414+
415+
describe '#set_key_transform after #set_type' do
416+
subject(:serializable_hash) { MovieSerializer.new(movie).serializable_hash }
417+
418+
before do
419+
MovieSerializer.set_type type_name
420+
MovieSerializer.set_key_transform :camel
421+
end
422+
423+
after do
424+
MovieSerializer.transform_method = nil
425+
MovieSerializer.set_type :movie
426+
end
427+
428+
context 'when sets singular type name' do
429+
let(:type_name) { :film }
430+
431+
it 'returns correct hash which type equals transformed set_type value' do
432+
expect(serializable_hash[:data][:type]).to eq :Film
433+
end
434+
end
435+
436+
context 'when sets plural type name' do
437+
let(:type_name) { :films }
438+
439+
it 'returns correct hash which type equals transformed set_type value' do
440+
expect(serializable_hash[:data][:type]).to eq :Films
441+
end
442+
end
443+
end
414444
end
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
require 'spec_helper'
2+
3+
describe FastJsonapi::ObjectSerializer do
4+
class List
5+
attr_accessor :id, :name, :items
6+
end
7+
8+
class ChecklistItem
9+
attr_accessor :id, :name
10+
end
11+
12+
class Car
13+
attr_accessor :id, :model, :year
14+
end
15+
16+
class ListSerializer
17+
include FastJsonapi::ObjectSerializer
18+
set_type :list
19+
attributes :name
20+
set_key_transform :dash
21+
has_many :items, polymorphic: true
22+
end
23+
24+
let(:car) do
25+
car = Car.new
26+
car.id = 1
27+
car.model = 'Toyota Corolla'
28+
car.year = 1987
29+
car
30+
end
31+
32+
let(:checklist_item) do
33+
checklist_item = ChecklistItem.new
34+
checklist_item.id = 2
35+
checklist_item.name = 'Do this action!'
36+
checklist_item
37+
end
38+
39+
context 'when serializing id and type of polymorphic relationships' do
40+
it 'should return correct type when transform_method is specified' do
41+
list = List.new
42+
list.id = 1
43+
list.items = [checklist_item, car]
44+
list_hash = ListSerializer.new(list).to_hash
45+
record_type = list_hash[:data][:relationships][:items][:data][0][:type]
46+
expect(record_type).to eq 'checklist-item'.to_sym
47+
record_type = list_hash[:data][:relationships][:items][:data][1][:type]
48+
expect(record_type).to eq 'car'.to_sym
49+
end
50+
end
51+
end

spec/lib/object_serializer_spec.rb

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,32 @@
158158
end
159159
end
160160

161+
context 'id attribute is the same for actors and not a primary key' do
162+
before do
163+
ActorSerializer.set_id :email
164+
movie.actor_ids = [0, 0, 0]
165+
class << movie
166+
def actors
167+
super.each_with_index { |actor, i| actor.email = "actor#{i}@email.com" }
168+
end
169+
end
170+
end
171+
172+
after { ActorSerializer.set_id nil }
173+
174+
let(:options) { { include: ['actors'] } }
175+
subject { MovieSerializer.new(movie, options).serializable_hash }
176+
177+
it 'returns all actors in includes' do
178+
179+
expect(
180+
subject[:included].select { |i| i[:type] == :actor }.map { |i| i[:id] }
181+
).to eq(
182+
movie.actors.map(&:email)
183+
)
184+
end
185+
end
186+
161187
context 'nested includes' do
162188
it 'has_many to belongs_to: returns correct nested includes when serializable_hash is called' do
163189
# 3 actors, 3 agencies
@@ -252,10 +278,24 @@ def advertising_campaign
252278
end
253279
end
254280

255-
it 'polymorphic throws an error that polymorphic is not supported' do
281+
it 'polymorphic has_many: returns correct nested includes when serializable_hash is called' do
256282
options = {}
257283
options[:include] = [:groupees]
258-
expect(-> { GroupSerializer.new([group], options)}).to raise_error(NotImplementedError)
284+
285+
serializable_hash = GroupSerializer.new([group], options).serializable_hash
286+
287+
persons_serialized = serializable_hash[:included].find_all { |included| included[:type] == :person }.map { |included| included[:id].to_i }
288+
groups_serialized = serializable_hash[:included].find_all { |included| included[:type] == :group }.map { |included| included[:id].to_i }
289+
290+
persons = group.groupees.find_all { |groupee| groupee.is_a?(Person) }
291+
persons.each do |person|
292+
expect(persons_serialized).to include(person.id)
293+
end
294+
295+
groups = group.groupees.find_all { |groupee| groupee.is_a?(Group) }
296+
groups.each do |group|
297+
expect(groups_serialized).to include(group.id)
298+
end
259299
end
260300
end
261301

0 commit comments

Comments
 (0)