Using Compojure's Renderable for Layouts

This is a blog about the development of Yeller, the Exception Analytics that helps you understand your exceptions faster.

Read more about Yeller here

Yeller is written in clojure/compojure. I made a change recently that gave me a lot more power when doing view layouts, and it enabled by compojure’s use of protocols.

I was pleased with how this worked out, so I thought I’d write it up.

I’ve been using hiccup since the start at yeller. When I first wrote up the page templating scheme, it looked somewhat like this:

(defn an-view [data]
  (layout {:title "a page title"}
    [:h2 "a page"]))

(defn layout [options & body]
  (html5
    [:head
      [:title (:title options "Yeller")]]
    [:body
      body]))

So each view would manually call the layout function, which would render HTML. I recently realized this wasn’t great, because there is some global state the layout needs to have access to, and each view would have had to pass it on to the layout function. In yeller, that state is added to the request by some custom middleware.

Compojure is the super tiny web framework yeller uses for all of its web responses.

One of the core abstractions in Compojure is that of Renderable. This turns what you return from a compojure route into an actual ring response, through a very simple protocol. This tiny bit of leverage gives you as an end user quite a bit of power, and let me do layouts with yeller pretty easily:

Instead of the layout function calling hiccup directly, and returning a string, it instead returns a record (created by defrecord), which would then implement compojure’s Renderable. In the render implementation for the record, we now have access to the request (which in my case gives me access to the db connection as well):

(defrecord Layout [options & body]
  Renderable
  (render [this request]
    ... render layout here))

Compojure will call render on this after you return it from a response.

This was a super nice change, as none of the codebase that called the layout function had to change, just the layout rendering calls themselves.

All of this was enabled by compojure giving me leverage, by letting me do my own thing when rendering, via a protocol.

This is a blog about the development of Yeller, Exception Analytics that helps you understand your exceptions faster.

Read more about Yeller here