Ring Handlers – Functional Decorator Pattern

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:

  1. 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.
  2. It then performs some work, in this case rebinding request to a map with a few more entries.
  3. 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:

  1. Given a request, map its :params into keywords (wrap-keyword-params).
  2. Then pass control to the next function in chain, wrap-params. It parses query string and adds :params map to request.
  3. 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:

  • In functional programming, the decorator pattern is elegantly represented as a higher order function. I find it much easier to grasp than the OO flavor – in Java I would need an interface and 3 implementing classes for the job, greatly limiting (re)usability and readability.
  • In case of Ring wrappers, typically we have “before” decorators that perform some preparations before calling the “real” business function. Since they are higher order functions and not direct function calls, they are applied in reversed order. If one depends on the other, the dependent one needs to be on the “inside”.

2 thoughts on “Ring Handlers – Functional Decorator Pattern

  1. nice and simple for a newbie to clojure. thanks for the insight.

    now, are there any “after” wrappers in ring? is there a convention to tell the difference?

  2. belun,

    there are “after” and “around” wrappers, but I don’t think they are easily distinguishable by name etc. The way to go is to think what a handler does and where it fits in the decorator chain.

    Wrappers like wrap-params or wrap-keyword-params are clearly useful for the “inner” handlers by doing something to the request map. If something relies on them (e.g. some form processing, like authentication), you need to chain them in the right order.

    One example of an “around” wrapper is wrap-flash. If you put something in :flash in a response, next time a request on the same session comes in it will include the same thing in :flash in request.

    For example of an “after” wrapper see wrap-content-type. If your response does not include Content-Type, it automatically adds it to the response.

Leave a Reply

Your email address will not be published. Required fields are marked *

Spam protection by WP Captcha-Free