Skip to content

Instantly share code, notes, and snippets.

@krainboltgreene
Created December 31, 2019 06:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krainboltgreene/bfef74c367b03b0cb6a8206cf0f62022 to your computer and use it in GitHub Desktop.
Save krainboltgreene/bfef74c367b03b0cb6a8206cf0f62022 to your computer and use it in GitHub Desktop.
defmodule Estate do
defmacro machine(column_name, events) do
Enum.each(events, fn ({event_name, transitions}) ->
quote do
Enum.each(unquote(transitions), fn ({from, to}) ->
def unquote(:"before_#{event_name}")(%{unquote(column_name) => from} = record) do
record
end
def unquote(:"after_#{event_name}")(%{unquote(column_name) => to} = record) do
record
end
def unquote(event_name)(%{unquote(column_name) => from} = record) do
record
|> unquote(:"before_#{event_name}")()
|> __MODULE__.changset(%{unquote(column_name) => to})
|> unquote(:"after_#{event_name}")()
end
end)
end
end)
end
end
defmodule Poutineer.Models.Review do
import Estate, only: [machine: 2]
machine(:moderation_state,
approve: [draft: :published],
reject: [draft: :rejected],
kill: [published: :killed],
archive: [published: :archived]
)
end
iex(1)> Poutineer.Models.Review.approve(%{:moderation_state => "draft"})
** (UndefinedFunctionError) function Poutineer.Models.Review.approve/1 is undefined or private
(poutineer) Poutineer.Models.Review.approve(%{moderation_state: "draft"})
@kipcole9
Copy link

Just in case, here's a version that does what I think you intend:

defmodule Estate do
  defmacro machine(column_name, events) do
    Enum.map(events, fn ({event_name, transitions}) ->
      Enum.map(transitions, fn ({from, to}) ->
        quote do
          def unquote(:"before_#{event_name}")(%{unquote(column_name) => unquote(from)} = record) do
            record
          end

          def unquote(:"after_#{event_name}")(%{unquote(column_name) => unquote(to)} = record) do
            record
          end

          def unquote(event_name)(%{unquote(column_name) => unquote(from)} = record) do
            record
              |>  unquote(:"before_#{event_name}")()
              |> __MODULE__.changeset(%{unquote(column_name) => unquote(to)})
              |>  unquote(:"after_#{event_name}")()
          end
        end
      end)
    end)
  end
end

@kipcole9
Copy link

You might also consider get_statem which is built into OTP. And there is a new lib called StatesLanguage that makes declaring state machines very cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment