Skip to content
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Ecto state machine

travis ci badge badge

This package allows to use finite state machine pattern in Ecto. Specify:

and go:

defmodule User do
  use Web, :model

  use EctoStateMachine,
    states: [:unconfirmed, :confirmed, :blocked, :admin],
    events: [
        name:     :confirm,
        from:     [:unconfirmed],
        to:       :confirmed,
        callback: fn(model) -> Ecto.Changeset.change(model, confirmed_at: Ecto.DateTime.utc) end # yeah you can bring your own code to these functions.
      ], [
        name:     :block,
        from:     [:confirmed, :admin],
        to:       :blocked
      ], [
        name:     :make_admin,
        from:     [:confirmed],
        to:       :admin

  schema "users" do
    field :state, :string, default: "unconfirmed"

now you can do:

user = Repo.get_by(User, id: 1)

# Create changeset transition user state to "confirmed". We can make him admin!
confirmed_user = User.confirm(user)     # =>

# We can validate ability to change user's state
User.can_confirm?(confirmed_user)       # => false
User.can_make_admin?(confirmed_user)    # => true

# Create changeset transition user state to "admin"
admin = User.make_admin(confirmed_user)

# Store changeset to the database

# List all possible states
# If column isn't `:state`, function name will be prefixed. IE,
# for column `:rules` function name will be `rules_states`
User.states # => [:unconfirmed, :confirmed, :blocked, :admin]

# List all possible events
# If column isn't `:state`, function name will be prefixed. IE,
# for column `:rules` function name will be `rules_events` # => [:confirm, :block, :make_admin]

You can check out whole test/dummy directory to inspect how to organize sample app.


If available in Hex, the package can be installed as:

  1. Add ecto_state_machine to your list of dependencies in mix.exs:

    def deps do [{:ecto_state_machine, "~> 0.1.0"}] end

Custom column name

ecto_state_machine uses state database column by default. You can specify column option to change it. Like this:

defmodule Dummy.User do
  use Dummy.Web, :model

  use EctoStateMachine,
    column: :rules,
    # bla-bla-bla

Now your state will be stored into rules column.


  1. Install dependencies mix deps.get
  2. Setup your config/test.exs & config/dev.exs
  3. Run migrations mix ecto.migrate & MIX_ENV=test mix ecto.migrate
  4. Develop new feature
  5. Write new tests
  6. Test it: mix test
  7. Open new PR!

Roadmap to 1.0

  • Cover by tests
  • Custom db column name
  • Validation method for changeset indicates its value in the correct range
  • Initial value
  • CI
  • Add status? methods
  • Introduce it at elixir-radar and my blog
  • Custom error messages for changeset (with translations by gettext ability)
  • Rely on last versions of ecto & elixir
  • Write dedicated module instead of requiring everything into the model
  • Write bang! methods which are raising exception instead of returning invalid changeset
  • Rewrite spaghetti description in README