Yaml2Text now supports YAML with Liquid
Introduction, redux!
Yaml2Text has been updated to support the popular Liquid syntax for
accessing YAML data in metanorma-standoc
version 1.5.3.
Note
|
This is an updated version of the original blog post located here. This new post represents the updated Yaml2Text block that uses Liquid syntax. Please see the original blog post to refer to the old syntax (metanorma-standoc ⇐ v1.5.3). |
When authoring technical documents, there is often a need to represent structured data one way or the other. If the data happens to be stored in one of the many machine-readable formats, one may be compelled to write a helper script that turns them into document elements like tables and bullet points (if your document format allows you to do so 😉).
Metanorma now supports using YAML files as a data source. In English, that means you can now bust out YAML data as AsciiDoc elements with the newly implemented template mini-language.
In this blog post, we’ll see how this is done.
See the Yaml2Text page for more details!
Meet the updated yaml2text block
The yaml2text
block defines a template, and the template gets its data from
the specified YAML file.
[yaml2text,my_data.yaml,my_yaml_context] (1)
----
This template will be rendered as AsciiDoc content. (2)
{{... my_yaml_context ...}} (3)
----
-
Define a
yaml2text
block. Data is sourced frommy_data.yaml
, which is represented by the context namemy_yaml_context
. Note that the YAML file path is relative to the directory of the AsciiDoc file defining the block. -
The template is whatever that goes inside the block.
-
Content from the YAML file is interpolated via the context name, surrounded by double curly braces.
That’s the gist of it.
Next, we’ll see how various YAML data structures can be represented:
The yaml2text
plugin also supports basic Liquid tags and expressions,
see Liquid code snippets
Accessing object properties
EXAMPLE:
Given:
# strings.yaml
---
foo: bar
dead: beef
The following block…
[yaml2text,strings.yaml,data]
----
I'm heading to the {{data.foo}} for {{data.dead}}.
----
... will render as:
I'm heading to the bar for beef.
Data from strings.yaml
are being accessed via the context name data
.
The foo
attribute is accessed via data.foo
, which is interpolated in the
template with curly braces.
Array length
EXAMPLE:
Given:
# array.yml
---
- lorem
- ipsum
- dolor
The following block…
[yaml2text,array.yml,data]
----
The length of the YAML array is {{data.size}}.
----
... will render as:
The length of the YAML array is 3.
{{data.size}}
evaluates to the length of the array.
Enumerating items in an array
EXAMPLE:
Given:
# strings.yaml
---
- lorem
- ipsum
- dolor
The following block…
[yaml2text,strings.yaml,arr]
----
{% for item in arr %}
=== {{forloop.index0}} {{item}}
This section is about {{item}}.
{% endfor %}
----
... will render as:
=== 0 lorem
This section is about lorem.
=== 1 ipsum
This section is about ipsum.
=== 2 dolor
This section is about dolor.
Here, the expression {% for item in arr %}
tells the template engine to define a
new context, item
, to represent each individual item from the array arr
.
The context item
is accessible (=== is under scope) within the lines between this
expression and the first occurrence of {% endfor %}
.
This template is then concatenated for each array item, in the original order of the array, as one might reasonably expect.
{{forloop.index0}}
gives the zero-based position of item item
in the parent array
arr
.
Generally, given an array array_name
, array_name[i]
returns the value at
index i
(zero-based: starts with 0
);
negative indices count from the end:
index -1
refers to the last item,
-2
the second last, etc., etc.
Object size
EXAMPLE:
Given:
# object.yaml
---
name: Lorem ipsum
desc: dolor sit amet
The following block…
[yaml2text,object.yaml,data]
----
=== {{data.name}}
{{data.desc}} {{data.size}}
----
... will render as:
=== Lorem ipsum
dolor sit amet 2
If data
represents a YAML object, then {{data.size}}
gives you the number of
key-value pairs in that object.
Enumerating keys in an object
EXAMPLE:
Given:
# object.yaml
---
name: Lorem ipsum
desc: dolor sit amet
The following block…
[yaml2text,object.yaml,my_item]
----
{% for item in my_item %}
=== {{item[0]}}
{{item[1]}}
{% endfor %}
----
... will render as:
=== name
Lorem ipsum
=== desc
dolor sit amet
item[0]
gives the key of each key-value pair of the object my_item
.
item[1]
gives the value corresponding to the current iteration.
Enumerating using attributes .keys
and .values
EXAMPLE:
Given:
# object.yaml
---
name: Lorem ipsum
desc: dolor sit amet
The following block…
[yaml2text,object.yaml,item]
----
.{{item.values[1]}}
[%noheader,cols="h,1"]
|===
{% for elem in item %}
| {{elem[0]}} | {{elem[1]}}
{% endfor %}
|===
----
... will render as:
.dolor sit amet
[%noheader,cols="h,1"]
|===
| name | Lorem ipsum
| desc | dolor sit amet
|===
item.values
gives an array of all values in the object item
.
It follows that item.values[1]
gives you the second value.
An array with interpolated file names (for AsciiDoc consumption)
yaml2text
blocks can be used for pre-processing document elements for AsciiDoc consumption.
EXAMPLE:
Given:
# strings.yaml
---
prefix: doc-
items:
- lorem
- ipsum
- dolor
The following block…
[yaml2text,strings.yaml,yaml]
------
{% for item in yml.items %}
[source,ruby]
----
include::{{yaml.prefix}}{{forloop.index0}}.rb[]
----
{% enfor %}
------
... will render as:
[source,ruby]
----
include::doc-0.rb[]
----
[source,ruby]
----
include::doc-1.rb[]
----
[source,ruby]
----
include::doc-2.rb[]
----
Putting it altogether — Array of objects
EXAMPLE:
Given:
# array_of_objects.yaml
---
- name: Lorem
desc: ipsum
nums: [3, 5]
- name: dolor
desc: sit
nums: []
- name: amet
desc: lorem
nums: [2, 4, 6]
The following block…
[yaml2text,array_of_objects.yaml,ar]
----
First array item of last array item is {{ar[-1].nums[0]}}.
Last array item of first array item is {{ar[0].nums[-1]}}.
{% for item in ar %}
{item.name}:: {item.desc}
{% for num in item.nums %}
- {{item.name}}: index = {{forloop.index0}}, index+1 = {{forloop.index0 | plus: 1 }},
{{num}} === {{ar[forloop.index0]}}, prev = {% capture prev_index %}{{forloop.index0 | minus: 1}}{% endcapture %}{{ar[prev_index]}}
{% endfor %}
{% endfor %}
----
... will render as:
First array item of last array item is 2.
Last array item of first array item is 5.
Lorem:: ipsum
- Lorem: index = 0, index+1 = 1,
3 === 3, prev = 5
- Lorem: index = 1, index+1 = 2,
5 === 5, prev = 3
dolor:: sit
amet:: lorem
- amet: index = 0, index+1 = 1,
2 === 2, prev = 6
- amet: index = 1, index+1 = 2,
4 === 4, prev = 4
- amet: index = 2, index+1 = 3,
6 === 6, prev = 2
You might also have noticed that one can use liquid math filters in order to do simple arithmetics in
interpolations and array indexing, like {{forloop.index0 | plus: 1 }}
and {{forloop.index0 | minus: 1}}
in
the example above.
Ending notes
In this blog post, we covered the most common use cases for including YAML data
in a Metanorma document using the yaml2text
block.
With the simple techniques shown in this article, you should be well equipped to handle any data structures YAML throws at you.
Happy authoring!