mirror of
https://github.com/ash-project/ash_state_machine.git
synced 2024-09-20 21:33:30 +12:00
122 lines
3.6 KiB
Markdown
122 lines
3.6 KiB
Markdown
# 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()
|
|
[
|
|
...
|
|
{:ash_state_machine, "~> 0.1.4"}
|
|
]
|
|
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
|
|
# create sets the state
|
|
defaults [:create, :read]
|
|
|
|
update :confirm do
|
|
# accept [...]
|
|
# you can change other attributes
|
|
# 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
|
|
})
|
|
|> Api.update()
|
|
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
|
|
```
|
|
|
|
## Learning more
|
|
|
|
- Check out the DSL documentation in `AshStateMachine`
|
|
|