• Stars
    star
    110
  • Rank 310,384 (Top 7 %)
  • Language
    Elixir
  • License
    MIT License
  • Created over 4 years ago
  • Updated about 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Database-backed state machines and statecharts for Elixir

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

More Repositories

1

hyperion

Clojure API for generic persistence.
Clojure
118
star
2

boucher

Rake tasks to admin AWS/EC2 deployed apps.
Ruby
25
star
3

cob_spec

A fitnesse suite for a web server
JavaScript
22
star
4

HangmanJava

Hangman in Java for code sparring.
Java
6
star
5

legacy_code_examples

Legacy code examples of command line Tic Tac Toe games written in various languages
C#
5
star
6

artisan_api

Artisan API Gem
Ruby
5
star
7

nodo_tanku

Nodo Tanku is a multiplayer tank game built on node.js for the inaugural Node Knockout competition.
JavaScript
4
star
8

algorithm-workshop-examples

Code examples from the algorithm workshop
Swift
3
star
9

filament

Rich client utilities for ClojureScript
Clojure
3
star
10

ocaml_koans

These are OCaml koans. Written as part of the Weirich Software Institute Mongeeses cohort
OCaml
3
star
11

CoffeeMaker

The Coffee Maker exercise - with a shell implementation
Java
2
star
12

Library

CSS
2
star
13

empty-webserver

Java
2
star
14

manifesto_public

Assets for SCM
JavaScript
2
star
15

demo_ruby_project

A template to set up new Ruby projects
Ruby
2
star
16

machine-learning

Machine Learning tools, techniques, gists and projects. Some of this code is referenced in our Blog.
Python
2
star
17

limelight_docs

Limelight documentation production
Ruby
1
star
18

SocketExample

Socket Example
Java
1
star
19

rails_postgres_livestream

Ruby
1
star
20

DigitalClock

The Broken Digital Cock - it's right more than twice a day.
Java
1
star
21

blue_green_deployment

An example Rails blue green deployment
Ruby
1
star
22

WCR2016

Ruby
1
star
23

scna5k

rails site for scna's (drumroll please) 5k
Ruby
1
star
24

thumbtrack-analytics

Analysis of site traffic and user events during RailsConf
Python
1
star
25

refactor-to-lambdas

Practise your Java 8 by removing the duplication by using lambdas.
Java
1
star
26

terraform-8thlight-modules

General purposes Terraform modules useful to 8th Light. No warranty.
1
star