Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/skx/templer
A modular extensible static-site-generator written in perl.
https://github.com/skx/templer
perl static-site-generator templer
Last synced: 2 months ago
JSON representation
A modular extensible static-site-generator written in perl.
- Host: GitHub
- URL: https://github.com/skx/templer
- Owner: skx
- License: other
- Created: 2012-12-01T13:21:43.000Z (about 12 years ago)
- Default Branch: master
- Last Pushed: 2022-07-01T05:36:19.000Z (over 2 years ago)
- Last Synced: 2024-09-29T19:01:25.059Z (3 months ago)
- Topics: perl, static-site-generator, templer
- Language: Perl
- Homepage:
- Size: 674 KB
- Stars: 63
- Watchers: 8
- Forks: 14
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: ChangeLog
- License: LICENSE
Awesome Lists containing this project
README
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.cfgEvery 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