Current section
Files
Jump to
Current section
Files
lib/simple_ecto_state_machine.ex
defmodule SimpleEctoStateMachine do
@moduledoc """
A simple state machine for Ecto Models. It works on the changeset of the
model when the changeset is updated. When a state machine is `use`d inside
and Ecto model, a function of the form `validate_{field}_update(changeset)` is
defined in the model, ready to test the changeset when is updated.
```
defmodule MyModule do
use Ecto.Model
schema "my_module" do
field :status
end
use SimpleEctoStateMachine,
field: :status,
transitions: [
%{from: "status_a". to: ["status_b, "status_c"]},
%{from: "status_b", to: ["status_c"]}
]
def changeset(model, params \\ :empty) do
model
|> cast(params, ["status"], [])
|> validate_status_update # <= Function defined by
# SimpleEctoStateMachine at compile time.
end
end
```
Callbacks can be defined to be called in the case of success or error.
Callbacks can be provided per destination. In case of a successful transition
the callback with the form `{to}_callback` will be called. The error callback
must be named `state_machine_error_callback` and will be called if a
transition with a valid `from` is provided but the new value does not match
any of the valid `to`s, i.e:
```
def success_callback(_changeset) do
IO.puts "Valid transition!"
end
def error_callback(_changeset) do
IO.puts "Invalid transition."
end
use SimpleEctoStateMachine,
field: status
transitions: [
%{
from: "status_a",
to: ["status_b, status_c"],
status_a_callback: &__MODULE__.success_callback/1,
status_b_callback: &__MODULE__.success_callback/1,
error_callback: &__MODULE__.error.callback/1
}
]
```
Make sure to only define one transition per `from`, since repeating `from`s
lead to an error in the state machine. All possible transitions for a `from`
must be defined in a single transition.
"""
defmacro __using__(opts) do
fn_name = :"validate_#{opts[:field]}_update"
quote do
def unquote(fn_name)(changeset, arg \\ nil) do
alias SimpleEctoStateMachine.Utils
opts = Map.new(unquote(opts))
Utils.execute_transition_if_valid(changeset, arg, opts)
end
end
end
end