class

Krikri::HostVarsVarsDict

Inherits Crinja::Object < Reference < Object

The per-host dicts inside hostvars in a Crinja render. Real Ansible wraps each host's vars in its own HostVarsVars object, whose attribute lookup RAISES on a miss (object of type 'HostVarsVars' has no attribute 'ansible_enp0s8' - a typo'd/computed interface fact name fails the task rather than rendering an empty value in its place; found via mrlesmithjr.ansible_consul_client's hostvars[inventory_hostname]['ansible_' + consul_client_bind_interface] with a bind interface that doesn't exist on the real host). Crinja's plain dict resolution (Resolver#resolve_with_hash_accessor) falls back to a lenient Undefined on the same miss, and that path can't be made blanket-strict (it is also the fallback for method-call dispatch and for this engine's own fact-coverage gaps - see Krikri::StrictTemplating's own comment). So the strictness rides on the hostvars VALUE ITSELF: the dict only reaches a render through the hostvars conversion (CrinjaRenderer.convert_hostvars), and its []? raises on a miss exactly when strict templating is enabled for the rendering fiber - every non-hostvars dict stays lenient, and outside strict mode (where the lenient {{ ... }} hand-rolled evaluator and when: conditions read these values) the behavior is unchanged. A Crinja::Object wrapper rather than a Hash subclass: Crinja::Value.new NORMALIZES any Hash into a plain Crinja::Dictionary (Crinja.value's Hash case), which would silently strip a subclass - only Crinja::Object instances survive as their own raw object.

Constructors

new(entries : Hash(String, Crinja::Value))
Source

Instance methods

crinja_attribute(attr : Crinja::Value) : Crinja::Value

Both the attribute form (hostvars[h].ansible_host) and the subscript form (hostvars[h]['ansible_host']) funnel through resolve_getattr -> crinja_attribute for a Crinja::Object.

A strict miss does NOT raise at lookup time: it returns an Undefined that only raises when actually forced (printed or compared). Raising at lookup time made is defined itself hard- fail the render (found via mullholland.motd round72000: {% if hostvars[inventory_hostname]['ansible_' ~ int] is defined %} with a genuinely-absent computed key - real Ansible takes the false branch, ok=3 changed=2), defeating the one construct that is supposed to never raise for a missing attribute. Deferring to force time keeps a plain {{ hostvars[h].typo }} failing the task with the exact message real Ansible's own HostVarsVars wrapper raises, while is defined, | default(...), and {% if %} all see a genuine Undefined.

The deferred raise is a plain RuntimeError, NOT an UndefinedError: Crinja's own evaluator rescues UndefinedError around attribute resolution and re-raises a generic "hostvars[node1][x] is undefined" that DISCARDS the cause's message - real Ansible's failure text for the plain-print case is the wrapper's own "object of type 'HostVarsVars' has no attribute ..." (an AttributeError surfacing verbatim), so the detail has to survive. A RuntimeError is not swallowed anywhere in the render path and surfaces the message as the task failure.

Source
crinja_call(name : String) : Crinja::Callable | Crinja::Callable::Proc | Nil
Source
each
Source
has_key?(*args, **options)

dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.

Source
has_key?(*args, **options, &)

dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.

Source
keys(*args, **options)

dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.

Source
keys(*args, **options, &)

dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.

Source
size(*args, **options)

dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.

Source
size(*args, **options, &)

dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.

Source
to_s(io : IO) : Nil

Appends a short String representation of this object which includes its class name and its object address.

class Person
  def initialize(@name : String, @age : Int32)
  end
end

Person.new("John", 32).to_s # => #<Person:0x10a199f20>
Source

Nested types