From: James Reeves <weavejes...@googlemail.com>
Date: Sat, 25 Apr 2009 15:32:13 -0700 (PDT)
Local: Sat, Apr 25 2009 6:32 pm
Subject: Draft: First four chapters of Compojure tutorial
Foreword
======== This is a very rough draft of the tutorial I'm going to put on
Criticism is very welcome; I'd like to know if anything is unclear or
Compojure from the bottom up
1. Handlers
In Compojure, HTTP requests and HTTP responses are represented by
{ request } --> handler --> { response }
A response map consists of three keys:
:status (Required, Integer)
:headers (Required, Map)
:body (Optional, {String, ISeq, File, InputStream})
A request map consists of many more keys. The most significant ones
:request-method (Keyword)
:uri (String)
See the request map documentation for more standard keys.
A handler processes the request map and returns a response map. The
(defn hello-world [request]
Compojure provides the inline function `servlet` to convert a handler
Here is an example of the a handler being turned into a servlet and
(run-server {:port 8080}
By combining a handler with the `run-server` function, a basic web
(ns example-app
(defn hello-world [request]
(run-server {:port 8080}
If you run this code, you should be able to access a web page at:
2. Middleware
*Middleware* are functions that take a handler as its first argument,
handler & args --> middleware --> handler
An example of a simple middleware function is one that adds a header
(defn with-header [handler header value]
To apply this to the existing `hello-world` handler, you can redefine
(def hello-world
But a more idiomatic way is to use the `decorate` macro:
(decorate hello-world
The decorate macro produces the same effect, but retains the original
A number of middleware functions are included in Compojure. These
- with-params
3. Routes
3.1. Route syntax
A *route* is a type of handler that returns nil if the request does
(defn index-route [request]
But as this is a very common task, Compojure provides macros that
(def index-route
The Compojure route syntax is very powerful, but is based on a few
3.1.1. The method macro
The first symbol is the *method macro* that denotes the HTTP request
GET, POST, PUT, DELETE and HEAD
Because sometimes you don't care what method is being used, there is
ANY
Which matches any method.
3.1.2. The path template
The second item in the route form is the *path template*. This matches
(GET "/product/:id" ...)
A parameter will match a string of any character apart from "/", ".",
(GET "/product/:id"
You can include more than one parameter, and even the same parameter
As well as parameters, you can match wildcards, denoted by a "*". A
(GET "/public/*"
As well as relative URIs, absolute URLs can also be matched:
(GET "http://www.example.com/" ...)
This behaviour is triggered when the beginning of the path template is
(GET "http://:subdomain.example.com/" ...)
But you cannot use a parameter to match the scheme. However, the
For more precise control over URI matching, the path template can be
(GET #"/product/(\d+)" ...)
In this case the :route-params key contains a vector corresponding to
(GET #"/product/(\d+)"
Unlike re-groups, the first element of the parameter vector is not the
3.1.3. The return value
In the Compojure route syntax, the return value represents a
{:status 200, :headers {}}
The class of the return value determines how it alters the response
java.lang.Integer
java.lang.String
clojure.lang.ISeq
java.io.File
java.io.InputStream
java.net.URL
clojure.lang.Keyword
java.util.Map
clojure.lang.Fn
clojure.lang.IPersistentVector
Some examples of usage follow:
(GET "/"
(ANY "*"
(GET "/image"
(GET "/new-product"
(GET "/map-example"
3.1.4. Local bindings
The final useful piece of functionality the route syntax provides is a
- params => (:params request)
The :params key and the associated params binding provides a merged
Thus, an idiomatic and concise way of refering to route params is:
(GET "/product/:id"
3.2. Combining routes
Routes can be combined with the `routes*` function:
(def main-routes
The `routes*` function returns a new route. When supplied with a
(defn routes* [& sub-routes]
The `routes*` function is the more primitive ancestor of the more
(defn routes [& sub-routes]
It is recommended that `routes` be preferred for normal use.
For convenience, Compojure also provides a `defroutes` macro:
(defroutes main-routes
4. HTML
4.1. Syntax
Compojure uses a syntax made up of vectors, maps and strings to
Here is an example of the syntax:
[:h1 {:id "title"} "Hello World"]
In Compojure, this is referred to as a tag vector, so called because
The first element in the vector is the tag name. This can be a
The second element can optionally be a map. If it is a map, it is
Any further elements are treated as the content of the tag. A tag's
[:div "Hello" "World"]
[:div [:div {:class "inner"} "Nested"]]
[:div [:span "Hello"] [:span "World"]]
A Clojure sequence is also considered valid content. Sequences are
[:div (list "Hello" "World")]
Is considered equivalent to:
[:div "Hello" "World"]
This functionality is useful for functions that have a rest-param:
(defn html-document [title & body]
Compojure also provides a shorthand for defining elements with id or
[:h1#title "Compojure"]
Similarly, any alphanumeric, "-" or "_" after a "." is used as the
[:div.summary "A Clojure web framework"]
You can define many classes, but only one id using this syntax.
[:pre#example1.source.clojure
- James You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
| ||||||||||||||