Packages
double_down
0.63.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/dispatch/stateful_handler.ex
defmodule DoubleDown.Dispatch.StatefulHandler do
@moduledoc """
Behaviour for stateful fake handler modules.
Implement this behaviour to make a stateful fake usable by module
name in `DoubleDown.Double.fallback/2..4`:
# Instead of:
Double.fallback(Repo, &Repo.OpenInMemory.dispatch/4, Repo.OpenInMemory.new())
# Write:
Double.fallback(Repo, Repo.OpenInMemory)
## Callbacks
* `new/2` — build initial state from seed data and options
* `dispatch/4` — stateful handler `(contract, operation, args, state) -> {result, new_state}`
* `dispatch/5` — stateful handler with cross-contract state access
Implement either `dispatch/4` or `dispatch/5` (or both). When both
are implemented, `dispatch/5` takes priority.
## Example
defmodule MyApp.InMemoryStore do
@behaviour DoubleDown.Dispatch.StatefulHandler
@impl true
def new(seed, _opts), do: seed
@impl true
def dispatch(_contract, :get, [id], state), do: {Map.get(state, id), state}
def dispatch(_contract, :put, [id, val], state), do: {:ok, Map.put(state, id, val)}
end
"""
@doc """
Build initial state from seed data and options.
Called by `Double.fallback/2..4` to construct the initial state for the
stateful handler.
* `seed` — seed data (e.g. `%{User => %{1 => %User{}}}` for Repo.OpenInMemory)
* `opts` — additional options (e.g. `fallback_fn: fn ... end`)
"""
@callback new(seed :: term(), opts :: keyword()) :: term()
@doc """
Stateful dispatch handler.
Receives the contract module, operation name, argument list, and
current state. Returns `{result, new_state}`.
"""
@callback dispatch(
contract :: module(),
operation :: atom(),
args :: [term()],
state :: term()
) ::
{term(), term()}
@doc """
Stateful dispatch handler with cross-contract state access.
Same as `dispatch/4` but receives a read-only snapshot of all
contract states as the 5th argument.
"""
@callback dispatch(
contract :: module(),
operation :: atom(),
args :: [term()],
state :: term(),
all_states :: map()
) ::
{term(), term()}
@optional_callbacks [dispatch: 4, dispatch: 5]
end