Skip to content

Instantly share code, notes, and snippets.

@twoism
Created May 13, 2015 19:06
Show Gist options
  • Save twoism/8ef0f7a3b1ebf6a8e76c to your computer and use it in GitHub Desktop.
Save twoism/8ef0f7a3b1ebf6a8e76c to your computer and use it in GitHub Desktop.
# Serializer: Generic serialization library
#
# class OtherThing < Struct.new(:name); end
#
# class Thing < Struct.new(:name)
# def other_things
# [OtherThing.new('a'), OtherThing.new('b')]
# end
# end
#
# class OtherThingSerializer < Serializer
# attributes :name
#
# has_many :other_things, ThingSerializer
# end
#
# class ThingSerializer < Serializer
# attributes :name
#
# has_many :other_things, ThingSerializer
# end
#
# thing = Thing.new('c')
# json = ThingSerializer.new(thing).to_json
# => "{\"name\":\"c\",\"other_things\":[{\"name\":\"a\"},{\"name\":\"b"}]}"
class Serializer < SimpleDelegator
attr_reader :subject
class << self
attr_accessor :_attributes
attr_accessor :_collections
attr_accessor :_members
end
# attributes: Field serializer
#
# class Thing < Serializer
# attributes :id, :name, :description
# end
def self.attributes(*attrs)
self._attributes = *attrs
end
# has_many: Collection serializer
#
# class Thing < Serializer
# has_many :things, ThingSerializer
# has_many :things, ThingSerializer, named: :other_things
# end
def self.has_many(collection_name, serializer_class, opts = {})
self._collections ||= []
self._collections << [collection_name, serializer_class, opts]
end
# has_one: Single object serializer
#
# class Thing < Serializer
# has_one :other_thing, ThingSerializer
# has_one :thing, ThingSerializer, named: :some_thing
# end
def self.has_one(member_name, serializer_class, opts = {})
self._members ||= []
self._members << [member_name, serializer_class, opts]
end
def self.collection(subjects)
subjects.map {|s| new(s) }
end
def initialize(subject)
@subject = subject
end
def serializable_hash
Hash[collected_pairs].tap do |hash|
serialize_collections(hash)
serialize_members(hash)
end
end
def to_h
serializable_hash
end
def to_json *args
serializable_hash.to_json
end
def as_json *args
serializable_hash
end
private
def attributes
self.class._attributes
end
def collections
self.class._collections
end
def members
self.class._members
end
def collected_pairs
return [] if attributes.nil?
attributes.map do |key|
self.respond_to?(key) ?
[key, self.__send__(key)] : [key, subject.__send__(key)]
end
end
def serialize_members(hash)
return if members.nil?
members.each do |name, klass, opts|
named = opts.fetch(:named, name)
hash[named] = klass.new(subject.__send__(name)).to_h
end
end
def serialize_collections(hash)
return if collections.nil?
collections.each do |name, klass, opts|
named = opts.fetch(:named, name)
hash[named] = subject.__send__(name).map { |m| klass.new(m).to_h }
end
end
end
require 'rails_helper'
require 'serializer'
module TestClasses
class A < Struct.new(:name)
def things
[C.new(1), C.new(2)]
end
def single_thing
C.new(1)
end
end
class B < Struct.new(:desc); end
class C < Struct.new(:position); end
class ABDelegate < SimpleDelegator
attr_reader :b
def initialize a, b
@b = b
super a
end
def desc
b.desc
end
end
class ThingSerializer < Serializer
attributes :position
end
class ABDelegateSerializer < Serializer
attributes :name, :desc, :local_method
has_many :things, ThingSerializer, named: :other_things
has_one :single_thing, ThingSerializer
def local_method
"hi!"
end
end
end
RSpec.describe TestClasses::ABDelegateSerializer do
let(:a) { TestClasses::A.new('Name for A') }
let(:b) { TestClasses::B.new('Desc for B') }
let(:delegate) { TestClasses::ABDelegate.new(a,b) }
let(:serializer) { TestClasses::ABDelegateSerializer.new(delegate) }
describe "delegate serialization" do
it "should have name from A" do
expect(delegate.name).to eql("Name for A")
end
it "should have desc from B" do
expect(delegate.desc).to eql("Desc for B")
end
subject(:hash) { serializer.serializable_hash }
it { is_expected.to have_key(:name) }
it { is_expected.to have_key(:desc) }
it { is_expected.to have_key(:other_things) }
it { is_expected.to have_key(:single_thing) }
it { is_expected.to have_key(:local_method) }
it "serializes without error" do
serializer.to_json
end
end
end
@ChuckJHardyGA
Copy link

Including ActiveModel::Serializers::JSON solved the issue for me. Posting here as rails-api/active_model_serializers#824 is locked.

class TestDecorator < SimpleDelegator
  include ActiveModel::Serializers::JSON

  def as_json(options = {})
    super(
      only: %i(id created_at),
      ...
    )
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment