Select Helper

I commonly create helper function to make tags with pre-built
options so I can just do something like:

<%=region_select_tag(default_region)%>

to generate a dropdown of values in my views. The helper function
already knows the options to choose from. I usually make three different
versions of each select helper. For example:

  • region_options_for_select(selected=nil, options={})
  • region_select_tag(name, selected=nil, options={}, html_options={})
  • region_select(object, method, options, html_options={})

This API is meant to mimic the Rails select helpers. It gives me just
the options if I want a custom tag, or it can give me the
select tag, or it can work of an object and attribute.

I have many of these and I want to dry up my code. For the most part the
only difference between all my select helpers is the
XXXX_options_for_select() method which actually defines the list. For
example here is a following sample implementation:


def time_length_options_for_select(selected=nil, options={})
time_lengths = TimeLength.find :all, :order=>‘length’
select_blank(select_options options) +
options_from_collection_for_select(time_lengths, ‘id’,
‘name’, selected.to_i)
end

def time_length_select_tag(name, selected=nil,
options={}, html_options={})
select_tag name, time_length_options_for_select(selected,
options), html_options
end

def time_length_select(object, method, options={}, html_options={})
object, method = object.to_s.dup, method.to_s.dup
selected = instance_variable_get("@#{object}").send method
time_length_select_tag “#{object}[#{method}]”, selected,
options, html_options
end


Called in the code is two utility functions that are defined as follows:


def select_options(options)
options = {
:include_blank => false,
:prompt => false,
}.merge options
options[:include_blank] = true if options[:prompt]
options
end

def select_blank(options)
return ‘’ unless options[:include_blank]
options[:prompt] = ‘’ unless options[:prompt].instance_of? String
content_tag ‘option’, options[:prompt], {:value => ‘’}
end


So my goal is to change the three time_length_* functions into one
statement that looks like:


select_helper :time_length do |selected|
time_lengths = TimeLength.find :all, :order=>‘length’
options_from_collection_for_select(time_lengths, ‘id’,
‘name’, selected.to_i)
end


So I tried an implementation that looks something like the following
defined in ApplicationHelper:

def self.select_helper(name, &block)
name = name.to_s
define_method “#{name}_options_for_select” do |*a|
select_blank(select_options(a[1] || {})) + block[a[0] || nil]
end
define_method “#{name}_select_tag” do |n, *a|
select_tag n, self.send("#{name}_options_for_select".to_sym,
(a[0] || nil), (a[1] || {})), (a[2] || {})
end
define_method “#{name}_select” do |object, method, options, *a|
object, method = object.to_s.dup, method.to_s.dup
selected = instance_variable_get("@#{object}").send method
self.send “#{name}_select_tag”.to_sym,
“#{object}[#{method}]”, selected, (a[0] || {}), (a[1] || {})
end
end


As you can see the implementation is a bit complicated because I want to
keep the arguments flexible but if I can implement it correctly I will
be able to reduce a lot of code and make it very quick to write a custom
select helper. The problem is that I have some sort of scope problem. I
get a message like the following:

undefined method `options_from_collection_for_select’ for
ApplicationHelper:Module

It seems that it cannot see the helper method defined by Rails. I’m new
to the meta-programming so I’m having difficulty getting the
implementation correct. Any help would be greatly appreciated.

Eric

As you can see the implementation is a bit complicated because I want to
keep the arguments flexible but if I can implement it correctly I will
be able to reduce a lot of code and make it very quick to write a custom
select helper. The problem is that I have some sort of scope problem. I
get a message like the following:

undefined method `options_from_collection_for_select’ for
ApplicationHelper:Module

Do you get this error when you call select_helper (where are you calling
that) or when you call the methods you have defined (and where do you
call those ?)

Fred

Frederick C. wrote:

Do you get this error when you call select_helper (where are you calling
that) or when you call the methods you have defined (and where do you
call those ?)

I get the error when I call the methods defined. I call them from a
view.

Eric