{% macro select_soup(file, selector) -%} {% set soup_result = d[file].select(selector) -%} {% if len(soup_result) == 0 %} {{ debug(d[file]) }} {{ throw("No results for selector %s in file %s" % (selector, file)) }} {% endif -%} {{ print_soup(soup_result[0].contents) }} {% endmacro %}
{% macro print_soup(soup) %}
{% for line in soup %}{{ line }}{% endfor %}
{% endmacro %}
{% macro docsissue(issue_number) %} {% set issue_info = d['docs-issues.json'].from_json()[str(issue_number)] -%} {% if issue_info['state'] == "closed" -%} WARN: Issue {{ issue_number }} has been closed. Replace issue block with test. {{ debug("Issue %s has been closed but is still referenced in user-guide.adoc." % issue_number) }} {% endif -%}
{{ issue_info.body }}
[github] TODO: {{ issue_info.title }}
Please feel free to ask questions or add comments about this issue {{ issue_info.html_url }}[on github].
{% endmacro %}
{% macro issue(issue_number) %} {% set issue_info = d['issues.json'].from_json()[str(issue_number)] -%} {% if issue_info['state'] == "closed" -%} WARN: Issue {{ issue_number }} has been closed. Replace issue block with test. {{ debug("Issue %s has been closed but is still referenced in user-guide.adoc." % issue_number) }} {% endif -%}
[github] Desired Feature: {{ issue_info.title }}
{{ issue_info.body }}
-
Updated at: {{ issue_info.updated_at }}
-
Assigned to: {{ issue_info.assignee }}
-
Milestone: {{ issue_info.milestone }}
{% endmacro %}
{% macro source(qualified_name) -%} [code]
{{ d['modules.txt'][qualified_name + ':source'] }}
{% endmacro %}
{% macro example(qualified_test_name) -%} {% set file_name, test_name = qualified_test_name.split(":") -%} [shield]
{{ d['tests/' + file_name + '.py|pydoc'][test_name + '__test:source'] }}
{% endmacro %}
{% set exs = "Clicking this won’t do anything, it’s just an example of what the icon looks like." -%}
This is the User Guide for dexy. It is intended to be a thorough guide to dexy features, but not an introductory tutorial or complete reference documentation. For links to other dexy documentation, please visit http://dexy.it/docs/
The User Guide is not complete yet, but it covers many topics and we have tried to focus on the most frustrating and confusing aspects of dexy as a priority (and hopefully we have also made them a little less frustrating in the process). In many places there are links to unit tests related to the content in this guide, you can click the shield icon to show/hide these sections. There are also embedded tickets which represent future planned development.
Dexy is an automation tool which will follow your instructions for what automated operations you would like done on your project. You give Dexy instructions about how to run your project using configuration files written using YAML syntax.
Configuration files should be named dexy.yaml
.
{{ docsissue(1) }}
The basic elements you can specify are:
-
file names/patterns and filters to apply to them
-
dependencies among documents (inputs are indented relative to parents)
-
settings for documents or filters (settings are indented under document)
Here’s a simple YAML config file demonstrating each of these: {{ example("configuration_test:basic_yaml") }}
{{ d['examples/basic.yaml|asciisyn'] }}
The format for a document specification is a file name or file pattern followed by zero or more filters. Here are a few examples:
{{ d['examples/list-dict.yaml|idio|asciisyn']['document-specifications'] }}
You can leave off the initial asterisk if your file pattern starts with a dot, i.e. if you are matching all files with a given file extension. This is done for convenience and because if you want to start a string with an asterisk in YAML you need to wrap it in quotes.
foo.txt
instructs dexy to create a document named foo.txt
where the source
is a file named foo.txt
in the project root.
{{ example("configuration_test:config_txt_single_file") }}
bar/foo.txt
instructs dexy to create a document named bar/foo.txt
where the
source is a file named foo.txt
in the bar
directory under the project root.
{{ example("configuration_test:config_txt_single_file_in_subdir") }}
.txt
instructs dexy to add all available files with ".txt" extension. You
could also write this as "*.txt"
(note the quotes) but dexy assumes entries
starting with a dot are wildcard expressions and adds the asterisk for you.
{{ example("configuration_test:config_txt_ext") }}
"*foo.txt"
instructs dexy to add all files named foo.txt in any directory.
Note that we need to wrap the expression in quotes because we are starting with
an asterisk and can’t use the usual shortcut because we are matching a file
name, not just an extension.
{{ example("configuration_test:config_txt_wildcard") }}
{{ docsissue(2) }}
You can make dexy process a file which doesn’t really exist on the file system
by using the contents
setting:
{{ example("configuration_test:virtual_file_contents") }}
{{ d['examples/list-dict.yaml|idio|asciisyn']['contents'] }}
{{ docsissue(3) }}
contents
is one of the available [_document_settings].
You can change the name with which a file will be output by specifying the
output-name
document setting.
If the name starts with a slash /
or contains any slashes it is assumed to be
a full local (within the project) path to the desired destination of the file.
If the name doesn’t contain a slash it is assumed you want to rename a file but
keep it in its original directory.
Dexy will automatically apply
string
interpolation or
string formatting
to the name if it sees a %
or {
character. The environment for formatting
will be populated with:
- all document settings
- name
corresponding to the original document name
- dirname
corresponding to the original document directory
- any local environment variables poopulated from a dexy-env.json file
Available variables are written to the dexy log at DEBUG
level.
This example was run with these environment variables defined:
{{ d['examples/custom-names/dexy-env.json|idio|t'] }}
Here is the dexy.yaml
:
{{ d['examples/custom-names/dexy.yaml|idio|t'] }}
And here are the input and output files:
{{ d['examples/templates.sh|idio|shint|asciisyn']['input-files'] }}
{{ d['examples/templates.sh|idio|shint|asciisyn']['output-files'] }}
A key: value pair indented under a document specification is a document
setting. For example, output: True
.
To see available document settings, you can use the dexy nodes
command.
{{ d['examples/filters.sh|idio|shint|asciisyn']['node-doc'] }}
When no filters are specified for a document, the original contents of the file
are made available to other documents, and the document will appear in Dexy’s
final output unless the output
setting has been set to False.
To specify filters, follow the filename/pattern with a pipe symbol |
and a
filter alias. You can chain as many filters as you want, in order, by adding
more pipes and aliases. The first filter operates on the original contents of
the source file, subsequent filters operate on the output from the previous
filter.
You can run a source file through different filter combinations, and each will be a separate document in dexy.
{{ d['examples/list-dict.yaml|idio|asciisyn']['different-filters'] }}
To customize filter settings, you need to first give the filter alias, and then a dictionary of the desired settings for that filter alias.
{{ d['examples/list-dict.yaml|idio|asciisyn']['filter-settings'] }}
To see available settings for a filter, you can use the dexy filters
command
with the -alias
option. See [_filter_documentation].
Document keys consist of the file name plus the filters. Document keys must be unique in Dexy. This poses a problem when you want to run a file through the same filters with different combinations of settings.
{{ d['examples/list-dict.yaml|idio|asciisyn']['without-aliases'] }}
To differentiate, you can place an alias filter at the end of your document key. This just needs to start with a hyphen, and then can optionally have some descriptive text.
{{ d['examples/list-dict.yaml|idio|asciisyn']['with-aliases'] }}
{{ issue(104) }}
The syntax of the dexy.yaml
file mixes list entries with dictionary (key:
value) entries. When you have deep nesting of settings, such as in this example:
{{ d['examples/list-dict.yaml|idio|asciisyn']['nested'] }}
It helps to use a more dictionary-like syntax with curly braces and commas. This makes it more clear to read and also prevents compiler errors.
{{ d['examples/list-dict.yaml|idio|asciisyn']['with-braces'] }}
You can also try to reduce the amount of nesting by using named bundles.
You can gather collections of documents together in named bundles and then refer to these bundles in other locations. This is helpful to make a more readable config file, reduce deep nesting and to re-use bundles of dependencies in different places.
{{ d['examples/list-dict.yaml|idio|asciisyn']['bundles'] }}
{{ docsissue(4) }}
You can use the dexy nodes
command to view more information about the
bundle
node type:
{{ d['examples/filters.sh|idio|shint|asciisyn']['node-bundle'] }}
Dexy guarantees that inputs are run before the documents which depend on them,
but it doesn’t make any guarantees about the order in which sibling documents
run. If you want to force dexy to run documents in a certain order, you do so
by placing them in a bundle whose name is preceded by the script:
prefix.
{{ d['examples/list-dict.yaml|idio|asciisyn']['script-bundles'] }}
The script:
prefix instructs Dexy to construct a special kind of node which
ensures its children are run in sequential order.
You can use the dexy nodes
command to view more information about the
script
node type:
{{ d['examples/filters.sh|idio|shint|asciisyn']['node-script'] }}
{{ docsissue(5) }}
Dexy’s command-line interface uses python-modargs
to process commands and arguments. All arguments can take any number of dashes,
so -r
and --r
and ---r
all do the same thing.
The dexy help
command gives you access to information about dexy commands:
{{ d['examples/help.sh|idio|shint|asciisyn']['dexy-help'] }}
The --all
flag will print out all the available dexy commands:
{{ d['examples/help.sh|idio|shint|asciisyn']['dexy-help-all'] }}
You can get help on a particular command using the -on
flag:
{{ d['examples/help.sh|idio|shint|asciisyn']['dexy-help-on'] }}
It can be inconvenient if you accidentally run the dexy
command somewhere you
didn’t mean to, like in your home directory, so dexy
won’t run unless it
finds a .dexy
directory in the current working directory. If you try to run
dexy by accident, you’ll see a message like this:
{{ d['examples/run.sh|idio|shint|asciisyn']['dexy-without-setup'] }}
Running dexy setup
creates the .dexy
directory:
{{ d['examples/run.sh|idio|shint|asciisyn']['setup'] }}
And now you can run dexy:
{{ d['examples/run.sh|idio|shint|asciisyn']['dexy'] }}
The .dexy
directory is used to store working files, cached files, the
dexy.log
and some dexy reports:
{{ d['examples/run.sh|idio|shint|asciisyn']['show-hidden-files'] }}
You can search the generated documents in the previous run via the dexy grep
command, and you can get more detailed information about a document via the
dexy info
command.
{{ docsissue(7) }}
There are some examples of using dexy info
in this document, use control+F
to search and find them.
{{ docsissue(8) }}
The dexy serve
command runs a simple web server to serve the static assets
generated by dexy. This command first looks for the output-site
directory
generated by the ws
reporter, and if it doesn’t find this it looks for the
output
directory generated by the output
reporter. It launches a static
server and prints out the port on which the files will be served.
Dexy stores cached files in the .dexy
directory to help speed up subsequent
runs. You shouldn’t have to manage this manually, but if you want to force dexy
to re-run everything you can empty the cache by running dexy with the -r
option or running the dexy reset
command.
Dexy might also create a .trash
directory although it should remove this
automatically.
The filters
command lets you list all available dexy filters:
{{ d['examples/filters.sh|idio|shint|asciisyn']['list-filters'] }}
To print the full docstring and available settings for a particular filter, use
the -alias
option:
{{ d['examples/filters.sh|idio|shint|asciisyn']['filter-detail'] }}
You use nodes (often without knowing it) when you write dexy.yaml
files. Dexy
guesses the node type you want, for example a PatternNode when you use a
wildcard or implicit wildcard, a Doc when you specify an individual file. You
can force a node to be of particular type by prefixing its name with the node
type alias and a colon, as when you create a script node via
script:screenshots
.
The nodes
command lets you list available node types:
{{ d['examples/filters.sh|idio|shint|asciisyn']['nodes'] }}
To print the full docstring and available settings for a particular node, use
the -alias
option:
{{ d['examples/filters.sh|idio|shint|asciisyn']['node-doc'] }}
The dexy env
command gives you information about the template environment
elements present. See the [_templating_filters] section.
{{ d['examples/help.sh|idio|shint|asciisyn']['env'] }}
When Dexy processes a file and applies filters, each stage of processing is stored in a Data instance. There are different types of Data based on what sort of information you are storing.
The dexy datas
command prints out a list of all data types:
{{ d['examples/run.sh|idio|shint|asciisyn']['datas'] }}
By default, documents start out using the Generic data type and subsequent
filters may change this depending on how the filters alter the data. You can
see which data type is being used for a particular document by running the
dexy info
command:
{{ d['examples/run.sh|idio|shint|asciisyn']['info'] }}
You can then get more information about methods defined on the data type by
running the dexy datas
command, as suggested in the output of dexy info
:
{{ d['examples/run.sh|idio|shint|asciisyn']['data-info'] }}
Custom data types are a way of exposing custom methods on data. For example the
bs4
data type lets you run BeautifulSoup queries on HTML content of a document.
This section deals with important concepts and features which are shared by all filters or groups of similar filters.
Many filters create a temporary workspace within the .dexy
directory when
they run. This workspace will mimic the directory structure of the main project
and will be populated with the desired input files in their correct states
(i.e. run through any applicable filters).
This provides a limited amount of isolation, in that processes are not changing files in the main project repository (unless there is a malicious or poorly-designed script), and any files generated as side effects do not clutter up the main project space.
{{ issue(103) }}
In this example, a bash script is being run through the shint
filter, and
running the pwd
command allows us to see the working directory where the code
is being executed:
{{ d['examples/workspaces.sh|idio|shint|asciisyn']['pwd'] }}
Check the filter documentation for each filter to see which of these workspace-related options are supported.
The use-wd
boolean setting controls whether or not to create and populate a
working directory and to set the process’s cwd
to the working directory. The
setting defaults to True.
{{ example("process_filters_test:use_wd_option_defaults_to_true") }}
When use-wd
is True (the default case), then a working directory is created
within the .dexy/work
directory.
{{ example("process_filters_test:if_use_wd_true_code_runs_in_work_dir") }}
When use-wd
is set to False, the code runs directly in the project root.
{{ example("process_filters_test:if_use_wd_false_code_runs_in_project_home") }}
Working directories can be populated with the documents specified as dependencies or inputs. This can end up being a lot of files, and sometimes we want to control more precisely which files are copied. Several settings help to manage which files are copied. {{ source('dexy.filter.Filter.include_input_in_workspace') }}
-
workspace-exclude-filters
A list of filter aliases. Input files which had these filters applied will be excluded. -
override-workspace-exclude-filters
A boolean specified on an input file. This input file will be included in working directories regardless of the parent’s workspace-exclude-filters setting. -
workspace-include
A list of filenames or wildcard patterns. These and only these will be written to the workspace. When this is set, workspace-exclude-filters and override-workspace-exclude-filters are ignored.
The workspace-exclude-filters
setting takes a list of filter aliases and it
doesn’t populate the working directory with any documents which include any of
these filter aliases. So if jinja
is in workspace-exclude-filters
then a
document named hello.txt|jinja
will not be written to the working directory.
{{ example("process_filters_test:workspace_exclude_filters_excluding_jinja") }}
To include all input files, set workspace-exclude-filters
to an empty list.
{{ example("process_filters_test:workspace_exclude_filters_no_excludes") }}
The workspace-exclude-filters
setting defaults to ['pyg']
since usually
syntax highlighted content is included in documents via templating, not via the
file system. When pyg
outputs image files or stylesheets, these have
override-workspace-exclude-filters
set to True by the filter.
{{ example("process_filters_test:workspace_exclude_filters_pyg_defaults") }}
Sometimes a tool expects a certain directory structure to exist when it runs, but this may not correspond to the directory structure of your project.
The mkdir and mkdirs settings let you specify extra directories which will be created in the working directory before the filter is run.
The mkdir
setting creates a single directory based on a string.
{{ example("process_filters_test:mkdir_creates_extra_directory_in_work_dir") }}
The mkdirs
setting creates multiple directories based on a list.
{{ example("process_filters_test:mkdirs_creates_extra_directories_in_work_dir") }}
One of the reasons we tend to run scripts in their own working directories is
because they generate extra files. LaTeX is notorious for generating .log
,
.aux
, .bbl
and a host of other files you usually aren’t interested in
unless you need to debug somtehing. So by default Dexy just ignores any extra
files which are created in working directories. If you need to do debugging,
you can look in the working directory.
Sometimes, though, these extra files are useful and may even be the whole point of running a script. We may be generating a PNG file containing a graph, or a JSON or CSV file containing data.
The add-new-files
setting controls how dexy treats these additional files.
{{ source('dexy.filters.process.SubprocessFilter.add_new_files') }}
By default, add-new-files
is False so Dexy ignores any new files which appear
in the working directory.
{{ example("process_filters_test:process_filters_have_add_new_files_false_by_default") }}
Some filters like casperjs
which are almost always invoked for side effects
will have add-new-files
be True by default, so check the individual filter
documentation.
{{ example("process_filters_test:casperjs_has_add_new_files_true_by_default") }}
When add-new-files
is False, no new files are added to dexy.
{{ example("process_filters_test:if_add_new_files_false_new_files_not_added") }}
When add-new-files
is True, new files are added to dexy.
{{ example("process_filters_test:if_add_new_files_true_new_files_are_added") }}
The add-new-files
setting can also be a list of expressions to match.
Entries in the list can be file extensions which should be added. {{ example("process_filters_test:add_new_files_list") }}
They can also be glob-style file patterns to match. {{ example("process_filters_test:add_new_files_pattern") }}
There is also an exclude-add-new-files
setting which lets you list
exceptions so you can skip directories, file names or patterns which otherwise
would be included.
{{ example("process_filters_test:exclude_add_new_files") }}
Sometimes running a filter will cause extra documents to be added to the Dexy
run. The split
filter, for example, takes a HTML file and splits it into
multiple files, each of which becomes an extra independent document. Extra
documents may also be added as a result of the add-new-files
setting (see the
[_adding_new_files] section).
When new documents are added, you may wish to customize some of their settings
or specify additional filters which should be applied to the new documents.
You can do this via additional-doc-filters
and additional-doc-settings
.
The additional-doc-filters
setting can be a string listing a single filter or
single filter chain (a sequence of filters separated with pipes just as you
would write in a dexy file) in which case every new document has these
additional filters applied.
{{ example("process_filters_test:additional_doc_filters") }}
If additional-doc-filters
is a list, then separate new documents are created
for each filter combination in the list.
{{ example("process_filters_test:additional_doc_filters_list") }}
additional-doc-filters
can also be a dictionary which maps file extensions to
the filters which should be applied to those file extensions. If a file is
found whose extension is not in the dictionary, then that file is added without
any extra filters being applied.
{{ example("process_filters_test:additional_doc_filters_dict") }}
The keep-originals
boolean setting can be combined with
additional-doc-filters
and it instructs Dexy to also add the original files
without any extra filters applied.
{{ example("process_filters_test:additional_doc_filters_keep_originals") }}
The additional-doc-settings
will apply extra settings to new documents. If
this is a dictionary, then the entries in the dictionary are assumed to be
setting names and values, and these will be applied to all new documents.
{{ example("process_filters_test:additional_doc_settings") }}
additional-doc-settings
can also be a list of lists where each element is a
file extension and a dictionary of settings which will be applied to all files
matching the extension. The ".*" extension can be used to provide default
settings.
{{ example("process_filters_test:additional_doc_settings_list") }}
One of the most common things you will probably want do in dexy is to insert
snippets of code into other documents using tags like
{{ "{{ d['foo.py|pyg'] }}" }}
using the jinja
filter. The jinja filter is
an example of a templating filter, and this chapter describes how these filters
work and what elements are available for you to use in your documents.
A templating tool lets you insert content into a document template. Templating
tools typically evaluate template tags like {{ "{{ foo }}" }}
against an
environment. An environment can be thought of as a hashmap like { "foo" : 123
}
. The values in the hashmap can be simple values like 123
, or they can be
any type of object which is supported by the templating tool. Jinja2, for
example, supports almost any kind of Python object including functions.
The TemplateFilter
base class in Dexy prepares a giant hashmap containing
various elements you might want to be able to refer to in your documents. It
does so by running several Template Plugins, each of which returns a hashmap.
For example, the DexyVersion template plugin returns a hashmap with one entry,
to let you refer to DEXY_VERSION
(currently {{ DEXY_VERSION }}) in your
documents.
{{ source('dexy.filters.templating_plugins.DexyVersion') }}
All these individual hashmaps are combined together to generate the full environment.
Subclasses of TemplateFilter take this full environment and pass it to the templating system so it can be used to evalute template tags.
By default, Dexy’s TemplateFilter includes all registered template plugins when it generates the template environment. (See the Cashew docs for details about how plugin registration works.) {{ example("template_plugins_test:by_default_dexy_runs_all_template_plugins") }}
If, instead, you want to specify which plugins to run, then you can use the
plugins
setting to specify a list of template plugin aliases to use.
{{ example("template_plugins_test:use_plugins_attribute_to_specify_whitelist") }}
If you just want to exclude a few plugins, then you can use the skip-plugins
filter setting to list template aliases you don’t want to be used.
{{ example("template_plugins_test:use_skip_plugins_attribute_to_specify_blacklist") }}
The dexy env
command prints all the environment elements which are available
from running all the template plugins.
The jinja
filter is the recommended templating filter to use. It is the most
widely tested and used. It uses the jinja2 templating system.
TODO verfiy this URL
Jinja has a lot of nice features, and you should familiarize yourself with the jinja template documentation.
One nice feature is jinja template filters. These are functions which you call by placing them after a pipe symbol like {{ "{{ 'foo' | indent }}" }}.
Yeah, we have a lot of "filters" and pipe symbols happening here, but that’s
because both dexy and jinja are following similar conventions. Pipes are a
classic unix concept and indicate that you take the output from one process and
"pipe" it to the next process in a chain. It’s the same idea as when you write
a pipe in a dexy.yaml
file. I’ll refer to jinja’s implementation of filters
as "jinja template filters" and dexy’s "jinja filter" as "the jinja dexy
filter".
You can use all of the standard jinja template filters in dexy documents which you process through the jinja dexy filter. In addition to the built-in jinja template filters, dexy also implements some custom jinja template filters.
You can see the additional jinja template filters provided by dexy when you run
dexy env
. They are indicated by an asterisk.
The jinja dexy filter allows you to make assertions about content you are including via jinja. For example, you may wish to assert that an error message does not appear in output, or you may wish to assert that a given HTML element is present, or that it contains certain text.
Here’s an example of a text file which includes output from a Python script:
{{ d['examples/assertions/docs-without-assertions.md'] }}
There’s no indication that anything is wrong when we run dexy, but the results show that there’s a problem:
{{ d['examples/assertions/docs-without-assertions-results.md'] | assert_contains("SyntaxError") }}
While Dexy does monitor for nonzero exit codes and notifies you (depending on the filter setting), not all errors will result in a nonzero exit code (for a variety of reasons). Making assertions is an alternative approach to ensuring that you are getting the content you expect from your inputs.
Now here’s the document with an assertion filter:
{{ d['examples/assertions/docs-with-assertions.md'] }}
When we run dexy, this is the result:
{{ d['examples/assertions.sh|idio|shint|asciisyn']['run-with-assertions'] }}
After fixing the file, we can run this again:
{{ d['examples/assertions/docs-with-assertions-results.md'] }}
The assertion filters return the original passed content if the assertion passes.
All available assertions can be seen by running the dexy env
command:
{{ d['examples/assertions.sh|idio|shint|asciisyn']['list-assertions'] }}
There are other subclasses of TemplateFilter
available, although many of
these are proofs of concept. If you have a reason not to use jinja then please
get in touch to discuss alternatives.
#cookbook #dummyfilter
Occasionally you may want to work on the prose of a document without worrying about the automation. For example, a technical writer may wish to concentrate on writing explanations which a developer will later help pair with examples. Or you may be working on a machine which doesn’t have everything configured for generating screenshots, which aren’t important to your work anyway. You want to be able to run subsequent filters like a markdown to HTML filter without having jinja crud get in the way.
This can be accomplished by adding an alternative configuration target which
calls the dummyjinja
filter instead of the jinja
filter. The dummyjinja
filter evaluates jinja tags but instead of using a real dexy environment, it
just inserts an insert stub which allows subsequent filters to run without
choking on curly braces.
Reporters are what present the output from your dexy run.
To see the available reporters, run:
{{ d['examples/reporters.sh|idio|shint|asciisyn']['all'] }}
The default
column indicates whether the reporter is enabled by default. You
can control which reporters run via the reports
argument to the main dexy
command. If this is blank then each report’s default
setting is used. If
reports
is not blank, it is interpreted as a string containing
space-separated report aliases to be run in order.
To see full documentation and settings for an individual reporter, use the
alias
argument:
{{ d['examples/reporters.sh|idio|shint|asciisyn']['output'] }}
You can customize settings for a reporter using a dexyplugin.yaml
file. An
entry should start with reporter:alias:
where alias is the reporter alias,
and then have setting keys and new values listed in an indented block (YAML
format, like dexy.yaml):
{{ d['dexyplugin.yaml|asciisyn'] }}
The output reporter populates the output/
directory which contains short,
canonical but potentially non-unique names for files. You can control whether a
document appears in the output/
directory via the output
setting. Setting
this to True will ensure the document appears, setting this to False will
ensure the document does not appear. The default behavior is determined by
filters. If any filter’s default output
setting is True then a document
passed through that filter will default to a True setting. It’s easiest to
start with defaults and then fine-tune to remove or add files from output/.
To change the name of the output/
directory, or to change any other report
settings, requires writing a custom plugin, which can be in YAML or Python
format.
The dir
setting controls where the output is written. You can provide a
different name for dir
or even a different path, for example you can set the
output to be in a gh-pages
github branch to generate github pages content:
{{ d['dexyplugin.yaml|asciisyn'] }}
If you set the dir
to be outside of the dexy project root then you will need
to run dexy with the --writeanywhere
setting, which you can pass on the
command line or in a dexy.conf
file:
{{ d['dexy.conf|asciisyn'] }}
The Website reporter is similar to the Output reporter, and it uses the same
criteria to determine whether it should include a file in output-site/
or
not. The difference is that the Website reporter adds some features intended to
help create a website-like output, such as applying HTML templates and help
with creating navigation and links to other pages.
By default the website reporter looks for templates named _template.html
. For
a given file foo/bar/baz.html
the website reporter will look first in
foo/bar/
, then in foo/
, then in the project root for a _template.html
file, so you can override templates in subdirectories without needing to do any
configuration.
You can change the default template name from _template.html
to something
else via the default-template
setting.
You can also use the ws-filter
setting on an individual file to specify an
alternative template file. The ws-filter
setting can also take boolean values
to override the default behavior of whether a template should be applied or
not. By default, templates are applied unless HTML header tags are already
found in the HTML.
When writing a template, a {{ "{{ content }}" }}
tag should indicate where the main
content should go.
Here’s a simple HTML template:
{{ d['examples/website-simple/_template.html|idio|t'] }}
And here’s an index.html
file containing some very simple content:
{{ d['examples/website-simple/index.html|idio|t'] }}
Here’s the index.html
file in the output-site/
directory:
{{ d['examples/website-simple/output-site/index.html|idio|t'] }}
The project was configured with --reports
setting including the ws
reporter:
{{ d['examples/website-simple/dexy.conf|idio|t'] }}
If HTML content has been split into sections then you can access section
content via the content
object, using either dictionary style access or
jinja’s dot syntax (if the section name forms a valid python attribute name).
Here is an example of a HTML file with sections delimited using idio
filter
syntax:
{{ d['examples/website-sections/index.html'] }}
When this is put through the htmlsections
filter the content will be split
into sections:
{{ d['examples/website-sections/dexy.yaml|idio|t'] }}
The htmlsections
filter is actually one of the aliases for the idio
filter.
It behaves a little differently than |idio
: it splits content into sections
following the idio
rules, it doesn’t apply syntax highlighting, and then it
sets output
to True. It’s intended for use in just the scenario being
described here.
The template can access these sections using either content.foo
or
content['foo']
. The former is shorter, the latter works even when section
names have spaces in them:
{{ d['examples/website-sections/_template.html|idio|t'] }}
Here’s the result:
{{ d['examples/website-sections/output-site/index.html|idio|t'] }}
You can also just call {{ "{{ content }}" }}
on a sectioned document and dexy
will insert all sections combined.
The website template provides several elements to assist with creating website navigation.
The page_title
element contains the title of the current page:
{{ select_soup('examples/website-navigation/_template.html', 'head') }}
Here’s the result for index.html
:
{{ select_soup('examples/website-navigation/output-site/index.html', 'head') }}
The source
element contains the canonical output filename of the current
page. The current_dir
and parent_dir
elements refer to the directory
containing the current page and its parent (if any), respectively:
{{ select_soup('examples/website-navigation/_template.html', '#pageinfo') }}
Here’s the result for index.html
:
{{ select_soup('examples/website-navigation/output-site/index.html', '#pageinfo') }}
Here’s the result for foo/index.html
:
{{ select_soup('examples/website-navigation/output-site/foo/index.html', '#pageinfo') }}
Here’s the result for foo/bar/index.html
:
{{ select_soup('examples/website-navigation/output-site/foo/bar/index.html', '#pageinfo') }}
The s
element contains the current page’s data
object. (The same
information is contained in the d
element, you can use either one.)
{{ select_soup('examples/website-navigation/_template.html', '#s-object') }}
Here’s the result for index.html
:
{{ select_soup('examples/website-navigation/output-site/index.html', '#s-object') }}
Here’s the result for foo/index.html
:
{{ select_soup('examples/website-navigation/output-site/foo/index.html', '#s-object') }}
Here’s the result for foo/bar/index.html
:
{{ select_soup('examples/website-navigation/output-site/foo/bar/index.html', '#s-object') }}
The Website reporter creates a Navigation
object which represents the tree of
directories in the project. Each directory is represented by a Node
object.
Two node objects are made directly available within a website template, the
root
object representing the root of the project, and the nav
object
representing the node corresponding to the file being processed. The whole
navigation tree is availalbe via the navtree
objects.
Here are some basic methods and attributes of nodes, using the nav
object
representing the node for the document being processed:
{{ select_soup('examples/website-navigation/_template.html', '#nav') }}
The children
attribute represents subdirectories. The docs
attribute
represents all documents found in the node’s directory. The index_page
attribute corresponds to the index.html
page, if there is one, in the node’s
directory. The level
attribute represents the number of directories above the
node’s directory in the project.
Here’s the result for index.html
:
{{ select_soup('examples/website-navigation/output-site/index.html', '#nav') }}
Here’s the result for foo/index.html
:
{{ select_soup('examples/website-navigation/output-site/foo/index.html', '#nav') }}
Here’s the result for foo/bar/index.html
:
{{ select_soup('examples/website-navigation/output-site/foo/bar/index.html', '#nav') }}
Here’s these attributes for the root node:
{{ select_soup('examples/website-navigation/_template.html', '#root') }}
Here’s the result for index.html
:
{{ select_soup('examples/website-navigation/output-site/index.html', '#root') }}
It’s the same for foo/bar/index.html
:
{{ select_soup('examples/website-navigation/output-site/foo/bar/index.html', '#root') }}
A common usage is to iterate over children
to produce a list of subdirectories.
You can create relative navigation on each page by using the nav
object, or
navgation for the entire site using the root
object and iterating recursively over
children.
There are some macros bundled with dexy which illustrate ways to use these elements to construct site navigation. You can use them as-is or copy them and modify them for your own needs.
The Navigation object is primarily used to generate the tree, but it can also
be accessed via the navtree
element. Its nodes
attribute is a dictionary of
all nodes accessed by path. The root
attribute is the root node, which you
can already access via root
. There is a debug
method which prints out all
nodes and their attributes which you can include in a document.
{{ select_soup('examples/website-navigation/_template.html', '#navtree') }}
Here’s the result for index.html
:
{{ select_soup('examples/website-navigation/output-site/index.html', '#navtree') }}
The link()
and section()
methods allow you to create a HTML <a>
link to
a dexy page based on the page’s title or key and a section’s name.
The link()
method links to a specified page, and optionally to a section on
that page. The section()
method lets you link to a specified section without
needing to know which page it’s on.
Here’s an example of using these methods:
{{ select_soup('examples/website-links/_home.html', '#links') }}
And here are the results:
{{ select_soup('examples/website-links/output-site/index.html', '#links') }}
The page entitled Home
has its title specified in dexy.yaml
:
{{ d['examples/website-links/dexy.yaml|idio|t']['home'] }}
The page entitled All About Foo
, on which we link to the Foo
and Welcome
sections uses two filters wihch are useful to know about.
The yamlargs
filter lets you add some YAML metadata to the top of a page. The
YAML is stripped off and the metadata is added to the page’s settings.
{{ d['examples/website-links/foo/index.html|idio|t'] }}
You can use the dexy links
command to print out a list of all valid keys
which are possible to use as the target of the link()
and section()
commands:
{{ d['examples/website-links.sh|idio|shint|asciisyn']['links-command'] }}
Dexy will complain if you use a non-unique title or section name.
In addiiton to the specific template elements covered above, the Website
reporter makes use of many of dexy’s Template Plugins. You can configure this
via the plugins
setting.
The dexy info
command provides information about indvidual documents in a
dexy run, and this command takes a ws
argument which adds customized
documentation about the website template elements available, and their values,
for this individual document.
Here’s an example. It includes website related tags, elements defined via template plugins, the contents of the Navigation tree, and methods available on nodes in the tree:
{{ d['examples/run.sh|idio|shint|asciisyn']['data-info-ws'] }}
This documentation was generated by Dexy and the Asciidoctor implementation of Asciidoc.
The source code lives on Github. If you have feedback or suggestions about this document please fell free to email info@dexy.it or open a github issue.
Here is the dexy.yaml
file for this document:
{{ d['dexy.yaml|asciisyn'] }}