# Bizarre issue with form builders (merging options and \*args)

**URL:** <https://www.ruby-forum.com/t/bizarre-issue-with-form-builders-merging-options-and-args/172222>\
**Category:** Rails\
**Created:** [July 17, 2009, 1:09am UTC](https://www.ruby-forum.com/t/bizarre-issue-with-form-builders-merging-options-and-args/172222 "2009-07-17T01:09:36Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![uberllama](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/a587f6/32.png) [@uberllama](https://www.ruby-forum.com/u/uberllama)\
**Post date:** [July 17, 2009, 1:09am UTC](https://www.ruby-forum.com/t/bizarre-issue-with-form-builders-merging-options-and-args/172222/1 "2009-07-17T01:09:36Z")

</div>

Okay guys, I’ve been beating my head against the wall with this one  
all day and was hoping I might garner some enlightenment from the  
gathered geniuses. I am including some pared down code to demonstrate  
my issue.

Basically, I want my form builder to automatically add an :onchange  
handler to a specific form field (for my example, I am using  
an :onclick with a generic form builder).

My problem is that unless I pass in at least one hash value to the  
field helper, the option doesn’t get added. i.e.:

These work (the onclick will get added):

```auto
<%= f.text_field :title, :class => 'sexy' %>
```

```auto
<%= f.text_field :title, :label => 'Topic' %>
```

[code]\<%= f.text\_field :title, :class =\> ‘sexy’, :label =\> ‘Topic’ %\>[/  
code]

This does not (no onclick, very sad):

```auto
<%= f.text_field :title %>
```

I have appended question marks to the blocks in question below.

```auto
class ExampleFormBuilder < ActionView::Helpers::FormBuilder

  def self.create_tagged_field(method_name)
    define_method(method_name) do |attr, *args|
      options = args.extract_options!

      # ?? This doesn't work unless the field helper in the view is
passing in a hash of options. ??
      #options.merge!(:onclick => "alert('foo');")
      # ?? Direct assignment doesn't work either ??
      #options[:onclick] = "alert('foo');"

      label_text = "#{options[:label] || attr.to_s.humanize}:"
      label = @template.content_tag('label', label_text, :for => "#
{@object_name}_#{attr}")

      # remove my custom hash key so it doesn't pollute the output
      options.delete_if {|key, value| key == :label}

      # ?? I don't need to do this, but why ??
      #args = (args << options) unless options.blank?

      @template.content_tag('p', label + '<br />' + super)
    end
  end

  field_helpers.each do |name|
    create_tagged_field(name)
  end

end
```

Even if I am not passing in a hash, the extract\_options! command  
should create a hash object from the args, so I don’t know what I’m  
missing here.

My second part to this question is around the splat operator, \*args,  
and options. Let’s say I pass the field helper the option :class =\>  
‘sexy’. Now, since I am extracting the options hash from the args, I  
assumed I needed to add them back in before calling super. But  
spookily enough, I don’t. Do the options extracted from \*args maintain  
a reference, or am I missing something completely obvious.

Any and all help would be massively appreciated. I will even send you  
a facebook gift. 😉

---

<div class="post-metadata">

**Author:** ![uberllama](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/a587f6/32.png) [@uberllama](https://www.ruby-forum.com/u/uberllama)\
**Post date:** [July 17, 2009, 2:49am UTC](https://www.ruby-forum.com/t/bizarre-issue-with-form-builders-merging-options-and-args/172222/2 "2009-07-17T02:49:30Z")

</div>

Hi –

On Thu, 16 Jul 2009, uberllama wrote:

> field helper, the option doesn’t get added. i.e.:
> 
> passing in a hash of options. ??
> 
> assumed I needed to add them back in before calling super. But  
> spookily enough, I don’t. Do the options extracted from \*args maintain  
> a reference, or am I missing something completely obvious.

Let me start with the last question first.

Short answer: super with implicit arguments, when you use  
define\_method and a block, doesn’t work the same as it does when you  
user super in a def-based method definition. You have to provide the  
arguments explicitly.

Longer answer:

If you do this:

class A  
def m(\*args)  
print "In A#m: "  
p args  
end  
end

class B \< A  
def m(\*args)  
print "In B#m: "  
p args  
args = [“hi!”]  
super  
end  
end

B.new.m(1,2,3)

you get this:

In B#m: [1, 2, 3]  
In A#m: [“hi!”]

The call to super uses the variable args – not even the object that  
was bound to args originally, but the variable args – to make the  
call to A#m. Now, look at this variation. Assume the same A, and then:

class C \< A  
define\_method(:m) do |\*args|  
print "In C#m: "  
p args  
args = [“hi!”]  
super  
end  
end

C.new.m(1,2,3)

This gives you this output:

In C#m: [1, 2, 3]  
In A#m: [1, 2, 3]

This time, the variable name “args”, which comes from a block  
parameter (and not a method parameter), doesn’t play the same role.  
Instead, as far as I can tell, super is using a copy of the original  
object that was bound to args. Even adding elements to args doesn’t  
cause A#m to produce anything different.

And… you will be very interested in what happens if I run the above  
under Ruby 1.9.1:

In C#m: [1, 2, 3]  
sup.rb:22:in `block in <class:C>': implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly. (RuntimeError) from sup.rb:27:in `’

In other words, you can’t do it any more anyway – probably because it  
didn’t really work in the first place, so it’s gone.

So… change super to super(attr, \*args), restore the args \<\< options  
thing (but write it more simply, like this:

args \<\< options unless options.empty?

🙂 and you should be OK (or very close to it).

David

–  
David A. Black / Ruby Power and Light, LLC  
Ruby/Rails consulting & training: [http://www.rubypal.com](http://www.rubypal.com)  
Now available: The Well-Grounded Rubyist ([The Well-Grounded Rubyist](http://manning.com/black2))  
Training! Intro to Ruby, with Black & Kastner, September 14-17  
(More info: [http://rubyurl.com/vmzN](http://rubyurl.com/vmzN))

---

<div class="post-metadata">

**Author:** ![uberllama](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/a587f6/32.png) [@uberllama](https://www.ruby-forum.com/u/uberllama)\
**Post date:** [July 17, 2009, 4:25pm UTC](https://www.ruby-forum.com/t/bizarre-issue-with-form-builders-merging-options-and-args/172222/3 "2009-07-17T16:25:32Z")

</div>

uberllama wrote:  
[…]

> Basically, I want my form builder to automatically add an :onchange  
> handler to a specific form field (for my example, I am using  
> an :onclick with a generic form builder).  
> […]

This is really not a good way of doing it. HTML and JS work best  
together when they are in completely separate files – so don’t put your  
onclick and onchange handlers in the form builder. (I know Rails  
encourages embedding JS in HTML, but that’s a problem with Rails.)  
Rather, attach the handlers in the (separate) JS file:  
$(‘whatever’).onClick = function () { … }, or some such (Prototype’s  
event handler enhancements may be fun here).

## Best,

Marnen Laibow-Koser  
[http://www.marnen.org](http://www.marnen.org)  
removed\_email\_address@domain.invalid
