coryodaniel/munson

View on GitHub
lib/munson/response_mapper.rb

Summary

Maintainability
A
25 mins
Test Coverage
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