Packages
reactor
0.15.6
1.0.2
1.0.1
1.0.0
0.17.0
0.16.0
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.1
0.9.0
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
An asynchronous, graph-based execution engine
Current section
Files
Jump to
Current section
Files
documentation/tutorials/reactor-cheatsheet.cheatmd
# Reactor Cheatsheet
Reactor is a dynamic, concurrent, dependency resolving saga orchestrator for Elixir.
## Basic Reactor Definition
{: .col-2}
### Simple Reactor
```elixir
defmodule MyReactor do
use Reactor
input :email
input :password
step :hash_password do
argument :password, input(:password)
run &Bcrypt.hash_pwd_salt/1
end
step :create_user, MyApp.CreateUser do
argument :email, input(:email)
argument :password_hash, result(:hash_password)
end
return :create_user
end
```
### Running Reactors
```elixir
# Basic execution
{:ok, result} = Reactor.run(MyReactor,
email: "user@example.com",
password: "secret"
)
# With context and options
{:ok, result} = Reactor.run(MyReactor,
inputs,
%{current_user: user},
async?: false,
max_concurrency: 10
)
# Halting and resuming
{:halted, state} = Reactor.run(MyReactor, inputs)
{:ok, result} = Reactor.run(state, %{}, %{})
```
## Step Types
{: .col-2}
### Basic Steps
```elixir
# Anonymous function
step :transform do
argument :data, input(:raw_data)
run fn %{data: data}, _context ->
{:ok, String.upcase(data)}
end
end
# Module implementation
step :create_user, MyApp.Steps.CreateUser do
argument :email, result(:validate_email)
argument :data, input(:user_data)
end
# Sync/async control
step :critical_operation do
async? false
run &important_work/1
end
```
### Debug Steps
```elixir
debug :log_user do
argument :user, result(:create_user)
argument :message, value("User created")
end
```
### Map Steps
```elixir
map :process_users do
source input(:user_list)
batch_size 10
allow_async? true
step :validate_user do
argument :user, element(:process_users)
run &validate_user/1
end
end
```
### Compose Steps
```elixir
compose :sub_workflow, MyApp.SubReactor do
argument :input_data, result(:prepare_data)
end
```
## Advanced Step Types
{: .col-2}
### Switch Steps
```elixir
switch :handle_user_type do
on result(:user)
matches? &(&1.type == :premium) do
step :setup_premium do
argument :user, result(:user)
run &setup_premium_features/1
end
end
default do
step :setup_basic do
argument :user, result(:user)
run &setup_basic_features/1
end
end
end
```
### Group Steps
```elixir
group :user_setup do
before_all &MyApp.setup_database/3
after_all &MyApp.cleanup_database/1
step :create_profile do
# Profile creation logic
end
step :send_welcome_email do
# Email logic
end
end
```
### Around Steps
```elixir
around :transaction, &MyApp.with_transaction/4 do
step :create_user do
# User creation in transaction
end
step :create_profile do
# Profile creation in transaction
end
end
```
### Collect Steps
```elixir
collect :user_summary do
argument :user, result(:create_user)
argument :profile, result(:create_profile)
argument :settings, result(:create_settings)
transform fn inputs ->
%{
user: inputs.user,
profile: inputs.profile,
settings: inputs.settings
}
end
end
```
## Arguments and Dependencies
{: .col-2}
### Argument Sources
```elixir
# From input
argument :email, input(:email)
# From step result
argument :user, result(:create_user)
# Static value
argument :timeout, value(5000)
# Nested value extraction
argument :user_id, result(:create_user, [:id])
# Input with path
argument :year, input(:date, [:year])
```
### Argument Transformations
```elixir
# Inline transformation
argument :user_id, result(:create_user) do
transform &(&1.id)
end
# Block form with source
argument :age do
source input(:birth_year)
transform fn year ->
Date.utc_today().year - year
end
end
```
### Dependencies
```elixir
# Wait for step without using data
wait_for :verify_email
# Wait for multiple steps
wait_for [:setup_user, :setup_profile]
```
## Conditional Execution
{: .col-2}
### Guards
```elixir
step :read_file_via_cache do
argument :path, input(:path)
run &File.read(&1.path)
guard fn %{path: path}, %{cache: cache} ->
case Cache.get(cache, path) do
{:ok, content} -> {:halt, {:ok, content}}
_ -> :cont
end
end
end
```
### Where Clauses
```elixir
step :conditional_step do
argument :user, result(:create_user)
where fn %{user: user} ->
user.active? and user.plan == :premium
end
run &process_premium_user/1
end
# Simple predicate
step :read_file do
argument :path, input(:path)
run &File.read(&1.path)
where &File.exists?(&1.path)
end
```
## Step Implementation
{: .col-2}
### Step Module
```elixir
defmodule MyApp.Steps.CreateUser do
use Reactor.Step
@impl true
def run(arguments, context, options) do
case create_user(arguments) do
{:ok, user} -> {:ok, user}
{:error, reason} -> {:error, reason}
end
end
@impl true
def compensate(reason, arguments, context, options) do
case reason do
%DBConnection.ConnectionError{} -> :retry
_other -> :ok
end
end
@impl true
def undo(user, arguments, context, options) do
case delete_user(user) do
:ok -> :ok
{:error, reason} -> {:error, reason}
end
end
end
```
### Return Values
```elixir
# Success
{:ok, value}
# Success with additional steps
{:ok, value, [additional_step]}
# Failure (triggers compensation)
{:error, reason}
# Retry step
:retry
{:retry, reason}
# Pause reactor
{:halt, reason}
```
## Error Handling
{: .col-2}
### Compensation
```elixir
def compensate(reason, arguments, context, options) do
case reason do
# Retry on network errors
%HTTPoison.Error{reason: :timeout} -> :retry
%HTTPoison.Error{reason: :econnrefused} -> :retry
# Continue with error for other failures
_other -> :ok
end
end
```
### Undo Operations
```elixir
def undo(resource, arguments, context, options) do
case delete_resource(resource) do
:ok -> :ok
{:error, :not_found} -> :ok # Already deleted
{:error, reason} -> {:error, reason}
end
end
```
## Inputs and Transformations
{: .col-2}
### Input Definition
```elixir
# Basic input
input :name
# Input with transformation
input :age do
transform &String.to_integer/1
end
# Input with description
input :email, description: "User's email address"
```
### Template Steps
```elixir
template :welcome_message do
argument :user, result(:create_user)
template """
Welcome <%= @user.name %>! 🎉
Your account is now active.
"""
end
```
## Middleware
{: .col-2}
### Adding Middleware
```elixir
defmodule MyReactor do
use Reactor
middlewares do
middleware MyApp.LoggingMiddleware
middleware Reactor.Middleware.Telemetry
end
# Steps...
end
```
### Custom Middleware
```elixir
defmodule MyApp.LoggingMiddleware do
use Reactor.Middleware
def init(context) do
Logger.info("Reactor starting")
{:ok, context}
end
def complete(result, context) do
Logger.info("Reactor completed")
{:ok, result}
end
def error(errors, context) do
Logger.error("Reactor failed: #{inspect(errors)}")
:ok
end
def event({:run_start, args}, step, context) do
Logger.debug("Step #{step.name} starting")
end
end
```
## Common Patterns
{: .col-2}
### Data Pipeline
```elixir
input :raw_data
step :validate do
argument :data, input(:raw_data)
run &validate_data/1
end
step :transform do
argument :data, result(:validate)
run &transform_data/1
end
step :store do
argument :data, result(:transform)
run &store_data/1
end
return :store
```
### Parallel Processing
```elixir
step :fetch_user do
argument :id, input(:user_id)
run &fetch_user/1
end
step :fetch_settings do
argument :id, input(:user_id)
run &fetch_settings/1
end
collect :user_with_settings do
argument :user, result(:fetch_user)
argument :settings, result(:fetch_settings)
end
```
### Error Recovery
```elixir
step :risky_operation do
run &might_fail/1
max_retries 3
compensate fn reason, _, _, _ ->
case reason do
%NetworkError{} -> :retry
%TimeoutError{} -> :retry
_other -> :ok
end
end
end
```
## Quick Reference
{: .col-3}
### Functions
- `input/1` - Define reactor input
- `step/2` - Define step
- `argument/2` - Define step dependency
- `result/1` - Reference step result
- `value/1` - Static value
- `wait_for/1` - Dependency without data
- `return/1` - Set reactor return
### Built-in Steps
- `step` - Basic step
- `debug` - Log information
- `map` - Process collections
- `compose` - Embed reactors
- `switch` - Conditional logic
- `group` - Shared setup/teardown
- `around` - Wrap execution
- `collect` - Gather values
- `template` - EEx templates
- `flunk` - Force failure
### Control Flow
- `guard` - Conditional execution
- `where` - Simple conditional
- `async?` - Control sync/async
- `max_retries` - Retry limits
- `batch_size` - Map processing