Racket implementation of Mustache templates.
Rastache is compliant with the Mustache spec v1.1+λ
Rastache can be installed as a collection within Racket with package manager:
$ raco pkg install "git://github.com/rcherrueau/rastache.git?path=rastache"
Or manually:
$ git clone git://github.com/rcherrueau/rastache.git
$ cd rastache/rastache
$ raco link .
$ raco setup
Once installed, here is the simplest way to use Rastache:
#lang racket/base
(require rastache)
; rast-compile/render: input-port hash -> output-port
(rast-compile/render (open-input-string "{{foo}}")
#hash{ (foo . "bar") }
(current-output-port))
The function rast-compile/render
takes three parameters, the
template as an input port, the context as a hash table and an output
port in which Rastache displays the rendered template.
The function rast-compile/render
is the composition of two auxiliary
functions, rast-compile
and rast-render
. Using those two
separately enables the compilation of a template and its memoization
for later uses.
#lang racket/base
(require rastache)
(let* ([template (open-input-file "huge-template.mustache")]
[compiled-template (rast-compile template)])
(define ctx1 #hash{#|context 1|#})
(define ctx2 #hash{#|context 2|#})
(define ctx3 #hash{#|context 3|#})
(for ([ctx (list ctx1 ctx2 ctx3)])
(rast-render compiled-template ctx (current-output-port))
(newline)))
Rastache also offers facilities for reading strings and files.
; rast-compile/open-string: string -> (listof token)
(rast-compile/open-string "{{foo}}")
; rast-compile/open-file: path -> (listof token)
(rast-compile/open-file "path/to/file.mustache")
See examples directory for more usage examples.
At the moment the project is under development but passes all Mustache spec tests. If you want to run the tests yourself, do the following:
$ git clone git://github.com/rcherrueau/rastache.git
$ cd rastache/rastache/tests/
$ raco test tests.rkt
The most basic tag type is the variable. A {{name}}
tag in a
basic template will try to find the name
key in the current
context.
All variables are HTML escaped by default. If you want to return
unescaped HTML, use the triple mustache {{{name}}}
or &
.
Template:
* {{name}}
* {{age}}
* {{company}}
* {{{company}}}
* {{& company}}
Context:
#hash{ (name . "Chris")
(company . "<b>GitHub</b>") }
Output:
* Chris
*
* <b>GitHub</b>
* <b>GitHub</b>
* <b>GitHub</b>
Dot notation may be used to access nested keys.
Template:
* {{type}}
* {{delorean.name}}: {{delorean.speed}}
Context:
#hash{ (type . "Time Machine")
(delorean . #hash{ (name . "DeLorean")
(speed . "88 mph") }) }
Output:
* Time Machine * DeLorean: 88 mph
If the value of a key is a lambda, it is called with the current context as its first argument. The second argument is a rendering function that uses the current context as its context argument.
Template:
Hello, {{lambda}}!
Context:
`#hash{ (planet . "world")
(lambda . ,(λ (_ render) (render "{{planet}}"))) }
Output:
Hello, world!
Sections render blocks of text one or more times, depending on the value of the key in the current context.
A section begins with a pound and ends with a slash. That is,
{{#person}}
begins a “person” section while {{/person}}
ends
it.
The behavior of the section is determined by the value of the key.
Template:
Shown.
{{#person}}
Never shown!
{{/person}}
Context:
#hash{ (person . #f) }
Output:
Shown.
If the person
key exists and has a non-false value, the HTML
between the pound and slash will be rendered and displayed one or
more times.
When the value is a non-empty list, the text in the block will be displayed once for each item in the list. The context of the block will be set to the current item for each iteration. In this way we can loop over collections.
Template:
Death List Five:
{{#death}}
<b>{{name}}</b>
{{/death}}
Context:
#hash{ (death . [#hash{ (name . "O-Ren Ishii") }
#hash{ (name . "Vernita Green") }
#hash{ (name . "Budd") }
#hash{ (name . "Elle Driver") }
#hash{ (name . "Bill") }]) }
Output:
Death List Five:
<b>O-Ren Ishii</b>
<b>Vernita Green</b>
<b>Budd</b>
<b>Elle Driver</b>
<b>Bill</b>
When looping over an array of strings, a .
can be used to refer
to the current item in the list.
Template:
{{#tmnt}}
* {{.}}
{{/tmnt}}
Context:
#hash{ (tmnt . ["Leonardo"
"Michelangelo"
"Donatello"
"Raphael"]) }
Output:
* Leonardo * Michelangelo * Donatello * Raphael
If the value of a section key is a lambda, it is called with the section’s literal block of text, un-rendered, as its first argument. The second argument is a special rendering function that uses the current context as its context argument.
Template:
<{{#lambda}}-{{/lambda}}>
Context:
`#hash{ (planet . "Earth")
(lambda . ,(λ (text render)
(render (string-append text
"{{planet}}"
text)))) }
Output:
<-Earth->
An inverted section begins with a caret (hat) and ends with a
slash. That is {{^person}}
begins a “person” inverted section
while {{/person}}
ends it.
Template:
{{#repo}}
<b>{{name}}</b>
{{/repo}}
{{^repo}}
No repos :{
{{/repo}}
Context:
#hash{ (repo . []) }
Output:
No repos :{
Comments begin with a bang and are ignored. The following template:
<h1>Today{{! ignore me }}.</h1>
Will render as follows:
<h1>Today.</h1>
Comments may contain newlines.
Partials allow you to include other templates. It begins with a
greater than sign, like {{> partialkey}}
.
The partialkey
can be a simple string, thus Rastache interprets
partialkey
as a file path.
Template:
Hello{{>partials/names}}
Context:
#hash{ (people . [ #hash{ (name . "Marty") }
#hash{ (name . "Emmet") }
#hash{ (name . "Einstein") } ]) }
Partial file `partials/names’:
{{#people}}, {{name}}{{/people}}
Output:
Hello, Marty, Emmet, Einstein
The partialkey
can also be an URIs, as specified in RFC 2396.
Thus Rastache uses `get-pure-port’ with redirection parameter set
to 1
to get the resource.
Template:
Hello{{>https://github.com/rcherrueau/rastache/raw/master/rastache/tests/partials/names}}
Context:
#hash{ (people . [ #hash{ (name . "Marty") }
#hash{ (name . "Emmet") }
#hash{ (name . "Einstein") } ]) }
Output:
Hello, Marty, Emmet, Einstein
Set Delimiter tags start with an equal sign and change the tag
delimiters from {{
and }}
to custom strings.
Consider the following contrived example:
* {{default_tags}}
{{=<% %>=}}
* <% erb_style_tags %>
<%={{ }}=%>
* {{ default_tags_again }}
Here we have a list with three items. The first item uses the default tag style, the second uses erb style as defined by the Set Delimiter tag, and the third returns to the default style after yet another Set Delimiter declaration.
I’ve given myself a project as I learn Racket. I’m particularly interested in Racket facilities for defining expander and reader and for packaging those two into a conveniently named language. For all these reasons, mustache implementation seems a good project.
Copyright (C) 2014 Ronan-Alexandre Cherrueau
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA