Skip to content

Instantly share code, notes, and snippets.

@twoism
Last active August 29, 2015 14:16
Show Gist options
  • Save twoism/4d850cdad5c346e238c1 to your computer and use it in GitHub Desktop.
Save twoism/4d850cdad5c346e238c1 to your computer and use it in GitHub Desktop.
# Serializer: Generic serialization library
#
# class Thing < Struct.new(:name)
# def other_things
# [Thing.new('a'), Thing.new('b')]
# end
# 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 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
serializable_hash.to_json
end
private
def attributes
self.class._attributes
end
def collections
self.class._collections
end
def members
self.class._members
end
def collected_pairs
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 do |member|
klass.new(member).to_h
end
end
end
end
require 'rails_helper'
require 'serializer'
module TestClasses
class A
include ActiveModel::Model
include ActiveModel::Serialization
attr_accessor :name
def things
[C.new(position: 1), C.new(position: 2)]
end
def single_thing
C.new(position: 1)
end
end
class B
include ActiveModel::Model
include ActiveModel::Serialization
attr_accessor :desc
end
class C
include ActiveModel::Model
include ActiveModel::Serialization
attr_accessor :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: 'Name for A') }
let(:b) { TestClasses::B.new(desc: '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) }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment