Use this cheatsheet to quickly lookup the APIs for XFsm.

Examples

Creating a state machine

defmodule ToggleMachine do
  use XFsm.Actor
  use XFsm.Machine

  initial :active
  context %{count: 0}

  state :active do
    entry :assign, &increment/1

    on :toggle, do: target :inactive
  end

  state :inactive do
    on :toggle, do: target :active
  end

  def increment(%{context: context}), do: %{count: context.count + 1}
end

alias XFsm.Actor

{:ok, pid} = ToggleMachine.start_link()

Actor.subscribe(pid, &IO.puts/1)

Actor.send(pid, %{type: :toggle})
# logs 'inactive' with context %{count: 1}

Actor.send(pid, %{type: :toggle})
# logs 'active' with context %{count: 2}

Actor.send(pid, %{type: :toggle})
# logs 'inactive' with context %{count: 2}

Guards

defmodule ToggleMachine do
  use XFsm.Actor
  use XFsm.Machine

  initial :active
  context %{activate?: false}

  state :inactive do
    on :toggle do
      target :active
      guard :toggle?
    end

    on :toggle do
      action :notify_not_allowed
    end
  end

  state :active do
    on :toggle do
      target :inactive
      guard :after_time?, %{time: "00:00"}
    end
  end

  def toggle?(%{context: context}), do: context.activate?

  def after_time?(_, %{time: time}) do
    now = DateTime.utc_now()
    [hour, minute] = String.split(time, ":")
    {hour, ""} = Integer.parse(hour)
    {minute, ""} = Integer.parse(minute)

    now.hour > hour and now.minute > minute
  end

  def notify_not_allowed(%{context: context}) do
    IO.puts("Cannot be toggled")
    context
  end
end

Actions

defmodule ToggleMachine do
  use XFsm.Actor
  use XFsm.Machine

  initial :active

  state :active do
    entry :activate
    exit :deactivate

    on :toggle do
      target :inactive
      action :notify
    end
  end

  state :inactive do
    on :toggle do
      target :active
      action :notify, %{message: "Some notification"}
    end
  end

  def activate(_) do
    # ...
  end

  def deactivate(_) do
    # ...
  end

  def notify(_) do
    # ...
  end

  def notify(_,  %{message: _}) do
    # ...
  end
end