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/supervision/supervisor.ex
defmodule Finitomata.Supervisor do
@moduledoc since: "0.26.0"
@moduledoc """
The behaviour for actual _FSM_ implementations across _Finitomata_ framework.
It ships with two predefined implementations, `Finitomata` for local
applications and `Infinitomata` for distributed ones.
"""
@typedoc "The module, implementing this behaviour"
@type t :: module()
@doc "Returns whether the _FSM_ instance under `id` “branch” and with `fsm_name` name is alive"
@callback alive?(id :: Finitomata.id(), fsm_name :: Finitomata.fsm_name()) :: boolean()
@doc "Starts the new _FSM_ instance under `id` “branch,” the semantics is similar to `DynamicSupervisor.start_child/2`"
@callback start_fsm(
id :: Finitomata.id(),
fsm_name :: Finitomata.fsm_name(),
implementation :: Finitomata.implementation(),
payload :: Finitomata.payload()
) :: DynamicSupervisor.on_start_child()
@doc "Sends the event with an optional payload to the running _FSM_ instance to initiate a transition"
@callback transition(
id :: Finitomata.id(),
fsm_name :: Finitomata.fsm_name(),
event_payload ::
Finitomata.Transition.event()
| {Finitomata.Transition.event(), Finitomata.State.payload()},
delay :: non_neg_integer()
) :: :ok
@doc "Effectively initiates the `on_timer/2` callback imminently, resetting the timer"
@callback timer_tick(id :: Finitomata.id(), fsm_name :: Finitomata.fsm_name()) :: :ok
@doc "The `Finitomata` “branch” child specification, used from supervision trees to start supervised _FSM_ “branchs”"
@callback child_spec(id :: Finitomata.id()) :: Supervisor.child_spec()
@doc "Returns all the active _FSM_ instances under the `id` “branch,” it might be a heavy map"
@callback all(Finitomata.id()) :: %{
optional(Finitomata.fsm_name()) => %{
optional(:node) => node(),
optional(:reference) => reference(),
optional(:module) => Finitomata.implementation(),
pid: pid()
}
}
@doc "Returns the state of the _FSM_ instance under `id` “branch” with `fsm_name` name"
@callback state(
id :: Finitomata.id(),
fsm_name :: Finitomata.fsm_name(),
reload? :: :cached | :payload | :state | :full | (Finitomata.State.t() -> any())
) ::
nil | Finitomata.State.t() | Finitomata.State.payload() | any()
use Supervisor
@doc false
def start_link(id \\ nil)
def start_link([]), do: start_link(nil)
def start_link(id: id), do: start_link(id)
def start_link(id),
do: Supervisor.start_link(__MODULE__, id, name: supervisor_name(id))
@impl Supervisor
@doc false
def init(id) do
children =
[
{Registry,
keys: :unique, name: registry_name(id), partitions: System.schedulers_online()},
{Finitomata.StateCache, id},
{Finitomata.Manager, name: manager_name(id)}
]
Supervisor.init(children, strategy: :rest_for_one)
end
@doc false
@spec infinitomata_name(Finitomata.id()) :: module()
def infinitomata_name(id \\ nil), do: fq_module(id, Infinitomata, true)
@doc false
@spec supervisor_name(Finitomata.id()) :: module()
def supervisor_name(id \\ nil), do: id |> fq_module(Supervisor, true) |> uninfinitomata()
@doc false
@spec registry_name(Finitomata.id()) :: module()
def registry_name(id \\ nil), do: id |> fq_module(Registry, true) |> uninfinitomata()
@doc false
@spec manager_name(Finitomata.id()) :: module()
def manager_name(id \\ nil), do: id |> fq_module(Manager, true) |> uninfinitomata()
@doc false
@spec throttler_name(Finitomata.id()) :: module()
def throttler_name(id \\ nil), do: id |> fq_module(Throttler, true) |> uninfinitomata()
@doc false
@spec state_cache_name(Finitomata.id()) :: module()
def state_cache_name(id \\ nil), do: id |> fq_module(StateCache, true) |> uninfinitomata()
@spec fq_module(id :: any(), who :: any(), atomize? :: boolean()) :: module() | [any()]
defp fq_module(id, who, false) when is_atom(id) and is_atom(who), do: [Finitomata, id, who]
defp fq_module(id, who, false) when is_atom(id),
do: fq_module(id, Module.concat([inspect(who)]), false)
defp fq_module(id, who, false), do: fq_module(Module.concat([inspect(id)]), who, false)
defp fq_module(id, who, true) when is_atom(id) and is_atom(who) do
id
|> fq_module(who, false)
|> Enum.reject(&is_nil/1)
|> Enum.map(&inspect/1)
|> smart_concat()
end
defp fq_module(id, who, true) when is_atom(id),
do: fq_module(id, Module.concat([inspect(who)]), true)
defp fq_module(id, who, true), do: fq_module(Module.concat([inspect(id)]), who, true)
defp uninfinitomata(mod),
do: mod |> Module.split() |> Enum.reject(&(&1 == "Infinitomata")) |> Module.concat()
defp smart_concat([fqn]), do: Module.concat([fqn])
defp smart_concat([fqn, id_who]) do
if String.starts_with?(id_who, fqn),
do: Module.concat([id_who]),
else: Module.concat([fqn, id_who])
end
defp smart_concat([fqn, id, who]) do
if String.starts_with?(id, fqn),
do: Module.concat([id, who]),
else: Module.concat([fqn, id, who])
end
end