public
Last active

Guestbook in Clojure

  • Download Gist
gistfile1.md
Markdown

Install Leiningen and create a project

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.

You do realize the main argument was that Python code was clearer than PHP's, right? This code is hardly readable, IMHO. However it might be because I have no experience whatsoever with Clojure, nor do I want to.

Just because you happen to be unfamiliar with the syntax doesn't mean the code is not clean or hard to read for somebody who actually works with the language. I certainly find it a hell of a lot more readable than PHP myself. Also, can you point out what specifically is hard to read or understand in there?

I agree, my opinion about the syntax doesn't really matter because I have no familiarity with the language, however, someone coming from PHP will probably have no problem understanding Python, whereas Clojure might appear really confusing. Also, in the main article, the author has also shown a method of deployment.

The templating syntax would be pretty hard to explain to designers (IMHO). And the usage of SQL might, to some, make the code harder to read(mixing languages), but you could probably get rid of those queries quite easilt.

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 run lein ring uberwar and it makes a deployable package that runs on any java appserver.

whereas Clojure might appear really confusing

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 would be pretty hard to explain to designers (IMHO)

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.

And the usage of SQL might, to some, make the code harder to read(mixing languages), but you could probably get rid of those queries quite easilt.

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.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.