Packages
double_down
0.52.3
0.69.0
0.68.0
0.66.0
0.65.0
0.64.1
0.64.0
0.63.3
0.63.2
0.63.1
0.63.0
0.62.1
0.61.0
0.60.4
0.60.3
0.60.2
0.60.1
0.60.0
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.0
0.54.0
0.53.0
0.52.3
0.52.2
0.52.1
0.52.0
0.51.0
0.50.1
0.50.0
0.49.0
0.48.1
0.48.0
0.47.2
0.47.1
0.47.0
0.46.3
0.46.2
0.46.1
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.2
0.37.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.31.0
0.30.1
0.30.0
0.29.0
0.28.1
0.28.0
0.27.0
0.26.0
0.24.0
Builds on the Mox pattern — generates behaviours and dispatch facades from `defcallback` declarations — and adds stateful test doubles powerful enough to test Ecto.Repo operations without a database.
Current section
Files
Jump to
Current section
Files
lib/double_down/contract/dispatch/handler_meta.ex
defmodule DoubleDown.Contract.Dispatch.HandlerMeta do
@moduledoc """
Structs describing the three handler types that can be installed for
a contract via `DoubleDown.Testing`.
Stored in `NimbleOwnership` under the contract module atom as key.
`DoubleDown.Contract.Dispatch.invoke_handler/5` pattern-matches on
the struct to select the dispatch strategy.
## Variants
* `HandlerMeta.Module` — delegate to a module implementing the contract behaviour
* `HandlerMeta.Stateless` — dispatch via a 3-arity `fn contract, operation, args -> result end`
* `HandlerMeta.Stateful` — dispatch via a 4/5-arity stateful function with
mutable state stored inline in the `:state` field
"""
defmodule Module do
@moduledoc "Handler meta for a module-based implementation."
@enforce_keys [:impl]
defstruct [:impl]
@type t :: %__MODULE__{
impl: module()
}
@doc "Create a new Module handler meta. Validates that `impl` is an atom."
@spec new(module()) :: t()
def new(impl) when is_atom(impl), do: %__MODULE__{impl: impl}
end
defmodule Stateless do
@moduledoc "Handler meta for a stateless 3-arity function handler `(contract, operation, args)`."
@enforce_keys [:fun]
defstruct [:fun]
@type t :: %__MODULE__{
fun: DoubleDown.Contract.Dispatch.Types.stateless_fun()
}
@doc "Create a new Stateless handler meta. Validates that `fun` is a 3-arity function."
@spec new(DoubleDown.Contract.Dispatch.Types.stateless_fun()) :: t()
def new(fun) when is_function(fun, 3), do: %__MODULE__{fun: fun}
end
defmodule Stateful do
@moduledoc """
Handler meta for a stateful (4/5-arity) function handler.
The `:state` field holds the mutable handler state directly —
for raw `set_stateful_handler` this is user-provided state,
for `Double`-managed handlers it is a `CanonicalHandlerState` struct.
"""
@enforce_keys [:fun, :state]
defstruct [:fun, :state]
@type t :: %__MODULE__{
fun: DoubleDown.Contract.Dispatch.Types.stateful_fun(),
state: term()
}
@doc "Create a new Stateful handler meta. Validates that `fun` is a 4 or 5-arity function."
@spec new(DoubleDown.Contract.Dispatch.Types.stateful_fun(), term()) :: t()
def new(fun, state) when is_function(fun, 4) or is_function(fun, 5) do
%__MODULE__{fun: fun, state: state}
end
@doc "Replace the state within a Stateful handler meta."
@spec put_state(t(), term()) :: t()
def put_state(%__MODULE__{} = meta, new_state) do
%{meta | state: new_state}
end
@doc "Update the state within a Stateful handler meta via a function."
@spec update_state(t(), (term() -> term())) :: t()
def update_state(%__MODULE__{state: state} = meta, update_fn)
when is_function(update_fn, 1) do
%{meta | state: update_fn.(state)}
end
end
end