require 'operatic/errors' require 'operatic/result' require 'operatic/version' module Operatic # @!visibility private def self.included(base) base.extend(ClassMethods) end module ClassMethods # The main way of calling an operation. # # The class is instantiated with supplied +attrs+ and calls {Operatic#call} # returning a frozen {Result} instance. # # @param attrs [Hash] an optional hash of key/values to # to the result. The class must have corresponding +attr_reader+s # # @return a [Result] which is fro def call(attrs = nil) new(attrs) .tap(&:call) .result .freeze end # Calls {#call} but raises {FailureError} if the returned {Result} is a # {Result#failure?} - useful for things like background jobs, rake tasks, # test setups, etc. # # @return [Result] def call!(attrs = nil) call(attrs).tap { |result| raise FailureError if result.failure? } end # Define a {Result} subclass with named accessors specific to the class via # {Result.generate}. # # @example # class SayHello # include Operatic # # attr_reader :name # # result :message # # def call # success!(message: "Hello #{name}") # end # end # # result = SayHello.call(name: 'Dave') # result.success? # => true # result.message # => "Hello Dave" def result(*args) @result_class = Result.generate(*args) end def result_class @result_class || Result end end # An instance of {Result} or a subclass generated by {ClassMethods#result}. # # @return [Result] attr_reader :result def initialize(attrs = nil) @result = self.class.result_class.new attrs.each do |key, value| instance_variable_set("@#{key}", value) end if attrs end # Override this method with your implementation. Use {#success!} or # {#failure!} methods to communicate the {#result}'s status and to attach # data it. Define convenience result accessors with {ClassMethods#result}. # # @example # class SayHello # include Operatic # # attr_reader :name # # def call # return failure! unless name # success!(message: "Hello #{name}") # end # end # # result = SayHello.call(name: 'Dave') # result.success? # => true # result.to_hash # => {:message=>"Hello Dave"} # # result = SayHello.call # result.failure? # => true # result.success? # => false # result.to_hash # => {} def call end # Convenience shortcut to the operation's {Result#failure!}. def failure!(data = nil) result.failure!(data) end # Convenience shortcut to the operation's {Result#success!}. def success!(data = nil) result.success!(data) end end