lib/munson/response_mapper.rb
module Munson
# Maps JSONAPI Responses to ruby objects.
#
# @note
# When a JSONAPI collection (data: <Array>) is received it maps the response
# into multiple JSONAPI resource objects (data: <Hash>) and passes each to the #initialize_resource method
# so that each resource can act independently of the collection. JSONAPI collection are wrapped in a Munson::Collection
# which will also contain metadata from the request
#
# @example Mapping an unregistered JSONAPI collection response
# json = {
# data: [
# {id: 1, type: :cats, attributes: {name: 'Gorbypuff'}},
# {id: 1, type: :cats, attributes: {name: 'Grumpy Cat'}}
# ]
# }
#
# mapper = ResponseMapper.new(json)
# mapper.collection #=>
# Munson::Collection([
# {data: {id: 1, type: :cats, attributes: {name: 'Gorbypuff'}}},
# {data: {id: 1, type: :cats, attributes: {name: 'Grumpy Cat'}}
# ])
#
# @example Mapping a registered JSONAPI collection response
# json = {
# data: [
# {id: 1, type: :cats, attributes: {name: 'Gorbypuff'}},
# {id: 1, type: :cats, attributes: {name: 'Grumpy Cat'}}
# ]
# }
# class Cat
# #... munson config
# def self.munson_initializer(resource)
# Cat.new(resource)
# end
#
# def new(attribs)
# #do what you want
# end
# end
# Munson.register_type(:cats, Cat)
#
# mapper.collection #=> Munson::Collection([cat1, cat2])
#
# ResourceMapper maps responses in 3 ways:
# @example Mapping a Munson::Resource
#
# @example Mapping a registered type
#
# @example Mapping an unregistered type
#
class ResponseMapper
# @param [Hash] response_body jsonapi formatted hash
def initialize(response_body)
@body = response_body
end
# Moved top level keys to the collection
# * errors: an array of error objects
# * meta: a meta object that contains non-standard meta-information.
# * jsonapi: an object describing the server’s implementation
# * links: a links object related to the primary data.
def collection
if errors?
raise Exception, "IMPLEMENT ERRORS JERK"
elsif collection?
# Make each item in :data its own document, stick included into that document
records = @body[:data].reduce([]) do |agg, resource|
json = { data: resource }
json[:included] = @body[:included] if @body[:included]
agg << json
agg
end
Collection.new(records.map{ |datum| Munson.factory(datum) },
meta: @body[:meta],
jsonapi: @body[:jsonapi],
links: @body[:links]
)
else
raise Munson::Error, "Called #collection, but response was a single resource. Use ResponseMapper#resource"
end
end
def resource
if errors?
raise Exception, "IMPLEMENT ERRORS JERK"
elsif resource?
Munson.factory(@body)
else
raise Munson::Error, "Called #resource, but response was a collection of resources. Use ResponseMapper#collection"
end
end
def jsonapi_resources
data = collection? ? @body[:data] : [@body[:data]]
included = @body[:included] || []
(data + included)
end
private def errors?
@body[:errors].is_a?(Array)
end
private def resource?
@body[:data].is_a?(Hash)
end
private def collection?
@body[:data].is_a?(Array)
end
end
end