During our last pairing session with Jacek Laskowski on Librarian there was a brief moment of confusion over Ring handlers. We struggled for a short while trying to figure out what order to put them in and what it really means to have code like:

(def app (-> routes ; sandbar (auth/with-security security-policy log-in) ; compojure helper that includes a few Ring handlers site ; sandbar again session/wrap-stateful-session))

It didn’t take us long to figure it out and the solution turns out to be a very elegant functional flavor of decorator.

It’s easy to dive too deep without proper understanding (and that’s what I admittedly did). Let’s start from the very beginning and see what these bits really mean. For starters, here’s a very basic app in plain Ring that simply returns the entire request :

(defn my-handler [request] {:body (str request)}) (def app my-handler)

When I hit http://localhost:3000/?my_param=54 in my browser, in return I get:

{:remote-addr "0:0:0:0:0:0:0:1", :scheme :http, :request-method :get, :query-string "my_param=54", :content-type nil, :uri "/", :server-name "localhost", :headers {"cookie" "__utma=111872281.60059650.1328613066.1328726201.1328785442.5; __utmz=111872281.1328613066.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)", "connection" "keep-alive", "accept-encoding" "gzip, deflate", "accept-language" "pl,en-us;q=0.7,en;q=0.3", "accept" "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "user-agent" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20100101 Firefox/11.0", "host" "localhost:3000"}, :content-length nil, :server-port 3000, :character-encoding nil, :body #<Input org.mortbay.jetty.HttpParser$Input@7c04703c>}

Note that my_param made it to :query-string , but obviously it’s quite inconvenient at this point and that’s not what we really want to deal with.

What is app at this point? No magic here, it’s just a very simple and boring function.

Let’s move on and add one of the seemingly magic Ring handlers – ring.middleware.params/wrap-params :

(def app (wrap-params my-handler))

This time for the same URL I get the same map, with a few new entries:

{:remote-addr "0:0:0:0:0:0:0:1", ; Trimmed for brevity :query-params {"my_param" "54"}, :form-params {}, :query-string "my_param=54", :params {"my_param" "54"}}

I can see that the wrapper added a few new entries: :query-params , :form-params and :params . Great, just like it was supposed to.

Now, what is app at this point? Just like before, it’s a regular function of request . So what does wrap-params really do? Let’s take a peek at (parts of) its source:

(defn wrap-params [handler & [opts]] (fn [request] (let [request (if (:query-params request) request (assoc-query-params request))] (handler request))))

assoc-query-params is no magic, it simply parses query params and merges it with the request map.

Now let’s take a close look at the last line and back at wrap-params signature. Here’s what’s really going on:

wrap-params takes handler (which is a function of request ) as argument. In our case, it’s the trivial function that returns request in body. It then performs some work, in this case rebinding request to a map with a few more entries. Eventually it calls the handler that it got as parameter with the augmented request map.

In other words, wrap-params takes a handler function, and returns a function that performs some extra work and invokes the original handler .

Does it look familiar? Yup, it’s the old good decorator pattern. Do some work, then pass control on to the next handler (which can also be a decorator and delegate it further). In this case, though, it’s astonishingly simple (compared to what it takes in Java).

Now let’s say I want to chain one more handler that relies on the previous one. Let’s say I dislike strings and want to map params by Clojure keywords. There’s a handler for it: ring.middleware.keyword-params/wrap-keyword-params .

No need to think too long, let’s jump in and use it:

(def app (wrap-keyword-params (wrap-params my-handler)))

… and I get:

{; Trimmed for brevity :params {"my_param" "54"}}

Whoops, that’s not what I expected. wrap-keyword-params was supposed to create a map with keys as keywords, not strings. Why didn’t it work?

Naive intuition tells me to treat wrappers as function calls. I wrap my-handler in wrap-params and pass the result of this invocation to wrap-keyword-params , right? Wrong!

Take a look at a sample wrapper above ( wrap-params ) and think what we were trying to do. What I really created here is a reversed chain like:

Given a request , map its :params into keywords ( wrap-keyword-params ). Then pass control to the next function in chain, wrap-params . It parses query string and adds :params map to request . Then pass control to my-handler which prints the whole thing to browser

Nothing happens in the first step, because :params does exist at this point – it’s only created by wrap-params in the second step.

If we reverse it, it works like expected:

(def app (wrap-params (wrap-keyword-params my-handler)))

{; Trimmed for brevity :params {:my_param "54"}}

To recap, a few things to remember from this lesson: