class Flows::Flow::Node

Node is the main building block for {Flow}.

Node is an object which can be executed ({#call}) with some input and execution context and produce output and the next route.

Node execution consists of 4 sequential parts:

  1. Pre-processor execution (if pre-processor defined).

Allows to modify input which will be transferred to the node's body.
Allows to modify execution context.
Has access to the node's metadata.
  1. Body execution. Body is a lambda which receives input and returns output.

  2. Post-processor execution (if post-processor defined).

Allows to modify output which was produced by the node's body.
Allows to modify execution context.
Has access to the node's metadata.
  1. {Router} execution to determine next node name.

Execution result consists of 2 parts: output and next route.

## Pre/postprocessors

Both have similar signatures:

preprocessor = lambda do |node_input, context, meta|
  # returns input for the BODY
  # format [args, kwargs]
end

postprocessor = lambda do |body_output, context, meta|
  # returns output of the NODE
end

Types for body input and `body_output` is under your control. It allows you to adopt node for very different kinds of bodies.

Without pre-processor `input` from {#call} becomes the first and single argument for the body. In the cases when your body expects several arguments or keyword arguments you must use pre-processor. Pre-processor returns array of 2 elements - arguments and keyword arguments. There are some examples of a post-processor return and the corresponding body call:

[[1, 2], {}]
body.call(1, 2)

[[], { a: 1, b: 2}]
body.call(a: 1, b: 2)

[[1, 2], { x: 3, y: 4 }]
body.call(1, 2, x: 3, y: 4)

[[1, 2, { 'a' => 3}], {}]
body.call(1, 2, 'a' => 3)

There were simpler solutions, but after [this](www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/) it's the most clear one.

`context` and `meta` are expected to be Ruby Hashes.

## Metadata and node names

As you may see any Node instance has no name. It's done for the reason: name is not part of a node; it allows to use the same node instance under different names. In the most cases we don't need this, but it's nice to have such ability.

In some cases we want to have a node name inside pre/postprocessors. For such cases `meta` is the right place to store node name:

Flows::Flow::Node.new(body: body, router: router, meta: { name: :step_a })

@see Flows::Flow some examples here

Attributes

meta[R]

Node metadata, a frozen Ruby Hash.

router[R]

Public Class Methods

new(body:, router:, meta: {}, preprocessor: nil, postprocessor: nil) click to toggle source

@param body [Proc] node body @param router [Router] node router @param meta [Hash] node metadata @param preprocessor [Proc, nil] pre-processor for the node's body @param postprocessor [Proc, nil] post-processor for the node's body

# File lib/flows/flow/node.rb, line 92
def initialize(body:, router:, meta: {}, preprocessor: nil, postprocessor: nil)
  @body = body
  @router = router

  @meta = meta.freeze

  @preprocessor = preprocessor
  @postprocessor = postprocessor
end

Public Instance Methods

call(input, context:) click to toggle source

Executes the node.

@param input [Object] input for a node. In the context of {Flow}

it's initial input or output of the previously executed node.

@param context [Hash] execution context. In case of {Flow}

shared between node executions.

@return [Array<(Object, Symbol)>] output of a node and next route.

`:reek:TooManyStatements` is disabled for this method because even one more call to a private method impacts performance here.

# File lib/flows/flow/node.rb, line 114
def call(input, context:)
  output =
    if @preprocessor
      args, kwargs = @preprocessor.call(input, context, @meta)

      # https://bugs.ruby-lang.org/issues/14415
      kwargs.empty? ? @body.call(*args) : @body.call(*args, **kwargs)
    else
      @body.call(input)
    end
  output = @postprocessor.call(output, context, @meta) if @postprocessor

  route = @router.call(output)

  [output, route]
end