ash_state_machine/documentation/tutorials/get-started-with-state-machines.md

123 lines
3.6 KiB
Markdown
Raw Normal View History

2023-04-23 13:38:33 +12:00
# Getting Started with State Machines
## Get familiar with Ash resources
If you haven't already, read the [Ash Getting Started Guide](https://hexdocs.pm/ash/get-started.html), and familiarize yourself with Ash and Ash resources.
## Bring in the ash_state_machine dependency
```elixir
def deps()
[
...
2023-08-05 09:48:38 +12:00
{:ash_state_machine, "~> 0.1.5"}
2023-04-23 13:38:33 +12:00
]
end
```
## 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.
## A basic state machine
```elixir
defmodule Order do
# leaving out data layer configuration for brevity
use Ash.Resource,
extensions: [AshStateMachine]
state_machine do
initial_states [:pending]
default_initial_state :pending
transitions do
transition :confirm, from: :pending, to: :confirmed
transition :begin_delivery, from: :confirmed, to: :on_its_way
transition :package_arrived, from: :on_its_way, to: :arrived
transition :error, from: [:pending, :confirmed, :on_its_way], to: :error
end
end
actions do
2023-04-23 18:44:30 +12:00
# create sets the state
2023-04-23 13:38:33 +12:00
defaults [:create, :read]
update :confirm do
2023-04-23 18:44:30 +12:00
# accept [...]
# you can change other attributes
2023-04-23 13:38:33 +12:00
# or do anything else an action can normally do
# this transition will be validated according to
# the state machine rules above
change transition_state(:confirmed)
end
update :begin_delivery do
# accept [...]
change transition_state(:on_its_way)
end
update :package_arrived do
# accept [...]
change transition_state(:arrived)
end
update :error do
accept [:error_state, :error]
change transition_state(:error)
end
end
changes do
# any failures should be captured and transitioned to the error state
change after_transaction(fn
changeset, {:ok, result} ->
{:ok, result}
changeset, {:error, error} ->
message = Exception.message(error)
changeset.data
|> Ash.Changeset.for_update(:error, %{
message: message,
error_state: changeset.data.state
})
2023-04-23 18:44:30 +12:00
|> Api.update()
2023-04-23 13:38:33 +12:00
end),
on: [:update]
end
attributes do
uuid_primary_key :id
# ...attributes like address/delivery options would go here
attribute :error, :string
attribute :error_state, :string
# :state attribute is added for you by `state_machine`
# however, you can add it yourself, and you will be guided by
# compile errors on what states need to be allowed by your type.
end
end
```
## Generating Flow Charts
run `mix ash_state_machine.generate_flow_charts` to generate flow charts for your resources. See the task documentation for more. Here is a chart generated from the example above:
```mermaid
stateDiagram-v2
pending --> confirmed: confirm
confirmed --> on_its_way: begin_delivery
on_its_way --> arrived: package_arrived
on_its_way --> error: error
confirmed --> error: error
pending --> error: error
2023-04-23 13:38:33 +12:00
```
2023-04-23 18:47:22 +12:00
## Learning more
- Check out the DSL documentation in `AshStateMachine`
2023-04-23 13:38:33 +12:00