I'm trying to mimic Wordpress permalink using Compojure, but why all the static files (css, js, and img) are not found when I use multiple parameter? Here is my code.
(defroutes approutes
(GET "/" [] (posts-for-home))
(GET "/:year/:month/:title" [year month title :as {uri :uri}] (single/tpl-single uri))
(route/resources "/")
(route/not-found "<h1>Page not found</h1>"))
(def app
(handler/site approutes))
I saw in browser debug console the css was served from http://localhost:3000/2014/11/test/css/main.css instead of http://localhost:3000/css/main.css. Then I added one test route and only used a parameter like this:
(GET "/:testparameter" [testparameter] (single/tpl-single testparameter))
and that route was working perfectly. When I visited http://localhost:3000/justfortest, all the static files were served from the root path. What should I do to solve this multiple parameters issue? Thanks in advance for the answer.
Finally I solved this issue after replacing <link rel="stylesheet" href="css/main.css"> to <link rel="stylesheet" href="/css/main.css">. What a trivial problem and I didn't notice it for a day. Lol so embarrasing.
Related
HTML5 allows <meta> tags to appear in the body, but Enlive does not seem to support this:
(deftest test-enlive
(testing "enlive"
(let [html-as-string "<!DOCTYPE html><html lang=\"en\"><body><div><meta foo=\"bar\"><span>the content</span></body></html>"
parsed-html (enlive/html-resource (java.io.StringReader. html-as-string))
span (enlive/select parsed-html [ :div :span ])
content (first (map enlive/text span))]
(is (= "the content" content)))))
This test fails, but will pass if you remove the meta tag.
This old thread led me to realize that it was the meta tag that was causing a problem.
I realize that Enlive depends on Tagsoup, but when I switch it out for JSoup (which claims to support HTML5) I get the same result.
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!"]
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.
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.
I've been experimenting with writing webapps in Clojure, and it's been pretty easy until now. I followed Chas Emerick's excellent screencast starting clojure and got an url shortener up and running pretty quickly. Next I wanted to be able to deploy it, and that's when the trouble started.
When I run it in development or deploy it to Jetty as the root webapp, everything is fine, but when I deploy it with a context path, it doesn't. Or, rather, it almost works. All my Compojure routes still work, but FORM action links in HTML files are broken and give me 404's.
This is the Compojure route setup:
(defroutes app*
(rt/resources "/")
(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*))
And here is the HTML for the homepage template:
<!DOCTYPE html>
<html>
<head>
<title>Insert title here</title>
<link type="text/css" rel="stylesheet" href="site.css" />
</head>
<body>
<form method="POST" action="shorten">
<input type="text" name="url" />
<input type="submit" value="Shorten!" />
</form>
</body>
</html>
The problem is the action="shorten"URL. When deployed to Jetty with a context path of /example everything work fine, until I trigger the form submit. Then Jetty complains that it can't find localhost:8080/shorten which means (I think) that it's not being treated as a relative path, but an absolute one.
So, my question is: how to fix this? I guess I could just specify the full path in the action link, but that would be inflexible and make it harder to run the servlet in development. Is there a way to configure my way out of this? Or some magic URL prefix (like ~/ in Razor) that will just do the right thing?
i had the same problem. Change the line:
(POST "/shorten" request
to
(POST "shorten" request
and it should work (well, it did for me)