lib/wolf_core/domain/domain_object.rb



# frozen_string_literal: true

module WolfCore
  class DomainObject
    include ActiveModel::Model
    include ActiveModel::Dirty
    include ActiveModel::Callbacks

    define_model_callbacks :initialize, only: [:after, :before]
    define_model_callbacks :validation, only: [:after, :before]
    define_model_callbacks :attribute_change, only: [:after, :before]

    after_initialize do
      parse_attributes
    end

    after_attribute_change do
      parse_attributes
    end

    class_attribute :fields, default: []

    def self.define_attributes(*attributes)
      self.fields += attributes

      attr_accessor(*attributes)

      define_attribute_methods(*attributes)

      attributes.each do |attribute|
        define_method(:"#{attribute}=") do |value|
          send(:"#{attribute}_will_change!") unless value == send(attribute)
          run_callbacks :attribute_change do
            instance_variable_set(:"@#{attribute}", value)
          end
          value
        end
      end
    end

    def self.cast_all(objects)
      objects.map { |object| cast(object) }
    end

    def self.cast(object)
      if object.is_a?(self)
        object
      else
        result = self.create(object)
        success_key = extract_success_key
        result.success? ? result.data.send(success_key) : object
      end
    end

    def self.create_all(objects)
      success_key = extract_success_key
      final_array = []
      objects.each do |object|
        result = create(object)
        if result.failure?
          return result
        else
          final_array << result.data[success_key.to_sym]
        end
      end
      Result.success(data: { success_key.pluralize.to_s => final_array })
    end

    def self.create(params = {}, **kwargs)
      object = self.new(parse_params(params, **kwargs))
      object.reset_changes
      if object.valid?
        success_key = extract_success_key
        Result.success(data: { success_key => object })
      else
        Result.failure(error: { message: object.errors.full_messages.to_sentence })
      end
    rescue ActiveModel::UnknownAttributeError => e
      Result.failure(error: { message: e.message, error_type: e.class.to_s })
    end

    def self.extract_success_key
      self.to_s.split('::').last.underscore
    end

    def self.parse_params(params = {}, **kwargs)
      params = {} unless params.is_a?(Hash)
      params = params.merge(kwargs)
      (params.to_h).with_indifferent_access
    end

    def initialize(attributes = {})
      run_callbacks :initialize do
        super
      end
    end

    def valid?(*args)
      run_callbacks(:validation) do
        super
      end
      errors.empty?
    end

    def attributes
      instance_values.slice(*self.class.fields.map(&:to_s)).with_indifferent_access
    end

    def attributes=(attrs)
      run_callbacks :attribute_change do
        super
      end
    end

    def reset_changes
      self.changes_applied
    end

    def parse_attributes; end
  end
end