Skip to content

Hook Base Methods

Every hook has a number of base methods that are implemented alongside the declaration of the hook. This document outlines each of these methods with an example of their use.

Loops

Hooks can be called in a loop based on specifying a list input in a for key and will return a list. Within the loop, the iterand is stored in a temporary variable item along with it's indexed position in a variable called index. For instance running:

printer:
  ->: var "We are at item {{ item }} and index {{ index }}"
  for:
    - stuff
    - things

Would result in:

printer:
  - We are at item stuff and index 0
  - We are at item things and index 1

The for key must be a list so if the input is a string, it is rendered by default like in this example which does the same as above:

a_list:
  - stuff
  - things

printer:
  ->: print "We are at item {{ item }} and index {{ index }}"
  for: a_list
printer_compact->: print "{{ item }}/{{ index }}" --for a_list

Loop iterands can be of any type as in this example:

printer:
  ->: print "The type is {{ item.type }}
  for:
    - name: foo
      type: stuff
    - name: bar
      type: things

Additionally, jinja hooks can be used to do some logic that could help with some patterns. For instance the keys hook can be used to create a list of keys from a map which can be used as an iterand.

inputs:
  foo:
    type: stuff
  bar:
    type: things

printer:
  ->: print "The type is {{ inputs[item].type }}
  for: "{{keys(inputs)}}"

reverse

To loop through a list in reverse, simply set a reverse key to true as in this example:

printer:
  ->: print "We are at item {{ item }} and index {{ index }}"
  for:
    - stuff
    - things
  reverse: true

Would result in:

printer:
  - We are at item things and index 1
  - We are at item stuff and index 0

Conditionals

if

Hooks can be conditionally called with an if key that needs to resolve to some kind of boolean. It is typically based on some kind of jinja expression. For instance:

planet->: select What planet you on? --choices ['earth','mars']
weather:
  ->: print {{ planet | title }} weather is nice!
  if: planet == 'earth'

Here we can see the if key that by default is wrapped with jinja braces and is evaluated as true or false depending on the value entered into the planet key. Full jinja syntax is supported so other assertions such as whether an item is in a list (ie if: 'a key' in a_list) is supported.

The if key is evaluated after a for loops are entered allowing list comprehensions to be done. For instance this example would only print "Hello world!".

words:
  - Hello
  - cruel
  - world!
expanded:
  ->: print {{item}}
  for: words
  if: item != 'cruel'
compact->: print {{item}} --for words --if "item != 'cruel'"

when

For imposing conditionality before a loop, the when method exists. For instance in this example the when is evaluated first, the loop is entered, and then the if condition is imposed to do a list comprehension.

words: ['Hello', 'cruel', 'world!']
expanded:
  ->: print {{item}}
  for: words
  when: "'Hello' in words"
  if: item != 'cruel'

else

If you want to return a different value when the if or when condition resolves to false, use the else key with the value you wish to return otherwise. For instance:

name->: input What is your name?
hello:
  ->: print Hello {{ name }}!
  if: name != 'Rob'
  else: Hello me

Which can also be rendered.

intro: Hello
...
  else: {{intro}} me

And could have hooks embedded in it.

hello:
...
  else: {{print('Hello me')}}

Or simply could be a dictionary output with further hooks.

hello:
...
  else:
    stuff:
      things->: print foo

Currently only jinja hooks are supported as string values. Future could add support for else->/else_> compact hook calls.

Checkout the match hook if needing to do a lot of conditionals which can satisfy regexes when catching cases.

Methods

chdir

Sometimes it is desirable to run the hook in another directory. For this there is the chdir key where the hook is called in the context of the directory being specified. For instance one could run the listdir hook in another directory:

contents:
  ->: listdir
  chdir: path/to/some/dir

merge

If the output of the hook call is a map, then one can merge that map into the parent keys. For instance given this block hook:

stuff: things

to merge->:
  merge: true
  stuff: more things

Would result in:

stuff: more things

Future work will support merging operations for lists as interpreted as an append operation

try / except

To catch errors, use the try method which also can run a context in the case of failure in an except method. For instance in both these example the print would execute.

a failed command->: command "foo bar" --try  # Would exit without try
p->: print Hello world!  # This would print
a failed command:
  ->: command "foo bar"
  try: true
  except:
    p->: print Hello world!

defer - Coming soon

Future versions of tackle fill have a defer functionality similar to Go where one can declare deferred actions that will run if there is a script error or when a tackle file / execution is finished. Details still being worked out.