Packages

Adds callbacks/hooks to Ecto: `after_insert`, `after_update`, `after_delete`, `after_get`, `before_insert`, `before_update`, `before_delete`. Useful for setting virtual fields and centralising logic.

Current section

Files

Jump to
ecto_hooks lib ecto_middleware resolution.ex
Raw

lib/ecto_middleware/resolution.ex

defmodule EctoMiddleware.Resolution do
@moduledoc "Struct for holding middleware resolution data"
@type t :: %__MODULE__{}
defstruct [
:repo,
:action,
:args,
:middleware,
:entity,
:before_middleware,
:after_middleware,
:before_input,
:before_output,
:after_input,
:after_output
]
defmacro new!(args) do
{caller, _arity} = __CALLER__.function
quote bind_quoted: [self: __MODULE__, action: caller, args: args] do
entity = List.first(args)
middleware = EctoMiddleware.middleware(__MODULE__, action, entity)
{before_middleware, after_middleware} =
EctoMiddleware.partition_middleware(__MODULE__, action, entity)
struct!(self,
repo: __MODULE__,
entity: entity,
action: action,
args: args,
middleware: middleware,
before_middleware: before_middleware,
after_middleware: after_middleware
)
end
end
@doc """
Executes all of the configured "before" middleware for the given resolution.
This function is intended to be used by the `EctoMiddleware` module, but can also be
used directly if you need to execute the "before" middleware for testing purposes.
Provide a `resolution` struct as the argument with `action`, `args`, and `entity` fields,
alongside the `repo` module that the middleware is being executed for.
"""
@spec execute_before!(t()) :: t()
def execute_before!(%__MODULE__{} = resolution) do
resolution = %__MODULE__{resolution | before_input: List.first(resolution.args)}
before_output =
resolution
|> Map.get(:before_middleware, [])
|> Enum.reduce(resolution.before_input, & &1.middleware(&2, resolution))
%__MODULE__{resolution | before_output: before_output}
end
@doc """
Executes all of the configured "after" middleware for the given resolution.
This function is intended to be used by the `EctoMiddleware` module, but can also be
used directly if you need to execute the "after" middleware for testing purposes.
Provide a `resolution` struct as the argument with `action`, `args`, and `entity` fields,
alongside the `repo` module that the middleware is being executed for, alongside the
expected return value of the given `Ecto.Repo` callback.
"""
@spec execute_after!(t(), input :: term()) :: t()
def execute_after!(%__MODULE__{} = resolution, input) do
resolution = %__MODULE__{resolution | after_input: input}
after_output =
resolution
|> Map.get(:after_middleware, [])
|> Enum.reduce(resolution.after_input, & &1.middleware(&2, resolution))
%__MODULE__{resolution | after_output: after_output}
end
end