Getting Started with State Machines
View SourceGet familiar with Ash resources
If you haven't already, read the Ash Getting Started Guide, and familiarize yourself with Ash and Ash resources.
Bring in the ash_state_machine dependency
{:ash_state_machine, "~> 0.2.12"}Add the extension to your resource
use Ash.Resource,
extensions: [AshStateMachine]Add initial states, and a default initial state
use Ash.Resource,
extensions: [AshStateMachine]
...
state_machine do
initial_states [:pending]
default_initial_state :pending
endAdd allowed transitions
state_machine do
initial_states [:pending]
default_initial_state :pending
transitions do
# `:begin` action can move state from `:pending` to `:started`/`:aborted`
transition :begin, from: :pending, to: [:started, :aborted]
end
endUse transition_state in your actions
For simple/static state transitions
actions do
update :begin do
# for a static state transition
change transition_state(:started)
end
endFor dynamic/conditional state transitions
defmodule Start do
use Ash.Resource.Change
def change(changeset, _, _) do
if ready_to_start?(changeset) do
AshStateMachine.transition_state(changeset, :started)
else
AshStateMachine.transition_state(changeset, :aborted)
end
end
end
actions do
update :begin do
# for a dynamic state transition
change Start
end
endDeclaring a custom state attribute
By default, a :state attribute is created on the resource that looks like this:
attribute :state, :atom do
allow_nil? false
default AshStateMachine.Info.state_machine_initial_default_state(dsl_state)
public? true
constraints one_of: [
AshStateMachine.Info.state_machine_all_states(dsl_state)
]
endYou can change the name of this attribute, without declaring an attribute yourself, like so:
state_machine do
initial_states([:pending])
default_initial_state(:pending)
state_attribute(:alternative_state) # <-- save state in an attribute named :alternative_state
endIf you need more control, you can declare the attribute yourself on the resource:
attributes do
attribute :alternative_state, :atom do
allow_nil? false
default :issued
public? true
constraints one_of: [:issued, :sold, :reserved, :retired]
end
endBe aware that the type of this attribute needs to be :atom or a type created with Ash.Type.Enum. Both the default and list of values need to be correct!
Making a resource into a state machine
The concept of a state machine (in this case a "Finite State Machine"), essentially involves a single state, with specified transitions between states. For example, you might have an order state machine with states [:pending, :on_its_way, :delivered]. However, you can't go from :pending to :delivered (probably), and so you want to only allow certain transitions in certain circumstances, i.e :pending -> :on_its_way -> :delivered.
This extension's goal is to help you write clear and clean state machines, with all of the extensibility and power of Ash resources and actions.