Sunday, June 1, 2008

Dissecting Rails form builder

In revamping AssociationFu, I've discovered a woeful lack of knowledge around the Rails render engine. How does forms_for and fields_for work?

BTW, this is for Rails 2.0.2.

So here's how forms_for works:

Step one:
It determines the object name, making it singlular and underscored. If the first argument to forms_for is an object or an array:

It determines the following options (unless they have already been set by the options argument) based on the supplied object (which, in the case of an array, is the last element in the array):

- :html => {:class, :id, :method}
- :url

It then prepends the args array with the object.

Step two:
It concats (outputs text to the view) the
tag with the binding of the supplied block. (BTW, forms_for raises an exception if a block is not provided).

Step three:
It calls fields_for, passing it the object_name, the args and the block. Cool and DRY.

Step four:
It finishes by cancating the closing
tag.

Okay, that was nice and simple. Now for fields_for:

Also raises an exception if a block is not provided.

Step one:
Similar to form_for, determine the object_name (singular, underscore). If an array, it determines the form options identically to form_for {:html => {:class, :id, :method}, :url}.

Step two:
Here's why custom form_builders work. The builder class_id is derived from either options[:builder] or defaults to ActionView::Base.default_form_builder.

Step three:
The supplied block is yielded and passed an instance of the builder, which is initialized with the object_name, object, self (ActionView object or "template"), options and the block).

Crazy. So what does builder do?

Here's how builder works:

Any instance of FormBuilder initializes instance variables:

@object_name
@object
@template (the ActionView instantiation)
@options
@proc (the block)

It then defines methods on itself for each of the field_helpers from the FormHelper class. It does this so it can pass the object_name, method (the field name) and object as options[:object].

Funny, but FormBuilder defines its own #fields_for method.

1 comments:

Chris said...

Hi Kevin! Yes, the fields_for method in FormBuilder does some clever things with the structure of the params hash. Have a look at this to see how this is useful.

This has tidied up one of my controllers no end, but I now need to fix my custom FormBuilder to have a custom fields_for...