There is one breaking change: `routes` and `defroutes` no longer add
parameter and cookie middleware by default.
The default routes middleware was convenient, but caused several
issues and restricted developer choice. So from now on, you'll need to
explicitly add this middleware to your handler.
To make this easier, I've added the compojure.handler namespace. This
contains functions that give your routes standard stacks of
middleware, in the correct order.
The `compojure.handler/api` function adds the wrap-params,
wrap-keyword-params and wrap-nested-params middleware. This function
is useful if you want to create a RESTful API, and you don't require
cookie or session support.
The `compojure.handler/site` function adds all the middleware from
`api`, along with wrap-multipart-params, wrap-cookies and
wrap-session. In other words, the basic middleware you need for a
standard web application.
You may notice that both `site` and `api` have nested keyword
parameters. This means that a standard parameter map like:
{"foo[bar]" "baz"}
It will be turned into a map like:
{:foo {:bar "baz"}}
This is the same syntax for grouping together parameters that is found
in frameworks like Ruby on Rails and Sinatra.
The "vector" bindings for Compojure routes will match either keywords
or string parameters, so you don't have to worry about bindings not
working if you choose not to use keyword parameters.
The next significant change is the introduction of the `context`
macro. You can use this macro to add a common prefix to a bunch of
routes:
(defroutes example
(context "/foo/:id" [id]
(GET "/" [] ...)
(GET "/edit" [] ...)
(POST "/" [] ...)))
The bound `id` parameter is accessible by each of the inner routes.
The context macro adds two additional keys to the request map:
:context and :path-info. The :context key is the path matched by the
context, and the :path-info key is the remaining suffix. If the
path-info is empty, it's set to "/". Routes will match on the
:path-info key first, and then the :uri if the path-info key does not
exist.
So, for example, if the URI being accessed was "/foo/10/edit", then
the context would be "/foo/10" and the path-info would be "/edit".
Finally, I've deprecated the wrap! macro. It's not very idiomatic
Clojure to re-define an existing var, and it doesn't accomplish much
more than the standard -> macro.
- James
(defroutes example
(context "/foo/:id" [id]
(GET "/" [] ...)
(GET "/edit" [] ...)
(POST "/" [] ...)))
One minor thing I forgot to mention: you can use the :as keyword in
vector bindings to get the full request map, e.g.
(GET "/foo/:id" [id :as request] ...)
This allows you to bind the parameters and any values in the request map:
(GET "/foo/:id" [id :as {cookies :cookies}] ...)
I'm not sure whether "as" is the right word for it, but it has the
same purpose as the :as keyword in normal Clojure bindings.
- James
I was thinking more like :request :)
(GET "/foo/:id" [id :request {cookies :cookies}] ...)
But I'll probably stick with :as for now, because that matches the
normal Clojure bindings:
(GET "/foo/:id" {:as request} ...)
(GET "/foo/:id" [:as request] ...)
- James
Thank you very much. As I've stated a few times on the mailing list,
it's something
I've wanted for a long time.
--
Omnem crede diem tibi diluxisse supremum.
We will give it a try in the next couple of weeks, feeling like a hamster in a wheel
these times.
Thank you James.
Luc P.
--
Luc P.
================
The rabid Muppet