Directories¶
This document explains how to render directories (i.e. multiple files) using diecutter web API. See also Files about how to render single files.
Here is service’s API overview:
- GET: list files in directory.
- POST: render templates in directory against context, as an archive.
- PUT: directories are automatically created when you PUT a file in it.
- other HTTP verbs aren’t implemented yet.
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/.
PUT¶
In order to create a directory, PUT
a file in it.
As an example, let’s create some “greetings” directory with “hello.txt” and “goodbye.txt” inside:
>>> name = 'greetings/hello.txt'
>>> files = {'file': ('client-side-name', 'Hello {{ name }}\n')}
>>> response = requests.put(diecutter_url + name, files=files)
>>> response.status_code
201
>>> name = 'greetings/goodbye.txt'
>>> files = {'file': ('client-side-name', 'Goodbye {{ name }}\n')}
>>> response = requests.put(diecutter_url + name, files=files)
>>> response.status_code
201
There is currently no way to create an empty directory. And no way to upload multiple files at once.
GET¶
GET
lists files in directory, recursively:
>>> response = requests.get(diecutter_url + 'greetings/')
>>> response.status_code
200
>>> print response.content
goodbye.txt
hello.txt
Notice that, if you don’t set the trailing slash, you get the same list with folder name as prefix:
>>> response = requests.get(diecutter_url + 'greetings')
>>> response.status_code
200
>>> print response.content
greetings/goodbye.txt
greetings/hello.txt
POST¶
POST
renders directory against data, and the result is an archive.
>>> response = requests.post(
... diecutter_url + 'greetings/',
... data={'name': 'Remy'})
>>> response.status_code
200
>>> archive = tarfile.open(fileobj=StringIO(response.content))
>>> print archive.getnames()
['goodbye.txt', 'hello.txt']
>>> print archive.extractfile('hello.txt').read()
Hello Remy
>>> print archive.extractfile('goodbye.txt').read()
Goodbye Remy
By default, archives are in tar.gz format. See accept header below for alternatives.
Rendering only parts of a directory¶
You can render only parts of a directory! It means you can render sub-directories or single files:
>>> response = requests.post(
... diecutter_url + 'greetings/hello.txt',
... data={'name': 'world'})
>>> print response.content
Hello world
This is because diecutter handles templates just as files and directories, in any filesystem it can read. If it can read a directory or a file, they can be used as a template, and so can be the sub-directories and files. diecutter does not need you to register templates, it only needs to be able to read the “filesystem” (repositories are kind of filesystems too) holding templates.
Dynamic trees¶
By default, all files in directory are rendered, and the context data does not vary. See Dynamic directory trees templates if you need to dynamically alter the list of files or the context data.
Trailing slash¶
Like with GET, the trailing slash affects filenames: without trailing slash, filenames are prefixed with directory name.
>>> response = requests.post(
... diecutter_url + 'greetings',
... data={'name': 'Remy'})
>>> archive = tarfile.open(fileobj=StringIO(response.content))
>>> print archive.getnames()
['greetings/goodbye.txt', 'greetings/hello.txt']
In the archive, dates reflect generation time:
>>> time_floor = int(time.time()) # Time before generation.
>>> response = requests.post(
... diecutter_url + 'greetings/',
... data={'name': 'Remy'})
>>> archive = tarfile.open(fileobj=StringIO(response.content))
>>> time_ceil = time.time() # Time after generation.
>>> info = archive.getmember('hello.txt')
>>> info.mtime != 0
True
>>> info.mtime >= time_floor
True
>>> info.mtime <= time_ceil
True
accept header¶
By default, diecutter returns TAR.GZ archives. You can get the content as a
ZIP archive using the HTTP accept
header:
>>> response = requests.post(
... diecutter_url + 'greetings/',
... data={'name': 'Remy'},
... headers={'accept': 'application/zip'})
>>> import zipfile
>>> zip_filename = os.path.join(diecutter_template_dir, 'response.zip')
>>> open(zip_filename, 'w').write(response.content)
>>> zipfile.is_zipfile(zip_filename) # File is actually a ZIP.
True
>>> archive = zipfile.ZipFile(zip_filename)
>>> archive.testzip() is None # ZIP integrity is OK.
True
>>> print archive.namelist()
['goodbye.txt', 'hello.txt']
>>> print archive.read('hello.txt')
Hello Remy
Supported archive formats¶
You can see all supported “accept” headers by requesting an unknown mime type:
$ curl -X POST --header "accept:fake/mime-type" -d name="world" http://localhost:8106/greetings
406 Not Acceptable
The server could not comply with the request since it is either malformed or
otherwise incorrect.
Supported mime types: */*, application/gzip, application/x-gzip,
application/zip