lib/the_help/service.rb in the_help-1.6.2 vs lib/the_help/service.rb in the_help-2.0.0

- old
+ new

@@ -13,89 +13,70 @@ # class CreateNewUserAccount < TheHelp::Service # input :user # input :send_welcome_message, default: true # # authorization_policy do - # authorized = false - # call_service(Authorize, permission: :admin_users, - # allowed: ->() { authorized = true }) - # authorized + # call_service(Authorize, permission: :admin_users).success? # end # # main do # # do something to create the user account # if send_welcome_message - # call_service(SendWelcomeMessage, user: user, - # success: callback(:message_sent)) + # call_service(SendWelcomeMessage, user: user) do |result| + # callback(:message_sent) if result.success? + # end # end + # result.success # end # # callback(:message_sent) do |message| # # do something really important with `message`, I'm sure # end # end # # class Authorize < TheHelp::Service # input :permission - # input :allowed # # authorization_policy allow_all: true # # main do # if user_has_permission? - # allowed.call + # result.success + # else + # result.error 'Permission Denied' # end # end # end # # class SendWelcomeMessage < TheHelp::Service # input :user - # input :success, default: ->(message) { } # # main do # message = 'Hello, world!' # # do something with message... - # run_callback(success, message) + # result.success message # end # end # # CreateNewUserAccount.(context: current_user, user: new_user_object) # # @example Calling services with a block # - # # Calling a service with a block when the service is not designed to - # # receive one will result in an exception being raised + # # The service result will be yielded to the block if a block is present. # - # class DoesNotTakeBlock < TheHelp::Service - # authorization_policy allow_all: true - # - # main do - # # whatever - # end - # end - # - # DoesNotTakeBlock.call { |result| true } # raises TheHelp::NoResultError - # - # # However, if the service *is* designed to receive a block (by explicitly - # # assigning to the internal `#result` attribute in the main routine), the - # # result will be yielded to the block if a block is present. - # # class CanTakeABlock < TheHelp::Service # authorization_policy allow_all: true # # main do - # self.result = :the_service_result + # result.success :the_service_result # end # end # # service_result = nil # - # CanTakeABlock.call() # works just fine - # service_result - # #=> nil # but obviously the result is just discarded + # CanTakeABlock.call { |result| service_result = result.value } # - # CanTakeABlock.call { |result| service_result = result } # service_result # #=> :the_service_result # # @note See README section "Running Callbacks" class Service @@ -124,13 +105,11 @@ # Convenience method to instantiate the service and immediately call it # # Any arguments are passed to #initialize def call(*args, &block) - result = new(*args).call(&block) - return result unless result.is_a?(self) - self + new(*args).call(&block) end # :nodoc: def inherited(other) other.instance_variable_set(:@required_inputs, required_inputs.dup) @@ -183,39 +162,84 @@ end self end end + # Holds the result of running a service as well as the execution status + # + # An instance of this class will be returned from any service call and will have a status of + # either :success or :error along with a value that is set by the service. + class Result + attr_reader :status, :value + + def initialize + self.status = :pending + self.value = nil + end + + def pending? + status == :pending + end + + def success? + status == :success + end + + def error? + status == :error + end + + def success(value = nil) + self.value = value + self.status = :success + freeze + end + + def error(value) + self.value = value + self.status = :error + freeze + end + + private + + attr_writer :status, :value + end + def initialize(context:, logger: Logger.new($stdout), not_authorized: CB_NOT_AUTHORIZED, **inputs) + @result = Result.new + self.context = context self.logger = logger self.not_authorized = not_authorized self.inputs = inputs self.stop_caller = false end + # Executes the service and returns the result + # + # @return [TheHelp::Service::Result] def call validate_service_definition catch(:stop) do authorize log_service_call main + check_result! self.block_result = yield result if block_given? end throw :stop if stop_caller return block_result if block_given? - return result if result_set? - self + return result end private attr_accessor :context, :logger, :not_authorized, :block_result, :stop_caller - attr_writer :result - attr_reader :inputs + attr_reader :inputs, :result alias service_context context alias service_logger logger def inputs=(inputs) @@ -255,16 +279,11 @@ def stop! throw :stop end - def result - raise TheHelp::NoResultError unless result_set? - @result - end - - def result_set? - defined?(@result) + def check_result! + raise TheHelp::NoResultError if result.pending? end def run_callback(callback, *args) continue = false continue = catch(:stop) do