Current section
Files
Jump to
Current section
Files
lib/orcha.ex
defmodule Orcha do
@type message() :: any()
@type service_state() :: any()
@type callback() :: {module(), func :: atom(), args :: list()}
@opaque realm() :: %__MODULE__{
registry: %{atom() => service_state()}
}
@callback handle_request(message(), realm(), service_state()) ::
{:result, result :: any()}
| {:result, result :: any(), service_state()}
| {:result, result :: any(), service_state(), realm()}
defstruct registry: %{}
@default_init :initialize
@spec initialize() :: realm()
def initialize(), do: %__MODULE__{}
@spec register(realm(), callback() | module()) :: realm()
def register(%__MODULE__{} = realm, {mod, func, args}) do
state = apply(mod, func, args)
put_state(mod, state, realm)
end
def register(%__MODULE__{} = realm, mod) when is_atom(mod) do
register(realm, {mod, @default_init, []})
end
@spec request({module(), message()}, realm()) ::
result :: any() | {result :: any(), realm()}
def request({mod, msg}, %__MODULE__{} = realm) do
with {:ok, state} <- Map.fetch(realm.registry, mod) do
case apply(mod, :handle_request, [msg, realm, state]) do
{:result, result} ->
result
{:result, result, new_state} ->
new_realm = put_state(mod, new_state, realm)
{result, new_realm}
{:result, result, new_state, %__MODULE__{} = new_realm} ->
new_realm = put_state(mod, new_state, new_realm)
{result, new_realm}
end
else
:error ->
raise ArgumentError, "Requesting unregistered service: #{inspect(mod)}."
end
end
defp put_state(mod, state, %__MODULE__{} = realm) do
update_in(realm.registry, &Map.put(&1, mod, state))
end
end