Question

I'm a Rails dev getting my feet wet in Clojure. I'm trying to do something which was very simple with ERB but I can't for the life of me figure it out in enlive.

Say I have a simple layout file for a website in layout.html:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
</body>
</html>

And I have these snippets, for instance, header.html and footer.html and this simple route.

(deftemplate layout "layout.html" [])

(defroutes home-routes
  (GET "/" [] layout))

How can I make it so whenever a request goes to "/" it transforms the layout and inserts the header and footer snippets into it?

Was it helpful?

Solution

defsnippet only matches a specific part of your html (which is why it takes a selector as an argument), and transforms it. deftemplate takes the entire html, and transforms it. Also, defsnippet returns a Clojure data structure while deftemplates returns a vector of strings, so a defsnippet is usually used within a deftemplate.

To give you an idea of what the data returned by a snippet (or selector) look like:

(enlive/html-snippet "<div id='foo'><p>Hello there</p></div>")
;=({:tag :div, :attrs {:id "foo"}, :content ({:tag :p, :attrs nil, :content ("Hello there")})})

In your case you want something like:

header.html:

<div id="my-header-root">
...
</div>

Clojure code:

(enlive/defsnippet header "path/to/header.html" [:#my-header-root] []
identity)

(enlive/defsnippet footer "path/to/footer.html" [enlive/root] []
identity)

(enlive/deftemplate layout "layout.html" [header footer]
[:head] (enlive/content header)
[:body] (enlive/append footer))

(defroutes home-routes
  (GET "/" [] (layout (header) (footer))

The identity function used in the snippets returns it's argument, which in this case is the data structure selected by the :#my-header-root selector (we don't do any transformation). If you want to include everything in i.e head.html you can use the root selector-step that comes with enlive.

You can view the html generated from a defsnippet using something like this:

(print (apply str (enlive/emit* (my-snippet))))

I also recommend the tutorial: https://github.com/swannodette/enlive-tutorial/ and the one by Brian Marick for some more details of how the defsnippet and deftemplate macros work.

Last tip, you can experiment with selectors and transformations using the sniptest macro that comes with enlive:

(enlive/sniptest "<p>Replace me</p>"
[:p] (enlive/content "Hello world!"))
;= "<p>Hello world!</p>"

OTHER TIPS

There is great answer with examples in the enlive tutorial.

Warning. All source files links seem broken. You need to insert enlive-tutorial/blob/master/ after https://github.com/swannodette/ in all links or just open them directly from tutorial project.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top