Generates a dispatch facade for a DoubleDown.Contract.
use DoubleDown.ContractFacade reads a contract's __callbacks__/0 metadata
and generates facade functions and key helpers that
dispatch via DoubleDown.Contract.Dispatch.
Combined contract + facade (simplest)
When :contract is omitted, it defaults to __MODULE__ and
use DoubleDown.Contract is issued implicitly. This gives a single-module
contract + facade:
defmodule MyApp.Todos do
use DoubleDown.ContractFacade, otp_app: :my_app
defcallback get_todo(id :: String.t()) :: {:ok, Todo.t()} | {:error, term()}
defcallback list_todos() :: [Todo.t()]
endMyApp.Todos is both the contract (has @callbacks, __callbacks__/0)
and the dispatch facade.
Separate contract and facade
For cases where you want the contract in a different module:
defmodule MyApp.Todos do
use DoubleDown.ContractFacade, contract: MyApp.Todos.Contract, otp_app: :my_app
endOptions
:contract— the contract module that defines port operations viause DoubleDown.Contractanddefcallbackdeclarations. Defaults to__MODULE__(combined contract + facade).:otp_app(required) — the OTP application name for config-based dispatch. Implementations are resolved fromApplication.get_env(otp_app, contract)[:impl].:test_dispatch?— controls whether the generated facade includes theNimbleOwnership-based test handler resolution step. Acceptstrue,false, or a zero-arity function returning a boolean. The function is evaluated at compile time. Defaults tofn -> Mix.env() != :prod end, so production builds get a config-only dispatch path with zeroNimbleOwnershipoverhead.:static_dispatch?— whentrueand:test_dispatch?isfalse, reads the implementation module from config at compile time viaApplication.compile_env/3and generates direct function calls — eliminating theApplication.get_envlookup at runtime entirely. Falls back to runtime config dispatch if the config is not available at compile time. Acceptstrue,false, or a zero-arity function. Defaults tofn -> Mix.env() == :prod end.
See also
DoubleDown.BehaviourFacade— generates dispatch facades for vanilla@behaviourmodules (when you don't control the contract definition).DoubleDown.DynamicFacade— Mimic-style bytecode interception for any module.
Configuration
# config/config.exs
config :my_app, MyApp.Todos, impl: MyApp.Todos.EctoTesting
# test/test_helper.exs
DoubleDown.Testing.start()
# test/my_test.exs
setup do
DoubleDown.Testing.set_fn_handler(MyApp.Todos, fn
:get_todo, [id] -> {:ok, %Todo{id: id}}
:list_todos, [] -> []
end)
:ok
end
test "gets a todo" do
assert {:ok, %Todo{}} = MyApp.Todos.get_todo("42")
end