require 'cgi'
require 'erb'
require 'action_view/helpers/form_helper'
module ActionView
module Helpers
# Provides a number of methods for turning different kinds of containers into a set of option tags.
# == Options
# The collection_select, country_select, select,
# and time_zone_select methods take an options parameter,
# a hash.
#
# * :include_blank - set to true if the first option element of the select element is a blank. Useful if there is not a default value required for the select element. For example,
#
# select("post", "category", Post::CATEGORIES, {:include_blank => true})
#
# could become:
#
#
#
# * :prompt - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
#
# Another common case is a select tag for an belongs_to-associated object. For example,
#
# select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] })
#
# could become:
#
#
module FormOptionsHelper
include ERB::Util
# Create a select tag and a series of contained option tags for the provided object and method.
# The option currently held by the object will be selected, provided that the object is available.
# See options_for_select for the required format of the choices parameter.
#
# Example with @post.person_id => 1:
# select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, { :include_blank => true })
#
# could become:
#
#
#
# This can be used to provide a default set of options in the standard way: before rendering the create form, a
# new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
# to the database. Instead, a second model object is created when the create request is received.
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
#
# By default, post.person_id is the selected option. Specify :selected => value to use a different selection
# or :selected => nil to leave all options unselected.
def select(object, method, choices, options = {}, html_options = {})
InstanceTag.new(object, method, self, nil, options.delete(:object)).to_select_tag(choices, options, html_options)
end
# Return select and option tags for the given object and method using options_from_collection_for_select to generate the list of option tags.
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
InstanceTag.new(object, method, self, nil, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
end
# Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
def country_select(object, method, priority_countries = nil, options = {}, html_options = {})
InstanceTag.new(object, method, self, nil, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options)
end
# Return select and option tags for the given object and method, using
# #time_zone_options_for_select to generate the list of option tags.
#
# In addition to the :include_blank option documented above,
# this method also supports a :model option, which defaults
# to TimeZone. This may be used by users to specify a different time
# zone model object. (See #time_zone_options_for_select for more
# information.)
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
end
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +Selected+
# may also be an array of values to be selected when using a multiple select.
#
# Examples (call, result):
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
# \n
#
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
# \n
#
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
# \n
#
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
# \n\n
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def options_for_select(container, selected = nil)
container = container.to_a if Hash === container
options_for_select = container.inject([]) do |options, element|
text, value = option_text_and_value(element)
selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
options << %()
end
options_for_select.join("\n")
end
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
# If +selected+ is specified, the element returning a match on +value_method+ will get the selected option tag.
#
# Example (call, result). Imagine a loop iterating over each +person+ in @project.people to generate an input tag:
# options_from_collection_for_select(@project.people, "id", "name")
#
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
options = collection.map do |element|
[element.send(text_method), element.send(value_method)]
end
options_for_select(options, selected)
end
# Returns a string of option tags, like options_from_collection_for_select, but surrounds them with