ginger
An implementation of the Jinja2 template language in Haskell
https://ginger.tobiasdammers.nl/
Version on this page: | 0.9.1.0 |
LTS Haskell 22.39: | 0.10.5.2 |
Stackage Nightly 2023-12-26: | 0.10.5.2 |
Latest on Hackage: | 0.10.5.2 |
ginger-0.9.1.0@sha256:dc19f586eff45161a0e4f1099eb26937c45bf7c993c77d21ad27b48696b283da,3766
Ginger
A Haskell implementation of the Jinja2 template language.
A note of warning: the git repository at https://bitbucket.org/tdammers/ginger has been deleted and restored with a rewritten commit tree on 2016-04-06 in order to clean up the messy history. This means that if you have a checkout from before that date, merging the bitbucket repo will most likely break things; please do a fresh clone to fix this. Sorry for the inconvenience.
Introduction
Ginger provides most of the original Jinja2 template language, as much as that makes sense in a Haskell host application.
We do, however, avoid some of the most blatant Pythonisms, especially where we felt the features in question were more of an accidental result of binding template constructs to Python constructs.
On top of that, we deviate on a few points, and add some features that we think are very useful and help make the language better and more consistent.
Installation
Ginger is available from Hackage, and it is in Stackage, so you can use it in plain Cabal projects as well as Stack ones.
Installing with Cabal:
cabal install ginger
Installing with Stack:
stack install ginger
Using as part of another project:
Add the following to your .cabal
’s build-depends
:
ginger >=0.7.4.0 && <0.8
Template Syntax
Full template syntax documentation is available from the /docs
subdirectory
in the project repository itself, or from the User Guide section on the
Ginger website.
Minimal example template
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
{# This is a comment. Comments are removed from the output. #}
<body>
<menu id="nav-main">
{% for item in navigation %}
<li><a href="{{ item.url }}">{{ item.label }}</a></li>
{% endfor %}
</menu>
<div class="layout-content-main">
<h1>{{ title }}</h1>
{{ body }}
</div>
</body>
</html>
There are three kinds of delimiters:
- Interpolation, to inject values into the output; these default to
{{
and}}
- Flow Control, to create conditionals, loops, and other control constructs;
these default to
{%
and%}
- Comments, which are removed from the output; these default to
{#
and#}
.
These delimiters can be changed on the Haskell side. In principle, any string is accepted for any delimiter; you may, however, get surprising results if you pick delimiters that clash with other Ginger syntax, or with one another (e.g., using the same string to start interpolations and flow control constructs will not work).
Haskell API
The Haskell API is documented fully through Haddock documentation, available from Hackage. We’ll just provide a short overview here.
General
On the Haskell side of things, executing a template is a two-step process. First, template source code is parsed into a ‘Template’ data structure, which is then fed to ‘runGinger’ or ‘runGingerT’.
Parsing
Because Ginger templates can include other templates, the parser needs a way of resolving template names. Instead of hard-wiring the parser into ‘IO’ though, Ginger will work on any Monad type, but requires the caller to provide a suitable template resolver function. For ‘IO’, the resolver would typically load a file from a template directory, but other monads might have access to some sort of cache, or expose template compiled into a program, or simply return ‘Nothing’ unconditionally to disable any and all imports. A suitable example implementation for ‘IO’ would look like this:
loadFile fn = openFile fn ReadMode >>= hGetContents
loadFileMay fn =
tryIOError (loadFile fn) >>= \e ->
case e of
Right contents ->
return (Just contents)
Left err -> do
print err -- remove this line if you want to fail silently
return Nothing
(Taken from cli/GingerCLI.hs
). This interprets the template name as a
filename relative to the CWD, and returns the file contents on success or
‘Nothing’ if there is any error.
If you don’t need a monadic context for resolving includes (e.g. because you have pre-loaded all template sources), you can use the pure ‘parseGinger’ flavor, which does not rely on a host monad.
Running
The core function for running a template is ‘runGinger’ (or its monadic flavor ‘runGingerT’); in order to pass an initial context to the template engine, pass a suitable ‘GingerContext’, which you can create using the ‘makeContext’ / ‘makeContextM’ functions.
An example call (for running a template in ‘IO’) would look something like this:
runGingerT (makeContextM scopeLookup (putStr . Text.unpack . htmlSource)) tpl
Changes
0.8.4.0
- Added builtin
apply
, making it possible to pass argument lists as arrays
0.8.3.0
- Added builtin
regex
module for POSIX regular expressions support
0.8.2.0
- Expose some internals of the
Run
type and the default implementations of built-in functions / filters
0.8.1.0
- Added built-ins:
partial
,zip
,zipwith
,compose
0.8.0.0
- Now compiles on GHC 8.4
- CLI: added a
system()
template function (for spawning subprocesses) - CLI accepts YAML
- Added
is
operator (also makes Ginger conform to Jinja2 test syntax) - Various bugfixes
- New builtins:
escaped
,in
, Python-style boolean operators,divisibleBy
,even
,odd
, and more - Boolean literals now also accepted in caps
- Improved documentation
GVal
instances forInteger
- Overridable delimiters
0.7.4.0
- Make concat() / ~ more generic (now also concatenates lists and dictionaries)
- CLI omits printing of
null
results. Useful when using as a filter. - Fixed excessive newlines in CLI output
0.7.3.0
- Expose parser error position details
0.7.2.0
- ‘|json’ filter
0.7.1.0
StripBlocks
andLTrimBlocks
options+
tag modifier to override whitespace stripping
0.7.0.0
keepTrailingNewlines
option- Ability to pass parser options into parseGinger
- Runtime warnings
0.6.0.2
- Documentation fixes
0.6.0.1
- Haddock documentation fix
0.6.0.0
- Exceptions / exception handling.
0.5.3.0
- Marshalling and hoisting: it is now possible to fully marshal
GVal
s between arbitrary carrier monads, as long as suitable conversion functions are provided.
0.5.2.0
- Added map(), upper(), lower() functions
0.5.1.3
- Documentation fixes
0.5.1.2
- Release-related fixups
0.5.1.1
- Bugfixes wrt indentation mode
0.5.1.0
- Expose parser error pretty-printer from the library
0.5.0.0
- Indentation mode:
{% indent %}
introduces an indentation context
0.4.0.0
- Statements can now return values
- Added
do
expressions (lift statements into expressions)
0.3.11.1
- Fixed a parser bug related to whitespace in script mode
0.3.11.0
- Fixed the way local scopes work in script mode
- Documented script mode
0.3.10.0
- Script mode: alternative syntax that makes it easier to use Ginger as a scripting language, used inside {% script %} blocks.
0.3.9.1
- Various dependency issues fixed
0.3.8.0
- Added a
{% switch %}
statement