With Docker:
$ docker run -v `pwd`:/data samoht/ramen
With opam:
$ opam install ramen
$ ramen
Check ramen --help
for more details.
The CLI help message for Ramen says:
Usage: ramen [--data=<path>] [--pages=<path>] [--site=<path>] [-v]
These three directories are:
-
site/
: the generated website, created by Ramen. Add images or stylesheet here but do not edit the contents of processed files there. Copy that directory to your live website to put your static website live. -
pages/
: the base templates. All the templates in that directory are processed by Ramen and the expanded results are copied intosite/
. -
data/
: the data read by Ramen used to feed the templates inpages/
.
There are 2 kinds of data:
-
raw data: Ramen uses these to complete template variables. Example of raw data could either be a raw file or a header value.
-
collections: Ramen uses these to expand for loops.
foo.bar
denotes the entrybar
in the collectionfoo
. These are built from directories indata/
or from structured files (with.yml
or.json
extensions). Collection are ordered:- when built from files, using lexicographic order.
- when built from JSON or Yaml, using the order in which items are declared.
Ramen predefines the global
collection, with the following contents:
global | description |
---|---|
site.date |
the date of build. |
site.pages |
contents of the pages/ directory. |
site.page |
the current page being built. |
Every template in pages has the following structure:
var_1: value_1
var_2: value_2
---
body
The body can contain templates of the form:
-
variables:
{{ var }}
: Ramen replaces these with their raw values defined in the page header or in the data directory (see bellow). Variables are alpha-numeric characters with-
and_
. Full variables can contain dots, to explore collections. For instance, iffoo
has two keysa
andb
(as for instance their exists two filesdata/foo/a
anddata/foo/b
) the contents of these could be accessed in template bodies usingfoo.a
andfoo.b
.When reading files in the
data/
directory, Ramen always removes the file extensions to build the variable names: the contents offoo/a.html
is available asfoo.a
. In some cases (see bellow for details), the contents is pre-processed.Raw data can also contains the
{{ .. }}
quotations. They are expanded recursively by Ramen. -
aliases:
{{ let n = var in }}
bindsn
to the contents of the variablevar
. This is useful to factorize some code. -
loops:
{{ for i in var do }} <body> {{ done }}
: Ramen expands the body for each entry in the collectionvar
.For instance, if
data/foo
contains two filesdata/foo/a.md
anddata/foo/b.md
which containstoto
andtiti
respectively, then:{{ for i in foo do }} Hello {{ i }}. {{ done }}
is equivalent to:
Hello toto. Hello titi.
When a collection is used as a parameter of a loop, two extra fields are added in the context:
first
andlast
. These are useful to test for start or end conditions, for instance:{{ for i in foo do }} {{ if (i = foo.first) }} Hello first {{ i }}. {{ else }} Hello {{ i }} {{ fi }} {{ done }}
Moreover, every collection can be seen as a doubly-linked list, where elements are linked by the
prev
andnext
fields. For instance, the previous code snippet is equivalent to:{{ for i in foo do }} {{ if (!i.prev) }} Hello first {{ i }}. {{ else }} Hello {{ i.prev.next }} {{ fi }} {{ done }}
The order in which the iteration is done is by default the order in which the collection is built (lexical order, order in which items appears in a file, etc). It is possible to control the iteration order by using the built-in function
rev(..)
andsort(..,<id>)
whereid
is the key in which to base the sort. -
conditions:
{{ if (cond_1) }} <body_1> ... {{ elif (cond_n) }} <body_n> {{ else }} <body_x> {{ fi }}
. Ramen picks the first<body_i>
such thatcond_i
is satisfied or uses<body_x>
otherwise (or an empty string if none of the conditions are true and the else clause is missing).Conditions expressions the compososition of:
- single variables:
var
; - equalities:
var_1 = var_2
orvar_1 = 'string'
- inequalities:
var_1 != var_2
orvar_1 != 'string'
- parentheses:
( expr )
- negations:
! expr
- conjonctions:
expr_1 && expr_2
- disjonctions:
expr_1 || expr_2
For instance:
{{ if (i.title && i = site.page) }} <div class="nav active">{{i.title}}</div> {{ elif (i.title) }} <div class"nav">{{i.title}}<div> {{ fi }}
- single variables:
-
dictionaries:
{{ xxx.[VAR].yyy }}
evaluates toxxx.v.yyy
wherev
is the contents ofVAR
. This could be used in conjunction with for loops to "join" various collections. For example, if you have two collectionsbooks
andpeople
, you can cross-reference them using:{{for i in books do}} <div class="book"> <div class="title">{{i.title}}</div> <div class="author">{{people.[i.author].name}}</div> </div> {{done}}
-
fonctions:
{{ VAR(k_1: v_1, ..., k_n: v_n) }}
this is similar to evaluatingVAR
in the context wherek_1
,...,k_n
are bound tov_1
,...v_n
. For instance, this could be used to parametrize a template to be re-used in various contexts.If
data/v.md
contains:entry: . ---- <b>{{entry.title}}</b>: {{entry.contents}}
then, assuming that
foo
andbar
are collections with atitle
andcontents
entries,v
can be used as follows:<ul> <li> {{ v(entry: foo) }} </li> <li> {{ v(entry: bar) }} </li> <ul>
The following file extensions are supported:
-
<file>.json
: the file is transformed into the collection<file>
. -
<file>.md
: the file's body is converted from HTML to markdown and is made available as<file>
. If the file has some headers, they are available using<file>.<var>
. -
<file>.yml
: the file is transformed into the collection<file>
. Note: only very limited support for yaml at the moment (no nesting, only key-value). -
every other files are considered as raw data:
<file>.<ext>
corresponds to binding where<file>
is bound to the contents of that file.
See the examples/ folder.