StateMachine (state_machine v0.1.6)
StateMachine package implements state machine abstraction. It supports Ecto out of the box and can work as both data structure and a process powered by gen_statem.
Check out the article for motivation.
Here's an example of a simple state machine created with this package:
defmodule Cat do
use StateMachine
defstruct [:name, :state, hungry: true]
defmachine field: :state do
state :asleep
state :awake
state :playing
state :eating, after_enter: &Cat.feed_up/1
event :wake do
transition from: :asleep, to: :awake
end
event :give_a_mouse do
transition from: :awake, to: :playing, unless: &Cat.hungry/1
transition from: :awake, to: :eating, if: &Cat.hungry/1
transition from: :playing, to: :eating
end
event :pet do
transition from: [:eating, :awake], to: :playing
end
event :sing_a_lullaby do
transition from: :awake, to: :asleep
transition from: :playing, to: :asleep
end
end
def hungry(cat) do
cat.hungry
end
def feed_up(cat) do
{:ok, %{cat | hungry: false}}
end
end
And later use it like this:
cat = %Cat{name: "Thomas", state: :asleep}
{:ok, %Cat{state: :awake}} = Cat.trigger(cat, :wake)
Features
- Validation of state machine definition at compile time
- Full support for callbacks (on states, events and transitions) and guards (on events and transitions)
- Optional payload can be supplied with the event
- One-line conversion to a state machine as a process (powered by gen_statem)
- With Ecto support activated every transition is wrapped in transaction
- With Ecto support activated the Ecto.Type implementation is generated automatically
Link to this section Summary
Link to this section Types
Link to this type
t(m)
Specs
t(m) :: %StateMachine{ events: %{optional(atom()) => StateMachine.Event.t(m)}, field: atom(), misc: keyword(), state_getter: (StateMachine.Context.t(m) -> atom()), state_setter: (StateMachine.Context.t(m), atom() -> StateMachine.Context.t(m)), states: %{optional(atom()) => StateMachine.State.t(m)} }