krim/light_serializer

View on GitHub
lib/light_serializer/hashed_object.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

module LightSerializer
  class HashedObject
    include Helpers::WithCustomRoot

    ACTIVE_RECORD_ENABLED = defined?(ActiveRecord::Relation)

    UnknownAttribute = Class.new(RuntimeError)

    attr_reader :raw_object, :serializer

    def self.get(raw_object, serializer)
      new(raw_object, serializer).get
    end

    def initialize(raw_object, serializer)
      @raw_object = raw_object
      @serializer = serializer
    end

    def get
      with_custom_root(serializer.root) { transform_to_hash(raw_object) }
    end

    private

    def transform_to_hash(object)
      serializer.class.attributes.each_with_object({}) do |attribute, result|
        obtain_values(attribute, object, result)
      end
    end

    def obtain_values(attribute, object, result)
      if attribute.is_a?(Hash)
        values_from_nested_resource(attribute, object, result)
      else
        values_from_current_resource(attribute, object, result)
      end
    end

    def values_from_nested_resource(attributes, object, result)
      attributes.each do |attribute_name, nested_serializer|
        value = obtain_value(object, attribute_name, nested_serializer)
        result[attribute_name] = hashed_nested_resource(value, nested_serializer)
      end
    end

    def hashed_nested_resource(value, nested_serializer)
      return hashed_entity(value, nested_serializer) unless can_be_enumerated?(value)

      value.each_with_object(nested_serializer).map { |entity, serializer| hashed_entity(entity, serializer) }
    end

    def can_be_enumerated?(value)
      value.is_a?(Enumerable) || active_record_relation?(value)
    end

    def active_record_relation?(object)
      ACTIVE_RECORD_ENABLED && object.is_a?(ActiveRecord::Relation)
    end

    def hashed_entity(entity, nested_serializer)
      nested_serializer.new(entity, context: serializer.context).to_hash
    end

    def values_from_current_resource(attribute, object, result)
      value = obtain_value(object, attribute)

      result[attribute] = if value.is_a?(Array)
        values_from_for_array(attribute, value, result)
      else
        value
      end
    end

    # :reek:ManualDispatch
    def obtain_value(object, attribute, nested_serializer = nil)
      if serializer.respond_to?(attribute)
        serializer.public_send(attribute)
      elsif object.respond_to?(attribute)
        object.public_send(attribute)
      elsif raw_object != object
        object
      elsif !nested_serializer
        raise UnknownAttribute, "Unknown attribute: #{attribute}"
      end
    end

    def values_from_for_array(attribute, value, result)
      value.map { |entity| entity.is_a?(Hash) ? entity : obtain_values(attribute, entity, result) }
    end
  end
end