Contexir.Layer (Contexir v0.2.0)
View SourceProvides the DSL for defining layers — refinements to existing functions that can be activated dynamically at runtime.
A “layer” is a module that declares zero or more partials (refinements) for existing functions, specifying how they should be modified under certain contexts.
Through this DSL you can:
- Define layers (via
deflayer) - Define refinements (partials) for specific functions, choosing execution mode:
:before,:around, or:afterviadefpartial. - Group layers (via
use_layers) so you can compose sets of behavior as a single logical layer.
This is the core mechanism that makes the rest of Contexir — dispatching, context propagation, and dynamic behavior — possible.
DSL Overview
deflayer LoggingLayer do
# runs before the original function
defpartial SomeModule.some_fun(arg1, arg2, ctx), mode: :before do
IO.puts("about to call some_fun")
end
# wraps around the original — must call `continue/3`
defpartial SomeModule.some_fun(arg1, arg2, ctx), mode: :around do
IO.puts("entering")
result = continue(SomeModule, :some_fun, [arg1, arg2, ctx])
IO.puts("exiting")
result
end
# runs after the original function
defpartial SomeModule.some_fun(arg1, arg2, ctx), mode: :after do
IO.puts("some_fun finished")
end
end
Summary
Functions
Declares that the current layer should run after another layer.
Declares that the current layer should run before another layer.
Declares that the current layer cannot be active with another layer.
Declares a context module that can activate layers from context values.
Declares a new layer module.
Defines a partial function — a refinement of an existing function from another module.
Returns compile-time metadata for a layer module.
Declares a context-activated layer inside defcontext.
Declares that the current layer requires another layer to be active.
Resolves layer composition metadata without activating the layers.
Resolves layer composition metadata or raises when relationships are invalid.
Resolves declarative context rules against a context map.
Resolves declarative context rules or raises when relationships are invalid.
Includes or reuses other layers inside the current one.
Functions
Declares that the current layer should run after another layer.
Declares that the current layer should run before another layer.
Declares that the current layer cannot be active with another layer.
Declares a context module that can activate layers from context values.
Declares a new layer module.
The deflayer macro defines a standard Elixir module configured as a Contexir
layer. Inside the block, you can use defpartial/3 to define refinements for
specific base module functions.
Example
deflayer LoggingLayer do
defpartial Account.withdraw(acc, amt, ctx), mode: :before do
IO.puts("[BEFORE] withdrawing #{amt}")
end
end
Defines a partial function — a refinement of an existing function from another module.
The defpartial macro declares behavior for a specific mode:
:before, :around, or :after.
:before— runs before the primary function:around— wraps the next layer or base function (must callcontinue/3):after— runs after the primary function returns
Example
defpartial Account.withdraw(acc, amt, ctx), mode: :around do
IO.puts("[AROUND] start")
result = continue(Account, :withdraw, [acc, amt, ctx])
IO.puts("[AROUND] end")
result
end
Returns compile-time metadata for a layer module.
Declares a context-activated layer inside defcontext.
Declares that the current layer requires another layer to be active.
Resolves layer composition metadata without activating the layers.
Resolution expands use_layers, removes duplicate layers while preserving the
first occurrence, and validates requires and conflicts_with relationships.
Resolves layer composition metadata or raises when relationships are invalid.
Resolves declarative context rules against a context map.
Resolves declarative context rules or raises when relationships are invalid.
Includes or reuses other layers inside the current one.
This macro allows grouping multiple layers together under a single layer name, so they can be activated as a unit. The composed layer will automatically delegate to all included layers during dispatch.
Example
deflayer SecureLayer do
use_layers [AuthLayer, LoggingLayer]
end
Contexir.with_layers [SecureLayer] do
Account.withdraw(%{balance: 100}, 10, %{user_authenticated: true})
endThis is equivalent to activating [AuthLayer, LoggingLayer] together.