# encoding: utf-8

module ServiceObjects

  module Helpers

    # Features for escaping from runtime errors
    #
    # @note
    #   A target class should **include** the module
    module Exceptions

      # Re-raises standard errors as <tt>ServiceObjects::Invalid</tt>
      #
      # Mutates the current object by adding error messages
      #
      # @example
      #   class MyClass
      #     includes ServiceObjects::Helpers::Exceptions
      #   end
      #
      #   begin
      #     MyClass.new.escape { fail StandardError.new "foo" }
      #   rescue => err
      #     puts err.class.name
      #     puts messages
      #   end
      #
      #   # => ServiceObjects::Invalid
      #   # => [<ServiceObject::Message type="error" text="foo" ...>]
      #
      # @yield  the block
      #
      # @raise  [ServiceObjects::Invalid]
      #   if the block raises +StandardError+
      #
      # @return [Object] the value returned by the block
      def escape
        yield if block_given?
      rescue => error
        collect_messages_from error
        raise Invalid.new(self)
      end

      private

      # @!parse include ServiceObjects::Helpers::Messages
      def self.included(klass)
        klass.include Messages
      end

      def collect_messages_from(error)
        if error.is_a? Invalid
          messages.concat(error.messages) if error.object != self
        else
          add_message type: "error", text: error.message
        end
      end

    end # module Exceptions

  end # module Helpers

end # module ServiceObjects