Enlive Predefined selector-steps - clojure

I'm trying to use Enlive to clone the replace-me script tag in this html.
<!DOCTYPE html>
<html lang="en">
<head>
[...]
<script src="/js/libs/less.min.js"></script>
<script src="replace-me"></script>
</head>
[...]
Question 1)
I'm struggling to figure out what the "idiomatic" selector for the last script element is. Currently I'm using
(h/select tpl [:head :> [h/last-of-type :script]])
is that correct?
Question 2)
I have this snippet to transform the entire head entity:
(h/defsnippet head "templates/base.html" [:head]
[{:keys [scripts]}]
[h/root :> [h/last-of-type :script]]
(h/clone-for [script scripts]
(h/set-attr :src (:src script))))
Is this the correct usage of the root selector-step? I'm assuming that within the context of defsnippet, root is referencing the [:head] selector, is that correct?
Thanks!

This works in this case, but you might prefer attr-has:
(h/select tpl [[:script (h/attr-has :src "replace-me")]])
(h/defsnippet head "templates/base.html" [:head]
[{:keys [scripts]}]
[h/root :> (h/attr-has :src "replace-me")]
(h/clone-for [script scripts]
(h/set-attr :src (:src script))))
In more complex cases you could define your own selectors. Here it's unnecessary because of attr-has, but for the sake of example:
(def my-pred (h/pred #(= #{"replace.me"} (h/attr-values % :src))))
(h/defsnippet head "templates/base.html" [:head]
[{:keys [scripts]}]
[h/root :> my-pred]
(clone-for [script scripts]
(h/set-attr :src (:src script))))
You can use emit* to check whether your snippets produce the desired results:
(apply println (h/emit* (head {:scripts [{:src "bar.js"}]})))

Related

Clojure: why is sniptest producing LazySeq

I'm creating a template in enlive and having trouble with this snippet which produces lazyseq. When I try this sniptest in REPL it produces "clojure.lang.LazySeq#ba6da9f2".
(h/sniptest (template-div)
[:div.Row] (h/content (map #(value-cell %)
(for [e(:data-content msh-contents)]
(vals e)))))
The rest of the code needed to test this looks like this
(require '[net.cgrand.enlive-html :as h])
(def msh-contents {:title "Events mashup",
:data-content [{:title "ICTM Study Group ",
:url "http://some-url.com"}
{:title "Volodja Balzalorsky - Hinko Haas",
:url "http://some- other-url.com"}
]})
(defn template-div[] (h/html-resource "index.html"))
(h/defsnippet value-cell (template-div)
[:div.Row :div.Cell] [value]
(h/content value))
The index.html file looks something like this (it can also be found here http://www.filedropper.com/index_25))
<div class="Table">
<div class="Title">
<p>This is a Table</p>
</div>
<div class="Heading">
<div class="Cell">
<p>Heading 1</p>
</div>
</div>
<div class="Row">
<div class="Cell">
<p>Row 1 Column 1</p>
</div>
</div>
I saw a similar question, but the solution was to use content instead of html-content. Not sure what causes the issue here...
Example from https://github.com/cgrand/enlive/wiki/Getting-started
x=> (sniptest "<html><body><span>Hello </span>"
[:span] (append "World"))
"<html><body><span>Hello World</span></body></html>"
From html-resource docstring: "Loads an HTML resource, returns a seq of nodes."
Notice how in the example the source is in the form of a html string and not a seq of nodes. Why it works the way it does beats me but you'll probably want the following:
(h/sniptest (clojure.string/join (h/emit* (template-div))) ; this feeds it a html str instead
[:div.Row] (h/content (map #(value-cell %)
(for [e(:data-content msh-contents)]
(vals e)))))
PS: What are you using sniptest for because I was unaware it existed until now. Then again I use enlive in an odd way(no deftemplates or defsnippets, use of hiccup style html, and heavy use of macros).

Enlive templates - how to append script to end of the page

I am adding custom backbone.js scrips to my pages and it needs to be at the end of page,
I have viewed the solution for adding scripts to the html head as described here : Enlive templates – add to head section
Unfortunately, I need to add scripts to page bottom.
<html>
<head><!-- all the normal css and js scripts--></head>
<body>
<div> <!-- html content that will be used by my scripts --> </div>
<!-- this is where i want to append custom javascripts -->
</body>
</html>
just realized its as simple as a conj :
My solution is non idiomatic and i added a lot of prints to understand what is under the hood ; once i figured out [:body] input to a transformer was a vector, conj became an obvious solution,
(html/sniptest "<html><body><header class=\"some_class\">Header Content</header><footer>sddsd</footer></body></html>"
[:body] (fn [html-source]
(let [conts (:content html-source)
newstuff {:tag :script
:attrs {:src "../javascripts/foundation/foundation2.forms.js"}
:content []}
_ (pprint conts)
_ (print (class conts))
conts2 (conj conts newstuff )]
(assoc html-source :content conts2))))

Cannot call enlive snippet via its name stored in a variable

I'm trying to do something simple with Clojure Enlive lib: I want the top menu of my page to be different based on language selected by the user (English or Japanese). I can't find how to implement this basic feature.
I have a "template/header.html" template with 2 sections (one per language):
<div class="header en-US">...</div>
<div class="header ja-JP">...</div>
I created 2 corresponding snippets in my templating code:
(:require [net.cgrand.enlive-html :as html])
...
(html/defsnippet header-ja-JP "templates/header.html" [:div.header.ja-JP] [])
(html/defsnippet header-en-US "templates/header.html" [:div.header.en-US] [])
Now when I load my main template (index.html) I want to populate my header menu code into my nav tag.
Below, the argument 'lang' value is either "en-US" or "ja-JP":
(defn process-template
[lang]
(let [header-selector (str “header-“ lang)
header-content (#(resolve (symbol header-selector)))]
(apply str
(html/emit*
(html/at
(html/html-resource "templates/index.html")
[:nav] (html/content (header-content)))))))
It throws a java.lang.NullPointerException
If I replace the last line by
[:nav] (html/content (header-ja-JP)))))))
or
[:nav] (html/content (header-en-US)))))))
it works fine (so the snippets work), but I've tried lots of different syntaxes and can't make the dynamic header work based on language.
The above code (let part) seems ok if I execute it manually in REPL, the header-content is JSON object containing the correct content so I don't understand why I get a NullPointerException.
Ideally I think I should have only one snippet taking the language as argument and returning the corresponding HTML section, but didn't manage to do that either.
I'm very new at Clojure so I'm probably missing something obvious here; if someone could point me to what's wrong in the code above or another way to make this work that will be awesome.
Thanks,
Olivier
If we want to do it differently as cgrand suggests, there are many, many ways to accomplish this. I have a hard time imagining how he'd do it with maps, but I'd do it this way:
(defn process-template [lang]
(html/select (html/html-snippet
"<html>
<head></head>
<body>
<nav>
<div class=\"en\"></div>
<div class=\"jp\"></div>
</nav>
</body>
</html>")
[(keyword (str "div." lang))]))
(apply str (html/emit* (process-template "en")))
=> "<div class=\"en\"></div>"
Of course read it from file instead of HTML in a string. The function plucks out the matching HTML node according to a selector.
Your (#(resolve (symbol header-selector))) returns a value.
(resolve (symbol header-selector)) returns a function.
[:nav] (html/content (header-content))))))) calls header-content as a function.
Replace (#(resolve (symbol header-selector))) with (resolve (symbol header-selector)).
I'm fuzzy on what html/at and html/html-resource do so if other problems crop up you might need to cram in clojure.java.io/file in there.

Hiding a element in Clojurescript Dommy

I'm trying to hide a element in the ClojureScript more specifically the Dommy library. This is what I got so far:
ClojureScript:
(ns fucking_about
(:require
[dommy.utils :as utils]
[dommy.core :as dommy])
(:use-macros
[dommy.macros :only [node sel sel1]]))
(map dommy/toggle! (sel :.hide))
HTML:
<html>
<head><title></title></head>
<body>
<ul>
<li class="show">Derp</li>
<li class="hide">Herp</li>
<li class="show">ies!</li>
</ul>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
But I get no results when opening the site in a browser and I have no idea why :< and I cannot find any documentation on ClojureScript beyond the Github page.
The thing is that map returns a lazy sequence of elements. Lazy sequence is some kind of unrealized promise of what you can get. You want your sequence to produce some side effects. To make it work you just need to realize it.
Problem is trivial. You have two choices:
1) Realize your lazy sequence using doall function, like that:
(doall (map dommy/toggle! (sel :.hide)))
That’s exactly goal of doall: walk through entire sequence and force any possible effects.
2) Use doseq which is meant to produce side effect, like that:
(doseq [el (sel :.hide)]
(dommy/toggle! el))
And that’s all! Both ways you should get the same result.
Any more questions?

How do I use snippets in enlive?

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?
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>"
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.