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:
- How do I access the Ring environment from within a routed function (e.g. the
workbenchfunction)? For example, say I wanted to access the HTTP_ACCEPT headers or some other part of the request/middleware? - 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!