Templer ======= Templer is yet another static site generator, written in Perl. It makes use of the [HTML::Template](http://search.cpan.org/perldoc?HTML%3A%3ATemplate) module for performing variable expansion within pages and layouts, along with looping and conditional-statement handling. Templer has evolved over time for my own personal use, but I believe it is sufficiently generic it could be useful to others. My motivation for putting it together came from the desire to change several hand-made, HTML-coded, sites to something more maintainable such that I could easily change the layout in one place. The design evolved over time but the key reason for keeping it around is that it differs from many other simple static-generators in several ways: * You may define global variables for use in your pages/layouts. * A page may define and use page-specific variables. * You may change the layout on a per-page basis if you so wish. * This was something that is missing from a lot of competing tools. * Conditional variable expansion is supported, via `HTML::Template`. * File contents, shell commands, and file-globs may be used in the templates * This allows the trivial creation of galleries, for example. * These are implemented via plugins. * Plugins are documented in the file [PLUGINS.md](PLUGINS.md). * You may also embed perl code in your pages. Another key point is that the layouts allow for more than a single simple "content" block to be placed into them - you can add arbitrary numbers of optional side-menus, for example. Although this tool was written and used with the intent you'd write your site-content in HTML you can write your input pages in Textile or Markdown if you prefer (these inputs are supported via [plugins](PLUGINS.md)). Concepts -------- A templer site comprises of three things: * A global configuration file. * This defines the paths to search for input pages, layout templates, plugins, etc. * This may contain global variable declarations. * Please see the example configuration file: [`templer.cfg`](https://raw.github.com/skx/templer/master/templer.cfg.sample). * A layout. * This is used to format the output pages, defining the common look and feel. * A series of pages & assets. * Pages have their content processed and inserted into the layout to produce output HTML. * Assets are not processed, but are copied into the output directory literally. In general we assume there is a tree like so: ├── input │ ├── index.wgn │ ├── ... │ ├── ... │ ├── favicon.ico │ └── robots.txt ├── layouts │ └── default.layout ├── output └── templer.cfg Every file in the input directory is either considered to be a page which is converted into HTML, or an asset which is copied into the output-tree with no changes made. In the example above `input/index.wgn` would become `output/index.html`. > **NOTE** The `.wgn` suffix is an example. You can define which suffix is considered a page via the configuration file. There is _also_ an "in-place" mode. When working in-place there is no distinct output directory, instead output is written to the same directory in which is encountered. Given an input directory you might see this kind of transformation: index.wgn -> index.html about.wgn -> about.html favicon.ico [Ignored and left un-modified.] robots.txt [Ignored and left un-modified.] .. There is _also_ a *synchronized* mode. When working synchronized any file in the output directory which do not have a source file in input directory (page or asset) is removed each time the site is rebuild. Pages ----- Your site will be made of pages, which are snippets of HTML you write. These snippets will be processed and inserted into the layout file before being output to disk. A page is a simple file which contains a header and some content. This is a sample page: Title: This is the title page. ----

This is the body of the page

The header of the page is delimited from the body by four dashes (`----`) and can contain an arbitrary number of variable definitions. Special Page Variables ----------------------- In your page you can define, and refer to, an arbitrary number of variables but some names are reserved - and any variable with one of those names will be treated specially: The special variable `layout` may be used to specify a different layout template for the current page. If there is no per-page layout specified then the global layout declared in the `templer.cfg` file will be used. The special variable `template-filter` may be used to specify some filters to apply on the used layout in order to transform it into valid `HTML::Template` file. If there is no per-page layout filter specified then the global layout declared in the `templer.cfg` file will be used. The special variable `output` may be used to specify an alternative output file. For example the input file `index.wgn` would normally become `index.html`, but you could make it become something else. The special variable `format` may be given a value of `textile`, `markdown`, or `perl` to enable processing the page body with the appropriate filter. These formatters are implemented as [plugins](PLUGINS.md), and will be available assuming their [dependencies are installed](#installation). Textile and markdown are well-known, and allow you to write your page content naturally. The perl-formatter is used to allow you to write dynamic content in Perl in your page-body, via the [Text::Template](http://search.cpan.org/perldoc?Text%3A%3ATemplate) module. Perl code to be executed is wrapped in `{` and `}` characters. Here is a sample page: Title: This page has code in it format: perl ----

This page has some code in it.

I am running on { `hostname` }...

I am { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $year += 1900; $year - 1976; } years old.

> **NOTE**: Formatters may be chained. For example "`format: perl, markdown`". Variable Definitions -------------------- Within the header of each page you may declare an arbitrary number of per-page variables. These variable declarations are then available for use within the page-body, using the standard [HTML::Template](http://search.cpan.org/perldoc?HTML%3A%3ATemplate) expansion facilities: Title: Page title Name: Steve Kemp ----

Hello, my name is .

> **NOTE**: All variable-names are transformed to lower-case for consistency, which is why we refer to the variable `name` rather than the defined `Name`. As well as simple "name: value" pairs there are also additional options implemented in [plugins](PLUGINS.md); * A variable may refer to the contents of a given file. * Using `read_file`. * A variable may refer to a list of filenames, matching a pattern. * Using `file_glob`. * A variable may contain the output of running a command. * Using `run_command`. * A variable may be based on the timestamp of the input page. * Using `timestamp`. * A variable may contain the contents of a remote RSS feed. * Using `rss(count, URL)`. * A variable may contain the result of a key-lookup from a Redis server. * Using `redis_get('foo')`. In addition to declaring variables in a page-header you may also declare __global__ variables in your `templer.cfg` file, or upon the command-line via `--define foo=bar`. Defining global variables is demonstrated in the sample [`templer.cfg`](https://raw.github.com/skx/templer/master/templer.cfg.sample) file. File Globbing Variables ----------------------- We've already seen simple variables declared by `key: value` in the page header, in addition to this you may define a variable that refers to a number of files by pattern. Here is a simple example of creating a gallery which will include files matching the pattern `img/*.jpg`: Title: My gallery Images: file_glob( "img/*.jpg" ) ---

No images were found.

> **TIP**: If your images are numbered numerically you can ensure their correct order by doing this: Title: This is my title images: file_glob( img/[0-9].jpg img/1[0-9].jpg ) ----

My gallery is now included in ascending numerical order:

This facility is implemented in the `Templer::Plugin::FileGlob` [plugin](PLUGINS.md). The file glob is primarily designed for handling image-galleries, which is why it will set the `height` and `width` attributes if your glob matches `*.jpg`, `*.png`, etc. However it can also be used for non-images. If your glob matches files which are not images it will populate the member `content`, being the text-content of the matching files. This allows you to include files easily. For example: Title: This is my news-page news: file_glob( news-*.txt ) ----

Here are the recent events:

This assumes you have files such as `news-20130912.txt`, etc, and will show the contents of each file in (glob)order.

If matching files are templer input files then all templer variables are populated instead of the text-content of the matching files. In all cases it will populate `dirname`, `basename` and `extension`, being parts of each matching files name. File Inclusion -------------- The [HTML::Template](http://search.cpan.org/perldoc?HTML%3A%3ATemplate) module supports file inclusion natively, via the following construct:

This is some text.

That was my password file.

In addition to this you may define a variable to contain the contents of a specified file. For example: Title: This file has my passwords! Passwd: read_file( "/etc/passwd" ) ----

Please see my passwords:


    
This facility is implemented in the `Templer::Plugin::FileContents` [plugin](PLUGINS.md). Include files, whether included via the `read_file` method, or via the native HTML::Template faclity, are searched for in the same fashion: * If the filename is fully-qualified, then the absolute path-name will be used. * Otherwise the include-path will be searched. * After the include-path has been searched the file will be looked for in the location relative to the input page location. This allows you to place all your include-files in a single directory which is outside your web-root. > **TIP**: The advantage of choosing to use `read_file` over the native HTML::Template support is that with the former the output page will be automatically rebuilt if you modify the include file. Shell Command Execution ----------------------- Pages may also define variables which receive the value of the output of shell commands. This is done via definitions like this: Title: This file is dynamic Host: run_command( "hostname" ) ----

This page was built upon .

This facility is implemented in the `Templer::Plugin::ShellCommand` [plugin](PLUGINS.md). Remote RSS Feeds ---------------- Pages may use snippets of RSS feeds, limiting them to the given number of entries. For example: title: About my site feed: rss(4, http://blog.steve.org.uk/index.rss ) ----

This page is about my site, here are my recent blog posts:

Redis Lookups ------------- If you have a redis-server running upon the local system you may configure page-variables to retrieve their values via lookups against it. For example: title: Site Statistics count: redis_get( "global_count" ) ----

There are entries.

Installation ------------ Templer is developed in a public fashion, here on github, but stable releases are also uploaded to CPAN: * [App::Templer](http://search.cpan.org/dist/App-Templer/) Installation should be as simple as any other CPAN-based module: $ git clone https://github.com/skx/templer.git $ cd templer $ perl Makefile.PL $ make test $ sudo make install (If you ever wish to remove the software you may run `sudo make uninstall`.) The code makes use of a reasonably large number of modules for its implementation, and you can see a brief overview of [the logical structure](#object-hierarchy) later.) If you want to get a standalone `templer` executable which includes all those `Templer` modules, you can use the `standalone` target of the generated `Makefile`: $ make standalone This will produce a script called `templer` in the base directory so that you should be able to use it alone without copying these used modules anywhere in perl library directories. The dependencies are minimal, to ease installation: * Perl * The [Module::Pluggable](http://search.cpan.org/perldoc?Module%3A%3APluggable) module for loading plugins. * The [HTML::Template](http://search.cpan.org/perldoc?HTML%3A%3ATemplate) module. * This may be installed, on a Debian system, with `apt-get install libhtml-template-perl`. * The following are optional modules: * The [Image::Size](http://search.cpan.org/perldoc?Image%3A%3ASize) module is used if available whenever you create `file_glob`-using loops of image files. * This will set the attributes `width` and `height` any images added via `file_glob`. * The [Text::Markdown](http://search.cpan.org/perldoc?Text%3A%3AMarkdown) module is required if you wish to write your page bodies in Markdown. * This may be installed, on a Debian system, with `apt-get install libtext-markdown-perl`. * The [Text::Textile](http://search.cpan.org/perldoc?Text%3A%3ATextile) module is required if you wish to write your page bodies in Textile. * This may be installed, on a Debian system, with `apt-get install libtext-textile-perl`. * The [Text::Template](http://search.cpan.org/perldoc?Text%3A%3ATemplate) module is required if you wish to include dynamic perl in your input pages. * This may be installed, on a Debian system, with `apt-get install libtext-template-perl`. * The [Redis](http://search.cpan.org/perldoc?Redis) module is required if you wish to use the Redis plugin. * This may be installed, on a Debian system, with `apt-get install libredis-perl`. * The [XML::Feed](http://search.cpan.org/perldoc?XML%3A%3AFeed) module is required if you wish to use the RSS plugin. * This may be installed, on a Debian system, with `apt-get install libxml-feed-perl`. If you prefer you can install Debian binary packages from my personal repository: * [templer repository for Debian GNU/Linux](http://packages.steve.org.uk/templer/) Creating a new site ------------------- There is a supplied script `templer-generate` which will create a new site-structure if you give in the name of a directory to create & write to: ~$ templer-generate my-site ~$ tree my-site/ my-site/ ├── include ├── input │ ├── about.wgn │ ├── index.wgn │ └── robots.txt ├── layouts │ └── default.layout ├── output └── templer.cfg If you prefer you may go through the process manually creating a directory, adding the [`templer.cfg`](https://raw.github.com/skx/templer/master/templer.cfg.sample) to it, and then creating the input tree and layout directory. There are several [examples](examples/) provided with the distribution to illustrate the software. These example sites are built automatically every evening and uploaded online - so you may easily compare the input and the generated output: * [simple example source](https://github.com/skx/templer/tree/master/examples/simple) * The online [generated output](http://www.steve.org.uk/Software/templer/examples/simple/output/). * [complex example source](https://github.com/skx/templer/tree/master/examples/complex) * The online [generated output](http://www.steve.org.uk/Software/templer/examples/complex/output/). * The generated "complex" example is designed to be a standalone introduction to templer. Rebuilding a site ----------------- If you're beneath the directory containing your `templer.cfg` file simply run `templer` with no arguments. You may optionally add flags to control what happens: * `templer --verbose` * To see more details of what is happening. * `templer --force` * To force a rebuild of the site. * `templer --define foo=bar` * Define the variable `foo` for use in your templates. This will over-ride any setting of foo in the configuration file you've loaded. In the general case `templer` should rebuild only the files which are needed to be built. A page will be rebuilt if: * The page source is edited. * The layout the page uses is edited. * Any include-file the page includes is edited. * This applies to those includes read via [read_file](#file-inclusion) rather than via `HTML::Template` includes > Previously it was required that you run `templer` from the top-level of your site, this has now changed. `templer` will walk upwards from the current working directory and attempt to find the site-root by itself. Object Hierarchy ---------------- Although `templer` is distributed and used as a single script it is written using a series of objects. Bundling into a single binary allows for easier distribution, installation and usage. In brief the control flow goes like this: * `templer` runs, parses the command line, etc. * A `Templer::Global` object is created to read the `templer.cfg` file, or the file passed via `--config=foo`. * The options from the command-line and the config file are merged. * From this point onwards `Templer::Global` is ignored. * A `Templer::Site` object is created, using the merged config values. * A `Templer::Timer` object is created to record the build-time. * The build process is contained in `Templer::Site::build()`: * A `Templer::Plugin::Factory` object is created to load plugins. * A `Templer::Site::Page` object is created for each appropriate input. * Each page is output. * The plugins are unloaded. * The assets are copied via `Templer::Site::copyAssets()`. * The output directory is cleaned via `Templer::Site::sync()`. * The build-time/build-count is reported and the process is complete. Each of the modules has a simple test-case associated with it. To test functionality, especially after making changes, please run the test-suite: $ make test prove --shuffle t/ t/style-no-tabs.t ........................... ok t/test-dependencies.t ....................... ok .. .. t/test-templer-plugin-filecontents.t ........ ok t/test-templer-site.t ....................... ok t/test-templer-plugin-timestamp.t ........... ok All tests successful. Files=15, Tests=286, 1 wallclock secs ( 0.11 usr 0.01 sys + 0.88 cusr 0.14 csys = 1.14 CPU) Result: PASS Any test-case failure is a bug, and should be reported as such. Problems -------- Please report any issue via the github repository: * https://github.com/skx/templer Author ------ Steve Kemp