Condition based on the three first letters of a string? - jinja2

In my Jinja template, model.DataType value can be user defined or built in. My requirenement is if model.DataType start with the three letters ARR, then do a specific operation.
Example of values:
ARRstruct124
ARR_int123
ARR123123
CCHAR
UUINT
etc.
{% set evenDataType = model.eventDataType %}
{%if evenDataType | regex_match('^ARR', ignorecase=False) %}
// do the operation
{%else%}
// do the operation
{% endif %}
With this template, I am getting the error
{%if evenDataType | regex_match('^ARR', ignorecase=False) %}
jinja2.exceptions.TemplateAssertionError: no filter named 'regex_match'

There is indeed no regex_match filter in the Jinja builtin filters. You might have found some examples using it, but this is an additional filter provided by Ansible, so it won't work outside of Ansible.
This said, your requirement does not need a regex to be fulfilled, you can use the startswith() method of a Python string.
So, you template should be:
{% set evenDataType = model.eventDataType %}
{% if evenDataType.startswith('ARR') %}
`evenDataType` starts with 'ARR'
{% else %}
`evenDataType` does not starts with 'ARR'
{% endif %}

Related

Passing list of Relation object to dbt_utils.union_relation macro fails

Related to dbt and jinja2
I am using union_relations from dbt_utils package (0.5.0).
I created my macro which takes list of fully qualified name (like database.schema.identifier) splits it and uses api.Relations.create (link) to create a relation and append each relation to a list.
{{ list_of_relation }} is given to dbt_utils.union_relations(as relations=my_macro([list of fully qualified names])), it's giving me an _is_relation error, I did use log to debug and see if it actually creates a relation and it does. What could be wrong?
It sounds like you have a macro written something like this:
{% macro my_macro(names) %}
{% set list_of_relations = [] %}
{% for name in names %}
{% set relation = something(name) %}
{% do list_of_relations.append(relation) %}
{% endfor %}
{{ list_of_relations }}
{% endmacro %}
Instead of using {{ list_of_relation }}, you’ll want {{ return(list_of_relation) }} or {% do return(list_of_relation) %}. The problem is that {{ ... }} turns things into strings in jinja macros, and macros by default return strings.
The documentation on return is here.

Compare two grain values inside state.sls using jinja expressions

I am creating a state which will trigger some module if both of the grains are not matching. I have tried several options, but no luck.
Based on comparison, False will trigger my module and that module will change GRAIN_B value to match with GRAIN_A. So during every highstate my module will not get triggered, unless there is a change in GRAIN_A.
Any suggestions please.
I have tried several jinja expressions.
{% if grains['GRAIN_A'] not in grains.get('GRAIN_B','None') %}
{% set GRAIN_B = grains.get('GRAIN_B','None') %}
{% if grains['GRAIN_A'] != {{ GRAIN_B }} %}```
```{% if grains['GRAIN_A'] not in grains.get('GRAIN_B','None') %}
MY_MODULE:
module.run:
- func: MYMODULE.FUNCTION_A
{% endif %}```
Issue fixed, there is a \n character in my GRAIN_A output, which makes the evaluation condition fails.
This condition works already.
{% if grains['GRAIN_A'] not in grains.get('GRAIN_B','None') %}
MY_MODULE:
module.run:
- func: MYMODULE.FUNCTION_A
{% endif %}

Liquid filter collection where not null

In my front matter for some pages (not all) I have:
---
top-navigation:
order: 2
---
Using liquid I want to filter all site pages which have a top-navigation object and sort by top-navigation.order.
I'm trying sort:'top-navigation.order' but that's throwing an exception of undefined method [] for nil:NilClass. I tried where:"top-navigation", true but it's not equating truthy values.
How can I filter for pages that have top-navigation and then sort?
Two steps:
Create an array with pages that contains the top-navigation key.
We create an empty array and then push the items that have the key.
{% assign navposts = ''|split:''%}
{% for post in site.posts %}
{% if post.top-navigation %}
{% assign navposts = navposts|push:post%}
{% endif %}
{% endfor %}
Sort the above array by top-navigation.order
{% assign navposts = navposts|sort: "top-navigation.order"%}
Printing results:
{% for post in navposts %}
<br>{{ post.title }} - {{post.top-navigation}}
{% endfor %}
For pages use site.pages.
In Jekyll 3.2.0+ (and Github Pages) you can use the where_exp filter like so:
{% assign posts_with_nav = site.posts | where_exp: "post", "post.top-navigation" %}
Here, for each item in site.posts, we bind it to the 'post' variable and then evaluate the expression 'post.top-navigation'. If it evaluates truthy, then it will be selected.
Then, putting it together with the sorting, you'd have this:
{%
assign sorted_posts_with_nav = site.posts
| where_exp: "post", "post.top-navigation"
| sort: "top-navigation.order"
%}
Liquid also has the where filter which, when you don't give it a target value, selects all elements with a truthy value for that property:
{% assign posts_with_nav = site.posts | where: "top-navigation" %}
Unfortunately, this variant does not work with Jekyll.

How to check if a variable is an integer in Jinja2?

The aim is to check whether a variable is an integer and if that is true insert hello.
Attempt
{% if int(variable) %} hello {% endif %}
Result
'int' is undefined"
To use the Jinja2 int builtin filter (which tries to cast the value to an int):
You need to use the filter format, like this:
{% if variable|int != 0 %} hello {% endif %}
By default, if casting to int fails it returns 0, but you can change this by specifying a different default value as the first parameter. Here I've changed it to -1 for a case where 0 might be a valid value for variable.
{% if variable|int(-1) != -1 %} hello {% endif %}
see the: Jinja2 Docs - int builtin filter for more info
To use the Jinja2 number builtin test (which returns true if the variable is already a number):
a better solution, rather than using the int filter (which will cast a integer like string to an int) is to use the builtin test number, like this:
{% if variable is number %} hello {% endif %}
see the: Jinja2 Docs - number builtin test
For anyone using Salt, this did not work for me when put is saltstack state.
{% if variable|number %} hello {% endif %}
This did work however:
{% if variable is number %} hello {% endif %}
None of these solutions worked for me, however this did :
{% if variable is even or variable is odd %}

Jinja2 for loop behaving similarly to with

I'd like to iterate over a set of objects and find the maximum of one particular attribute, however jinja2 ignores any action within an iterator on a variable declared outside of the iterator. For example:
{% set maximum = 1 %}
{% for datum in data %}
{% if datum.frequency > 1 %}
{% set maximum = datum.frequency %}
{% endif %}
{% endfor %}
{# maximum == 1 #}
datum.frequency is definitely greater than 1 for some datum in data.
EDIT (solution)
This is similar to this post, but there's a bit more to it. The following works and is very ugly.
{% set maximum = [1] %}
{% for datum in data %}
{% if datum.freq > maximum[-1] %}
{% if maximum.append( datum.freq ) %}{% endif %}
{% endif %}
{% endfor %}
{% set maximum = maximum[-1] %}
Have you considered writing a custom filter to return the highest value of a particular attribute within your collection? I prefer to minimize the amount of logic I use in Jinja2 templates as part of maintaining a 'separation of concerns'.
Here is a link to a very good example of how one can be written in python:
Custom jinja2 filter for iterator
Once you have your filter returning the value you need access it by using '|' like so:
{% set maximum = datum|filtername %}