Quickstart
View SourceThis guide will help you get started with XFsm. You will learn how to create a state machine, create an actor from the state machine, send events to the actor, and use it outside of an actor.
Info
The quickest way to start with XFsm is to check out the Basic Payment Machine. There you will find an example of a machine that covers the basics of XFsm.
Installing XFsm
XFsm is a declarative state management and orchestration library for Elixir.
def deps do
[
{:xfsm, "~> 0.3"}
]
end
Create a Machine
In XFsm, a machine is a module that contains all the logic for the actor. In this example, we will create a simple toggle machine that can be in one of two states: on
or off
. The press
event will toggle the state between on
and off
.
defmodule LightbulbSwitch do
use XFsm.Actor
use XFsm.Machine
initial(:off)
state :off do
on :press do
target(:on)
end
end
state :on do
on :press do
target(:off)
end
end
end
Just by looking at the declaration, you can get a sense of how it works. To familiarize yourself with the foundational concepts, read our introduction to state machines guide.
Start an Actor and Send Events
An actor is a running process that can receive events, send events to other actors, and change its behavior based on the events it receives.
alias XFsm.{Actor, Snapshot}
# Here, we start the actor.
{:ok, pid} = LightbulbSwitch.start_link()
# Grab a snapshot of the actor's state.
%Snapshot{state: :off} = Actor.snapshot(pid)
# Send the `press` event to the actor.
:ok = Actor.send(pid, %{type: :press})
# Notice that the state of the actor has now changed?!
%Snapshot{state: :on} = Actor.snapshot(pid)
# Send another `press` event to the actor.
:ok = Actor.send(pid, %{type: :press})
# Now we're back to the initial state.
%Snapshot{state: :off} = Actor.snapshot(pid)
Info
Ensure your machine utilizes the use XFsm.Actor
behavior. Additionally, XFsm actors are GenServers under the hood but have a more restricted scope of behavior.
Context Data
Context is how you store data within a state machine actor.
defmodule ToggleMachine do
use XFsm.Actor
use XFsm.Machine
import XFsm.Actions
initial(:inactive)
context(%{count: 0})
state :inactive do
on :toggle do
target(:active)
end
end
state :active do
# Increment `count` every time we transition into this state.
entry(assigns(%{count: &(&1.context.count + 1)}))
on :toggle do
target(:inactive)
end
end
end
Machine Input
Machine Input is how initial data can be passed to a machine actor. Meanwhile, Guards are used to conditionally allow or disallow transitions.
defmodule ToggleMachine do
use XFsm.Actor
use XFsm.Machine
import XFsm.Actions
initial(:inactive)
context(%{input: input}, do: %{count: 0, max_count: input.max_count})
state :inactive do
on :toggle do
target(:active)
guard(%{context: context}, do: context.count < context.max_count)
end
end
state :active do
entry(assigns(%{count: &(&1.context.count + 1)}))
on :toggle do
target(:inactive)
end
end
end
alias XFsm.Actor
{:ok, pid} = ToggleMachine.start_link(input: %{max_count: 10})
Actor.subscribe(pid, &IO.inspect/1)
Actor.send(pid, %{type: :toggle})