pedestal/pedestal — pull request #301 by ohpauleez (2014-12-14 19:38:39+00:00)
Title: Interceptor refactor

Updated Interceptor API
===================

### THIS IS A BREAKING CHANGE - Version is now 0.4.0-SNAPSHOT

I usually don't do Pull-Requests for my own branches, but this is such a significant breaking change, I wanted to document somewhere besides just the release notes (for those who work from Pedestal-master).

#### The problem
Pedestal's core is just a collection of Interceptors (Records), and a mechanism hook them together (into an Interceptor Chain/Path). Given that, there have always been a number of ways to create interceptors, but at the bottom of it all, was a single Record. Most developers who were comfortable working in Pedestal opted for the most direct approach (calling `(interceptor ...)` and `(definterceptor ...)`), but there were various utilities for common cases (handlers, before, after, around, middleware, etc). These utilities obfuscated the core simplicity of interceptors. This issue has been well documented on the mailing list and on issue #129

#### The solution
There are four parts to the solution: Put interceptor creation behind a protocol, ensure all interceptors that are created are valid at compile/creation time, improve error messaging for malformed/created interceptors, and migrate all auxiliary interceptor-creating functions into a dedicated namespace.

The new interceptor protocol is called `IntoInterceptor`. You should never have to interface with this directly - care has been given to capture all of the common cases for interceptor creation. **The way to directly create interceptors is with the `(io.pedestal.interceptor/interceptor ...)` function.** This function ensures correct interceptor creation and has helpful error messages when things go wrong.

The new namespace for the auxiliary functions is `io.pedestal.interceptor.helpers`. **You will need to update the namespace for all hand-written interceptors created with these functions - before, defbefore, after, defafter, around, defaround, etc.**

**There is no more `definterceptorfn`.** Creation and generation of interceptors are all driven by functions. This has two implications, and two breaking changes. Everywhere in code that previously used `definterceptorfn` is now just `defn` - you'll need to update your code. **You can no longer pass bare interceptor-fns into the routes - they MUST be wrapped in parenthesis/called.**
For example, consider the old, Pedestal 0.3.X code below (taken from the CSRF tests):

```clojure
(defhandler terminator
"An interceptor that creates a valid ring response and places it in
the context, terminating the interceptor chain."
[request]
{:status 200 :body success-msg :headers {}})

(defafter token-sniffer
"Relay the token to the requester behind the scenes to mimic the
use of a conventional form with a hidden field."
[context]
(let [token (get-in context [:response :session "__anti-forgery-token"])
headers (merge {} (when token {"csrf-token" token}))]
(assoc-in context [:response :headers] headers)))

(defroutes request-handling-routes
[[:request-handling "csrf-test.pedestal"
["/anti-forgery" ^:interceptors [rm/session token-sniffer anti-forgery]
["/leaf" {:any [:leaf terminator]}]]]])
```

Look back at the `defroutes` - you'll see that `/anti-forgery` passes in two interceptorfn's, `rm/session` and `anti-forgery`. Since those are now just `defn`s in Pedestal 0.4.X, the new routes would look like:

```clojure
(defroutes request-handling-routes
[[:request-handling "csrf-test.pedestal"
["/anti-forgery" ^:interceptors [(rm/session) token-sniffer (anti-forgery)]
["/leaf" {:any [:leaf terminator]}]]]])
```

Symbols that resolve to functions are **always** treated like handlers now.