Jido.Plan (Jido Action v2.0.0-rc.0)

View Source

Plans define DAGs (Directed Acyclic Graphs) of Instructions using keyword lists or builder methods.

A Plan is a struct that can be built up using various methods and then normalized into a directed graph of Instructions for execution.

Simple Sequential Plan

plan = Plan.new()
|> Plan.add(:fetch, MyApp.FetchAction)
|> Plan.add(:validate, MyApp.ValidateAction, depends_on: :fetch)
|> Plan.add(:save, MyApp.SaveAction, depends_on: :validate)

Plan with Parameters

plan = Plan.new()
|> Plan.add(:fetch, {MyApp.FetchAction, %{source: "api"}})
|> Plan.add(:validate, {MyApp.ValidateAction, %{strict: true}}, depends_on: :fetch)
|> Plan.add(:save, {MyApp.SaveAction, %{destination: "/tmp"}}, depends_on: :validate)

Plan with Parallel Steps

plan = Plan.new()
|> Plan.add(:fetch_users, MyApp.FetchUsersAction)
|> Plan.add(:fetch_orders, MyApp.FetchOrdersAction)
|> Plan.add(:fetch_products, MyApp.FetchProductsAction)
|> Plan.add(:merge, MyApp.MergeAction, depends_on: [:fetch_users, :fetch_orders, :fetch_products])

From Keyword Lists

plan_def = [
  fetch: MyApp.FetchAction,
  validate: {MyApp.ValidateAction, depends_on: :fetch},
  save: {MyApp.SaveAction, %{dest: "/tmp"}, depends_on: :validate}
]

{:ok, plan} = Plan.build(plan_def)

Plans can be normalized into a directed graph for execution analysis and validation.

Summary

Functions

Adds a single instruction to the plan.

Creates a Plan from a keyword list definition.

Same as build/3 but raises on error.

Adds dependencies between steps.

Returns the execution phases for the plan (topological sort).

Creates a new empty Plan struct.

Normalizes the Plan into a directed graph and list of PlanInstructions.

Same as normalize/1 but raises on error.

Converts the plan back to a keyword list format.

Types

step_def()

@type step_def() ::
  module()
  | {module(), map()}
  | {module(), keyword()}
  | {module(), map(), keyword()}
  | Jido.Instruction.t()

t()

@type t() :: %Jido.Plan{context: map(), id: binary(), steps: map()}

Functions

add(plan, step_name, step_def, opts \\ [])

@spec add(t(), atom(), step_def(), keyword()) :: t() | no_return()

Adds a single instruction to the plan.

Parameters

  • plan - The plan to add to
  • step_name - Atom name for the step
  • step_def - Step definition (action module, tuple, or instruction)
  • opts - Optional keyword list with :depends_on and other options

Examples

iex> plan = Plan.new()
iex> plan = Plan.add(plan, :fetch, MyApp.FetchAction)
iex> plan = Plan.add(plan, :save, MyApp.SaveAction, depends_on: :fetch)

build(plan_def, context \\ %{})

@spec build(
  keyword(),
  map()
) :: {:ok, t()} | {:error, term()}

Creates a Plan from a keyword list definition.

Parameters

  • plan_def - Keyword list defining the plan structure
  • context - Optional context map (default: %{})

Returns

  • {:ok, %Plan{}} - Successfully created plan
  • {:error, term()} - If plan definition is invalid

Examples

iex> plan_def = [
...>   fetch: MyApp.FetchAction,
...>   validate: {MyApp.ValidateAction, depends_on: :fetch}
...> ]
iex> {:ok, plan} = Plan.build(plan_def)
{:ok, %Plan{...}}

build!(plan_def, context \\ %{})

@spec build!(
  keyword(),
  map()
) :: t() | no_return()

Same as build/3 but raises on error.

depends_on(plan, step_name, deps)

@spec depends_on(t(), atom(), atom() | [atom()]) :: t() | no_return()

Adds dependencies between steps.

Parameters

  • plan - The plan to update
  • step_name - Step to add dependencies to
  • deps - Single dependency or list of dependencies

Examples

iex> plan = Plan.new()
iex> |> Plan.add(:step1, Action1)
iex> |> Plan.add(:step2, Action2)
iex> |> Plan.depends_on(:step2, :step1)

execution_phases(plan)

@spec execution_phases(t()) :: {:ok, [[atom()]]} | {:error, term()}

Returns the execution phases for the plan (topological sort).

Examples

iex> plan = Plan.new() |> Plan.add(:step1, Action1)
iex> {:ok, phases} = Plan.execution_phases(plan)
{:ok, [[:step1]]}

new(opts \\ [])

@spec new(keyword()) :: t()

Creates a new empty Plan struct.

Examples

iex> Plan.new()
%Plan{steps: %{}}

iex> Plan.new(context: %{user_id: "123"})
%Plan{context: %{user_id: "123"}}

normalize(plan)

@spec normalize(t()) ::
  {:ok, {Graph.t(), [Jido.Plan.PlanInstruction.t()]}} | {:error, term()}

Normalizes the Plan into a directed graph and list of PlanInstructions.

Returns

  • {:ok, {graph, plan_instructions}} - Graph and list of plan instructions
  • {:error, term()} - If normalization fails

Examples

iex> plan = Plan.new() |> Plan.add(:step1, MyAction)
iex> {:ok, {graph, plan_instructions}} = Plan.normalize(plan)
iex> [%Plan.PlanInstruction{name: :step1}] = plan_instructions

normalize!(plan)

@spec normalize!(t()) :: {Graph.t(), [Jido.Plan.PlanInstruction.t()]} | no_return()

Same as normalize/1 but raises on error.

to_keyword(plan)

@spec to_keyword(t()) :: keyword()

Converts the plan back to a keyword list format.