View Source Alembic.Context (alembic v0.1.0)
Runtime variable store the Evaluator uses to resolve variable paths.
Behaves as a scoped symbol table: a stack of maps (scopes), head is the
innermost scope. Inner scopes shadow outer scopes without mutating them —
push_scope/2 and pop_scope/1 are pure and always return a new struct.
{% assign %} variables live in assigns, consulted after every scope.
Summary
Functions
Sets a {% assign %} variable. Always lands in the root-level assigns
map, so it persists across push_scope/2/pop_scope/1 cycles (e.g. it
survives a {% for %} loop ending).
Sets per-call custom filter modules, layered on top of (and taking
precedence over, on a name collision) whatever is registered globally via
config :alembic, custom_filters: [...]. Used by Alembic.render/3's
:custom_filters option — see Alembic.Filters.apply/4.
Builds the "forloop" metadata map injected into a {% for %} body's
scope for one iteration.
Sets the loader function Alembic.Evaluator uses to resolve
{% include %} targets. Not part of issue 1.4.1's original spec — added
when integration testing (issue 1.5.4) revealed {:include, _, _} had no
eval_node clause at all in the Evaluator (issue 1.4.2's task list never
listed it, unlike extends/block which were explicitly deferred to
Alembic.Inheritance). Threading a loader function through a new
positional parameter on every eval_nodes/eval_node call would have
touched far more of the Evaluator than storing it once here.
Looks up a single variable name, searching scopes from innermost to
outermost, then assigns last.
Wraps an initial bindings map as the root scope.
Pops the innermost scope, restoring whatever was shadowed. Raises if called on the root scope — popping past the root is a programming error.
Pushes a new innermost scope (entering a {% for %} body or an
{% include %}). Pure — returns a new struct, never mutates.
Resolves a dot-notation path, traversing nested maps, keyword lists, and list indices after the first segment.
Enables or disables strict mode: when true, an undefined variable path
makes Alembic.Evaluator return {:error, {:undefined_variable, path}}
instead of rendering as an empty string / evaluating as nil. Used by
Alembic.render/3's strict: true option (issue 1.5.3).
Types
Functions
Sets a {% assign %} variable. Always lands in the root-level assigns
map, so it persists across push_scope/2/pop_scope/1 cycles (e.g. it
survives a {% for %} loop ending).
Examples
iex> ctx = Alembic.Context.new(%{}) |> Alembic.Context.assign("count", 10)
iex> Alembic.Context.lookup(ctx, "count")
{:ok, 10}
Sets per-call custom filter modules, layered on top of (and taking
precedence over, on a name collision) whatever is registered globally via
config :alembic, custom_filters: [...]. Used by Alembic.render/3's
:custom_filters option — see Alembic.Filters.apply/4.
Examples
iex> ctx = Alembic.Context.new(%{}) |> Alembic.Context.custom_filters([MyFilter])
iex> ctx.custom_filters
[MyFilter]
@spec forloop_meta(non_neg_integer(), non_neg_integer(), any()) :: map()
Builds the "forloop" metadata map injected into a {% for %} body's
scope for one iteration.
Examples
iex> Alembic.Context.forloop_meta(0, 3, "a")
%{"forloop" => %{"index" => 1, "index0" => 0, "rindex" => 3, "rindex0" => 2, "first" => true, "last" => false, "length" => 3}}
Sets the loader function Alembic.Evaluator uses to resolve
{% include %} targets. Not part of issue 1.4.1's original spec — added
when integration testing (issue 1.5.4) revealed {:include, _, _} had no
eval_node clause at all in the Evaluator (issue 1.4.2's task list never
listed it, unlike extends/block which were explicitly deferred to
Alembic.Inheritance). Threading a loader function through a new
positional parameter on every eval_nodes/eval_node call would have
touched far more of the Evaluator than storing it once here.
Examples
iex> ctx = Alembic.Context.new(%{}) |> Alembic.Context.loader(fn _name -> {:error, :not_found} end)
iex> is_function(ctx.loader_fn, 1)
true
Looks up a single variable name, searching scopes from innermost to
outermost, then assigns last.
Examples
iex> ctx = Alembic.Context.new(%{"name" => "Alice"})
iex> Alembic.Context.lookup(ctx, "name")
{:ok, "Alice"}
iex> ctx = Alembic.Context.new(%{})
iex> Alembic.Context.lookup(ctx, "missing")
:not_found
Wraps an initial bindings map as the root scope.
Examples
iex> ctx = Alembic.Context.new(%{"name" => "Alice"})
iex> Alembic.Context.lookup(ctx, "name")
{:ok, "Alice"}
Pops the innermost scope, restoring whatever was shadowed. Raises if called on the root scope — popping past the root is a programming error.
Examples
iex> ctx = Alembic.Context.new(%{"name" => "Alice"})
iex> ctx = ctx |> Alembic.Context.push_scope(%{"name" => "Bob"}) |> Alembic.Context.pop_scope()
iex> Alembic.Context.lookup(ctx, "name")
{:ok, "Alice"}
Pushes a new innermost scope (entering a {% for %} body or an
{% include %}). Pure — returns a new struct, never mutates.
Examples
iex> ctx = Alembic.Context.new(%{"name" => "Alice"})
iex> ctx = Alembic.Context.push_scope(ctx, %{"name" => "Bob"})
iex> Alembic.Context.lookup(ctx, "name")
{:ok, "Bob"}
Resolves a dot-notation path, traversing nested maps, keyword lists, and list indices after the first segment.
Examples
iex> ctx = Alembic.Context.new(%{"user" => %{"address" => %{"city" => "Lisbon"}}})
iex> Alembic.Context.resolve_path(ctx, ["user", "address", "city"])
{:ok, "Lisbon"}
iex> ctx = Alembic.Context.new(%{"posts" => ["a", "b", "c"]})
iex> Alembic.Context.resolve_path(ctx, ["posts", "1"])
{:ok, "b"}
Enables or disables strict mode: when true, an undefined variable path
makes Alembic.Evaluator return {:error, {:undefined_variable, path}}
instead of rendering as an empty string / evaluating as nil. Used by
Alembic.render/3's strict: true option (issue 1.5.3).
Examples
iex> ctx = Alembic.Context.new(%{}) |> Alembic.Context.strict(true)
iex> ctx.strict
true