lib/upgrow/action.rb in upgrow-0.0.2 vs lib/upgrow/action.rb in upgrow-0.0.3
- old
+ new
@@ -1,7 +1,9 @@
# frozen_string_literal: true
+require_relative 'result'
+
module Upgrow
# Actions represent the entry points to the app’s core logic. These objects
# coordinate workflows in order to get operations and activities done.
# Ultimately, Actions are the public interface of the app’s business layers.
#
@@ -21,34 +23,82 @@
#
# Actions respond to a single public method perform. Each Action defines its
# own set of required arguments for perform, as well what can be expected as
# the result of that method.
class Action
+ # Module to be prepended to subclasses of Action. This allows Action to
+ # decorate methods implemented by subclasses so they can have additional
+ # behaviour.
+ module Decorator
+ def perform(...)
+ catch(:failure) do
+ super
+ result
+ end
+ end
+ end
+
class << self
- attr_writer :result_class
+ attr_writer :exposures
- # Each Action class has its own Result class with the expected members to
- # be returned when the Action is called successfully.
+ # Each Action class has its own list of exposed instance variables to be
+ # included in the returned Result when the Action is called.
#
- # @return [Result] the Result class for this Action, as previously
- # defined, or a Result class with no members by default.
- def result_class
- @result_class ||= Result.new
+ # @return [Array] the list of exposed instance variables.
+ def exposures
+ @exposures ||= []
end
- # Sets the Action Result class with the given members.
+ # Sets the given instance variable names as exposed in the Result.
#
- # @param args [Array<Symbol>] the list of members for the Result class.
- def result(*args)
- @result_class = Result.new(*args)
+ # @param names [Array<Symbol>] the list of instance variable names to be
+ # exposed as part of the Result.
+ def expose(*names)
+ @exposures += names
end
+
+ private
+
+ def inherited(subclass)
+ super
+ subclass.exposures = exposures.dup
+ subclass.prepend(Decorator)
+ end
end
- # Instance method to return the Action's Result class. This method delegates
- # to the Action class's method (see #result_class).
+ # Throws a Result populated with the given errors.
#
- # @return [Result] the Result class for this Action.
- def result
- self.class.result_class
+ # @param errors [Array<Error>, ActiveModel::Errors] the errors object to
+ # be set as the Result errors. If an ActiveModel::Errors object is
+ # provided, it will be converted to an Array of Errors.
+ #
+ # @return [:failure, Result] the Result instance populated with the given
+ # errors.
+ def failure(*errors)
+ errors = errors.first if errors.first.respond_to?(:each)
+
+ if errors.respond_to?(:full_message)
+ errors = errors.map do |error|
+ Error.new(
+ attribute: error.attribute,
+ code: error.type,
+ message: error.full_message
+ )
+ end
+ end
+
+ throw(:failure, result(errors: errors))
+ end
+
+ private
+
+ def result(members = {})
+ result_class = Result.new(*self.class.exposures)
+
+ values = self.class.exposures.to_h do |name|
+ [name, instance_variable_get("@#{name}")]
+ end
+
+ result_class.new(**values.merge(members))
end
end
end