Is there any method to make jinja2 more readable? - jinja2

When use jinja2 to make complex config, I feel confused. Is there any method to make jinja2 more readable?
{ set disk_ssd_count = 0 }
{ set disk_sata_count = 0 }
{{ inventory_hostname }}:
{% if ansible_devices | length == 0 %}
rs: []
{% else %}
rs:
{% for disk in ansible_devices | sort %}
{% if disk | string | is_disk %}
{% if ansible_devices[disk]['rotational'] | disk_type == 'ssd' %}
{% set disk_ssd_count = disk_ssd_count + 1 %}
- { disk_name: {{ disk | string }}, disk_size: {{ ansible_devices[disk]['size'] }} }
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
pfd:
{% for disk in ansible_devices | sort %}
{% if disk | string | is_disk %}
{% if ansible_devices[disk]['rotational'] | disk_type == "sata" %}
- { disk_name: {{ disk | string }}, disk_size: {{ ansible_devices[disk]['size'] }} }
{% endif %}
{% endif %}
{% endfor %}

Related

Unable to use concat on `nil`

I am trying to build a related post include file for my Jekyll site. The site is based around the concept of members, attractions and parks (each as collections). Each post has a many to many relationships. I am trying to up a combined array of each of the page attributes (members, attractions and parks) loop through the array and find posts with a common number of tags.
It's quite simple but I am getting stuck with one section, not all the posts have members, attractions and parks fields so they are returning nil but the concat filter requires an array. I am trying to default the variable to an [] but it always gets set to nil. Any ideas?
Here's the code:
<ul class="row">
{% assign pageTags = [] %}{% if page.tags.first %}{% assign pageTags = page.tags %}{% endif %}
{% assign pageAttractions = [] %}{% if page.attractions.first %}{% assign pageAttractions = page.attractions %}{% endif %}
{% assign pageMembers = [] %}{% if page.members.first %}{% assign pageMembers = page.members %}{% endif %}
{% assign pageParks = [] %}{% if page.parks.first %}{% assign pageParks = page.parks %}{% endif %}
{% assign pageTagList = pageTags | concat: pageAttractions | concat: pageMembers | concat: pageParks %}
{% for post in site.documents %}
{% assign sameTagCount = 0 %}
{% assign commonTags = '' %}
{% assign postTags = [] %}{% if post.tags %}{% assign postTags = post.tags %}{% endif %}
{% assign postAttractions = [] %}{% if post.attractions %}{% assign postAttractions = post.attractions %}{% endif %}
{% assign postMembers = [] %}{% if post.members %}{% assign postMembers = post.members %}{% endif %}
{% assign postParks = [] %}{% if post.parks %}{% assign postParks = post.parks %}{% endif %}
{% assign postTageList = postTags | concat: postAttractions | concat: postMembers | concat postParks %}
{% if post.hidden == true %}
{% break %}
{% endif %}
{% for tag in postTageList %}
{% if post.url != page.url %}
{% if pageTagList contains tag %}
{% assign sameTagCount = sameTagCount | plus: 1 %}
{% capture tagmarkup %} <span class="label label-default">{{ tag }}</span> {% endcapture %}
{% assign commonTags = commonTags | append: tagmarkup %}
{% endif %}
{% endif %}
{% endfor %}
{% if sameTagCount >= minCommonTags %}
<li class="col-lg-4 col-md-12">
<div class="main-image">
</div>
<h5>{{ post.categories | first }}</h5>
<h3>{{ post.title | replace: 'Review', '' }}</h3>
<p>
{% if post.description %}
{{ post.description }}
{% else %}
{{ post.content | markdownify | strip_html | truncatewords: 20 }}
{% endif %}
</p>
<p>
Read Article →
</p>
</li>
{% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %}
{% if maxRelatedCounter >= maxRelated %}
{% break %}
{% endif %}
{% endif %}
{% endfor %}
</ul>
You can see the repo here: https://github.com/dtsn/jungleskipper/blob/feature/members/_includes/related-posts.html
From the Liquid documentation:
You cannot initialize arrays using only Liquid.
You can, however, use the split filter to break a string into an array of substrings.
You should look at compact which removes any nil values from an array.
Here is a link to the doc on shopify.
Example from Liquid documentation
Input:
{% assign site_categories = site.pages | map: "category" %}
{% for category in site_categories %}
- {{ category }}
{% endfor %}
Output:
- business
- celebrities
-
- lifestyle
- sports
-
- technology
With Compact
Input:
{% assign site_categories = site.pages | map: "category" | compact %}
{% for category in site_categories %}
- {{ category }}
{% endfor %}
Output:
- business
- celebrities
- lifestyle
- sports
- technology

Nested loops in jinja2 template

I am templating JSON with jinja2 and trying to iterate through list of lists, using 4 nested loops, and it is failing with message:
"AnsibleError: template error while templating string: expected token ':', got '}'"
As original JSON template is a bit bigger, I miss to paste here simple JSON key:value pairs.
Can you please support me on this. Thanx
This is path of template where main logic is:
{ ....
"panels": [
{% set frame_meter_servers= ["frame-meter10-246-44-20", "frame-meter10-246-45-92", "frame-meter10-246-46-234"] %}
{% set frame_procstat_meter_services= ["frame-meter-clock.service", "frame-meter-web.service"] %}
{% set frame_cpanel_backend_config_cluster_nodes= ["10.246.45.189", "10.246.46.102", "10.246.44.163"] %}
{% set frame_procstat_cpanel_services= ["frame-cpanel-backend"] %}
{% set frame_services_monitoring_hosts= ["frame_cpanel_backend_config_cluster_nodes", "frame_meter_servers"] %}
{% set frame_platform_services= ["frame_procstat_cpanel_services", "frame_procstat_meter_services"] %}
{% set id = namespace(total=1) %}
{% set z = namespace(total=0) %}
{% for node in frame_services_monitoring_hosts %}
{% set nodeloop = loop %}
{% for service in frame_platform_services %}
{% if nodeloop.index == loop.index %}
{
...
"x": 0,
"y": {{ z.total }}
},
"id": {{ id.total }},
"panels": [],
...
},
{% set x = namespace(total=0) %}
{% set y = namespace(total=1) %}
{% for hosts in node %}
{% set x = namespace(total=0) %}
{% for services in service %}
{
...
"x": {{ x.total }},
"y": {{ y.total }}
},
"id": {{ id.total }},
...
},
{% set x.total = 4 + x.total %}
{% set id.total = 1 + id.total %}
{% endfor %}
{% set y.total = 6 + y.total %}
{% endfor %}
{% set y = namespace(total=4) %}
{% for hosts in node %}
{% set x = namespace(total=0) %}
{% for services in service %}
{
...
],
"valueName": "current"
} {% set x.total = 4 + x.total %} {% set id.total = 1 + id.total %} {% if not loop.last %},{% endif %}
{% endfor %}
{% set y.total = 6 + y.total %} {% if not loop.last %},{% endif %}
{% endfor %}
{% if node|length == 2 %} {% set z.total = 14 + z.total %} {% else %} {% set z.total = 20 + z.total %}{% endif %}{% if not loop.last %},{% endif %}{% set id.total = 1 + id.total %}
{% endif %}
{% endfor %}
{% set id.total = 1 + id.total %}{% if not loop.last %},{% endif %}
{% endfor %}
],
...
{% if {{ nodeloop.index }} == {{ loop.index }} %}
That is incorrect syntax; everything inside the {% and %} is conceptually Python; what you want to say is:
{% if nodeloop.index == loop.index %}
I didn't check the rest of that wall of text, but I'm super positive that if was wrong, so let's start there

How to use Jekyll to sort posts by a custom YAML front matter variable?

I'm trying to create a page in my Jekyll site that will display a custom variable and list the posts that contain that custom variable.
I have created a movie review blog using a template Thiago Rossener created:
Thiago's Template: https://github.com/thiagorossener/jekflix-template
My Site: https://www.howdareyoureview.com/
in each post, I have defined custom variables in the YAML front matter that relate to the movie's details (i.e actor, director score, etc.)
for example:
---
layout: post
title: "Baby Driver"
image: 'https://res.cloudinary.com/how-dare-you-review/image/upload/c_fill,h_399,w_760/v1529865791/baby-driver.png'
tags:
- action
score: 72
director: Edgar Wright
written-by: Edgar Wright
staring:
- Ansel Elgort
- Lily James
- Eiza González
- Jon Hamm
- Jamie Foxx
---
I want to create pages exactly like the tags page that already exists in this template:
https://www.howdareyoureview.com/tags/
except I would want to sort by director, starring, etc. instead of by tags.
the tags page is created using the following code in a tags.html file:
---
layout: minimal
title: "#Tags"
permalink: /tags/index.html
description: "Procure por sua #tag favorita."
---
<div class="tags">
{% assign tags_list = site.tags %}
{% if tags_list.first[0] == null %}
{% for tag in tags_list %}
{{ tag }}
{% endfor %}
{% else %}
{% for tag in tags_list %}
{{ tag[0] }}
{% endfor %}
{% endif %}
{% assign tags_list = nil %}
</div>
{% for tag in site.tags %}
<div class="tag-wrapper">
<span class="tag-title" id="{{ tag[0] | slugify }}">{{ tag[0] }}</span>
<ul class="post-list">
{% assign pages_list = tag[1] %}
{% for post in pages_list reversed %}
{% if post.title != null %}
{% if group == null or group == post.group %}
<li>{{ post.title }}<span class="entry-date"><time datetime="{{ post.date | date_to_xmlschema }}" itemprop="datePublished">{{ post.date | date: "%m/%d/%Y" }}</time></li>
{% endif %}
{% endif %}
{% endfor %}
{% assign pages_list = nil %}
{% assign group = nil %}
</ul>
</span>
</div>
{% endfor %}
To achieve this for the custom variables I created I tried replacing "tag/tags" with director and saving the file out into to root directory as "directors.html" but the page is blank.
---
layout: minimal
title: "#Directors"
permalink: /directors/index.html
description: "Procure por sua director favorita."
---
<div class="directors">
{% assign directors_list = site.director %}
{% if directors_list.first[0] == null %}
{% for director in directors_list %}
{{ director }}
{% endfor %}
{% else %}
{% for director in directors_list %}
{{ director[0] }}
{% endfor %}
{% endif %}
{% assign directors_list = nil %}
</div>
{% for director in site.director %}
<div class="director-wrapper">
<span class="director-title" id="{{ tag[0] | slugify }}">{{ director[0] }}</span>
<ul class="post-list">
{% assign pages_list = director[1] %}
{% for post in pages_list reversed %}
{% if post.title != null %}
{% if group == null or group == post.group %}
<li>{{ post.title }}<span class="entry-date"><time datetime="{{ post.date | date_to_xmlschema }}" itemprop="datePublished">{{ post.date | date: "%m/%d/%Y" }}</time></li>
{% endif %}
{% endif %}
{% endfor %}
{% assign pages_list = nil %}
{% assign group = nil %}
</ul>
</span>
</div>
{% endfor %}
Since the code and the concept is exactly the same as the way tags are populated - I cannot understand why this doesn't work - I'm hoping someone can assist!
Here is my entire directory for reference:
https://github.com/howdareyoureview/howdareyoureview.github.io
Tags page uses site.tags, which is an array of site.posts grouped by tag, created by Jekyll at generation time.
You're trying to replicate by targeting site.directors but this expected array doesn't exist. But, you can use the group_by filter to achieve your goal.
<div class="directors">
{% assign directors = site.posts | group_by: 'director' | sort: "name" %}
{% for director in directors %}
{% if director.name == "" %}
{% assign name = "Anonymous" %}
{% else %}
{% assign name = director.name %}
{% endif %}
{{ name }}
{% endfor %}
</div>
{% for director in directors %}
<div class="director-wrapper">
{% if director.name == "" %}
{% assign name = "Anonymous" %}
{% else %}
{% assign name = director.name %}
{% endif %}
<span class="director-title" id="{{ name | slugify }}">{{ name | debug }}</span>
<ul class="post-list">
{% assign pages_list = director.items %}
{% for post in pages_list reversed %}
<li>{{ post.title }}<span class="entry-date"><time datetime="{{ post.date | date_to_xmlschema }}" itemprop="datePublished">{{ post.date | date: "%m/%d/%Y" }}</time></li>
{% endfor %}
</ul>
</span>
</div>
{% endfor %}
Tip : You can use the inspect filter to debug your vars. {{ myvar | inspect }}

jekyll: Is it possible to use a page.variable, as an operator inside a conditional if statement?

JSON file in path: _data/integers.json which looks like this:
{
"100": [
{
"value": "true"
}
]
}
In Jekyll page:
---
integers:
- 100
- 200
---
What I'm trying to do:
{% assign json = site.data.integers %}
{% for integer in page.integers %} // loop
{% if json.{{ integer }}[0].value == "true" %} ... {% endif %}
{% endfor %}
e.g. use the {{ integer }} (aka page.integer[0]) as an operator inside the conditional statement.
Is there a method? ... asking for a friend.
If we keep your json and page.integers as is :
{% assign json = site.data.integers %}
{{ json | inspect }}
{% for integer in page.integers %}
{% comment %} Here we cast our integer to a string,
as json keys are strings
(100 | append:"" => "100")
{% endcomment %}
{% assign intAsStr = integer | append:"" %}
{% comment %} as a possible json[intAsStr] returns an array,
we retrieve the first and only element in it
{% endcomment %}
{% assign data = json[intAsStr].first %}
{% if data["value"] == "true" %}
<h1>We have a match on {{ intAsStr }}</h1>
{% endif %}
{% endfor %}
We can simplify a little with some refactoring
data/integers.json
{
"100": { "value": true }
}
jekyll page
---
integers:
- "100"
- "200"
---
{% assign json = site.data.integers %}
{{ json | inspect }}
{% for integer in page.integers %}
{% assign data = json[integer] %}
{% if data["value"] == true %}
<h1>We have a match on {{ integer }}</h1>
{% endif %}
{% endfor %}

Downcase a liquid object

i want to downcase Liquid-object in Jekyll:
{{ page.tags | downcase }}
{{ page.tags | downcase }} doesn't work. Do you have an idea?
Thanks!
If you want to print a list of downcased page's tags, you can do :
{% comment %}Create an empty array{% endcomment %}
{% assign tagArray = "" | split: "/" %}
{% for tag in page.tags %}
{% assign tagDowncased = tag | downcase %}
{% assign tagArray = tagArray | push: tagDowncased %}
{% endfor %}
<p>{{ tagArray | joint: ", " }}</p>