defmodule Kvasir.Storage.Freezer.Hooks do
@callback pre(storage :: atom, topic :: Kvasir.Topic.t(), event :: Kvasir.Event.t()) ::
:ok | {:error, atom}
@callback post(storage :: atom, topic :: Kvasir.Topic.t(), event :: Kvasir.Event.t()) ::
:ok | {:error, atom}
end
defmodule Kvasir.Storage.Freezer.HookImpl do
@moduledoc false
@behaviour Kvasir.Storage.Freezer.Hooks
def pre(storage, topic, event)
def pre(_storage, _topic, _event), do: :ok
def post(storage, topic, event)
def post(_storage, _topic, _event), do: :ok
end
defmodule Kvasir.Storage.Freezer do
@spec configure_hooks(module :: module) :: :ok
def configure_hooks(module) do
Code.compiler_options(ignore_module_conflict: true)
Code.compile_quoted(
quote do
defmodule Kvasir.Storage.Freezer.HookImpl do
@moduledoc false
@behaviour Kvasir.Storage.Freezer.Hooks
defdelegate pre(storage, topic, event), to: unquote(module)
defdelegate post(storage, topic, event), to: unquote(module)
end
end
)
Code.compiler_options(ignore_module_conflict: false)
:ok
end
defmacro __using__(opts \\ []) do
source = opts[:source] || raise "Need to set EventSource."
topic = opts[:topic]
{storage, storage_name} = opts[:storage]
quote do
@doc false
@spec child_spec(Keyword.t()) :: map
def child_spec(_opts \\ []) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, []}
}
end
@doc false
@spec start_link :: {:ok, pid} | {:error, term}
def start_link do
unquote(source).subscribe(unquote(topic), __MODULE__, group: inspect(__MODULE__))
end
@doc false
@spec init(Kvasir.Topic.t(), non_neg_integer, term) :: {:ok, Kvasir.Topic.t()}
def init(topic, _partition, _opts), do: {:ok, topic}
@doc false
@spec event(Kvasir.Event.t(), Kvasir.Topic.t()) :: :ok | {:error, atom}
def event(event, topic) do
with :ok <- unquote(__MODULE__.HookImpl).pre(unquote(storage_name), topic, event),
:ok <- unquote(storage).freeze(unquote(storage_name), topic, event),
:ok <- unquote(__MODULE__.HookImpl).post(unquote(storage_name), topic, event),
do: :ok
end
end
end
end