Packages
finitomata
0.29.8
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/accessible.ex
defmodule Finitomata.Accessible do
@moduledoc since: "0.26.0"
@moduledoc """
The convenience module, exposing `start_link/1` to embed `Finitomata.Supervisor`
implementation into a supervision tree in an agnostic way.
This module implements `Access` in a contrived way, allowing to deal with underlying
_FSM_ instances in a following way.
```elixir
iex|🌢|1 ▶ Finitomata.Accessible.start_link(type: Finitomata, implementation: L, id: L1)
{:ok, #PID<0.214.0>}
iex|🌢|2 ▶ sup = Finitomata.Accessible.lookup(L1)
%Finitomata.Accessible{
type: Finitomata,
id: L1,
implementation: Finitomata.Test.Log,
last_event: nil,
cached_pid: #PID<0.214.0>
}
iex|🌢|3 ▶ put_in(sup, ["MyFSM"], :accept)
07:16:34.736 [debug] [→ ↹] […]
07:16:34.738 [debug] [✓ ⇄] with: [current: :*, event: :__start__, event_payload: %{payload: nil, __retries__: 1}, state: %{}]
07:16:34.738 [debug] [← ↹] […]
07:16:34.738 [debug] [→ ↹] […]
07:16:34.739 [debug] [✓ ⇄] with: [current: :idle, event: :accept, event_payload: nil, state: %{}]
07:16:34.739 [debug] [← ↹] […]
%Finitomata.Accessible{
type: Finitomata,
id: L1,
implementation: Finitomata.Test.Log,
last_event: {"MyFSM", :accept},
cached_pid: #PID<0.214.0>
}
iex|🌢|4 ▶ get_in(sup, ["MyFSM"])
#Finitomata<[
name: {Finitomata.L1.Registry, "MyFSM"},
state: [current: :accepted, previous: :idle, payload: %{}],
internals: [errored?: false, persisted?: false, timer: false]
]>
```
"""
schema = [
type: [
required: false,
default: Infinitomata,
type: {:custom, Finitomata, :behaviour, [Finitomata.Supervisor]},
doc:
"The actual `Finitomata.Supervisor` implementation (typically, `Finitomata` or `Infinitomata`)"
],
implementation: [
required: true,
type: :atom,
doc: "The actual implementation of _FSM_ (the module, having `use Finitomata` clause)"
],
id: [
required: false,
default: nil,
type: :any,
doc:
"The unique `ID` of this _Finitomata_ “branch,” when `nil` the `implementation` value would be used"
]
]
@schema NimbleOptions.new!(schema)
@doc """
Supervision tree embedder.
## Options to `Finitomata.Accessible.start_link/1`
#{NimbleOptions.docs(@schema)}
"""
def start_link(opts \\ []) do
opts = NimbleOptions.validate!(opts, @schema)
type = Keyword.fetch!(opts, :type)
implementation = Keyword.fetch!(opts, :implementation)
id = Keyword.fetch!(opts, :id)
on_start = type.start_link(id)
case on_start do
{:ok, pid} ->
:persistent_term.put(
{__MODULE__, id},
struct!(__MODULE__, type: type, id: id, implementation: implementation, cached_pid: pid)
)
{:error, {:already_started, pid}} ->
:persistent_term.put(
{__MODULE__, id},
struct!(__MODULE__, type: type, id: id, implementation: implementation, cached_pid: pid)
)
_ ->
nil
end
on_start
end
@doc """
Looks up the `Finitomata.Accessible` instance for the given `id`
"""
@spec lookup(Finitomata.id()) :: t()
def lookup(id), do: :persistent_term.get({__MODULE__, id}, nil)
@typedoc "The convenience struct representing the `Finitomata.Supervisor` with `Access`"
@type t :: %{
__struct__: __MODULE__,
type: Finitomata.Supervisor.t(),
id: Finitomata.id(),
implementation: module(),
last_event: nil | {Finitomata.fsm_name(), Finitomata.event_payload()},
cached_pid: nil | pid()
}
defstruct [:type, :id, :implementation, :last_event, :cached_pid]
@behaviour Access
@doc false
@impl Access
def fetch(%{__struct__: __MODULE__, type: type, id: id}, fsm_name) do
case type.state(id, fsm_name, :full) do
%Finitomata.State{} = state -> {:ok, state}
_ -> :error
end
end
@doc false
@impl Access
def pop(%{__struct__: __MODULE__} = data, _key),
do: {nil, data}
@doc false
@impl Access
def get_and_update(%{__struct__: __MODULE__, type: type, id: id} = data, fsm_name, function) do
_ =
if not type.alive?(id, fsm_name) do
{:ok, _pid} = type.start_fsm(id, fsm_name, data.implementation, %{})
end
state = type.state(id, fsm_name, :full)
case function.(state) do
:pop ->
{state, data}
{_state, event_payload} ->
:ok = type.transition(id, fsm_name, event_payload)
{state, %{data | last_event: {fsm_name, event_payload}}}
end
end
end