Machinery
Machinery is a State Machine library for structs in general that integrates with Pheonix out of the box. It also aims to have (when implemented with Phoenix) an optional build-in GUI that will represent each resource’s state.
Don’t forget to check the Machinery Docs
Installing
The package can be installed by adding machinery
to your list of
dependencies in mix.exs
:
def deps do
[
{:machinery, "~> 0.4.1"}
]
end
Declaring States
Declare the states as an argment when importing Machinery
on the module that
will control your states transitions.
Machinery expects a Keyword
as argument with two keys states
and transitions
.
states
: A List of Atoms representing each state.transitions
: A Map for each state and it allowed next state(s).
Example
defmodule YourProject.UserStateMachine do
use Machinery,
# The first state declared will be considered
# the intial state
states: [:created, :partial, :complete],
transitions: %{
created: [:partial, :complete],
partial: :completed
}
end
Changing States
To transit a struct into another state, you just need to call Machinery.transition_to/2
.
Machinery.transition_to/2
It takes two arguments:
struct
: Thestruct
you want to transit to another state.next_event
: Anatom
of the next state you want the struct to transition to.
Guard functions, before and after callbacks will be checked automatically.
Machinery.transition_to(your_struct, :next_state)
# {:ok, updated_struct}
Example:
user = Accounts.get_user!(1)
UserStateMachine.transition_to(user, :partial)
Guard functions
Create guard conditions by adding signatures of the guard_transition/2
function, pattern matching the desired state you want to guard.
def guard_transition(struct, :state), do: true
Guard conditions should return a boolean:
true
: Guard clause will allow the transition.false
: Transition won’t be allowed.
Example:
defmodule YourProject.UserStateMachine do
use Machinery,
states: [:created, :complete],
transitions: %{created: :complete}
# Guard the transition to the :complete state.
def guard_transition(struct, :complete) do
Map.get(struct, :missing_fields) == false
end
end
Before and After callbacks
You can also use before and after callbacks to handle desired side effects and reactions to a specific state transition.
You can just declare before_transition/2
and after_transition/2
,
pattern matching the desired state you want to.
Make sure Before and After callbacks should return the struct.
# callbacks should always return the struct.
def before_transition(struct, :state), do: struct
def after_transition(struct, :state), do: struct
Example:
defmodule YourProject.UserStateMachine do
use Machinery,
states: [:created, :partial, :complete],
transitions: %{
created: [:partial, :complete],
partial: :completed
}
def before_transition(struct, :partial) do
# ... overall desired side effects
struct
end
def after_transition(struct, :completed) do
# ... overall desired side effects
struct
end
end