Dynamic directory trees templates

Directory trees can be computed from templates.

Thus you can use all features of template engines to render filenames, select templates or even alter context.

Note

In the examples below, let’s communicate with a diecutter server using Python requests.

This diecutter serves diecutter’s demo templates.

The diecutter_url variable holds root URL of diecutter service, i.e. something like http://diecutter.io/api/.

Use cases

While rendering a directory...

  • skip some files based on variables ;
  • render a single template several times with different output filenames ;
  • alter template context data for some templates ;
  • use loops, conditions... and all template-engine features...

The template tree template

When you POST to a directory, diecutter looks for special ”.directory-tree” template in that directory. If present, it renders ”.directory-tree” against context, decodes JSON, then iterates over items to actually render the directory.

Except when rendering a directory resource, ”.directory-tree” template is a normal template resource file. Manage it as any other template file.

Example

Let’s explain this feature with an example...

Let’s work on directory resource dynamic-tree/.

>>> dynamic_tree_url = diecutter_url + 'dynamic-tree/'

It’s a directory. So GET lists the templates it contains:

>>> response = requests.get(dynamic_tree_url)
>>> print response.content
.directory-tree
greeter.txt

greeter.txt is a template, with nothing special:

>>> response = requests.get(dynamic_tree_url + 'greeter.txt')
>>> print response.content
{{ greeter|default('Hello') }} {{ name|default('world') }}!

directory-tree is a also a template, with a special name:

>>> response = requests.get(dynamic_tree_url + '.directory-tree')
>>> print response.content
[
  {% for greeter in greeting_list|default(['hello', 'goodbye']) %}
    {
      "template": "greeter.txt",
      "filename": "{{ greeter }}.txt",
      "context": {"greeter": "{{ greeter }}"}
    }{% if not loop.last %},{% endif %}
  {% endfor %}
]

directory-tree template renders as a list of templates, in JSON:

>>> response = requests.post(dynamic_tree_url + '.directory-tree',
...                          {'greeting_list': [u'bonjour', u'bonsoir']})
>>> print response.content
[

    {
      "template": "greeter.txt",
      "filename": "bonjour.txt",
      "context": {"greeter": "bonjour"}
    },

    {
      "template": "greeter.txt",
      "filename": "bonsoir.txt",
      "context": {"greeter": "bonsoir"}
    }

]

JSON-encoded list items are dictionaries with the following keys:

  • “template”: relative path to a template, i.e. content to be rendered ;
  • “filename”: filename to return to the client ;
  • “context”: optional dictionary of context overrides.

When rendering a directory, diecutter first computes the tree of templates in the directory. By default, it just reads the filesystem. But if there is a .directory-tree file, then this special file is rendered first, and the result is used as the list of templates to render.

So, when you render the directory, you generate the files according to the generated directory-tree template:

>>> response = requests.post(dynamic_tree_url,
...                          {'name': u'Remy',
...                           'greeting_list': [u'bonjour', u'bonsoir']})
>>> archive = tarfile.open(fileobj=StringIO(response.content), mode='r:gz')
>>> print '\n'.join(archive.getnames())
bonjour.txt
bonsoir.txt
>>> print archive.extractfile('bonjour.txt').read()
bonjour Remy!
>>> print archive.extractfile('bonsoir.txt').read()
bonsoir Remy!
>>> archive.close()

Here, the greeter.txt template has been rendered several times, with different context data.