Last active
September 22, 2017 20:49
-
-
Save Samsinite/a07c1ecb73d6f7c529758c2f82b9a7f0 to your computer and use it in GitHub Desktop.
Serializers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ApiMe | |
module Serializers | |
class Base | |
attr_reader :object | |
def self.serializer_meta | |
@serializer_meta ||= SerializerMeta.new | |
end | |
def self.has_many(name, options = {}) | |
descriptor = HasManyDescriptor.new(name, options) | |
serializer_meta.add_has_many(descriptor) | |
end | |
def self.attributes(*attrs) | |
attrs.each { |attr| serializer_meta.add_attribute(attr) } | |
end | |
def initialize(object) | |
@object = object | |
end | |
def batch_relations | |
batch_has_many | |
self | |
end | |
def batch_has_many | |
@lazy_has_many_relations = self.class.serializer_meta.has_many_relations.map do |relation| | |
{ relation.name => relation.batch(self.object) } | |
end | |
end | |
def hash_has_many_relations | |
@lazy_has_many_relations.reduce({}) { |relations_hash, lazy_hash| | |
relations_hash.merge!(lazy_hash) | |
} | |
end | |
def hash_attributes | |
self.class.serializer_meta.attributes.reduce({}) do |hash, attr| | |
if self.respond_to?(attr) | |
hash.merge!(attr => self.send(attr)) | |
else | |
hash.merge!(attr => object.send(attr)) | |
end | |
end | |
end | |
def serializable_hash | |
hash_has_many_relations.merge!(hash_attributes) | |
end | |
def as_json | |
serializable_hash.as_json | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CollectionSerializer | |
attr_reader :collection | |
def initialize(collection) | |
@collection = collection | |
end | |
def get_serializer(resource) | |
if resource.respond_to?(:serializer_class) | |
resource.serializer_class | |
else | |
resource_class = resource.class == Class ? resource : resource.class | |
"#{resource_class}Serializer".constantize | |
end | |
end | |
def build_serializer(resource) | |
get_serializer(resource).new(resource) | |
end | |
def serializable_hash | |
collection.map { |r| build_serializer(r).batch_relations.serializable_hash } | |
end | |
def as_json | |
serializable_hash.as_json | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Example use of code above. In practice, `collection_serializer.as_json` would | |
# automatically get called by the Rails hook. It would also need to inject the `user` | |
# because serializers can do different serialization based off of user access. It | |
# would also probably go through an adapter that would call `as_json` on the collection | |
# so that it could potentailly pass in includes or similar fields to limit serialization | |
# based off of only what the REST request wants. | |
collection = Post.all | |
collection_serializer = CollectionSerializer.new(collection) | |
puts collection_serializer.as_json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class HasManyDescriptor | |
attr_reader :name, :table_name, :inverse_of, :class_name | |
def initialize(name, table_name: nil, inverse_of: nil, class_name: nil) | |
@name = name | |
@class_name = class_name || name.to_s.classify.constantize | |
@inverse_of = inverse_of | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'batch-loader' | |
class HasManyRelation | |
attr_reader :descriptor | |
def initialize(descriptor) | |
@descriptor = descriptor | |
end | |
def relation_class | |
descriptor.class_name | |
end | |
def table_name | |
relation_class.table_name | |
end | |
def name | |
descriptor.name | |
end | |
def batch(object) | |
inverse_name = descriptor.inverse_of || object.class.table_name | |
BatchLoader.for(object.id).batch do |relation_ids, loader| | |
relation_class.joins(inverse_name.to_sym) | |
.where(id: relation_ids) | |
.pluck("#{object.class.table_name}.id", "#{table_name}.id") | |
.group_by { |set| set[0] } | |
.each_pair do |key, value| | |
loader.call(key, value.map { |set| set[1] }) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# An Example serializer | |
class PostSerializer < ApiMe::Serializers::Base | |
has_many :tags | |
attributes :name | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SerializerMeta | |
def initialize | |
@has_many_relationships = [] | |
@attributes = [] | |
end | |
def add_has_many(descriptor) | |
@has_many_relationships.push(HasManyRelation.new(descriptor)) | |
end | |
def add_attribute(name) | |
@attributes.push(name) | |
end | |
def has_many_relations | |
@has_many_relationships.each | |
end | |
def attributes | |
@attributes.each | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment