Enlive is a selector-based (à la CSS) templating library for Clojure.
An Enlive template has two parts: a HTML file and a deftemplate
form somewhere in a clj file.
David Nolen wrote a nice tutorial
(most recent first)
By default selector-transformation pairs are run sequentially. When you know
that several transformations are independent, you can now specify (as an
optimization) to process them in lockstep. Note that this doesn’t work with
fragments selectors.
Example:
[:a :selector] a-transformation
[:another :selector] another-transformation
[:a :dependent :selector] yet-another-transformation
If the first two tarnsformations are independent you can rewrite this code as:
:lockstep
{[:a :selector] a-transformation
[:another :selector] another-transformation}
[:a :dependent :selector] yet-another-transformation
Transformations are now slightly restricted in their return values: a node or
a collection of nodes (instead of freely nested collections of nodes).
Dynamic selectors: selectors aren’t compiled anymore. It means that you don’t
need to wrap them in (selector …) forms anymore nor to eval thme in the most
dynamic cases.
Fragment selectors allow to select adjacent nodes. They are denoted by a map of
two node selectors (eg {[:h1] [:p]}
), bounds are inclusive and they select
the smallest matching fragments.
Transformations (the right-hand parts of rules) are now plain old closures.
These functions take one arg (the selected node) and return nil, another node
or a collection of nodes.
Rules are applied top-down: the first rule transforms the whole tree and the
resulting tree is passed to the next rules.
Nodes are transformed deep-first, that is: if a selector selects several nodes,
descendants are transformed first. Hence, when the transformation is applied to
an ancestor, you can “see” the transformed descendants (but you can not see
your transformed siblings).
/B /(T B) A if A and B are selected and transformed by T the the resulting tree is (T A ) \C \C
A snippet is a function that returns a seq of nodes, it can be used as a
building block for more complex templates.
A template is a function that returns a seq of string — basically it’s a
snippet whose output is serialized. Templates return a seq of strings to avoid
building the whole string.
Templates and snippets transform a source (specified as a path (to access
resources on the classpath), a File, a Reader, an InputStream, an URI, an URL,
an element or a seq of nodes).
The at
form is the most important form in Enlive. There are implicit at
forms in snippet
and template
.
(at a-node [:a :selector] a-transformation [:another :selector] another-transformation ...)
The right-hand value of a rule can be nil. It’s the idiomatic way to remove an
element.
Transformations are closures which take one arg (the selected node) and return
nil, another node or an arbitrarily nested collection of nodes.
Rules are applied top-down: the first rule transforms the whole tree and the
resulting tree is passed to the next rules.
Enlive selectors can match either nodes or fragments (several adjacent nodes).
See syntax.html
Some examples:
Enlive CSS ======================================================= [:div] div [:body :script] body script #{[:ul.outline :> :li] [:ol.outline :> li]} ul.outline > li, ol.outline > li [#{:ul.outline :ol.outline} :> :li] ul.outline > li, ol.outline > li [[#{:ul :ol} :.outline] :> :li] ul.outline > li, ol.outline > li [:div :> :*] div > * [:div :> text-node] (text children of a div) [:div :> any-node] (all children (including text nodes and comments) of a div) {[:dt] [:dl]} (fragments starting by DT and ending at the *next* DD)
A transformation is a function that returns either a node or collection of node.
Enlive defines several helper functions:
content (content “xyz” a-node “abc”)
html-content (html-content “please no”)
wrap (wrap :div) or (wrap :div {:class "foo"})
unwrap unwrap
set-attr (set-attr :attr1 “val1” :attr2 “val2”)
remove-attr (remove-attr :attr1 :attr2)
add-class (add-class “foo” “bar”)
remove-class (remove-class “foo” “bar”)
do→ (do→ transformation1 transformation2)
clone-for (clone-for [item items] transformation)
or (clone-for [item items]
selector1 transformation1
selector2 transformation2)
append (append “xyz” a-node “abc”)
prepend (prepend “xyz” a-node “abc”)
after (after “xyz” a-node “abc”)
before (before “xyz” a-node “abc”)
substitute (substitute “xyz” a-node “abc”)
move (move [:.footnote] [:#footnotes] content)
- No namespaces support (hence unsuitable for most XML)