Ondim is a library for creating templating engines for arbitrary formats, inspired by Heist (which only works for HTML). It also includes HTML, LaTeX and Whiskers (a new template language very similar to Mustache) targets that work out-of-the-box.
The main idea is to reuse existing libraries that will provide the target language’s parser, types and renderer, and easily build an “expansion” layer on top. If you are familiar with Heist, for HTML this is done by binding named expansions (in Heist they are called Splices) that programatically transform the contents of HTML elements whose tags match the name.
For instance, this is how you could define an if/else expansion with Ondim
:
ifElse :: (Monad m, OndimNode t) => Bool -> Expansion m t
ifElse cond node = do
let els = children node
yes = filter ((Just "else" /=) . identify) els
no =
maybe [] children $
find ((Just "else" ==) . identify) els
if cond
then expandSubs yes
else expandSubs no
Now, suppose you have a variable isCat :: Bool
and you want your templates to choose between two snippets depending on its value. Such HTML template could have type [HtmlNode]
, so that if you “lift” it into the Ondim monad binding ifElse isCat
to the name if-cat
,
liftNodes myTemplateContent
`binding` do
"if-cat" #* ifElse isCat
you can now write in your HTML templates…
<e:if-cat>
<p>I have a <i>beautiful</i> cat!</p>
<e:else>
<p>I don't have a cat 😭</p>
</e:else>
</e:if-cat>
…and the output will depend on the value of isCat
.
The beauty of ondim (and one aspect that takes it further from Heist) is that the ifElse
splice is polymorphic on the format type, so the same code works for formats other than HTML. If you want to template JSON, the same code above would work if src_haskell{myTemplateContent} had type src_haskell{[Value]}, and a similar template could be written as:
- $: if-cat
$children:
- I have a beautiful cat!
- $: else
$children:
- I don't have a cat 😭
Or TeX:
\@ifCat{
I have a \emph{beautiful} cat!
\@else{
I don't have a cat 😭
}
}