What's the "big idea" behind compojure routes?

Viewed 21831

I'm new to Clojure and have been using Compojure to write a basic web application. I'm hitting a wall with Compojure's defroutes syntax, though, and I think I need to understand both the "how" and the "why" behind it all.

It seems like a Ring-style application begins with an HTTP request map, then just passes the request through a series of middleware functions until it gets transformed into a response map, which gets sent back to the browser. This style seems too "low level" for developers, thus the need for a tool like Compojure. I can see this need for more abstractions in other software ecosystems as well, most notably with Python's WSGI.

The problem is that I don't understand Compojure's approach. Let's take the following defroutes S-expression:

(defroutes main-routes
  (GET "/"  [] (workbench))
  (POST "/save" {form-params :form-params} (str form-params))
  (GET "/test" [& more] (str "<pre>" more "</pre>"))
  (GET ["/:filename" :filename #".*"] [filename]
    (response/file-response filename {:root "./static"}))
  (ANY "*"  [] "<h1>Page not found.</h1>"))

I know that the key to understanding all of this lies within some macro voodoo, but I don't totally understand macros (yet). I've stared at the defroutes source for a long time, but just don't get it! What's going on here? Understanding the "big idea" will probably help me answer these specific questions:

  1. How do I access the Ring environment from within a routed function (e.g. the workbench function)? For example, say I wanted to access the HTTP_ACCEPT headers or some other part of the request/middleware?
  2. What's the deal with the destructuring ({form-params :form-params})? What keywords are available for me when destructuring?

I really like Clojure but I am so stumped!

5 Answers
Related