lib/wolf_core/infrastructure/instance_application_serializer.rb



# frozen_string_literal: true

module WolfCore
  class InstanceApplicationSerializer
    attr_reader :options, :object

    def initialize(object: nil, collection: nil, options: nil)
      @object = object
      @collection = collection
      @options = options || {}
    end

    def self.attributes(*attrs)
      _attributes.concat(attrs.map(&:to_sym)).uniq!
    end

    def self.has_many(relationship, serializer:) # rubocop:disable Naming/PredicateName
      _relationships[relationship.to_sym] = serializer
    end

    def self.has_one(relationship, serializer:) # rubocop:disable Naming/PredicateName
      _relationships[relationship.to_sym] = serializer
    end

    def self._attributes
      @_attributes ||= []
    end

    def self._relationships
      @_relationships ||= {}
    end

    def serialize_all(collection: nil, options: nil)
      Result.try do
        @collection = collection if collection

        options ||= {}
        @options = @options.merge(options)

        results = @collection.map do |item|
          result = serialize(object: item, options: @options)
          result.raise_error
          result
        end
        Result.success(data: { serialized_collection: results.map { |result| result.data.serialized_object } })
      end
    end

    def serialize(object: nil, options: nil)
      Result.try do
        @object = object if object
        options ||= {}
        @options = @options.merge(options)

        serialized_object = as_json(object: @object).with_indifferent_access
        Result.success(data: { serialized_object: serialized_object })
      end
    end

    private

    def serialized_attributes
      self.class._attributes.each_with_object({}) do |attribute, hash|
        hash[attribute] = if respond_to?(attribute, true)
                            send(attribute)
                          elsif @object.respond_to?(attribute)
                            @object.public_send(attribute)
                          end
      end
    end

    def serialized_relationships
      self.class._relationships.each_with_object({}) do |(key, serializer_class), hash|
        serializer = serializer_class.new(options: @options)
        related_object = @object.public_send(key)
        hash[key] = if related_object.is_a?(Enumerable)
                      serializer.serialize_all(collection: related_object)
                    else
                      serializer.serialize(object: related_object)
                    end
      end
    end

    def as_json
      attributes = serialized_attributes
      relationships = serialized_relationships
      attributes.merge(relationships)
    end
  end
end