Packages
finitomata
0.40.0
0.41.0
0.40.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.30.3
0.30.2
0.30.1
0.30.0
0.29.10
0.29.9
0.29.8
0.29.7
0.29.6
0.29.5
0.29.4
0.29.3
0.29.2
0.29.1
0.29.0
0.28.1
0.28.0
0.27.1
0.27.0
0.26.4
0.26.3
0.26.2
0.26.1
0.26.0
0.25.0
0.24.4
0.24.3
0.24.2
0.24.1
0.24.0
0.23.7
0.23.6
0.23.5
0.23.4
0.23.3
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.2
0.20.1
0.20.0
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.1
0.17.0
0.16.0
0.15.1
0.15.0
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.13.0
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.1
0.1.0
The FSM implementation generated from PlantUML textual representation.
Current section
Files
Jump to
Current section
Files
lib/finitomata/persistency.ex
defmodule Finitomata.Persistency do
@moduledoc """
The behaviour to be implemented by a persistent storage to be used
with `Finitomata` (pass the implementation as `persistency: Impl.Module.Name`
to `use Finitomata`.)
Once declared, the FSM attempts to load its current state from the storage on start
via `c:load/1`, and writes back on every successful transition via `c:store/3`
(and `c:store_error/4` on a failed one).
## Built-in adapters
Two zero-dependency adapters ship with the library and only require their owner
process to be added to the supervision tree:
- `Finitomata.Persistency.ETS` — in-memory snapshots that survive an individual
FSM restart (good for development, tests, and single-node setups);
- `Finitomata.Persistency.DETS` — the same, persisted to disk so snapshots survive
a node restart.
For database-backed persistence, implement this behaviour directly, or implement the
`Finitomata.Persistency.Persistable` protocol for the carried struct and use the
`Finitomata.Persistency.Protocol` adapter (see `examples/ecto_integration`).
## The `load/1` contract
`Finitomata.Engine` does not call `c:load/1` with a bare id; it builds a
`{type, fields}` descriptor from the start payload (a module, a struct, or a
`%{type: type, id: id}` map) and expects back a `{lifecycle, {state, payload}}`
tuple. A `:loaded` lifecycle skips the entry transition and resumes from the
persisted `state`; `:created`/`:unknown` proceed through the normal entry flow.
"""
alias Finitomata.{State, Transition}
@typedoc "The entity descriptor `Finitomata.Engine` passes to `c:load/1`"
@type load_descriptor :: {module(), keyword() | map()} | Finitomata.fsm_name()
@typedoc "The lifecycle of the loaded entity, driving whether the entry transition runs"
@type lifecycle :: :loaded | :created | :unknown
@type transition_info :: %{
from: Transition.state(),
to: Transition.state(),
event: Transition.event(),
event_payload: Finitomata.event_payload(),
object: State.payload()
}
@doc """
The function to be called from `init/1` callback upon FSM start to load the state and
payload from the persistent storage.
It receives the `{type, fields}` descriptor built from the start payload and returns
`{lifecycle, {state, payload}}`, where `state` is `nil` for a freshly created entity.
"""
@callback load(descriptor :: load_descriptor()) ::
{lifecycle(), {Transition.state() | nil, State.payload()}}
@doc """
The function to be called from `on_transition/4` handler to allow storing the state
and payload to the persistent storage
"""
@callback store(
id :: Finitomata.fsm_name(),
object :: State.payload(),
transition :: transition_info()
) ::
:ok | {:ok, State.payload()} | {:error, any()}
@doc """
The function to be called from `on_transition/4` handler on non successful
transition to allow storing the failed attempt to transition to the persistent storage
"""
@callback store_error(
id :: Finitomata.fsm_name(),
object :: State.payload(),
reason :: any(),
transition :: transition_info()
) :: :ok | {:error, any()}
@optional_callbacks store_error: 4
end