2010-12-12

Improved Sandbar Forms

There has been a flurry of improvements made to the Clojure web stack
during the past year. Compojure has matured, there's some non-trivial
code available (for example, see Brian Carper's
cow-blog) and the new
Sandbar library which
brings a higher-level of abstraction on top of Compojure and Ring. For
now, it provides a stateful session mechanism, authorization +
authentication and a clever way of defining forms layout, processing and
validation. Also there's much more to come, you can look at the details
in the following
roadmap.

For today, I'll discuss the recent changes to the forms
namespace. There has been a lot of work done on that part during the
past week, but the changes to the API are minimal. I'll go over each
options of the defform macro, but first lets talk about the code
behind. As Stuart Halloway stated in his
book, the
first rule of the Macro Club is: "Don't Write Macros." So the most
significant alteration is the rewrite of the defform macro. Previously,
it was a big piece of code (134 lines) generating a bunch of definitions
of all sorts. It was quite hard to modify and had the usual constraints
of using macros that way. It now has been replaced by a much more simple
macro that call the new make-form function.

Lets see a sample form written for the current (0.3) version of Sandbar.

It was nice but had a severe limitation: you were forced to use a fixed
set of routes. When creating a record, "/new" was appended to the given
URI else the "id" key was used. Also, the fields option tended to
become cluttered with the data source bindings. These are the two main
point I'll address here.

Firstly, lets rewrite the fields option, it's quite similar in
taking a vector of field descriptions. The difference is in the field
description functions that are taking the name of the field (as a
keyword) followed by a list of optional key/value pairs.

Each field functions can take a label option, then most have an
optional boolean required option which auto-generate the
corresponding validator for that field and finally there's the
prompt option for the select field. Any other options will be
added to the field's HTML attribute.

Secondly, there are the new options for managing the form action and
method attributes. Each are prefixed by create or update
followed by -action or -method whether it's for an action or
method. They can take parametrized routes which will get their
parameters replaced by the matching route parameters from the incoming
request.

:create-action"/groups":update-action"/groups/:id":update-method:put

Thirdly, here's the new bindings option which take a map of field
names followed by their respective binding information in a map.

The source option needs a function that fetch the relevant data,
the visible option determines what field to show on the page and
the value and data options represent the actual value to use
in the source data map and the form data map respectively.

Finally here's the whole code for this example using the new forms
features using RESTful routes.

Furthermore, other modifications include that most defform options
can take a function of the request instead of just a value and that
redirection URIs in the on-success and on-cancel options can
be parametrized.

The forms namespace is still a work in progress and is still being
improved. Particularly there is talk concerning the way forms get
rendered, which currently lack flexibility as pointed out by David Nolen
in this
thread.
Nothing has been done yet in this regard, so it's the right time for
anyone interested to chime in this discussion.