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