Access yaml key name with liquid - yaml

Is it possible to access the key names along with key values from a yaml file using liquid?
For example from a .yaml file that looks like this:
- type: a
id: b
author: c
website: d
Have liquid code that looks something like this:
{%- for item in YAMLFILE -%}
{{ item.??? }} = {{ item.??? }}
{%- endfor -%}
with the following output:
type = a
id = b
author = c
website = d
I am trying to do this this way because my yaml file has different key names along with values and it would be a pain having to embed a bunch of different if else statements to account for every possible key name.
Thank you all so much!

Assuming your yaml file is in _data/test, you can use:
{% for list in site.data.test %}
{% for item in list %}
{{ item[0] }} = {{ item[1] }}
{% endfor %}
{% endfor %}
item[0] is the key and item[1] is the value. It will work with different number of keys and names in each list.
Also be aware that four spaces in Markdown formats the content of the second for loop as a code block. Simply do not indent if putting this code in a Markdown file.

Related

How to add a column in a line in Ansible Jinja2 template using for loop?

I want to update a line in Jinja2 template as follows
Let's say, I have a variable x equal to 4, then, I want a row/line as:
abc 1 2 3
in my template:
{% for val in range(1, x | int) %}
abc {{ val }} {{ val+1 }} .......
{% endfor %}
If I use for loop it adds a new line, Is their way I can achieve aforementioned requirement?
Here you are:
abc{% for val in range(1, x|int) %} {{ val }}{% endfor %}
Jinja2 is a templating engine. If you put a newline character in the source template, it will be present in the output.
You can either write everything in one line, or use whitespace control to remove unnecessary space.

How can one generate a dict in Jinja/Ansible as a var?

I need to dynamically generate a dict and assign it to an Ansible var so that the same data is available to every host and does not need to be regenerated on each one. This will be looped over by a task and template. It is possible to build lists by nesting Jinja in a var definition like so:
var_list: >
[
{% set data = [] %}
{% for host in groups['all'] %}
{{ data.append(host) }}
{% endfor %}
{{ data }}
]
However, I am not able to do so with dicts:
var_dict: >
{
{% set data = dict() %}
{% for host in groups['all'] %}
{{ data | combine({host: []}) }}
{% endfor %}
{{ data }}
}
This results in a giant, garbled string: {u'host.subdom.dom.tld': []} {}...
What I am expecting is a var set to a true dict object that can have its components referenced individually (like {{ var_dict.host1[0] }}), with a structure similar to the following JSON:
{
"host1": [],
"host2": []
}
Is there a way to cause this to expand into a proper dict when assigned to the variable? Or is there perhaps another way to generate a dict from a loop without that awful set_fact hackery?
Either of your templates is broken (the first one produces nested lists, the second one prints data in loop with this expression {{ data | combine({host: []}) }}, the value of data remains empty until the end).
Jinja2 is a templating engine, you don't really need to create data structures to print them later. You can form your output directly.
For the list:
var_list: >
[ {% for host in groups['all'] %}'{{ host }}',{% endfor %} ]
For the dictionary:
var_dict: >
{ {% for host in groups['all'] %}'{{ host }}': [],{% endfor %} }
As you expect the values to be interpreted by Ansible, you don't need to pay attention to the trailing coma, otherwise there is loop.last.

Expanding variables into Jekyll Front-End

It is possible to expand a variable into a post Front-Matter?
I use a series of items for links into my template, like:
related_links:
- text: foo
link: bar
But sometimes I need to refer to other posts into my site. Normally I would use just /bar into link, but this is also used as shownotes into a podcast, I want to expand the link for http://example.com/bar. But using {% post_url YYYY-MM-DD-bar %} results into:
Error: could not read file [REDACTED]: (<unknown>): found character that cannot start any token while scanning for the next token at line 33 column 12
Any tips?
If you use
related_links:
- text: foo
link: {% post_url YYYY-MM-DD-bar %}
you will get an error because the { will start a flow style mapping and % cannot start a token in YAML. You have to put the whole scalar in (double) quotes:
related_links:
- text: foo
link: "{% post_url YYYY-MM-DD-bar %}"
What you want to do does not work because of Jekyll's pipeline:
parse YAML front matter
process rest of document with Liquid
parse result of previous step with Markdown
{% post_url YYYY-MM-DD-bar %} is a Liquid command. As you see, Liquid processes just the part of your file below the YAML front matter. Therefore, no Liquid replacement takes place there.
You can instead write something like this below the front matter:
{% assign link = post_url YYYY-MM-DD-bar %}
And then use {{link}} elsewhere. If you have multiple links, things get hacky. Something like this might work, but I am not enough of a Liquid user to know for sure:
{% capture nl %}
{% endcapture %}
{% capture rawlinks %}
{% post_url YYYY-MM-DD-bar %}
{% post_url YYYY-MM-DD-bar %}
{% endcapture %}
{% assign links = rawlinks | split nl %}
You can then specify indexes in your YAML front matter:
related_links:
- text: foo
linkindex: 0
And finally, somewhere in your document:
{{ links[related_link.linkindex] }}
YMMV if this level of uglyness is justified for your use-case.
I found a more elegant at my own:
{% assign real_link=link.link %}
{% assign link_start = real_link | slice: 0 %}
{% if link_start == "/" %}{% assign real_link = real_link | prepend: site.url %}{% endif %}
As I start al my locak links with a / to warrant that the link will be related with the root in the site, that was a better way for me.

How can I use Jinja2 to generate an index-like list?

In an index, references are listed alphabetically and grouped by letter. Also, instead of a simple alphabetical sort, leading articles and punctuation are removed before sorting. The result would look like this:
A
Abba
The Archer
Away From Here
B
"Bangkok" Mike
Bullets
I'm using the Pelican static site generator, which uses Jinja2 in its templates, and I'd like to create an index like this of TV shows that I've blogged about on my site.
TV show names are listed in a metadata field {{ article.showname }} (only one show per post, so only one show in the metadata field) and so are easily retrieved. But stripping the leading article/punctuation, grouping by first letter and inserting the correct letter of the alphabet is more than I know how to do.
Possible steps I could take
I could strip the leading article/punctuation and create another metadata field with the show name that's ready to be sorted, called, say, {{ article.shownamesort }}. For that matter, if there's not an easy way to group the names by their first letter, I could create a metadata field with the show's first (sortable) letter, {{ article.shownameletter }}.
For each letter, I could create a loop that begins by grabbing that letter's posts:
{% if article.shownameletter == "A" %}
Once that's done, I don't know how to sort the text from one metadata field based on the text of a different metadata field. That is, I don't know how to print the {{ article.showname }} list that's sorted by the {{ article.shownamesort }} list. Can this be done in Jinja2? For the record, if there's a solution that takes a completely different approach, I'd be very happy with it, including if it required different/more metadata fields.
I did something like that on the tags and categories pages. It iterates over all the characters and then prints the results
Iterate over the letters:
{% for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"|list %}
...
{% endfor %}
Comparison for show or not show the tag:
{% if char == tag|upper|truncate(1, True, end='') %}
...
{% endif %}
Full example:
{% for char in "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"|list %}
<h2>{{char}}</h2>
{% for tag, articles in tags|sort %}
{% if char == tag|upper|truncate(1, True, end='') %}
<dt>{{ tag }}</dt>
{% for article in articles|sort %}
<dd>{{ article.title }}</dd>
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}

Liquid templates - accessing members by name

I'm using Jekyll to create a new blog. It uses Liquid underneath.
Jekyll defines certain "variables": site, content, page, post and paginator. These "variables" have several "members". For instance, post.date will return the date of a post, while post.url will return its url.
My question is: can I access a variable's member using another variable as the member name?
See the following example:
{% if my_condition %}
{% assign name = 'date' %}
{% else %}
{% assign name = 'url' %}
{% endif %}
I have a variable called name which is either 'date' or 'url'.
How can I make the liquid equivalent of post[name] in ruby?
The only way I've found is using a for loop to iterate over all the pairs (key-value) of post. Beware! It is quite horrible:
{% for property in post %}
{% if property[0] == name %}
{{ property[1] }}
{% endif %}
{% endfor %}
Argh! I hope there is a better way.
Thanks.
I don't know what I was thinking.
post[name] is a perfectly valid liquid construction. So the for-if code above can be replaced by this:
{{ post[name] }}
I thought that I tried this, but apparently I didn't. D'oh!
Liquid admits even fancier constructs; the following one is syntactically correct, and will return the expected value if post, element, categories, etc are correctly defined:
{{ post[element.id].categories[1].name }}
I am greatly surprised with Liquid. Will definitively continue investigating.
Actually, this did not work for me. I tried a bunch of different combinations and what finally worked was
<!-- object myObj.test has the string value "this is a test" -->
{% assign x = 'test' %}
{{ myObj.[x] }}
Output:
this is a test

Resources