--- ---

Liquid Templating Guide

Liquid 4++ templating methods for DocOps Lab projects.

DocOps Lab’s Liquid 4 environment is invoked through a special bootstrapping procedure. It introduces numerous custom Liquid tags and filters.

Throughout DocOps Lab applications, Jekyll’s extended and modified version of Liquid 4 is standard.

This means all Jekyll’s filters and its special include tag are available within and outside Jekyll sites.

Custom Tags

Not all custom tags are always available, but the following are generally available in DocOps Lab projects:

Table 1. Table of custom tags, their source gems, and usage notes.
Tag Name Source Gem Usage Notes

include

Jekyll

Transcludes the content of another file, with optional variable arguments going in but no transcendent affect on the context.

embed

AsciiSourcerer

Transcludes another file, with full pass-through of variables (no arguments needed) with full write access to the context (can add/change variables permanently).

Liquid Filters

A canonical listing of custom filters is available in the AsciiSourcerer gem at lib/specs/data/liquid-filters.yml.

The full compendium includes all filters available in Liquid 5 and Jekyll 4, even though officially Jekyll and AsciiSourcerer standardize on Liquid 4. Shopify’s novel Liquid 5 filters are hard-coded in AsciiSourcerer.

All other Shopify-sourced filters are conveyed directly from their Liquid 4.0.4 versions.

Where official Jekyll filters override their Liquid 4 counterparts, the Jekyll versions are used. Where an AsciiSourcerer filter overrides either upstream counterpart, the AsciiSourcerer version is used.

Custom Filters

Slugify (slugify)

Convert a string into a lowercase URL "slug".

This is not the Jekyll version.
Category

String Conversion

Examples
Input
{{ "The _config.yml file" | slugify }}
Output
the-config-yml-file
Input
{{ "The _config.yml file" | slugify: "_" }}
Output
the_config_yml_file
Inspect (inspect)

Convert an object into its String representation for debugging. Overrides Jekyll’s inspect filter, adding an optional format argument: html (default) reproduces Jekyll’s own behavior exactly (an HTML-escaped Object#inspect string, quotes become "); yaml and json render the object in those formats instead.

Category

Object Analysis

Examples
Example 1. Default (html) format, matches Jekyll’s inspect
Input
{{ tags_array | inspect }}
Output
["foo", "bar", "baz"]
Example 2. YAML format
Input
{{ tags_array | inspect: "yaml" }}
Output
---
- foo
- bar
- baz
Example 3. JSON format
Input
{{ tags_array | inspect: "json" }}
Output
["foo","bar","baz"]
Sum (sum)

Sums a numeric array, or an array of Hashes at the given property.

(This filter duplicates Shopify’s sum filter.)

Category

Math Operations

Examples
Input
{{ data.members | sum: "grad_year" }}
Output
6040
Remove Last (remove_last)

Removes the last instance of a substring from a string.

(This filter duplicates Shopify’s remove_last filter.)

Category

String Conversion

Examples
Input
{{ "Ground control to Major Tom." | remove_last: "o" }}
Output
Ground control to Major Tm.
Replace Last (replace_last)

Replaces the last instance of a substring in a string with a replacement.

(This filter duplicates Shopify’s replace_last filter.)

Category

String Conversion

Examples
Input
{{ "Ground control to Major Tom." | replace_last: "o", "0" }}
Output
Ground control to Major T0m.
Squish (squish)

Strips leading and trailing whitespace and collapses interior runs of whitespace to a single space.

(This filter duplicates Liquid 5’s squish filter.)

Category

String Conversion

Examples
Input
{{ "  A string that   is obviously  longer than 25 characters  " | squish }}
Output
A string that is obviously longer than 25 characters
Wrap (wrap)
Category

String Conversion

Examples
Input
{{ "A string that is obviously longer than 25 characters" | wrap: 25 }}
Output
A string that is
obviously longer than 25
characters
Comment Wrap (commentwrap)
Category

String Conversion

Examples
Input
{{ comment_text | commentwrap: 25, "// " }}
Output
// A string that is
// obviously longer than 25
// characters
Input
{{ comment_text | commentwrap: 25, "xml" }}
Output
<!-- A string that is
obviously longer than 25
characters -->
Input
{{ comment_text | commentwrap: 25, "/*|*/" }}
Output
/* A string that is
obviously longer than 25
characters
*/
Convert to CLI Arguments (to_cli_args)

Transform a Hash into a string of CLI-formatted arguments. Accepts an optional template name to format the output. See parameters section for all available templates and their output formats. When no template is specified, defaults to long_space format. Arguments are joined with a space by default; can be customized via delimiter parameter.

Category

Object Conversion

Examples
Example 4. Default (long_space) format
Input
{{ my_flat_hash | to_cli_args }}
Output
--key1 valuu_one --key2 val_two --key3 third value
Example 5. Long equals format
Input
{{ my_flat_hash | to_cli_args: "long_equals" }}
Output
--key1=valuu_one --key2=val_two --key3=third value
Example 6. Single-letter flags with values
Input
{{ cli_flags_hash | to_cli_args: "short_space" }}
Output
-k valuu_one -v val_two -o third value
Example 7. Positional arguments only
Input
{{ my_flat_hash | to_cli_args: "positional" }}
Output
valuu_one val_two third value
Example 8. Environment variable format (uppercase keys)
Input
{{ my_flat_hash | to_cli_args: "env_arg" }}
Output
KEY1=valuu_one KEY2=val_two KEY3=third value
Example 9. Key=value format
Input
{{ my_flat_hash | to_cli_args: "keyval" }}
Output
key1=valuu_one key2=val_two key3=third value
Data objects to YAML (to_yaml)

Turn any parameter or data object into YAML format.

Category

Object Conversion

Examples
Input
{{ my_flat_hash | to_yaml }}
Output
---
key1: valuu_one
key2: val_two
key3: third value
Input
{{ my_flat_hash | to_yaml: "flow" }}
Output
{key1: "valuu_one", key2: "val_two", key3: "third value"}
Input
{{ page.tags | to_yaml }}
Output
---
- Seattle
- Tacoma
- Spokane
Input
{{ page.tags | to_yaml: "flow" }}
Output
["Seattle", "Tacoma", "Spokane"]
Input
{{ page.tags | to_yaml: "flow", "quotes" }}
Output
["Seattle", "Tacoma", "Spokane"]
Data objects to JSON (to_json)

Turn any parameter or data object into JSON format.

Category

Object Conversion

Examples
Input
{{ my_flat_hash | to_json }}
Output
{"key1":"valuu_one","key2":"val_two","key3":"third value"}
Input
{{ page.tags | to_json }}
Output
["Seattle","Tacoma","Spokane"]
Input
{{ odd_keys_hash | to_json }}
Output
{"key 1":"valuu_one","$key2":"val_two","key:3":"third value"}
Regular Expression Replace (replace_regex)

Use regular expressions to match and replace text patterns.

Category

String Management

Examples
Input
{{ "hello world" | replace_regex: "world", "there" }}
Output
hello there
Input
{{ "one,two,three" | replace_regex: ",", "-" }}
Output
one-two-three
Pattern Match (match)

Returns of string or number matches pattern.

Category

String Analysis

Examples
Input
{% assign matched = "testword" | match: "^.*word$" %}
{% if matched %}It matched!{% endif %}
Output
It matched!
Holds Liquid (holds_liquid)

Returns true if a snippet of text contains Liquid markup tags.

Category

Object Analysis

Examples
Input
{{ liquidy_text | holds_liquid }}
Output
true
Input
{{ "This is just text." | holds_liquid }}
Output
false
Metastore list concat (store_list_concat)

Concatenates each Array-formatted value of the same-named property across all nodes in an Array of Hashes (Metastore). Accepts an Array of Hashes and the keyname of a property to collect from.

Category

Array Management

Examples
Input
{{ data.metastore | store_list_concat: "children" | jsonify }}
Output
["child1","child2","child3","child4","child5","child6"]
Metastore list duplicates (store_list_dupes)

Returns a list of duplicate items among multiple Arrays across multiple same-named properties in an Array of Hashes (Metastore). Accepts an Array of Hashes and the keyname of a Scalar property to check; returns the names of duplicate properties.

Category

Array Management

Examples
Input
{{ data.metastore | store_list_dupes: "children" | jsonify }}
Output
["child1"]

Liquid Syntax Styles

DocOps Lab contributions should standardize around certain Liquid syntax conventions, namely around indentation and integration with various markup languages we generate from templates.

HTML Rendering Style

HTML is by far the most common output target for Liquid templates, but DocOps Lab house style prefers a certain form of indentation.

Our preference is to indent the Liquid tags in cadence with the surrounding HTML tags, so that the Liquid tags are visually aligned with the HTML they are generating.

{% for item in items %}
  <div class="item">
    <h2>{{ item.title }}</h2>
    <p>
      {{ item.description }}
      {% if item.link %}
      <hr>
      <a href="{{ item.link }}">Read more</a>
      {% endif %}
    </p>
  </div>
{% endfor %}

AsciiDoc Rendering Style

Because AsciiDoc does not involve much indentation, Liquid syntax meant to render AsciiDoc output is a little awkward.

Maintain left-flush, un-indented Liquid tags, but indent the tag internals to match the intended AsciiDoc output.

{%- for item in items %}
{{ item.title }};;
{{ item.description }}
{%-   if item.link %}
Link:;;; {{ item.link }}
{%-   endif %}
{% endfor %}

Use left-side whitespace control ({%-) to avoid extra blank lines in the output, but skip it on typical {% endfor %} tags to ensure a blank line between iterations where it matters.

YAML Rendering Style

YAML pre-processing with Liquid can be fairly straightforward. YAML is a great target for Liquid, even though indentation and whitespace control matter more than for HTML or AsciiDoc.

Intertwine Liquid on its own indentation scale and it should map to the YAML fairly well.

{%- for item in items %}
- title: {{ item.title }}
  description: {{ item.description }}
  {%- if item.link %}
  link: {{ item.link }}
  {%- endif %}
{% endfor %}

Means of Invocation

If you are developing a Ruby gem or app in the DocOps Lab ecosystem, the basic Jekyll/Liquid engine is invoked via the asciisourcerer gem.

require 'asciisourcerer'

Sourcerer::Rendering.render_outputs([
  {
    template: 'templates/release-notes.liquid',
    data: 'data/release.yml',
    out: 'build/docs/release-notes.md',
    key: 'release',
    attrs: 'README.adoc',
    engine: 'liquid'
  },
  {
    converter: 'MyProject::JsonRenderer',
    data: 'data/release.yml',
    out: 'build/api/release.json'
  }
])

Direct invocation of Liquid rendering is also possible, but the above method is recommended for most use cases in DocOps Lab projects, as it provides a consistent user experience in terms of availability and behavior of tags and filters.