Skip to content

Database-backed state machines and statecharts for Elixir

License

Notifications You must be signed in to change notification settings

8thlight/ex_state

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ExState

Hex.pm Hex Docs

Elixir state machines, statecharts, and workflows for Ecto models.

Installation

If available in Hex, the package can be installed by adding ex_state to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_state_ecto, "~> 0.3"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/ex_state.

Usage

Without Ecto

Ecto Setup

defmodule MyApp.Repo.Migrations.AddWorkflows do
  def up do
    # Ensure Ecto.UUID support is enabled:
    execute("CREATE EXTENSION IF NOT EXISTS pgcrypto")

    ExState.Ecto.Migration.up()
  end

  def down do
  end
end
config :ex_state, repo: MyApp.Repo

Defining States

Define the workflow:

defmodule SaleWorkflow do
  use ExState.Definition

  alias MyApp.Repo

  workflow "sale" do
    subject :sale, Sale

    participant :seller
    participant :buyer

    initial_state :pending

    state :pending do
      on :send, :sent
      on :cancel, :cancelled
    end

    state :sent do
      parallel do
        step :acknowledge_receipt, participant: :buyer
        step :close, participant: :seller
      end

      on :cancelled, :cancelled
      on_completed :acknowledge_receipt, :receipt_acknowledged
      on_completed :close, :closed
    end

    state :receipt_acknowledged do
      step :close, participant: :seller
      on_completed :close, :closed
    end

    state :closed

    state :cancelled do
      on_entry :update_cancelled_at
    end
  end

  def guard_transition(:pending, :sent, %{sale: %{address: nil}}) do
    {:error, "missing address"}
  end

  def guard_transition(_from, _to, _context), do: :ok

  def update_cancelled_at(%{sale: sale}) do
    sale
    |> Sale.changeset(%{cancelled_at: DateTime.utc_now()})
    |> Repo.update()
  end
end

Add the workflow association to the subject:

defmodule Sale do
  use Ecto.Schema
  use ExState.Ecto.Subject

  import Ecto.Changeset

  schema "sales" do
    has_workflow SaleWorkflow
    field :product_id, :string
    field :cancelled_at, :utc_datetime
  end
end

Add a workflow_id column to the subject table:

alter table(:sales) do
  add :workflow_id, references(:workflows, type: :uuid)
end

Transitioning States

Using ExState.transition/3:

def create_sale(params) do
  Multi.new()
  |> Multi.insert(:sale, Sale.new(params))
  |> ExState.Ecto.Multi.create(:sale)
  |> Repo.transaction()
end

def cancel_sale(id, user_id: user_id) do
  sale = Repo.get(Sale, id)

  ExState.transition(sale, :cancel, user_id: user_id)
end

Using ExState.Execution.transition_maybe/2:

sale
|> ExState.create()
|> ExState.Execution.transition_maybe(:send)
|> ExState.persist()

Using ExState.Execution.transition/2:

{:ok, execution} =
  sale
  |> ExState.load()
  |> ExState.Execution.transition(:cancelled)

ExState.persist(execution)

Using ExState.Execution.transition!/2:

sale
|> ExState.load()
|> ExState.Execution.transition!(:cancelled)
|> ExState.persist()

Completing Steps

def acknowledge_receipt(id, user_id: user_id) do
  sale = Repo.get(Sale, id)

  ExState.complete(sale, :acknowledge_receipt, user_id: user_id)
end

Running Tests

Setup test database

MIX_ENV=test mix ecto.create
mix test

TODO

  • Extract ex_state_core, and other backend / db packages.
  • Multiple workflows per subject.
  • Allow configurable primary key / UUID type for usage across different databases.
  • Tracking event history with metadata.
  • Add SCXML support
  • Define schema for serialization / json API usage / client consumption.
  • Parallel states
  • History states

About

Database-backed state machines and statecharts for Elixir

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages