# frozen_string_literal: true
require "hanami/utils/basic_object"
require "hanami/utils/class_attribute"
require "hanami/utils/hash"
module Hanami
# Hanami Interactor
#
# @since 0.3.5
module Interactor
# Result of an operation
#
# @since 0.3.5
class Result < Utils::BasicObject
# Concrete methods
#
# @since 0.3.5
# @api private
#
# @see Hanami::Interactor::Result#respond_to_missing?
METHODS = ::Hash[initialize: true,
success?: true,
successful?: true,
failure?: true,
fail!: true,
prepare!: true,
errors: true,
error: true].freeze
# Initialize a new result
#
# @param payload [Hash] a payload to carry on
#
# @return [Hanami::Interactor::Result]
#
# @since 0.3.5
# @api private
def initialize(payload = {})
@payload = payload
@errors = []
@success = true
end
# Checks if the current status is successful
#
# @return [TrueClass,FalseClass] the result of the check
#
# @since 0.8.1
def successful?
@success && errors.empty?
end
# @since 0.3.5
alias_method :success?, :successful?
# Checks if the current status is not successful
#
# @return [TrueClass,FalseClass] the result of the check
#
# @since 0.9.2
def failure?
!successful?
end
# Forces the status to be a failure
#
# @since 0.3.5
def fail!
@success = false
end
# Returns all the errors collected during an operation
#
# @return [Array] the errors
#
# @since 0.3.5
#
# @see Hanami::Interactor::Result#error
# @see Hanami::Interactor#call
# @see Hanami::Interactor#error
# @see Hanami::Interactor#error!
def errors
@errors.dup
end
# @since 0.5.0
# @api private
def add_error(*errors)
@errors << errors
@errors.flatten!
nil
end
# Returns the first errors collected during an operation
#
# @return [nil,String] the error, if present
#
# @since 0.3.5
#
# @see Hanami::Interactor::Result#errors
# @see Hanami::Interactor#call
# @see Hanami::Interactor#error
# @see Hanami::Interactor#error!
def error
errors.first
end
# Prepares the result before to be returned
#
# @param payload [Hash] an updated payload
#
# @since 0.3.5
# @api private
def prepare!(payload)
@payload.merge!(payload)
self
end
protected
# @since 0.3.5
# @api private
def method_missing(method_name, *)
@payload.fetch(method_name) { super }
end
# @since 0.3.5
# @api private
def respond_to_missing?(method_name, _include_all)
method_name = method_name.to_sym
METHODS[method_name] || @payload.key?(method_name)
end
# @since 0.3.5
# @api private
def __inspect
" @success=#{@success} @payload=#{@payload.inspect}"
end
end
# Override for Module#included.
#
# @since 0.3.5
# @api private
def self.included(base)
super
base.class_eval do
extend ClassMethods
end
end
# Interactor legacy interface
#
# @since 0.3.5
module LegacyInterface
# Initialize an interactor
#
# It accepts arbitrary number of arguments.
# Developers can override it.
#
# @param args [Array