Class: Substation::Chain

Inherits:
Object
  • Object
show all
Includes:
Adamantium::Flat, Enumerable, Outgoing
Defined in:
lib/substation/chain.rb,
lib/substation/chain/dsl.rb

Overview

Implements a chain of responsibility for an action

An instance of this class will typically contain (in that order) a few handlers that process the incoming Request object, one handler that calls an action (Pivot), and some handlers that process the outgoing Response object.

Both Incoming and Outgoing handlers must respond to ‘#call(response)` and `#result(response)`.

Examples:

chain handlers (used in instance method examples)


module App

  class Handler

    def initialize(handler = nil)
      @handler = handler
    end

    protected

    attr_reader :handler

    class Incoming < self
      include Substation::Chain::Incoming
    end

    class Outgoing < self
      include Substation::Chain::Outgoing

      private

      def respond_with(response, output)
        response.class.new(response.request, output)
      end
    end
  end

  class Validator < Handler::Incoming
    def call(request)
      result = handler.call(request.input)
      if result.valid?
        request.success(request.input)
      else
        request.error(result.violations)
      end
    end
  end

  class Pivot < Handler
    include Substation::Chain::Pivot

    def call(request)
      handler.call(request)
    end
  end

  class Presenter < Handler::Outgoing
    def call(response)
      respond_with(response, handler.new(response.output))
    end
  end
end

Defined Under Namespace

Modules: Incoming, Outgoing Classes: DSL

Constant Summary collapse

Pivot =

Supports chaining the Pivot handler

Outgoing
EMPTY =

Empty chain

Class.new(self).new([])

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Outgoing

#result

Class Method Details

.build(dsl, other, &block) ⇒ Chain

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Build a new chain instance

Parameters:

  • dsl (DSL)

    the dsl klass to use for defining the chain

  • other (Chain)

    another chain to build on top of

  • block (Proc)

    a block to instance_eval in the context of dsl

Returns:



19
20
21
# File 'lib/substation/chain/dsl.rb', line 19

def self.build(dsl, other, &block)
  new(dsl.processors(other, &block))
end

Instance Method Details

#call(request) ⇒ Response::Success, Response::Failure

Call the chain

Invokes all handlers and returns either the first Response::Failure that it encounters, or if all goes well, the Response::Success returned from the last handler.

Examples:


module App
  SOME_ACTION = Substation::Chain.new [
    Validator.new(MY_VALIDATOR),
    Pivot.new(Actions::SOME_ACTION),
    Presenter.new(Presenters::SomePresenter)
  ]

  env     = Object.new # your env would obviously differ
  input   = { 'name' => 'John' }
  request = Substation::Request.new(env, input)

  response = SOME_ACTION.call(request)

  if response.success?
    response.output # => the output wrapped in a presenter
  else
    response.output # => if validation, pivot or presenter failed
  end
end

Parameters:

  • request (Request)

    the request to handle

Returns:

Raises:

  • (Exception)

    any exception that isn’t explicitly rescued in client code



174
175
176
177
178
179
180
# File 'lib/substation/chain.rb', line 174

def call(request)
  handlers.inject(request) { |result, handler|
    response = handler.call(result)
    return response unless response.success?
    handler.result(response)
  }
end

#each(&block) {|handler| ... } ⇒ self

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Iterate over all processors

Parameters:

  • block (Proc)

    a block passed to #handlers each method

Yields:

  • (handler)

Yield Parameters:

  • handler (#call)

    each handler in the chain

Returns:

  • (self)


195
196
197
198
199
# File 'lib/substation/chain.rb', line 195

def each(&block)
  return to_enum unless block
  handlers.each(&block)
  self
end