Skip to content

Instantly share code, notes, and snippets.

@Arkham
Last active May 23, 2017 15:36
Show Gist options
  • Save Arkham/bc0e4c13d9035049e021ad3a9289a750 to your computer and use it in GitHub Desktop.
Save Arkham/bc0e4c13d9035049e021ad3a9289a750 to your computer and use it in GitHub Desktop.
defmodule Enchainer.Command do
@callback do_call(term) :: {:ok, term} | {:error, term}
@callback on_success(term, term) :: term
@callback on_failure(term, term) :: term
@callback on_revert(term) :: term
defmacro __using__(_params) do
quote do
@behaviour Enchainer.Command
def call(context) do
case do_call(context) do
{:ok, value} -> on_success(value, context)
{:error, reason} -> on_failure(reason, context)
{:revert, reason} -> {:revert, reason}
end
end
def do_call(context) do
{:ok, :success}
end
def on_success(_value, context) do
context
end
def on_failure(_reason, context) do
context
end
def on_revert(context) do
context
end
defoverridable [call: 1, do_call: 1, on_success: 2, on_failure: 2, on_revert: 1]
end
end
end
defmodule First do
use Enchainer.Command
def do_call(_context), do: {:ok, true}
def on_success(_result, context) do
IO.puts "First succeeded"
context
end
def on_failure(_reason, context) do
IO.puts "First failed"
context
end
def on_revert(context) do
IO.puts "First reverted"
context
end
end
defmodule Second do
use Enchainer.Command
def do_call(_context), do: {:error, :unknown}
def on_success(_result, context) do
IO.puts "Second succeeded"
context
end
def on_failure(reason, context) do
IO.puts "Second failed"
Map.put(context, :reason, reason)
end
def on_revert(context) do
IO.puts "Second reverted"
context
end
end
defmodule Enchainer do
def chain(commands) do
initial_state = %{context: %{}, processed: []}
final_state = Enum.reduce_while(commands, initial_state, fn(command, state) ->
%{context: context, processed: processed} = state
case command.call(context) do
{:revert, _reason} -> {:halt, state}
result -> {:cont, %{result | processed: [command|processed]}}
end
end)
case final_state do
%{context: context, processed: ^commands} -> context
%{context: initial_context, processed: processed} -> Enum.reduce(processed, initial_context, fn command, context ->
command.on_revert(context)
end)
end
end
def test do
chain([First, Second])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment