How to use Hickory with Clojurescript (OM/Reagent)? - clojure

In my Reagent project, I'm parsing HTML with Hickory and rendering a Hiccup page. The hiccup is rendered. But when I change the page (!reset my view atom), React.js goes crazy because Hickory has generated:
[:div (as-hiccup (parse "<h1>HELLO WORLD!</h1>"))]
=> ([:html {} [:head {}] [:body {} [:h1 {} HELLO WORLD!]]])
As you can see, it has generated <html> <head> <body> tags which I think is causing Reactjs to blow up because my view already have those tags. Ideally, I want it to only generate [:h1 {} HELLO WORLD!]

(map as-hiccup (parse-fragment "<h1>HELLO WORLD!</h1>"))
generates [:h1 "HELLO WORLD!"]

Related

Easy way to access the request map in every selmer template?

I want to access the current page URL in my Selmer templates so that I can pass it to an edit page action so that this page can include a link back to the 'calling' page even after editing.
Here's the template code in my Selmer template -- this seems OK:
<a href="/photos/_edit/{{p.path}}{% if back %}?back={{back}}{% endif %}"
class="btn btn-warning btn-sm">edit</a>
Here's how I set the back value when searching:
(defn photo-search [word req]
(layout/render
"search.html"
{:word word
:photos (db/photos-with-keyword-starting word)
:back (str (:uri req) "?" (:query-string req))
}))
;; ...
(defroutes home-routes
;; ...
(GET "/photos/_search" [word :as req] (photo-search word req))
This works OK. However I have other methods that return lists of photos, and it seems to violate the DRY principle to add this code to all other methods.
Is there an easier way to do this, maybe with some middleware?
One approach you could try is creating your own render function that wraps selmer's and provides the common functionality you want on every page. Something like:
(defn render
[template request data]
(let [back (str (:uri req) "?" (:query-string req))]
(layout/render template (assoc data :back back))))
(defroutes home-routes
(GET "/photos/" [:as req]
(->> {:photos (db/recent-photos)}
(render "list.html" req)))
(GET "/photos/_search" [word :as req]
(->> {:word word
:photos (db/photos-with-keyword-starting word)}
(render "search.html" req))))
(For some reason I really like using threading macros in routes, even though their arguably aren't enough links in the thread to justify it...)

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))))

Necessary code changes to run server live over local

I have never done any kind of web programming before so I have no idea what I need to do to get this Clojure app I wrote to run on live server. The url of my page is http://rowdy.msudenver.edu/~jnels124/. Here is my Clojure code
(ns startingclojure.core
(:use (compojure handler[core :only (GET POST defroutes)])
[clojure.pprint])
(:require [net.cgrand.enlive-html :as en]
[ring.util.response :as response]
[ring.adapter.jetty :as jetty]))
(defonce counter (atom 10000))
(defonce urls (atom {}))
( defn shorten
[url]
(let [id (swap! counter inc)
id (Long/toString id 36)]
(swap! urls assoc id url)
id))
(en/deftemplate homepage
(en/xml-resource "homepage.html")
[request]
[:#listing :li]
(en/clone-for [[id url] #urls]
[:a] (comp ;; comp composes any number of functions
(en/content (format "%s : %s" id, url))
(en/set-attr :href (str \/ id)))))
(defn redirect
[id]
(response/redirect (#urls id)))
(defroutes app*;; * ususally means implementation detail or lower level operation
(GET "/" request (homepage request))
(POST "/shorten" request
(let [id (shorten (-> request :params :url))]
(response/redirect "/")))
(GET "/:id" [id] (redirect id)))
(def app (compojure.handler/site app*))
Here is the html
<html>
<head>
<link type="text/css" rel="stylesheet" href="style.css"/>
</head>
<body>
<form method="POST" action="/shorten">
<input type="text" name="url"/>
<input type="submit" value="Shorten!"/>
</form>
<ul id="listing">
<li>
id :url
</li>
</ul>
</body>
</html>
So here are the actual questions I have.
How do I get the Clojure to execute on the server. I have been loading this code in the repel and starting with (def server(jetty/run-jetty #'app {:port 8080 :join? false}))?
Also, what is the correct way to bring this project into view(i.e file structure). The file structure on the server is just
Top level: bin public_html
in public_html is cgi-bin index.html startingclojure(my app, from tutorial)
in index html is what you see when that page is entered and startingclojure has the leiningen file structure.
I am not sure if I have provided the all of the necessary information but I am happy to provide anything you may need to help me get started. Thanks in advance.
Running lein, or even a repl, on a production box is a bad idea.
You should define a -main function that creates and runs your web server process if you execute lein run locally. Then, when you are ready to deploy, run lein uberjar to create a jar containing your entire app and all its dependencies. Once you have that jar file on the server, you can run it via java -jar my-app.jar. You will probably want a script that also sets up all the right java execution variables and the port it should run on etc. (these can be provided on the command line and are passed in as strings to -main).
If the app requires certain files to be unpacked, you will need to extract the jar (or at least that part of the jar) and make sure the app process can find it at runtime. A jar is a zip file, and can be extracted with the unzip command from the command line. If you need to refer to resources that are still inside the jar for reading, you should replace any references to clojure.java.io.file (or native java File objects) with clojure.java.io/resource.
It will probably take a while to work out all these inter-environment differences, but luckily each of these steps can be tested locally as you refine them.

CSS fails with extended paths in Compojure / Hiccup

I've been converting some Noir websites to Compojure.
I have a function here that creates the layout of the page:
(defn layout [title & content]
(html5
[:head
[:title "My Site | " title]
(include-css "css/main.css")
[:body
[:header
[:h1 (link-to "/" "My Site")]]
content]))
And this is the function and the routes:
(defn home-page []
(layout
"Home"
[:div [:p "Home Page"]])))
(defn article-list []
(layout
"Article List"
[:div [:p "Article List"]])))
(defroutes app-routes
(GET "/" [] (home-page))
(GET "/article-list" [] (article-list))
When I open up localhost:3000/article-list all of the CSS rules work fine.
However, when I attempt to extend the URL path and change the program to:
(defn article-list []
(layout
"Article List"
[:div [:p "Article List"]])))
(defn article-one []
(layout
"Article One"
[:div [:p "Article One"]])))
(defroutes app-routes
(GET "/" [] (home-page))
(GET "/article-list" [] (article-list)
(GET "/article-list/article-one" [] (article-one))
And go to localhost:3000/article-list/article-one, I get all of the HTML but the CSS rules no longer work. When I inspect the page, the css paths are included in the < head > element but there are no styles on the page.
I've searched for a solution to this issue, but there doesn't seem to be any writing on this. I've also tried pulling out the routes so that I have:
(defroutes article-routes
(GET "/article-list/article-one" [] (article-one))
(defroutes app-routes
(GET "/" [] (home-page))
(GET "/article-list" [] (article-list)
(context "article-list" [] article-routes)
but I have the same issue. How can I get the CSS rules to work on pages with extended paths?
Your CSS is being included with a relative path, which means the when you go to localhost:3000/article-list/article-one your browser is looking for the CSS at localhost:3000/article-list/css/main.css.
The easiest way to fix this would be to include the CSS with (include-css "/css/main.css"). The / at the beginning will ensure it always searches for localhost:3000/css/main.css.

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.