Ootempl.Filters (ootempl v0.3.0)

Formatting filters applied to placeholder values.

Filters let template authors control how a value is rendered without bloating the placeholder text. They are written Jinja/Liquid style after the variable, separated by |:

{{ invoice.date | date: "%d %B %Y" }}
{{ total | round: 2 | currency: "USD" }}
{{ name | upcase }}
{{ middle_name | default: "—" }}

Filter functions

A filter is a 2-arity function (value, args) -> {:ok, term} | {:error, reason} where value is the raw, un-stringified value resolved from the data (so a %Date{} arrives as a %Date{}, not a string) and args is the list of parsed arguments. Returning a term (not necessarily a string) lets filters chain: the output of one filter becomes the input of the next, and the final value is converted to a string for the document.

On {:error, reason} the chain halts and the failure is reported through the usual Ootempl.PlaceholderError batch reporting.

Nil values

A bare placeholder whose value is nil is reported as a missing-value error. Once a filter is applied, the filter decides how to handle nil: default substitutes a fallback, the numeric/date filters raise a filter error, and the string filters (upcase, etc.) treat nil as an empty string. Use default when a value may legitimately be absent.

Registering and overriding filters

Filters are resolved from three layers, each overriding the previous:

  1. The built-ins returned by builtins/0.
  2. Application config: config :ootempl, filters: %{"money" => &MyApp.money/2}.
  3. A per-call :filters option passed to Ootempl.render/4.

Filter names are matched case-insensitively. Registering a filter under the name of a built-in overrides that built-in.

Built-in filters

NameExampleNotes
datedate: "%Y-%m-%d"Calendar.strftime on a Date/DateTime/ISO string
timetime: "%H:%M"Time/DateTime/ISO string
datetimedatetime: "%Y-%m-%d %H:%M"NaiveDateTime/DateTime/ISO string
roundround: 2Rounds a number to N places (default 0)
numbernumber: 2Thousands separators, optional decimal places
currencycurrency: "USD"Currency symbol + grouped number (default $)
upcase / downcase / capitalizeupcaseString case
trimtrimStrips surrounding whitespace
truncatetruncate: 20Truncates with an ellipsis (default length 50)
defaultdefault: "N/A"Fallback when the value is nil or empty

Summary

Functions

Returns the filter registry active for the current process, set by with_registry/2, or default_registry/0 when none is active.

Applies a chain of filters to a value using the given registry.

Returns the built-in filter registry, keyed by lowercased filter name.

The default registry (built-ins + application config) with no per-call overrides. Used as the default when callers don't supply filters.

Builds the effective filter registry by layering built-ins, application config, and a per-call override map (highest precedence). All keys are normalized to lowercase strings.

Runs fun with registry installed as the active filter registry for the current process, restoring the previous value afterward.

Types

registry()

@type registry() :: %{
  optional(String.t()) => (term(), [term()] -> {:ok, term()} | {:error, term()})
}

Functions

active_registry()

@spec active_registry() :: registry()

Returns the filter registry active for the current process, set by with_registry/2, or default_registry/0 when none is active.

apply_chain(value, filters, registry)

@spec apply_chain(term(), [Ootempl.Placeholder.filter()], registry()) ::
  {:ok, term()} | {:error, term()}

Applies a chain of filters to a value using the given registry.

Returns {:ok, value} with the transformed value, or {:error, reason} on the first failure (unknown filter or a filter returning an error).

Parameters

  • value - The raw value to transform
  • filters - List of %{name:, args:} filters from Ootempl.Placeholder
  • registry - Map of filter name to filter function

builtins()

@spec builtins() :: registry()

Returns the built-in filter registry, keyed by lowercased filter name.

default_registry()

@spec default_registry() :: registry()

The default registry (built-ins + application config) with no per-call overrides. Used as the default when callers don't supply filters.

resolve(call_filters \\ nil)

@spec resolve(map() | nil) :: registry()

Builds the effective filter registry by layering built-ins, application config, and a per-call override map (highest precedence). All keys are normalized to lowercase strings.

Parameters

  • call_filters - Optional map of per-call filters (string or atom keys)

with_registry(registry, fun)

@spec with_registry(registry(), (-> result)) :: result when result: term()

Runs fun with registry installed as the active filter registry for the current process, restoring the previous value afterward.

A render runs synchronously in a single process, so this lets the whole pipeline (including deeply-nested table-row replacement) pick up a per-call registry via active_registry/0 without threading it through every function. The previous value is always restored, even if fun raises.