CatchCake provides a lightweight and flexible state machine implementation in Elixir. It allows you to define state transitions, handle events, and manage application context without the complexity of external frameworks.
The state machine consists of three main parts:
- Definition: A map structure describing states, events, and transitions.
- Context: A map that carries data between events and actions.
- Actions: Functions executed during transitions that can modify the context or control the flow.
If available in Hex, the package can be installed
by adding cc_statemachine to your list of dependencies in mix.exs:
def deps do
[
{:cc_statemachine, "~> 1.0.0"}
]
endA state machine is defined using a map where the keys represent states. The start key defines the initial state.
start: The initial state. Contains aninittransition.event: Defines what happens when a specific event occurs in a state.next: The target state after the event.action: (Optional) A function receiving(context, event).
%{
start: %{
init: %{
next: :next_state,
action: fn context, _event -> context end
}
},
next_state: %{
ignite: %{
next: :fire,
action: fn context, {:ignite, data} -> Map.put(context, :data, data) end
}
}
}Use CatchCake.StateMachine.new/2 to create a machine with a specific ID.
Use CatchCake.StateMachine.new/3 to initialize it with a context map.
machine = CatchCake.StateMachine.new(definition, "my_machine_id")Call CatchCake.StateMachine.handle_event/2 to process an event. This updates the current state and executes the corresponding action.
machine = CatchCake.StateMachine.handle_event(machine, :run)Actions are functions executed during a transition. They receive the current context and the event.
- Return
context: The state machine stops processing. - Return
{event, context}: The state machine continues processing the same event again (useful for chaining or conditional logic). - Return
contextwith data modified: The context is updated, and the machine stops.
A simple machine that moves from start to next upon initialization.
iex> definition = %{
...> start: %{init: %{next: :next}},
...> next: %{}
...> }
...> machine = CatchCake.StateMachine.new(definition, "test")
...> machine.state
:nextYou can pass initial data to the machine via the context map.
iex> definition = %{
...> start: %{init: %{next: :next, action: fn context, _event -> context end}},
...> next: %{}
...> }
...> machine = CatchCake.StateMachine.new(definition, "test", %{user_id: 123})
...> machine.context.user_id
123Events can be atoms or tuples containing data. Actions can modify the context based on this data.
iex> definition = %{
...> start: %{init: %{next: :next, action: fn context, _event -> context end}},
...> next: %{
...> ignite: %{
...> next: :fire,
...> action: fn context, {:ignite, data} -> Map.put(context, :data, data) end
...> }
...> }
...> }
...> machine = CatchCake.StateMachine.new(definition, "test")
...> machine = CatchCake.StateMachine.handle_event(machine, {:ignite, "test"})
...> machine.state
:fire
...> machine.context.data
"test"An action can return {event, context} to trigger the next event immediately after the current one processes.
iex> definition = %{
...> start: %{
...> init: %{
...> next: :next,
...> action: fn context, :init -> {:ignite, context} end
...> }
...> },
...> next: %{
...> ignite: %{
...> next: :fire,
...> action: fn context, _event -> context end
...> }
...> },
...> fire: %{}
...> }
...> machine = CatchCake.StateMachine.new(definition, "test")
...> machine.state
:fire- Define your states and events in a nested map.
- Create the machine with
new/2ornew/3. - Process events using
handle_event/2. - Control flow via action return values (
contextto stop,{event, context}to continue).
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/cc_statemachine.