
What's interesting is that it does all this without using business objects, mappers, etc.
Clojure allows you to deserialize the json response from Couch into a hash-map and access the elements of that hash-map via symbols like so (client :address), no need for strings or accessing by a numerical index. As you can see this allows you to access values in the hash-map like fields on an object.
Clojure also has a thrush (or pipe) operator -> which can be used to pass objects into methods. While this is only syntactic sugar it allows you to use functions as if they where methods. e.g:
(-> client print-name-and-address)
This approach may not meet everyone's needs but I think it's a really great way to make lightweight, easily extensible, data driven web applications.
In the following example all the code is lumped together in 1 file just for the sake simplicity. In production code you would probably want to brake functions out into separate files for those that work on json, documents & vehicles as well as the core routing logic. (Error handling has been omitted to keep the example simple)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns webapp.routes | |
(:use compojure.core | |
hiccup.core) | |
(:require [clj-http.client :as client] | |
[compojure.route :as route] | |
[clj-json.core :as json])) | |
;Make everything £40 for simplicity | |
(defn calculate-price [vehicle] | |
(assoc vehicle :price-per-day 40)) | |
;Remove CouchDB revision, if there is one | |
(defn remove-revision-number [document] | |
(dissoc document :_rev)) | |
;Hard-coded connection string, for brevity | |
(defn get-by-id [id] | |
(let [url (str "http://127.0.0.1:5984/vehicles/" id)] | |
(json/parse-string ((client/get url) :body) true))) | |
;Format hashmap as json reponse | |
(defn json-response [data & [status]] | |
{ :status (or status 200) | |
:headers {"Content-Type" "application/json"} | |
:body (json/generate-string data)}) | |
(defn get-vehicle-data [id] | |
(let [vehicle (get-by-id id)] | |
(-> vehicle | |
remove-revision-number | |
calculate-price | |
json-response))) | |
(defroutes main-routes | |
(GET "/scooter/:id" [id] (get-vehicle-data id)) | |
(route/not-found (html [:h3 "Resource not found"]))) |