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, 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(payload)
@errors = []
@success = true
end
# Check if the current status is successful
#
# @return [TrueClass,FalseClass] the result of the check
#
# @since 0.3.5
def success?
@success && errors.empty?
end
# Force 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
# Prepare the result before to be returned
#
# @param payload [Hash] an updated payload
#
# @since 0.3.5
# @api private
def prepare!(payload)
@payload.merge!(_payload(payload))
self
end
protected
# @since 0.3.5
# @api private
def method_missing(m, *)
@payload.fetch(m) { 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 _payload(payload)
Utils::Hash.new(payload).symbolize!
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
prepend Interface
extend ClassMethods
end
end
# Interactor interface
#
# @since 0.3.5
module Interface
# Initialize an interactor
#
# It accepts arbitrary number of arguments.
# Developers can override it.
#
# @param args [Array