module Primalize::JSONAPI

Constants

VERSION

Public Class Methods

[](type=nil, **options) click to toggle source
# File lib/primalize/jsonapi.rb, line 169
def self.[] type=nil, **options
  @serializer_map[type] ||= Class.new(Single) do
    @_type = type

    # This is useful for situations like this:
    #   class MySerializer < Primalize::JSONAPI[:movies]
    #   end
    define_singleton_method :inherited do |inheriting_class|
      JSONAPI[type] = inheriting_class
    end

    def self.type
      if @_type
        @_type
      else
        superclass.type
      end
    end

    def self.model_primalizer
      original_primalizer = self

      @model_primalizer ||= Class.new(Single) do
        def self.attributes **attrs
          attribute_primalizer.attributes attrs
        end

        define_singleton_method :to_s do
          "#{original_primalizer}.model_primalizer"
        end
        define_singleton_method(:name) { to_s }

        define_singleton_method :attribute_primalizer do
          @attribute_primalizer ||= Class.new(Single) do
            def initialize model, original:
              super model
              @original = original
            end

            def self.attributes(**attrs)
              super

              attrs.each do |attr, type|
                define_method attr do
                  if @original.respond_to? attr
                    @original.class.new(object).public_send attr
                  else
                    object.public_send attr
                  end
                end
              end
            end

            define_singleton_method :to_s do
              "#{original_primalizer}.model_primalizer.attribute_primalizer"
            end

            define_singleton_method(:name) { to_s }
          end
        end

        def self.relationships
          @relationships ||= Relationships.new
        end

        def self.has_many *args, &block
          relationships << HasMany.new(*args, &block)
        end

        def self.has_one *args, &block
          relationships << HasOne.new(*args, &block)
        end

        _attributes(
          id: string(&:to_s),
          type: string,
          attributes: object,
          relationships: optional(object),
        )

        attr_reader :cache

        def initialize model, original:, include: [], cache: Cache.new
          super model
          @original = original
          @include = include
          @cache = cache
        end

        define_method :type do
          original_primalizer.type.to_s
        end

        def attributes
          self.class.attribute_primalizer.new(object, original: @original).call
        end

        def call
          super.tap(&:compact!)
        end

        def relationships
          self.class.relationships.metadata object, cache: cache
        end
      end
    end

    def self.attributes **attrs
      model_primalizer.attributes attrs
    end

    _attributes data: array(primalize(model_primalizer))

    def self.has_many *args, &block
      model_primalizer.has_many *args, &block
    end

    def self.has_one *args, &block
      model_primalizer.has_one *args, &block
    end

    attr_reader :cache

    def initialize models, include: [], meta: nil, cache: Cache.new
      super models

      @include = include
      @meta = meta
      @cache = cache
    end

    def call
      super.tap do |value|
        if @meta
          value[:meta] = @meta
        end

        unless @include.to_a.empty?
          included = Set.new

          @include.each do |rel|
            Array(object).each do |model|
              primalizer = self.class.model_primalizer.relationships[rel]
              relationship = primalizer.call(model, cache: cache)

              case relationship
              when Array
                relationship.each do |object|
                  object[:data].each do |data|
                    data.delete :relationships
                    included << data
                  end
                end
              when Hash
                data = relationship[:data].first
                data.delete :relationships
                included << data
              end
            end
          end

          value[:included] = included.to_a
        end
      end
    end

    def data
      Array(object).map do |model|
        self.class.model_primalizer.new(model, original: self, include: @include, cache: cache).call
      end
    end
  end
end
[]=(type, serializer) click to toggle source
# File lib/primalize/jsonapi.rb, line 158
def self.[]= type, serializer
  @serializer_map[type] = serializer
end
attributes(**attrs) click to toggle source
# File lib/primalize/jsonapi.rb, line 192
def self.attributes **attrs
  attribute_primalizer.attributes attrs
end
fetch(type) click to toggle source
# File lib/primalize/jsonapi.rb, line 162
def self.fetch type
  @serializer_map.fetch type do
    raise ArgumentError,
      "No Primalize::JSONAPI primalizer defined for #{type.inspect}"
  end
end
has_many(*args, &block) click to toggle source
# File lib/primalize/jsonapi.rb, line 234
def self.has_many *args, &block
  relationships << HasMany.new(*args, &block)
end
has_one(*args, &block) click to toggle source
# File lib/primalize/jsonapi.rb, line 238
def self.has_one *args, &block
  relationships << HasOne.new(*args, &block)
end
model_primalizer() click to toggle source
# File lib/primalize/jsonapi.rb, line 188
def self.model_primalizer
  original_primalizer = self

  @model_primalizer ||= Class.new(Single) do
    def self.attributes **attrs
      attribute_primalizer.attributes attrs
    end

    define_singleton_method :to_s do
      "#{original_primalizer}.model_primalizer"
    end
    define_singleton_method(:name) { to_s }

    define_singleton_method :attribute_primalizer do
      @attribute_primalizer ||= Class.new(Single) do
        def initialize model, original:
          super model
          @original = original
        end

        def self.attributes(**attrs)
          super

          attrs.each do |attr, type|
            define_method attr do
              if @original.respond_to? attr
                @original.class.new(object).public_send attr
              else
                object.public_send attr
              end
            end
          end
        end

        define_singleton_method :to_s do
          "#{original_primalizer}.model_primalizer.attribute_primalizer"
        end

        define_singleton_method(:name) { to_s }
      end
    end

    def self.relationships
      @relationships ||= Relationships.new
    end

    def self.has_many *args, &block
      relationships << HasMany.new(*args, &block)
    end

    def self.has_one *args, &block
      relationships << HasOne.new(*args, &block)
    end

    _attributes(
      id: string(&:to_s),
      type: string,
      attributes: object,
      relationships: optional(object),
    )

    attr_reader :cache

    def initialize model, original:, include: [], cache: Cache.new
      super model
      @original = original
      @include = include
      @cache = cache
    end

    define_method :type do
      original_primalizer.type.to_s
    end

    def attributes
      self.class.attribute_primalizer.new(object, original: @original).call
    end

    def call
      super.tap(&:compact!)
    end

    def relationships
      self.class.relationships.metadata object, cache: cache
    end
  end
end
new(model, original: super model) click to toggle source
# File lib/primalize/jsonapi.rb, line 203
def initialize model, original:
  super model
  @original = original
end
relationships() click to toggle source
# File lib/primalize/jsonapi.rb, line 230
def self.relationships
  @relationships ||= Relationships.new
end
serialize(include: [], **models) click to toggle source
# File lib/primalize/jsonapi.rb, line 9
def self.serialize include: [], **models
  if models.one?
    type = models.each_key.first
    cache = Cache.new
    Array(models[type])
      .map { |model| self[type].new(model, include: include, cache: cache).call }
      .reduce({ data: [] }) { |result, hash|
        result.merge!(hash) do |key, left, right|
          case key
          when :data
            left + right
          when :included
            (left + right).tap(&:uniq!)
          else
            left.merge!(right)
          end
        end
      }
  else
    raise ArgumentError, "cannot supply more than one resource type"
  end
end
type() click to toggle source
# File lib/primalize/jsonapi.rb, line 180
def self.type
  if @_type
    @_type
  else
    superclass.type
  end
end

Public Instance Methods

attributes() click to toggle source
# File lib/primalize/jsonapi.rb, line 262
def attributes
  self.class.attribute_primalizer.new(object, original: @original).call
end
call() click to toggle source
Calls superclass method
# File lib/primalize/jsonapi.rb, line 266
def call
  super.tap(&:compact!)
end
data() click to toggle source
# File lib/primalize/jsonapi.rb, line 335
def data
  Array(object).map do |model|
    self.class.model_primalizer.new(model, original: self, include: @include, cache: cache).call
  end
end
relationships() click to toggle source
# File lib/primalize/jsonapi.rb, line 270
def relationships
  self.class.relationships.metadata object, cache: cache
end