I like Enlive, but it got me somewhat confused when I observed the following.
Consider the following Clojure code (also available on github):
(ns enlivetest.core
(:require [net.cgrand.enlive-html :refer [deftemplate defsnippet] :as html]))
(deftemplate page "index.html"
[ctx]
[:.foobar] (html/content (do (println "GENERATING FOOBAR")
"===FOOBAR===")))
and this HTML template (resources/index.html) here:
<!DOCTYPE html>
<html>
<body>
</body>
</html>
When calling the page
template, I'd expect it to ignore the right hand side of its rule (the transformation) completely, as there is no HTML tag that matches the rule's selector :.foobar
.
However, as it turns out, the right hand side of the rule does in fact get evaluated:
user=> (require '[enlivetest.core :as c])
nil
user=> (c/page {})
GENERATING FOOBAR
GENERATING FOOBAR
("<!DOCTYPE html>\n" "<" "html" ">" "\n " "<" "body" ">" "\n " "</" "body" ">" "\n\n" "</" "html" ">")
(Obviously, it even gets evaluated twice - once for each root HTML element in the template as it seems).
But why is it being evaluated at all, although there is no element matching the selector? Is this correct behaviour? Am I missing something obvious here?
This example uses Enlive 1.1.6, just as its README suggests.
Clarifications are greatly appreciated.
EDIT #1:
As it turns out (thanks to @leetwinski), my assumption of how things work was incorrect:
I was assuming that the deftemplate
macro would only evaluate the right hand side of a rule (the transformation part) when the selector of that rule matches an element in the given HTML.
But correct is this:
The right hand side of a rule will always get evaluated during a call to the defined template function (e.g. page
) and is expected to evaluate to a function that will in turn evaluate to the desired content (e.g. "===FOOBAR===" in this example) when called. It is this function that will get called only for elements that match the selector.
This means that e.g. html/content
evaluates to such a function (and not to the desired content directly).
In order to make things work as I expected originally, I could write it like this:
(deftemplate page "index.html"
[ctx]
[:.foobar] #((html/content (do (println "GENERATING FOOBAR")
"===FOOBAR===")) %))
which will result in the following output:
user=> (c/page {})
("<!DOCTYPE html>\n" "<" "html" ">" "\n " "<" "body" ">" "\n " "</" "body" ">" "\n\n" "</" "html" ">")
or when adding a <div class="foobar"></div>
to the HTML template:
user=> (c/page {})
GENERATING FOOBAR
("<!DOCTYPE html>\n" "<" "html" ">" "\n " "<" "body" ">" "\n\t\t" "<" "div" " " "class" "=\"" "foobar" "\"" ">" "===FOOBAR===" "</" "div" ">" "\n " "</" "body" ">" "\n\n" "</" "html" ">")
EDIT #2:
It's been a few weeks, but I'm still struggeling with how this is implemented in Enlive. I see myself wrapping the transformation parts of rules into #((html/content ...) %)
over and over again.
Does anybody have an explanation for why Enlive evaluates transformations (at all or even multiple times) even when they are not even relevant for the current rendering process?
I might be overlooking something, as I'm really surprised that this doesn't seem to bother anybody but me.