Jinja¶
At the core of all tackle logic is the jinja templating language which enables the majority of the features in the syntax.
Variable¶
By default, all fields in tackle hooks are rendered if they have curly braces. For instance given the following hook call, the output would print stuff
then things
:
stuff: things
string->: print stuff
jinja->: print {{stuff}}
For Loops¶
When running within a for loop, tackle automatically keeps track of the iterand and integer index of the loop. For instance the following would print out stuff 0
and things 1
:
printer:
->: print {{item}} {{index}}
for:
- stuff
- things
Alternatively that could be written as:
printer->: print {{item}} {{index}} --for ['stuff','things']
Rendering By Default¶
Not all fields need to be wrapped with jinja though including several base methods and hook fields. For instance the if
and for
methods are automatically interpreted as jinja expressions and do not need to be wrapped with braces. This is because writing an if
statement makes little sense unless it is dynamic which jinja is needed for. Along the same thread, inputs of type string for for
loops implicitly mean they need to be rendered. For instance the following would print out stuff
and things
.
a_list:
- stuff
- things
printer:
->: print {{item}}
if: "'stuff' in a_list"
for: a_list
Note the additional quotes in the if statement which is a yaml parsing nuance of ruamel, the parsing package used by tackle.
Hook fields additionally can be marked as being rendered by default in both python and declarative hooks. For instance with the var
hook, a python hook used to render variables, the input is rendered by default so the following would work:
stuff: things
call->: var stuff
check->: assert "{{call}}" things
This field can additionally be added to declarative hooks like so:
some_hook<-:
input:
default: foo
render_by_default: true
stuff: things
schema->: some_hook --input stuff # Not things
check->: assert "{{schema.input}}" things
Jinja Expressions¶
Jinja offers a rich expression syntax that is similar to python's and allows checking whether items are equal, items are in a list / map, and other things outside the scope of these docs that you can find in jinja's excellent documentation.
For instance to conditionally run a key based on a user input:
user_input->: select Input what? --choices ['stuff','things']
run_stuff->: print Stuff --if user_input=='stuff'
run_things:
->: print Things
if: user_input == 'things'
Or one could check if an item is in a list:
Note:
checkbox
hook is a multi-select prompt that returns a list
user_input->: checkbox Input what? --choices ['stuff','things'] --checked
run_stuff->: print Stuff --if "'stuff' in user_input"
run_things:
->: print Things
if: "'stuff' in user_input"
Notice in this example the extra quoting which is an artifact of yaml parsing and need to be encapsulated for the parser.
Jinja Filters¶
Jinja natively has numerous builtin functions that allow a wide variety of actions similar to tackle but with less options. To see the full list, check out jinja's documentation.
List Comprehensions¶
While tackle supports list comprehensions, jinja conveniently does as well and can be done in a single line.
input_list:
- stuff
- and
- things
reject_list:
- and
list_comprehension->: "{{ input_list | reject('in', reject_list) | list }}"
Here, reject
and list
are builtin jinja filters, not tackle hooks.
Calling Hooks from Jinja¶
Calling hooks from jinja can be convenient in a lot of situations when one wants to string hooks together. For instance here is an example using the yaml hook that reads a key from a file using a dynamic path based on variable inputs.
read_key_in_file->: "{{yaml(path_join([a_str_var,join(a_list_var),'values.yaml'])).a_key}}"
Note: The above example uses a convenience macro whereby without an actual hook declaration and jinja templating, tackle interprets that as an object to rander.
Another nice pattern is to prompt a user to do something within an if statement as a one liner:
do_thing->: do_stuff 'input' --if confirm('Do the thing?')