module

Krikri::VariableSubstitutor

The ONE shared "recursive re-templating" helper: re-renders value when its raw form is still a String containing Jinja markers. This used to exist as four independently-maintained copies (FilterEngine's, ComparisonEvaluator's, ConditionalEvaluator's class methods, and VariableLookup's - the last deliberately NOT folded in here, see below). Every one of the folded-in copies had the same multi-span bug ("{{ a }}-{{ b }}" mangled by naive 2-char slicing) and the same "{%"/"{#" block-tag gap fixed independently, and each fix had to be re-applied to the others by hand - the comment trails in the originals documented that treadmill explicitly.

Algorithm (identical to what the three folded copies did):

  • nil/untouched values pass through unchanged (also when vars is nil - FilterEngine's optional-context case).
  • {%/{# block tags or comments go through the FULL Crinja renderer (ExpressionEvaluator has no concept of block tags).
  • exactly one {{ }} span spanning the WHOLE string goes through ExpressionEvaluator directly - the only path that preserves a non-string result type.
  • anything else (multiple spans, literal text around a span) goes through VarSubstitutor#substitute.

VariableLookup keeps its own copy ON PURPOSE: it routes the mixed/ multi-span case through CrinjaRenderer instead of VarSubstitutor, a deliberate behavioral difference fixed after real-host bugs - see its own comments before even thinking about unifying that one too.

Class methods

walk_dotted_path(base : JSON::Any, parts : Indexable(String)) : JSON::Any | Nil

The ONE shared dotted-path walker for plain hash navigation (result.rc, ansible_facts.os_family) - resolves parts (the split of a dotted expression) against base by successive Hash lookups, nil on the first miss or on a non-Hash hop. Used to exist as three divergent copies (VariableLookup, ComparisonEvaluator, ArraySlicer); VariableLookup keeps its own richer walker ON PURPOSE (it also handles list indexing, numeric dot-indexing and method calls - see its own comments), but the two simple consumers now share this one so a fix lands once for both.

Source

Nested types