Skip to content

Commit

Permalink
Handle a heterogeneous collection via serializers option
Browse files Browse the repository at this point in the history
  • Loading branch information
dvandersluis committed Apr 30, 2019
1 parent a4ee4cb commit 07e02a0
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 5 deletions.
22 changes: 17 additions & 5 deletions lib/fast_jsonapi/object_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ def hash_for_one_record

return serializable_hash unless @resource

serializable_hash[:data] = self.class.record_hash(@resource, @fieldsets[self.class.record_type.to_sym], @params)
serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
serializer_class = serializer_for(@resource)
serializable_hash[:data] = serializer_class.record_hash(@resource, @fieldsets[serializer_class.record_type.to_sym], @params)
serializable_hash[:included] = serializer_class.get_included_records(@resource, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
serializable_hash
end

Expand All @@ -53,10 +54,11 @@ def hash_for_collection

data = []
included = []
fieldset = @fieldsets[self.class.record_type.to_sym]
@resource.each do |record|
data << self.class.record_hash(record, fieldset, @params)
included.concat self.class.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
serializer_class = serializer_for(record)
fieldset = @fieldsets[serializer_class.record_type.to_sym]
data << serializer_class.record_hash(record, fieldset, @params)
included.concat serializer_class.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
end

serializable_hash[:data] = data
Expand Down Expand Up @@ -89,6 +91,11 @@ def process_options(options)
@includes = options[:include].reject(&:blank?).map(&:to_sym)
self.class.validate_includes!(@includes)
end

@serializers = options.fetch(:serializers, {}).transform_keys do |key|
next key if key.is_a?(Class)
key.to_s.constantize
end
end

def deep_symbolize(collection)
Expand All @@ -109,6 +116,11 @@ def is_collection?(resource, force_is_collection = nil)
resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
end

def serializer_for(record)
return self.class if @serializers.blank?
@serializers[record.class] || raise(ArgumentError, "no serializer defined for #{record.class}")
end

class_methods do

def inherited(subclass)
Expand Down
114 changes: 114 additions & 0 deletions spec/lib/object_serializer_heterogeneous_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
require 'spec_helper'

describe FastJsonapi::ObjectSerializer do
class Vehicle
attr_accessor :id, :model, :year

def type
self.class.name.downcase
end
end

class Car < Vehicle
attr_accessor :purchased_at
end

class Bus < Vehicle
attr_accessor :passenger_count
end

class Truck < Vehicle
attr_accessor :load
end

class VehicleSerializer
include FastJsonapi::ObjectSerializer
attributes :model, :year
end

class CarSerializer < VehicleSerializer
attribute :purchased_at
end

class BusSerializer < VehicleSerializer
attribute :passenger_count
end

let(:car) do
car = Car.new
car.id = 1
car.model = 'Toyota Corolla'
car.year = 1987
car.purchased_at = Time.new(2018, 1, 1)
car
end

let(:bus) do
bus = Bus.new
bus.id = 2
bus.model = 'Nova Bus LFS'
bus.year = 2014
bus.passenger_count = 60
bus
end

let(:truck) do
truck = Truck.new
truck.id = 3
truck.model = 'Ford F150'
truck.year = 2000
truck
end

context 'when serializing a heterogenous collection' do
it 'should use the correct serializer for each item' do
vehicles = VehicleSerializer.new([car, bus], serializers: { Car: CarSerializer, Bus: BusSerializer }).to_hash
car, bus = vehicles[:data]

expect(car[:type]).to eq(:car)
expect(car[:attributes]).to eq(model: 'Toyota Corolla', year: 1987, purchased_at: Time.new(2018, 1, 1))

expect(bus[:type]).to eq(:bus)
expect(bus[:attributes]).to eq(model: 'Nova Bus LFS', year: 2014, passenger_count: 60)
end

context 'if there is no serializer given for the class' do
it 'should raise ArgumentError' do
expect { VehicleSerializer.new([truck], serializers: { Car: CarSerializer, Bus: BusSerializer }).to_hash }
.to raise_error(ArgumentError, 'no serializer defined for Truck')
end
end

context 'when given an empty set of serializers' do
it 'should use the serializer being called' do
data = VehicleSerializer.new([truck], serializers: {}).to_hash[:data][0]
expect(data[:type]).to eq(:vehicle)
expect(data[:attributes]).to eq(model: 'Ford F150', year: 2000)
end
end
end

context 'when serializing an arbitrary object' do
it 'should use the correct serializer' do
data = VehicleSerializer.new(car, serializers: { Car: CarSerializer, Bus: BusSerializer }).to_hash[:data]

expect(data[:type]).to eq(:car)
expect(data[:attributes]).to eq(model: 'Toyota Corolla', year: 1987, purchased_at: Time.new(2018, 1, 1))
end

context 'if there is no serializer given for the class' do
it 'should raise ArgumentError' do
expect { VehicleSerializer.new(truck, serializers: { Car: CarSerializer, Bus: BusSerializer }).to_hash }
.to raise_error(ArgumentError, 'no serializer defined for Truck')
end
end

context 'when given an empty set of serializers' do
it 'should use the serializer being called' do
data = VehicleSerializer.new(truck, serializers: {}).to_hash[:data]
expect(data[:type]).to eq(:vehicle)
expect(data[:attributes]).to eq(model: 'Ford F150', year: 2000)
end
end
end
end

0 comments on commit 07e02a0

Please sign in to comment.