Packages
reactor
0.10.1
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
lib/reactor/executor/hooks.ex
defmodule Reactor.Executor.Hooks do
@moduledoc """
Handles the execution of reactor middleware hooks.
"""
alias Reactor.{Middleware, Utils}
require Logger
@doc "Run the init hooks collecting the new context as it goes"
@spec init(Reactor.t(), Reactor.context()) :: {:ok, Reactor.context()} | {:error, any}
def init(reactor, context) do
context =
Map.put(context, :__reactor__, %{
id: reactor.id,
inputs: reactor.inputs,
middleware: reactor.middleware,
step_count: step_count(reactor),
initial_state: reactor.state
})
reactor.middleware
|> Utils.reduce_while_ok(context, fn middleware, context ->
Code.ensure_loaded!(middleware)
if function_exported?(middleware, :init, 1) do
middleware.init(context)
else
{:ok, context}
end
end)
end
@doc "Run the halt hooks collecting the new context as it goes"
@spec halt(Reactor.t(), Reactor.context()) :: {:ok, Reactor.context()} | {:error, any}
def halt(reactor, context) do
Utils.reduce_while_ok(reactor.middleware, context, fn middleware, context ->
if function_exported?(middleware, :halt, 1) do
middleware.halt(context)
else
{:ok, context}
end
end)
end
@doc "Run the completion hooks allowing the result to be replaced"
@spec complete(Reactor.t(), any, Reactor.context()) :: {:ok, any} | {:error, any}
def complete(reactor, result, context) do
Utils.reduce_while_ok(reactor.middleware, result, fn middleware, result ->
if function_exported?(middleware, :complete, 2) do
middleware.complete(result, context)
else
{:ok, result}
end
end)
end
@doc "Run the error hooks allowing the error to be replaced"
@spec error(Reactor.t(), any, Reactor.context()) :: {:error, any}
def error(reactor, reason, context) do
Enum.reduce(reactor.middleware, {:error, reason}, fn middleware, {:error, reason} ->
with true <- function_exported?(middleware, :error, 2),
{:error, reason} <- middleware.error(reason, context) do
{:error, reason}
else
_ -> {:error, reason}
end
end)
end
@doc "Run any get_process_context hooks"
@spec get_process_contexts(Reactor.t()) :: %{optional(Middleware.t()) => any}
def get_process_contexts(reactor) do
Enum.reduce(reactor.middleware, %{}, fn middleware, result ->
if function_exported?(middleware, :get_process_context, 0) do
result
|> Map.put(middleware, middleware.get_process_context())
else
result
end
end)
end
@doc "Run set_process_context hooks given the result of `get_process_contexts/1`"
@spec set_process_contexts(%{optional(Middleware.t()) => any}) :: :ok
def set_process_contexts(contexts) do
for {middleware, context} <- contexts do
if function_exported?(middleware, :set_process_context, 1) do
middleware.set_process_context(context)
else
Logger.warning(
"Unable to set process context for middleware `#{inspect(middleware)}`: `set_process_context/1` is not defined."
)
end
end
:ok
end
@doc "Emit a step event."
@spec event(Reactor.t(), Middleware.step_event(), Reactor.Step.t(), Reactor.context()) :: :ok
def event(reactor, event, step, context) do
for middleware <- reactor.middleware do
if function_exported?(middleware, :event, 3) do
middleware.event(event, step, context)
end
end
:ok
end
defp step_count(reactor) when is_nil(reactor.plan), do: length(reactor.steps)
defp step_count(reactor) do
vertices = Graph.num_vertices(reactor.plan)
length(reactor.steps) + vertices
end
end