# Help with custom FormBuilder

**URL:** <https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520>\
**Category:** Rails\
**Created:** [March 21, 2007, 12:04am UTC](https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520 "2007-03-21T00:04:00Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [March 21, 2007, 12:04am UTC](https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520/1 "2007-03-21T00:04:00Z")

</div>

Hi all

I’m working on a custom FormBuilder that should be able to handle habtm  
and has\_many relationships with just one single method call, similar to  
the existing text\_field, text\_area etc. methods.

My code in a view looks like that:

\<%= error\_messages\_for ‘member’ %\>

\<%= form.text\_field :email %\>  
\<%= form.text\_area :signature %\>  
\<%= form.has\_and\_belongs\_to\_many :preferred\_music\_styles %\>  
\<%= form.belongs\_to :origin\_country %\>

And this results in a well-known textfield and textarea for email and  
signature AND in checkboxes for the habtm relation AND a dropdown select  
list for the belongs\_to association.

Anyway, I have adapted some code from the Rails Recipes book and it  
looks like the following so far:

```
def self.create_tagged_field(method_name)
  define_method(method_name) do |label, *args|
    html_surround(label, super) # Provides the HTML code that

```

surrounds the form label and the method (super) that generates the input  
tag(s)  
end  
end

```
field_helpers.reject{|h| h == 'hidden_field'}.concat([:select]).each

```

do |name|  
create\_tagged\_field(name)  
end

```
def html_surround(label, content, options = {})
  label_options = {}
  label_options[:for] = "#{@object_name}_#{label}" unless

```

options[:print\_label\_for]  
@template.content\_tag(:div,  
@template.content\_tag(:div,  
@template.content\_tag(:label,  
nil || label.to\_s.humanize, # TODO: optional argument  
:caption instead of nil!  
label\_options  
), :class =\> :label  
) + @template.content\_tag(‘div’,  
content, :class =\> :field), :class =\> :row  
)  
end

Now I’d like to offer some more options to the programmer, e.g. to set a  
custom caption instead of using the name of the database field:

\<%= form.belongs\_to :origin\_country, :caption =\> ‘Where do you come  
from?’ %\>

Sadly I have no clue where to catch this option and remove it from the  
options for further processing, because so far Rails creates a “caption”  
attribute in the input tag with the given value:

…

This is for sure not the wished effect, and it pollutes my perfectly  
valid HTML code! 🙂

So can anybody tell me how to

1. display this custom label instead of the default caption when this  
option is set and
2. how to prevent this option from being transmitted to the later Rails  
processings?

Thanks a lot for help.  
Josh

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [March 21, 2007, 8:20pm UTC](https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520/2 "2007-03-21T20:20:25Z")

</div>

Josh,

I’ve spent a bit of time with custom form builders. I originally  
started with help from the cookbook, but later branched out on my own,  
almost rewriting many of the field helpers to fit exactly what I  
wanted the output to be.

Now, I don’t see where in your code you’re handling your own  
belongs\_to helper, but I imagine it just does some processing to get  
the list of options and calls the select helper. The idea is to  
manipulate the options rather than simply passing them on as-is.  
Inspect args from the end (args.last) and see if it’s a hash. If so,  
those are your options (and if not, no options were supplied). The  
important thing to remember is that you want to manipulate the args,  
passing some of them on to html\_surround and keeping them from the  
form helper (e.g. select). This means you have to provide explicit  
arguments to super. (Otherwise, the parent class’s method will be  
called with the original arguments.)

So take

html\_surround(label, super)

and change it to

# manipulation goes here

html\_surround(label, super(arguments\_for\_super),  
options\_for\_html\_surround)

The select helper is a little trickier than the rest because it has  
two sets of options (‘options’ and ‘html\_options’) where the rest only  
have one.

–  
-yossef

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [March 21, 2007, 8:34pm UTC](https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520/3 "2007-03-21T20:34:25Z")

</div>

Thanks for these hints, I’m gonna check it out. 🙂

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [March 21, 2007, 10:06pm UTC](https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520/4 "2007-03-21T22:06:56Z")

</div>

I’ve got another question…

```
def self.create_tagged_field(method_name, options = {})
  define_method(method_name) do |label, *args|
    html_surround(label, super, options) # Provides the HTML code 

```

that surrounds the form label and the method (super) that generates the  
input tag(s)  
end  
end

```
field_helpers.reject{|h| h == 

```

‘hidden\_field’}.concat([‘has\_and\_belongs\_to\_many’, ‘belongs\_to’]).each  
do |name|  
label\_clickable = true unless name == ‘has\_and\_belongs\_to\_many’  
create\_tagged\_field(name, :label\_clickable =\> label\_clickable)  
end

How do I find out in define\_method part what id attribute the created  
form field method will get?

So far I’m guessing it myself in html\_surround:

caption\_options[:for] = “#{@object\_name}\_#{label}”

But sometimes this is not correct. Using a text\_field it generates

member\_nickname

which is fine but in other cases - e.g. a collection\_select - it  
generates

member\_origin\_country

which is often incorrect because the ID depends there on the association  
name itself. In my case it should be member\_country\_id.

So I guess there’s a way to examine the ID that Rails will assign to the  
form element — but how? 🙂

Thanks  
Josh

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [March 25, 2007, 4:48pm UTC](https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520/5 "2007-03-25T16:48:23Z")

</div>

Josh,

About knowing the ID of the form element, there are methods in Action  
Pack for that. I can’t recall them off the top of my head, and to be  
honest, some of my immense form builder bypassed and provided its own  
IDs, so I knew exactly what was going on. But look for something like  
“assign\_default\_name\_and\_id” and you’ll see what’s going on.

As for your Array#merge problem, if I’m not mistaken it’s just that  
you’re calling super(label, args) instead of super(label, \*args). See  
this:

def args\_length(\*args)  
args.length  
end

def blah(arg1, \*args)  
args\_length(args)  
end

blah(‘test’)  
=\> 1  
blah(‘test’, 1, 2, 3, 4)  
=\> 1

def blah(arg1, \*args)  
args\_length(\*args)  
end

blah(‘test’)  
=\> 0  
blah(‘test’, 1, 2, 3, 4)  
=\> 4

## And there’s one thing that jumps out at me. You’ve changed it so you’re calling super for your brand-new association methods (has\_and\_belongs\_to\_many, belongs\_to), but the parent class doesn’t have them (I assume).

-yossef

---

<div class="post-metadata">

**Author:** ![josh](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/45deac/32.png) [@josh](https://www.ruby-forum.com/u/josh)\
**Post date:** [March 21, 2007, 11:03pm UTC](https://www.ruby-forum.com/t/help-with-custom-formbuilder/89520/6 "2007-03-21T23:03:54Z")

</div>

Hrm about your above answer… I changed it to the following:

```
def self.create_tagged_field(method_name, options = {})
  define_method(method_name) do |label, *args|
    html_surround(label, super(label, args), options) # Provides the 

```

HTML code that surrounds the form label and the method (super) that  
generates the input tag(s)  
end  
end

But now I get the following error:

undefined method `merge’ for []:Array
