wget https://raw.github.com/technomancy/leiningen/preview/bin/lein
lein self-install
lein plugin install lein-noir 1.2.1
lein noir new guestbook
cd guestbook
#Add dependencies
edit project.clj
and add jdbc dependencies
(defproject guestbook "0.1.0-SNAPSHOT"
:description "FIXME: write this!"
:dependencies [[org.clojure/clojure "1.3.0"]
[org.clojure/java.jdbc "0.2.3"]
[org.xerial/sqlite-jdbc "3.7.2"]
[noir "1.2.1"]]
:main guestbook.server)
run the project
lein run
update common namespace with our layout
(ns guestbook.views.common
(:use [noir.core :only [defpartial]]
[hiccup.page-helpers :only [include-css html5]]))
(defpartial layout [& content]
(html5
[:head
[:title "guestbook"]
(include-css "/css/reset.css")]
[:body
[:section#wrapper content]
[:footer#footer "My awesome guestbook"]]))
add the main page to welcome namespace
(ns guestbook.views.welcome
(:require [guestbook.views.common :as common]
[noir.content.getting-started]
[guestbook.models.db :as db])
(:use noir.core hiccup.core hiccup.form-helpers))
(defn show-guests []
(into [:ul.guests]
(for [{:keys [message name timestamp]} (db/read-guests)]
[:li
[:blockquote message]
[:p "-" [:cite name]]
[:time timestamp]])))
(defpage "/" {:keys [name message error]}
(common/layout
[:h1 "Guestbook"]
[:p "Welcome to my guestbook!"]
[:p error]
(show-guests)
[:hr]
(form-to [:post "/"]
[:p "Name:" (text-field "name" name)]
[:p "Message:" (text-area {:rows 10 :cols 40} "message" message)]
(submit-button "comment"))))
(defn error [params error]
(assoc params :error error))
(defpage [:post "/"] params
(cond
(empty? (:name params )) (render "/" (error params "Some dummy who forgot to leave a name"))
(empty? (:message params )) (render "/" (error params "WOW THIS IS THE BEST WEBSITE EVER"))
:else
(do
(db/save-message params)
(render "/"))))
create the database handler
(ns guestbook.models.db
(:require [clojure.java.jdbc :as sql])
(:import java.sql.DriverManager))
(def db {:classname "org.sqlite.JDBC",
:subprotocol "sqlite",
:subname "db.sq3"})
(defn create-table []
(sql/with-connection
db
(sql/create-table
:guestbook
[:id "INTEGER PRIMARY KEY AUTOINCREMENT"]
[:timestamp "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"]
[:name "TEXT"]
[:message "TEXT"])
(sql/do-commands "CREATE INDEX timestamp_index ON guestbook (timestamp)")))
(defn db-read [query & args]
(sql/with-connection
db
(sql/with-query-results res (vec (cons query args)) (doall res))))
(defn read-guests []
(db-read "SELECT * FROM guestbook ORDER BY timestamp DESC"))
(defn save-message [{:keys [name message]}]
(sql/with-connection
db
(sql/insert-values
:guestbook
[:name :message :timestamp]
[name message (new java.util.Date)])))
and we're done.
The deployment for the Clojure version is simply doing
lein uberjar
which will produce a runnable jar, then you can run it with java -jar guestbook.jar, if you wanted to run it on an app server it's not any harder, you just runlein ring uberwar
and it makes a deployable package that runs on any java appserver.While it's different syntax, I would say Clojure is a lot less quirky than PHP or Python, in fact here's a perspective somebody coming from Ruby http://briancarper.net/blog/536/clojure-from-a-ruby-perspective it's just not that different.
The templating syntax translates to HTML one to one, but you can always use a different templating library that actually uses xml, like enlive, I have yet to see that being an issue though.
They're the same queries you have in PHP and Python, so I'm not sure how that's an argument. But yes you can use a Clojure library like korma to avoid writing SQL completely.