View Source Reactor (reactor v0.3.0)

Reactor is a dynamic, concurrent, dependency resolving saga orchestrator.

usage

Usage

You can construct a reactor using the Reactor Spark DSL:

defmodule HelloWorldReactor do
  @moduledoc false
  use Reactor

  input :whom

  step :greet, Greeter do
    argument :whom, input(:whom)
  end

  return :greet
end
iex> Reactor.run(HelloWorldReactor, %{whom: "Dear Reader"})
{:ok, "Hello, Dear Reader!"}

or you can build it programmatically:

iex> reactor = Builder.new()
...> {:ok, reactor} = Builder.add_input(reactor, :whom)
...> {:ok, reactor} = Builder.add_step(reactor, :greet, Greeter, whom: {:input, :whom})
...> {:ok, reactor} = Builder.return(reactor, :greet)
...> Reactor.run(reactor, %{whom: nil})
{:ok, "Hello, World!"}

dsl-documentation

DSL Documentation

index

Index

  • reactor
    • around
      • argument
    • group
      • argument
    • input
    • step
      • argument
    • compose
      • argument

docs

Docs

reactor

reactor

The top-level reactor DSL


  • :return (atom/0) - Specify which step result to return upon completion.

around

around

Wrap a function around a group of steps.

  • argument

  • :name (atom/0) - Required. A unique name of the group of steps.

  • :fun - Required. The around function.
    See Reactor.Step.Around for more information.

  • :allow_async? (boolean/0) - Whether the emitted steps should be allowed to run asynchronously.
    Passed to the child Reactor as it's async? option. The default value is false.

argument

Specifies an argument to a Reactor step.

Each argument is a value which is either the result of another step, or an input value.

Individual arguments can be transformed with an arbitrary function before being passed to any steps.

Examples:

argument :name, input(:name)
argument :user, result(:create_user)
argument :user_id, result(:create_user) do
  transform & &1.id
end
argument :three, value(3)
  • :name (atom/0) - Required. The name of the argument which will be used as the key in the arguments map passed to the implementation.

  • :source - Required. What to use as the source of the argument.
    See Reactor.Dsl.Argument for more information.

  • :transform - An optional transformation function which can be used to modify the argument before it is passed to the step. The default value is nil.

group

group

Call functions before and after a group of steps.

  • argument

  • :name (atom/0) - Required. A unique name for the group of steps.

  • :before_all - Required. The before function.
    See Reactor.Step.Group for more information.

  • :after_all - Required. The after function.
    See Reactor.Step.Group for more information.

  • :allow_async? (boolean/0) - Whether the emitted steps should be allowed to run asynchronously.
    Passed to the child Reactor as it's async? option. The default value is true.

argument

Specifies an argument to a Reactor step.

Each argument is a value which is either the result of another step, or an input value.

Individual arguments can be transformed with an arbitrary function before being passed to any steps.

Examples:

argument :name, input(:name)
argument :user, result(:create_user)
argument :user_id, result(:create_user) do
  transform & &1.id
end
argument :three, value(3)
  • :name (atom/0) - Required. The name of the argument which will be used as the key in the arguments map passed to the implementation.

  • :source - Required. What to use as the source of the argument.
    See Reactor.Dsl.Argument for more information.

  • :transform - An optional transformation function which can be used to modify the argument before it is passed to the step. The default value is nil.

input

input

Specifies an input to the Reactor.

An input is a value passed in to the Reactor when executing. If a Reactor were a function, these would be it's arguments.

Inputs can be transformed with an arbitrary function before being passed to any steps.

Examples:

input :name
input :age do
  transform &String.to_integer/1
end
  • :name (atom/0) - Required. A unique name for this input.
    The name is used to allow steps to depend on it.

  • :transform - An optional transformation function which can be used to modify the input before it is passed to any steps. The default value is nil.

step

step

Specifies a Reactor step.

Steps are the unit of work in a Reactor. Reactor will calculate the dependencies graph between the steps and execute as many as it can in each iteration.

See the Reactor.Step behaviour for more information.

Examples:

step :create_user, MyApp.Steps.CreateUser do
  argument :username, input(:username)
  argument :password_hash, result(:hash_password)
end
step :hash_password do
  argument :password, input(:password)

  run fn %{password: password}, _ ->
    {:ok, Bcrypt.hash_pwd_salt(password)}
  end
end
  • :name (atom/0) - Required. A unique name for the step.
    This is used when choosing the return value of the Reactor and for arguments into another step.

  • :impl - The step implementation.
    Provides an implementation for the step with the named module. The module must implement the Reactor.Step behaviour.

  • :run - Provide an anonymous function which implements the run/3 callback.
    You cannot provide this option at the same time as the impl argument.

  • :undo - Provide an anonymous function which implements the undo/4 callback.
    You cannot provide this option at the same time as the impl argument.

  • :compensate - Provide an anonymous function which implements the undo/4 callback.
    You cannot provide this option at the same time as the impl argument.

  • :max_retries - The maximum number of times that the step can be retried before failing.
    This is only used when the result of the compensate/4 callback is :retry. The default value is :infinity.

  • :async? (boolean/0) - When set to true the step will be executed asynchronously via Reactor's TaskSupervisor. The default value is true.

  • :transform - An optional transformation function which can be used to modify the entire argument map before it is passed to the step. The default value is nil.

argument

Specifies an argument to a Reactor step.

Each argument is a value which is either the result of another step, or an input value.

Individual arguments can be transformed with an arbitrary function before being passed to any steps.

Examples:

argument :name, input(:name)
argument :user, result(:create_user)
argument :user_id, result(:create_user) do
  transform & &1.id
end
argument :three, value(3)
  • :name (atom/0) - Required. The name of the argument which will be used as the key in the arguments map passed to the implementation.

  • :source - Required. What to use as the source of the argument.
    See Reactor.Dsl.Argument for more information.

  • :transform - An optional transformation function which can be used to modify the argument before it is passed to the step. The default value is nil.

compose

compose

Compose another Reactor into this one.

Allows place another Reactor into this one as if it were a single step.

  • argument

  • :name (atom/0) - Required. A unique name for the step.
    Allows the result of the composed reactor to be depended upon by steps in this reactor.

  • :reactor - Required. The reactor module or struct to compose upon.

argument

Specifies an argument to a Reactor step.

Each argument is a value which is either the result of another step, or an input value.

Individual arguments can be transformed with an arbitrary function before being passed to any steps.

Examples:

argument :name, input(:name)
argument :user, result(:create_user)
argument :user_id, result(:create_user) do
  transform & &1.id
end
argument :three, value(3)
  • :name (atom/0) - Required. The name of the argument which will be used as the key in the arguments map passed to the implementation.

  • :source - Required. What to use as the source of the argument.
    See Reactor.Dsl.Argument for more information.

  • :transform - An optional transformation function which can be used to modify the argument before it is passed to the step. The default value is nil.

Link to this section Summary

Types

When set to false forces the Reactor to run every step synchronously, regardless of the step configuration.

Use a Reactor.Executor.ConcurrencyTracker.pool_key to allow this Reactor to share it's concurrency pool with other Reactor instances.

How long to wait for asynchronous steps to complete when halting.

Specify the maximum number of asynchronous steps which can be run in parallel.

The maximum number of iterations which after which the Reactor will halt.

t()

Specify the amount of execution time after which to halt processing.

Link to this section Types

@type async_option() :: {:async?, boolean()}

When set to false forces the Reactor to run every step synchronously, regardless of the step configuration.

Defaults to true.

Link to this type

concurrency_key_option()

View Source
@type concurrency_key_option() :: {:concurrency_key, reference()}

Use a Reactor.Executor.ConcurrencyTracker.pool_key to allow this Reactor to share it's concurrency pool with other Reactor instances.

If you do not specify one then the Reactor will initialise a new pool and place it in it's context for any child Reactors to re-use.

Only used if async? is set to true.

@type context() :: %{optional(atom()) => any()}
@type context_arg() :: Enumerable.t({atom(), any()})
@type halt_timeout_option() :: {:halt_timeout, pos_integer() | :infinity}

How long to wait for asynchronous steps to complete when halting.

Defaults to 5000ms.

@type inputs() :: %{optional(atom()) => any()}
Link to this type

max_concurrency_option()

View Source
@type max_concurrency_option() :: {:max_concurrency, pos_integer()}

Specify the maximum number of asynchronous steps which can be run in parallel.

Defaults to the result of System.schedulers_online/0. Only used if async? is set to true.

Link to this type

max_iterations_option()

View Source
@type max_iterations_option() :: {:max_iterations, pos_integer() | :infinity}

The maximum number of iterations which after which the Reactor will halt.

Defaults to :infinity.

@type state() :: :pending | :executing | :halted | :failed | :successful
@type t() :: %Reactor{
  context: context(),
  id: any(),
  inputs: [atom()],
  intermediate_results: %{required(any()) => any()},
  plan: nil | Graph.t(),
  return: any(),
  state: state(),
  steps: [Reactor.Step.t()],
  undo: [{Reactor.Step.t(), any()}]
}
@type timeout_option() :: {:timeout, pos_integer() | :infinity}

Specify the amount of execution time after which to halt processing.

Note that this is not a hard limit. The Reactor will stop when the first step completes after the timeout has expired.

Defaults to :infinity.

Link to this section Functions

Link to this function

run(reactor, inputs \\ %{}, context \\ %{}, options \\ [])

View Source
@spec run(t() | module(), inputs(), context_arg(), options()) ::
  {:ok, any()} | {:error, any()} | {:halted, t()}

Run a reactor.