Some ideas & implementation for the new forms modification

11 views
Skip to first unread message

Nicolas Buduroi

unread,
Dec 5, 2010, 7:38:52 PM12/5/10
to Sandbar Library
Hi, I've looked more deeply into your new forms code and it's really
much easier to work with. While at it, I've worked on some
modification I previously talked about and sent you a pull request.
With these changes, it's quite simple to make RESTful routes a la
Rails for form handlers. I've added four new options to control the
form attributes: create-action, create-method, update-action and
update-action. Here's the code that I've used to test it:

(forms/defform group-form
"Form handler for Group entity."
:fields [(forms/textfield :name :title "Group's name :")
(forms/textfield :category :title "Category :")
#_(forms/textarea :description :title "Description :")]
:load #(fetch-group %)
:on-cancel "/groups"
:on-success #(do (store-group %)
(session/flash-put! :user-message
"Group has been saved.")
"/groups")
:title "Edit group"
:create-action "/groups"
:update-action "/groups/:id"
:update-method :put)

(defroutes forms-routes
(GET "/groups/new" request (group-form request))
(POST "/groups" request (group-form request))
(GET "/groups/:id/edit" request (group-form request))
(PUT "/groups/:id" request (group-form request)))

While working on that code I was thinking about renaming some
variables/functions, but I'd like your approbation on these (quite
boring) changes before making them.

- In the field function, changing the "title" argument to "flabel"
to remove confusion with form title.
- Changing "post-form" to "submit-form" because it could also be
used with the PUT method with my modifications.
- Changing "get-form" to "view-form", "show-form" or "display-form"
just because I find "get-*" functions irritating! ;-)

None of them are essential obviously. What do you think?

Brenton

unread,
Dec 6, 2010, 1:35:19 AM12/6/10
to Sandbar Library
Nicolas,

I pulled in your changes. I think I like all of them except the part
about returning the map. Most of the time, the form is not the only
thing on the page. I often need to wrap the form in a layout or
combine it with some other content. In this situation the form title
would not be the same as the page title and it would be easier to just
return the body of the form.

The name changes are fine. I like title or label for the field labels
though.

For the RESTful routes: It would be nice if we could make this
simpler. I'll put some thought into that.

I will be doing a lot of work on this tomorrow.

Thanks for your help,
Brenton

Brenton

unread,
Dec 6, 2010, 12:17:49 PM12/6/10
to Sandbar Library
After sleeping on it. I have decided that I like that make-form
returns a map with title and body.

I like your changes. Great work!

Brenton

Nicolas Buduroi

unread,
Dec 6, 2010, 12:27:05 PM12/6/10
to Sandbar Library
On Dec 6, 1:35 am, Brenton <bashw...@gmail.com> wrote:
> The name changes are fine. I like title or label for the field labels
> though.

Great! I'll go on with the variable name changes then.

> For the RESTful routes: It would be nice if we could make this
> simpler. I'll put some thought into that.

I'll be looking forward to it.

Nicolas Buduroi

unread,
Dec 6, 2010, 12:32:20 PM12/6/10
to Sandbar Library
On Dec 6, 12:17 pm, Brenton <bashw...@gmail.com> wrote:
> After sleeping on it. I have decided that I like that make-form
> returns a map with title and body.

Great, just to be clear on how I'm reusing the title, I'm using a
variation of the wrap-layout middleware from Brian Carper cow-blog:

https://github.com/briancarper/cow-blog/blob/master/src/blog/layout.clj#L61

In the page layout function I concatenate the site title with the
current page title. The idea was really just to enforce the DRY
principle. Glad you like it!

Brenton

unread,
Dec 7, 2010, 5:52:16 PM12/7/10
to Sandbar Library
Nicolas,

I just finished working on the multi-checkbox, multi-select and select
form elements and I think I finally have something that I like.

I have an example form that I have been working on here:

https://github.com/brentonashworth/sandbar/blob/master/src/sandbar/example/forms/dev/complex.clj

The goal is to make it so that I can have a Clojure data structure
that can be mapped to a form and then back again. The form fields have
been simplified and all of the data source stuff is now in a :bindings
option. I moved this to a separate option because the bindings are
used both to display the form and to marshal the data when the form is
submitted. Each data source has four elements, :value :visible :source
and :data.

I will need to write a bunch of documentation for this but I think it
is a huge improvement.

Thanks again for all of the contributions this week. Great work.

Brenton

On Dec 5, 4:38 pm, Nicolas Buduroi <nbudu...@gmail.com> wrote:

Nicolas Buduroi

unread,
Dec 7, 2010, 6:29:22 PM12/7/10
to Sandbar Library
On Dec 7, 5:52 pm, Brenton <bashw...@gmail.com> wrote:
> The goal is to make it so that I can have a Clojure data structure
> that can be mapped to a form and then back again. The form fields have
> been simplified and all of the data source stuff is now in a :bindings
> option. I moved this to a separate option because the bindings are
> used both to display the form and to marshal the data when the form is
> submitted. Each data source has four elements, :value :visible :source
> and :data.

For now I'm just using the simple field types but I like the :bindings
option, I'll try it soon.

> I will need to write a bunch of documentation for this but I think it
> is a huge improvement.

I'll probably write a blog post on the new form features, maybe next
week-end if I ever get back some motivation to write. ;-)

In the meantime, there's another improvement I was thinking about
yesterday. Currently the code take for granted that a record's primary
key is called id which is a good convention (it's what I'm used to
coming from RoR) but might not be the preferred choice of everybody.
I've not looked much into it but I think it would be rather simple to
fix, we could just add a :key option and use it in place of "id". If
you think it's a good idea, I'll provide an implementation.

> Thanks again for all of the contributions this week. Great work.

Thanks

Brenton

unread,
Dec 7, 2010, 6:58:55 PM12/7/10
to Sandbar Library
I have been thinking about this as well. I think that the only
dependency that sandbar has on the id is that this is what gets passed
to the load function. I was thinking of making the load function a
function of the params. That way we don't force anyone to use a
specific key and it opens the option to pass addition parameters to
the form.

What you do think?

Brenton

Nicolas Buduroi

unread,
Dec 7, 2010, 7:07:27 PM12/7/10
to sandbar...@googlegroups.com
On Tue, Dec 7, 2010 at 6:58 PM, Brenton <bash...@gmail.com> wrote:
I have been thinking about this as well. I think that the only
dependency that sandbar has on the id is that this is what gets passed
to the load function.

It's also used in show-form to distinguish between create and update for the method and action to be used. I used it because it was the only way I've found, I didn't really like that tough. Can't think of anything else at the moment.
 
I was thinking of making the load function a
function of the params. That way we don't force anyone to use a
specific key and it opens the option to pass addition parameters to
the form.

What you do think?

This is a good idea.

Brenton

unread,
Dec 8, 2010, 8:19:27 PM12/8/10
to Sandbar Library
Nicolas,

Here is my idea for makeing RESTful forms simpler.

https://github.com/brentonashworth/sandbar/blob/master/src/sandbar/forms.clj#L1040

This would allow you to create RESTful forms like this:

(defrestform user-form
:resource "/users"
:page-layout #(views/layout %)
:fields [(textfield :username)]
:load #(db/fetch %)
:on-cancel "/"
:on-success
#(do
(db/store %)
(flash-put! :user-message "User has been saved.")
"/"))

(defroutes my-routes
user-form
(route/not-found "<h1>Not Found</h1>"))

Not sure I like the name defrestform.

Brenton

David Nolen

unread,
Dec 9, 2010, 12:09:55 PM12/9/10
to Sandbar Library
I have recently finished a large-ish project in Django that uses
forms. What's being discussed so far on this thread will only re-
create the kinds of annoyances I ran into there.

I propose that you define a well-thought out series of form
*protocols*. A quick sketch:

(defprotocol Form
(fields [this])
(on-cancel [this])
(on-success [this])
(render [this data]))

(defprotocol DBForm
(save [this conn]))

(defprotocol RestfulForm
(route [this])
(process-request [this request])

(defprotocol FormValidation
(validate [this field]))

Sandbar can provide a easy-to-use defrecord that implements these that
can you get going quickly. At the same time it is now trivial for
someone to specify a custom implementation. Anything else in my
experience will create pain.

David

Eric Lavigne

unread,
Dec 10, 2010, 10:39:14 PM12/10/10
to sandbar...@googlegroups.com
I find this code confusing. It looks like ":load #(db/fetch %)"
pre-populates the user form. Does % represent a user id? A user name?
Where does that information come from? Is the url /users or
/users/:id?

Eric Lavigne

unread,
Dec 10, 2010, 10:43:00 PM12/10/10
to sandbar...@googlegroups.com
> I have recently finished a large-ish project in Django that uses
> forms. What's being discussed so far on this thread will only re-
> create the kinds of annoyances I ran into there.

What kind of annoyances did you run into with Django? Not liking the
way Django forms look? Difficulty reusing the same form or field in
more than one part of your application?

Brenton

unread,
Dec 10, 2010, 11:23:35 PM12/10/10
to Sandbar Library
Eric,

The load function is a function of the "id" which returns data to
populate the form. It is called when the id param is present in the
request params. How that parameter gets there is up to you.

There has been some discussion about making this more general by being
a function of the params or the request.

There is documentation for the 0.3.0 version here:

https://github.com/brentonashworth/sandbar/wiki/Forms

The current version has many changes which simplify the implementation
code and make defining forms more flexible. I think, as David has
pointed out, we can do a lot better. There will be many more changes
in the future.

Brenton

Nicolas Buduroi

unread,
Dec 12, 2010, 9:19:48 PM12/12/10
to Sandbar Library
I've just added a small post on my blog about some of the new forms
improvements:

http://whollyweirdwyrd.blogspot.com/2010/12/improved-sandbar-forms.html

While writing it, I had an idea. we could let the field name in field
functions behave like Hiccup tag, so that we can define the tag id and
class attributes with a css-like syntax.

Also, I wonder why the bindings source option must be a function,
can't it just be a value?

Brenton

unread,
Dec 13, 2010, 6:55:04 PM12/13/10
to Sandbar Library
Nicolas,

Good job on the post.

The bindings source is a function because you may want to show
different options depending on the request.

I like the CSS idea. Are you thinking that would be a macro
transformation that defform does or something deeper?

Brenton

Nicolas Buduroi

unread,
Dec 15, 2010, 3:36:51 PM12/15/10
to sandbar...@googlegroups.com
On Mon, Dec 13, 2010 at 6:55 PM, Brenton <bash...@gmail.com> wrote:
Good job on the post.

Thanks.
 
The bindings source is a function because you may want to show different options depending on the request.

Oh yeah, forgot about that!
 
I like the CSS idea. Are you thinking that would be a macro
transformation that defform does or something deeper?

I'm more thinking of changing the field functions themselves using the same technique as in Hiccup. Here's a prototype modification of the textfield function:

(def re-tag #"([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?")

(defn textfield
  [field-name & {:keys [label required] :as options}]
  (let [[_ tag id class] (re-matches re-tag (name field-name))
        field-name (keyword tag)
        options (-> (merge {:size 35
                            :id id
                            :class class} options)
                    (dissoc :label :required))]
...

I would wait to see how the new forms protocols develop before making these changes across the board. I'll be looking at a way of respecting the DRY principle, maybe refactor the field functions.

Reply all
Reply to author
Forward
0 new messages