yaks/lib/yaks/reader/json_api.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module Yaks
  module Reader
    class JsonAPI
      def call(parsed_json, _env = {})
        included = parsed_json['included'].nil? ? {} : parsed_json['included'].dup

        if parsed_json['data'].is_a?(Array)
          CollectionResource.new(
            attributes: parsed_json['meta'].nil? ? nil : {meta: parsed_json['meta']},
            members: parsed_json['data'].map { |data| call('data' => data, 'included' => included) }
          )
        else
          attributes = parsed_json['data'].dup
          links = attributes.delete('links') || {}
          relationships = attributes.delete('relationships') || {}
          type = attributes.delete('type')
          attributes.merge!(attributes.delete('attributes') || {})

          embedded   = convert_embedded(Hash[relationships], included)
          links      = convert_links(Hash[links])

          Resource.new(
            type: Util.singularize(type),
            attributes: Util.symbolize_keys(attributes),
            subresources: embedded,
            links: links
          )
        end
      end

      def convert_embedded(relationships, included)
        relationships.flat_map do |rel, relationship|
          # A Link doesn't have to contain a `data` member.
          # It can contain URLs instead, or as well, but we are only worried about *embedded* links here.
          data = relationship['data']
          # Resource data MUST be represented as one of the following:
          #
          # * `null` for empty to-one relationships.
          # * a "resource identifier object" for non-empty to-one relationships.
          # * an empty array ([]) for empty to-many relationships.
          # * an array of resource identifier objects for non-empty to-many relationships.
          if data.nil?
            NullResource.new(rels: [rel])
          elsif data.is_a? Array
            if data.empty?
              NullResource.new(collection: true, rels: [rel])
            else
              CollectionResource.new(
                members: data.map { |link|
                  data = included.find{ |item| (item['id'] == link['id']) && (item['type'] == link['type']) }
                  call('data' => data, 'included' => included)
                },
                rels: [rel]
              )
            end
          else
            data = included.find{ |item| (item['id'] == data['id']) && (item['type'] == data['type']) }
            call('data' => data, 'included' => included).with(rels: [rel])
          end
        end.compact
      end

      def convert_links(links)
        links.map do |rel, link|
          Resource::Link.new(rel: rel.to_sym, uri: link)
        end
      end
    end
  end
end