Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

wire.values

import wire.values

wire lifts part of this module out to its own top level; each name below is shown with the path that reaches it. Anything still spelled wire.values.* needs import wire.values.

How Wire reads the values a template is given: what counts as true, what a value looks like once it reaches the page, and how a string says that it is already markup and must not be escaped again.

Constants

MAX_ARGUMENTS

wire.values.MAX_ARGUMENTS = 8

The most arguments a filter or a template function can be called with.

Zuri has no way to spread a list into a call, so an argument list built at render time has to be handed over one position at a time and the positions have to be written out. Eight is far past what any filter has ever wanted and stops the dispatch below from growing without end.

Functions

safe()

wire.safe(value) -> Safe

Marks value as markup that Wire must not escape.

A Safe passed back in is returned unchanged rather than wrapped twice. Anything that is not already a string is rendered with stringify() first, so safe(nil) is an empty string rather than the word “nil”.

Parameters

  • value (any)

Returns Safe

is_safe()

wire.is_safe(value) -> bool

True when value is a Safe.

Parameters

  • value (any)

Returns bool

unwrap()

wire.values.unwrap(value) -> any

Strips the Safe wrapper off value, leaving anything else alone.

Filters work on plain values, so this runs on the way into one and the filter decides for itself whether its result is markup.

Parameters

  • value (any)

Returns any

stringify()

wire.stringify(value) -> string

What value looks like once it reaches the page, before escaping.

nil renders as an empty string rather than as the word “nil”, which is what lets an optional variable be dropped into a page without a guard around it. Everything else is rendered by its own to_string().

Parameters

  • value (any)

Returns string

truthy()

wire.truthy(value) -> bool

Whether value counts as true in an x-if, an x-not, or a boolean operator inside an expression.

Wire deliberately does not reuse Zuri’s own truthiness here, because one of Zuri’s answers is wrong for a template: Zuri treats an empty list and an empty dictionary as true, so x-if="items" would render a section for a search that found nothing. Wire treats an empty collection as false.

Everything else agrees with Zuri: nil and false are false, zero and NaN are false while every other number is true, an empty string is false, and anything Wire has no special knowledge of (an instance, a function, a range) is true.

A variable that was never supplied arrives here as nil, which is why an optional section can be written as x-if="user" with nothing else around it.

Parameters

  • value (any)

Returns bool

is_empty()

wire.values.is_empty(value) -> bool

Whether value has nothing in it, for the empty filter and for anything else that wants the question asked of a collection rather than of a value in general.

nil is empty. A number is never empty, not even zero; that is a question about truthiness, not about emptiness.

Parameters

  • value (any)

Returns bool

is_blank()

wire.values.is_blank(text: string) -> bool

Whether text is empty or is nothing but whitespace.

Parameters

  • text (string)

Returns bool

compare()

wire.values.compare(a, b) -> number

Orders a before, with or after b, returning -1, 0 or 1.

Zuri’s ordering operators only take numbers, so anything a template sorts or compares that is not one has to be ordered here. Strings are compared a character at a time by code point, which puts them in the order a person expects for one alphabet and in a defined order for everything else. Two values of different kinds are ordered by how they render, which is arbitrary but stable, and stable is what a sort actually needs.

nil sorts before everything, so a record missing the field being sorted on still appears rather than dropping out of the list.

Parameters

  • a (any)
  • b (any)

Returns number

invoke()

wire.values.invoke(target, arguments: list) -> any

Calls target with arguments spread into its parameters.

Passing fewer arguments than the function declares leaves the rest nil, which is how a filter written as def round(value, places) can be used as a bare |round.

Parameters

  • target (callable)
  • arguments (list)

Returns any

Raises ArgumentError when there are more than MAX_ARGUMENTS.

type_name()

wire.values.type_name(value) -> string

Wire’s name for value’s type, used in error messages so that a complaint reads “expected a list, got a number” rather than naming an internal class.

Parameters

  • value (any)

Returns string

Classes

Safe

class wire.Safe

A string that is already markup and must be written out as it is.

Wire escapes everything it interpolates. That is the right default and it is not negotiable per-variable from the outside, so the only way to get live markup into a page is to say so explicitly, either with the raw filter in a template or by handing Wire one of these from Zuri code.

import wire

var tpl = wire.wire()

# Escaped, because that is the default.
echo tpl.render_string('{{ note }}', { note: '<b>hi</b>' })
# &lt;b&gt;hi&lt;/b&gt;

# Not escaped, because the value says it is markup.
echo tpl.render_string('{{ note }}', { note: wire.safe('<b>hi</b>') })
# <b>hi</b>

Wrapping a value is a promise that it is safe in an HTML context. Wrapping something that came from a user is how a template engine becomes a cross-site scripting hole, so wrap the markup you built, never the input you received.

  • printable — has a @to_string(), so echo and print() show something useful

Fields

FieldTypeDescription
valueThe markup this value carries.

Constructor

wire.Safe(value: string)

Parameters

  • value (string)

Safe.to_string()

wire.Safe.to_string() -> string

The markup, unchanged.

Returns string


2026, Richard Ore and The Zuri Contributors