Krikri::VariableSubstitutor::LazyCrinjaContext
Inherits Crinja::Context < Crinja::Util::ScopeMap < Reference < Object
A Crinja::Context backed directly by the raw Hash(String, JSON::Any) variable scope, converting one entry to Crinja::Value
only when a template actually reads it - see
CrinjaRenderer#build_lazy_context's own comment for why (turns
O(all vars) per renderer into O(vars a template actually
references), typically 1-5 out of a context that can run into the
thousands on a real hardening/collection role).
Safe to subclass this way because Crinja::Context < Crinja::Util:: ScopeMap(String, Crinja::Value), and #[]/#has_key? are the
ONLY two ScopeMap methods anything in lib/crinja ever calls on a
context (Resolver#resolve for #[]; the {% from %} import tag
for #has_key? - checked directly, not assumed, via grep -rn 'context\[|context\.has_key\?' lib/crinja/src) - keys/values/
entries/session_bindings are never read, so a context that only
implements those two lazily is behaviorally identical to one that
eagerly materializes the whole scope up front.
Constructors
Instance methods
Precedence, matching the eager version's exact behavior (git
history - finish_crinja_vars): the "vars" magic dict always
wins over a real variable literally named "vars" (checked
first); a real variable always wins over the "omit" sentinel
default (checked only once @raw_vars itself doesn't have the
key, i.e. the old vars["omit"] ||= ...); a real variable is
otherwise looked up and memoized into scope on first read;
anything else falls through to the parent context (normally
shared_env.context, the process root - practically always
empty) or, failing that, Undefined.
ScopeMap#keys (and, in terms of it, the base class's own
values/entries/has_value?) is real - lookup('varnames', pattern) (jinja_filters.cr's own env.context.keys.select { ... }) genuinely needs every variable NAME in scope to
pattern-match against, not just the ones some earlier expression
happened to read. The original "nothing in lib/crinja ever calls
keys/entries/values on a context" audit only grepped
lib/crinja/src - true for the vendored library itself, but
missed this codebase's OWN lookup('varnames', ...)
implementation, found the hard way when crystal spec's full
suite (not this file in isolation - the pre-existing
require-ordering gap the isolation run hits masked it) caught 2
real failures. Returning just the NAMES (not converting every
value) keeps this cheap - O(N) string collection, not O(N) full
per-key conversion - so varnames stays correct without
reintroducing the eager-conversion cost this whole class exists
to avoid.