Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryanhuntesl/786f1209a046ba13651261b41af2412b to your computer and use it in GitHub Desktop.
Save bryanhuntesl/786f1209a046ba13651261b41af2412b to your computer and use it in GitHub Desktop.
Abstract base class for generated code pattern in Elixir

A mix code generator plugin (or perhaps a macro) will parse a PlantUML state machine diagram using nimble_parsec and output the following generated code:

defmodule Switch do
  defmodule Switch.Switchable do
    @callback flip_off_to_on_event(data :: Any.t()) :: Any.t()
    @callback flip_on_to_off_event(data :: Any.t()) :: Any.t()
  end

  defmacro __using__(_) do
    quote do
      @behaviour Switch.Switchable
      use GenStateMachine

      def handle_event(:cast, :flip, :off, data) do
        {:next_state, :on, __MODULE__.flip_off_to_on_event(data)}
      end

      def handle_event(:cast, :flip, :on, data) do
        {:next_state, :off, __MODULE__.flip_off_to_on_event(data)}
      end

      def handle_event({:call, from}, :get_count, state, data) do
        {:next_state, state, data, [{:reply, from, data}]}
      end

      def flip_off_to_on_event(data), do: data
      def flip_on_to_off_event(data), do: data
      defoverridable flip_off_to_on_event: 1
      defoverridable flip_on_to_off_event: 1
    end
  end
end

The implementor will then override whichever event handlers they wish to implement - the event handler is responsible for mutating the data part of the state machines state.

defmodule SwitchImpl do
  use Switch

  @impl true
  def flip_off_to_on_event(data), do: data + 1

  @impl true
  def flip_on_to_off_event(data), do: data

  def start() do
    GenStateMachine.start_link(SwitchImpl, {:off, 0})
  end

  def flip(pid), do: GenStateMachine.cast(pid, :flip)

  def get_count(pid), do: GenStateMachine.call(pid, :get_count)
end

Example of usage :

iex(2)> {_,pid} = SwitchImpl.start
{:ok, #PID<0.279.0>}
iex(3)> SwitchImpl.flip(pid)
:ok     
iex(4)> SwitchImpl.get_count(pid)
1       
iex(5)> SwitchImpl.flip(pid)     
:ok     
iex(6)> SwitchImpl.get_count(pid)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment