Fixed that form helpers would treat string and symbol keys differently in html_options (and possibly create duplicate entries) #112 [bitsweat]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@833 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2005-03-06 11:50:41 +00:00
parent db41d2dd5c
commit dfac1cea3d
17 changed files with 280 additions and 229 deletions

@ -1,5 +1,7 @@
*SVN*
* Fixed that form helpers would treat string and symbol keys differently in html_options (and possibly create duplicate entries) #112 [bitsweat]
* Fixed that broken pipe errors (clients disconnecting in mid-request) could bring down a fcgi process
* Added the original exception message to session recall errors (so you can see which class wasnt required)

@ -15,7 +15,7 @@ module Helpers
module ActiveRecordHelper
# Returns a default input tag for the type of object returned by the method. Example
# (title is a VARCHAR column and holds "Hello World"):
# input("post", "title") =>
# input("post", "title") =>
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
def input(record_name, method)
InstanceTag.new(record_name, method, self).to_tag
@ -41,7 +41,7 @@ def input(record_name, method)
# It's possible to specialize the form builder by using a different action name and by supplying another
# block renderer. Example (entry is a new record that has a message attribute using VARCHAR):
#
# form("entry", :action => "sign", :input_block =>
# form("entry", :action => "sign", :input_block =>
# Proc.new { |record, column| "#{column.human_name}: #{input(record, column.name)}<br />" }) =>
#
# <form action='/post/sign' method='post'>
@ -56,16 +56,17 @@ def input(record_name, method)
# form << content_tag("b", "Department")
# form << collection_select("department", "id", @departments, "id", "name")
# end
def form(record_name, options = {})
record = instance_eval("@#{record_name}")
def form(record_name, options = nil)
options = (options || {}).symbolize_keys
record = instance_eval("@#{record_name}")
options[:action] ||= record.new_record? ? "create" : "update"
action = url_for(:action => options[:action])
action = url_for(:action => options[:action])
submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
id_field = record.new_record? ? "" : InstanceTag.new(record_name, "id", self).to_input_field_tag("hidden")
formtag = %(<form action="#{action}" method="post">#{id_field}) + all_input_tags(record, record_name, options)
yield formtag if block_given?
formtag + %(<input type="submit" value="#{submit_value}" /></form>)
@ -73,7 +74,7 @@ def form(record_name, options = {})
# Returns a string containing the error message attached to the +method+ on the +object+, if one exists.
# This error message is wrapped in a DIV tag, which can be specialized to include both a +prepend_text+ and +append_text+
# to properly introduce the error and a +css_class+ to style it accordingly. Examples (post has an error message
# to properly introduce the error and a +css_class+ to style it accordingly. Examples (post has an error message
# "can't be empty" on the title attribute):
#
# <%= error_message_on "post", "title" %> =>
@ -86,19 +87,20 @@ def error_message_on(object, method, prepend_text = "", append_text = "", css_cl
"<div class=\"#{css_class}\">#{prepend_text + (errors.is_a?(Array) ? errors.first : errors) + append_text}</div>"
end
end
# Returns a string with a div containing all the error messages for the object located as an instance variable by the name
# of <tt>object_name</tt>. This div can be tailored by the following options:
#
# * <tt>header_tag</tt> - Used for the header of the error div (default: h2)
# * <tt>id</tt> - The id of the error div (default: errorExplanation)
# * <tt>class</tt> - The class of the error div (default: errorExplanation)
def error_messages_for(object_name, options={})
def error_messages_for(object_name, options = {})
options = options.symbolize_keys
object = instance_eval "@#{object_name}"
unless object.errors.empty?
content_tag("div",
content_tag(
options[:header_tag] || "h2",
options[:header_tag] || "h2",
"#{pluralize(object.errors.count, "error")} prohibited this #{object_name.gsub("_", " ")} from being saved"
) +
content_tag("p", "There were problems with the following fields:") +
@ -107,7 +109,7 @@ def error_messages_for(object_name, options={})
)
end
end
private
def all_input_tags(record, record_name, options)
input_block = options[:input_block] || default_input_block
@ -116,7 +118,7 @@ def all_input_tags(record, record_name, options)
def default_input_block
Proc.new { |record, column| "<p><label for=\"#{record}_#{column.name}\">#{column.human_name}</label><br />#{input(record, column.name)}</p>" }
end
end
end
class InstanceTag #:nodoc:

@ -10,15 +10,15 @@ module AssetTagHelper
# either be <tt>:rss</tt> (default) or <tt>:atom</tt> and the +options+ follow the url_for style of declaring a link target.
#
# Examples:
# auto_discovery_link_tag # =>
# auto_discovery_link_tag # =>
# <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.curenthost.com/controller/action" />
# auto_discovery_link_tag(:atom) # =>
# auto_discovery_link_tag(:atom) # =>
# <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.curenthost.com/controller/action" />
# auto_discovery_link_tag(:rss, :action => "feed") # =>
# auto_discovery_link_tag(:rss, :action => "feed") # =>
# <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.curenthost.com/controller/feed" />
def auto_discovery_link_tag(type = :rss, options = {})
tag(
"link", "rel" => "alternate", "type" => "application/#{type}+xml", "title" => type.to_s.upcase,
"link", "rel" => "alternate", "type" => "application/#{type}+xml", "title" => type.to_s.upcase,
"href" => url_for(options.merge(:only_path => false))
)
end
@ -38,7 +38,7 @@ def javascript_include_tag(*sources)
content_tag("script", "", "language" => "JavaScript", "type" => "text/javascript", "src" => source)
}.join("\n")
end
# Returns a css link tag per source given as argument. Examples:
#
# stylesheet_link_tag "style" # =>
@ -48,7 +48,7 @@ def javascript_include_tag(*sources)
# <link href="/stylesheets/random.styles" media="screen" rel="Stylesheet" type="text/css" />
# <link href="/css/stylish.css" media="screen" rel="Stylesheet" type="text/css" />
def stylesheet_link_tag(*sources)
sources.collect { |source|
sources.collect { |source|
source = "/stylesheets/#{source}" unless source.include?("/")
source = "#{source}.css" unless source.include?(".")
tag("link", "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => source)

@ -5,7 +5,7 @@ module Helpers
# The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the select-type methods
# share a number of common options that are as follows:
#
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday" would give
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday" would give
# birthday[month] instead of date[month] if passed to the select_month method.
# * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
# * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true, the select_month
@ -13,11 +13,11 @@ module Helpers
module DateHelper
DEFAULT_PREFIX = "date" unless const_defined?("DEFAULT_PREFIX")
# Reports the approximate distance in time between to Time objects. For example, if the distance is 47 minutes, it'll return
# Reports the approximate distance in time between to Time objects. For example, if the distance is 47 minutes, it'll return
# "about 1 hour". See the source for the complete wording list.
def distance_of_time_in_words(from_time, to_time)
distance_in_minutes = ((to_time - from_time) / 60).round
case distance_in_minutes
when 0 then "less than a minute"
when 1 then "1 minute"
@ -28,7 +28,7 @@ def distance_of_time_in_words(from_time, to_time)
else "#{(distance_in_minutes / 1440).round} days"
end
end
# Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
def distance_of_time_in_words_to_now(from_time)
distance_of_time_in_words(from_time, Time.now)
@ -48,7 +48,7 @@ def distance_of_time_in_words_to_now(from_time)
#
# date_select("post", "written_on")
# date_select("post", "written_on", :start_year => 1995)
# date_select("post", "written_on", :start_year => 1995, :use_month_numbers => true,
# date_select("post", "written_on", :start_year => 1995, :use_month_numbers => true,
# :discard_day => true, :include_blank => true)
# date_select("post", "written_on", :order => [:day, :month, :year])
# date_select("user", "birthday", :order => [:month, :day])
@ -107,7 +107,7 @@ def select_minute(datetime, options = {})
0.upto(59) do |minute|
minute_options << ((datetime.kind_of?(Fixnum) ? datetime : datetime.min) == minute ?
"<option selected=\"selected\">#{leading_zero_on_single_digits(minute)}</option>\n" :
"<option selected=\"selected\">#{leading_zero_on_single_digits(minute)}</option>\n" :
"<option>#{leading_zero_on_single_digits(minute)}</option>\n"
)
end
@ -122,7 +122,7 @@ def select_hour(datetime, options = {})
0.upto(23) do |hour|
hour_options << ((datetime.kind_of?(Fixnum) ? datetime : datetime.hour) == hour ?
"<option selected=\"selected\">#{leading_zero_on_single_digits(hour)}</option>\n" :
"<option selected=\"selected\">#{leading_zero_on_single_digits(hour)}</option>\n" :
"<option>#{leading_zero_on_single_digits(hour)}</option>\n"
)
end
@ -137,18 +137,18 @@ def select_day(date, options = {})
1.upto(31) do |day|
day_options << ((date.kind_of?(Fixnum) ? date : date.day) == day ?
"<option selected=\"selected\">#{day}</option>\n" :
"<option selected=\"selected\">#{day}</option>\n" :
"<option>#{day}</option>\n"
)
end
select_html("day", day_options, options[:prefix], options[:include_blank], options[:discard_type])
end
# Returns a select tag with options for each of the months January through December with the current month selected.
# The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are used as values
# (what's submitted to the server). It's also possible to use month numbers for the presentation instead of names --
# set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you want both numbers and names,
# set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you want both numbers and names,
# set the <tt>:add_month_numbers</tt> key in +options+ to true. Examples:
#
# select_month(Date.today) # Will use keys like "January", "March"
@ -158,7 +158,7 @@ def select_month(date, options = {})
month_options = []
1.upto(12) do |month_number|
month_name = if options[:use_month_numbers]
month_name = if options[:use_month_numbers]
month_number
elsif options[:add_month_numbers]
month_number.to_s + " - " + Date::MONTHNAMES[month_number]
@ -167,16 +167,16 @@ def select_month(date, options = {})
end
month_options << ((date.kind_of?(Fixnum) ? date : date.month) == month_number ?
%(<option value="#{month_number}" selected="selected">#{month_name}</option>\n) :
%(<option value="#{month_number}" selected="selected">#{month_name}</option>\n) :
%(<option value="#{month_number}">#{month_name}</option>\n)
)
end
select_html("month", month_options, options[:prefix], options[:include_blank], options[:discard_type])
end
# Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius
# can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the +options+. The <tt>date</tt> can also be substituted
# can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the +options+. The <tt>date</tt> can also be substituted
# for a year given as a number. Example:
#
# select_year(Date.today, :start_year => 1992, :end_year => 2007)
@ -187,14 +187,14 @@ def select_year(date, options = {})
(options[:start_year] || default_start_year).upto(options[:end_year] || default_end_year) do |year|
year_options << ((date.kind_of?(Fixnum) ? date : date.year) == year ?
"<option selected=\"selected\">#{year}</option>\n" :
"<option selected=\"selected\">#{year}</option>\n" :
"<option>#{year}</option>\n"
)
end
select_html("year", year_options, options[:prefix], options[:include_blank], options[:discard_type])
end
private
def select_html(type, options, prefix = nil, include_blank = false, discard_type = false)
select_html = %(<select name="#{prefix || DEFAULT_PREFIX})
@ -206,7 +206,7 @@ def select_html(type, options, prefix = nil, include_blank = false, discard_type
return select_html
end
def leading_zero_on_single_digits(number)
number > 9 ? number : "0#{number}"
end
@ -217,32 +217,32 @@ class InstanceTag #:nodoc:
def to_date_select_tag(options = {})
defaults = { :discard_type => true }
options = defaults.merge(options)
options_with_prefix = Proc.new { |position| options.update({ :prefix => "#{@object_name}[#{@method_name}(#{position}i)]" }) }
options = defaults.merge(options)
options_with_prefix = Proc.new { |position| options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") }
date = options[:include_blank] ? (value || 0) : (value || Date.today)
date_select = ""
date_select = ""
options[:order] = [:month, :year, :day] if options[:month_before_year] # For backwards compatibility
options[:order] ||= [:year, :month, :day]
position = {:year => 1, :month => 2, :day => 3}
discard = {}
discard[:year] = true if options[:discard_year]
discard[:month] = true if options[:discard_month]
discard[:day] = true if options[:discard_day] or options[:discard_month]
options[:order].each do |param|
date_select << self.send("select_#{param}", date, options_with_prefix.call(position[param])) unless discard[param]
end
return date_select
end
def to_datetime_select_tag(options = {})
defaults = { :discard_type => true }
options = defaults.merge(options)
options_with_prefix = Proc.new { |position| options.update({ :prefix => "#{@object_name}[#{@method_name}(#{position}i)]" }) }
options = defaults.merge(options)
options_with_prefix = Proc.new { |position| options.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") }
datetime = options[:include_blank] ? (value || 0) : (value || Time.now)
datetime_select = select_year(datetime, options_with_prefix.call(1))
@ -250,7 +250,7 @@ def to_datetime_select_tag(options = {})
datetime_select << select_day(datetime, options_with_prefix.call(3)) unless options[:discard_day] || options[:discard_month]
datetime_select << " &mdash; " + select_hour(datetime, options_with_prefix.call(4)) unless options[:discard_hour]
datetime_select << " : " + select_minute(datetime, options_with_prefix.call(5)) unless options[:discard_minute] || options[:discard_hour]
return datetime_select
end
end

@ -8,10 +8,10 @@ module Helpers
# The following is an example of a complete form for a person object that works for both creates and updates built
# with all the form helpers. The <tt>@person</tt> object was assigned by an action on the controller:
# <form action="save_person" method="post">
# Name:
# Name:
# <%= text_field "person", "name", "size" => 20 %>
#
# Password:
# Password:
# <%= password_field "person", "password", "maxsize" => 20 %>
#
# Single?:
@ -26,12 +26,12 @@ module Helpers
# ...is compiled to:
#
# <form action="save_person" method="post">
# Name:
# <input type="text" id="person_name" name="person[name]"
# Name:
# <input type="text" id="person_name" name="person[name]"
# size="20" value="<%= @person.name %>" />
#
# Password:
# <input type="password" id="person_password" name="person[password]"
# Password:
# <input type="password" id="person_password" name="person[password]"
# size="20" maxsize="20" value="<%= @person.password %>" />
#
# Single?:
@ -43,7 +43,7 @@ module Helpers
# </textarea>
#
# <input type="submit" value="Save">
# </form>
# </form>
#
# If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
# used by render_collection_of_partials, the "index" option may come in handy. Example:
@ -51,10 +51,10 @@ module Helpers
# <%= text_field "person", "name", "index" => 1 %>
#
# becomes
#
#
# <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
#
# There's also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
# There's also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
# link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
module FormHelper
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
@ -64,7 +64,7 @@ module FormHelper
# Examples (call, result):
# text_field("post", "title", "size" => 20)
# <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
def text_field(object, method, options = {})
def text_field(object, method, options = {})
InstanceTag.new(object, method, self).to_input_field_tag("text", options)
end
@ -95,12 +95,12 @@ def file_field(object, method, options = {})
def text_area(object, method, options = {})
InstanceTag.new(object, method, self).to_text_area_tag(options)
end
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). It's intended that +method+ returns an integer and if that
# integer is above zero, then the checkbox is checked. Additional options on the input tag can be passed as a
# hash with +options+. The +checked_value+ defaults to 1 while the default +unchecked_value+
# is set to 0 which is convenient for boolean values. Usually unchecked checkboxes don't post anything.
# is set to 0 which is convenient for boolean values. Usually unchecked checkboxes don't post anything.
# We work around this problem by adding a hidden value with the same name as the checkbox.
#
# Example (call, result). Imagine that @post.validated? returns 1:
@ -119,13 +119,13 @@ def check_box(object, method, options = {}, checked_value = "1", unchecked_value
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
# radio button will be checked. Additional options on the input tag can be passed as a
# hash with +options+.
# hash with +options+.
# Example (call, result). Imagine that @post.category returns "rails":
# radio_button("post", "category", "rails")
# radio_button("post", "category", "java")
# <input type="radio" id="post_category" name="post[category] value="rails" checked="checked" />
# <input type="radio" id="post_category" name="post[category] value="java" />
#
#
def radio_button(object, method, tag_value, options = {})
InstanceTag.new(object, method, self).to_radio_button_tag(tag_value, options)
end
@ -135,9 +135,10 @@ class InstanceTag #:nodoc:
include Helpers::TagHelper
attr_reader :method_name, :object_name
DEFAULT_FIELD_OPTIONS = { "size" => 30 } unless const_defined?("DEFAULT_FIELD_OPTIONS")
DEFAULT_TEXT_AREA_OPTIONS = { "wrap" => "virtual", "cols" => 40, "rows" => 20 } unless const_defined?("DEFAULT_TEXT_AREA_OPTIONS")
DEFAULT_FIELD_OPTIONS = { "size" => 30 }.freeze unless const_defined?(:DEFAULT_FIELD_OPTIONS)
DEFAULT_TEXT_AREA_OPTIONS = { "wrap" => "virtual", "cols" => 40, "rows" => 20 }.freeze unless const_defined?(:DEFAULT_TEXT_AREA_OPTIONS)
DEFAULT_DATE_OPTIONS = { :discard_type => true }.freeze unless const_defined?(:DEFAULT_DATE_OPTIONS)
def initialize(object_name, method_name, template_object, local_binding = nil)
@object_name, @method_name = object_name, method_name
@ -146,50 +147,69 @@ def initialize(object_name, method_name, template_object, local_binding = nil)
@auto_index = @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}").id
end
end
def to_input_field_tag(field_type, options = {})
html_options = DEFAULT_FIELD_OPTIONS.merge(options)
html_options.merge!({ "size" => options["maxlength"]}) if options["maxlength"] && !options["size"]
html_options.delete("size") if field_type == "hidden"
html_options.merge!({ "type" => field_type})
html_options.merge!({ "value" => value_before_type_cast }) if options["value"].nil? && field_type != "file"
add_default_name_and_id(html_options)
tag("input", html_options)
options = options.stringify_keys
if field_type == "hidden"
options.delete("size")
else
options["size"] ||= options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"]
end
options["type"] = field_type
options["value"] ||= value_before_type_cast unless field_type == "file"
add_default_name_and_id(options)
tag("input", options)
end
def to_radio_button_tag(tag_value, options={})
html_options = DEFAULT_FIELD_OPTIONS.merge(options)
html_options.merge!({ "checked" => "checked" }) if value == tag_value
html_options.merge!({ "type" => "radio", "value"=> tag_value.to_s })
add_default_name_and_id(html_options)
tag("input", html_options)
def to_radio_button_tag(tag_value, options = {})
options = DEFAULT_FIELD_OPTIONS.merge(options.stringify_keys)
options["type"] = "radio"
options["value"] = tag_value
options["checked"] = "checked" if value == tag_value
add_default_name_and_id(options)
tag("input", options)
end
def to_text_area_tag(options = {})
options = DEFAULT_TEXT_AREA_OPTIONS.merge(options)
options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
add_default_name_and_id(options)
content_tag("textarea", html_escape(value_before_type_cast), options)
end
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options.merge!({ "checked" => "checked" }) if !value.nil? && ((value.is_a?(TrueClass) || value.is_a?(FalseClass)) ? value : value.to_i > 0)
options.merge!({ "type" => "checkbox", "value" => checked_value })
options = options.stringify_keys
options["type"] = "checkbox"
options["value"] = checked_value
checked = case value
when TrueClass, FalseClass
value
when NilClass
false
when Integer
value != 0
else
value.to_i != 0
end
if checked
options["checked"] = "checked"
else
options.delete("checked")
end
add_default_name_and_id(options)
tag("input", options) << tag("input", ({ "name" => options['name'], "type" => "hidden", "value" => unchecked_value }))
tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value)
end
def to_date_tag()
defaults = { "discard_type" => true }
defaults = DEFAULT_DATE_OPTIONS.dup
date = value || Date.today
options = Proc.new { |position| defaults.update({ :prefix => "#{@object_name}[#{@method_name}(#{position}i)]" }) }
options = Proc.new { |position| defaults.merge(:prefix => "#{@object_name}[#{@method_name}(#{position}i)]") }
html_day_select(date, options.call(3)) +
html_month_select(date, options.call(2)) +
html_month_select(date, options.call(2)) +
html_year_select(date, options.call(1))
end
def to_boolean_select_tag(options = {})
options = options.stringify_keys
add_default_name_and_id(options)
tag_text = "<select"
tag_text << tag_options(options)
@ -210,7 +230,7 @@ def value
def value_before_type_cast
unless object.nil?
object.respond_to?(@method_name + "_before_type_cast") ?
object.respond_to?(@method_name + "_before_type_cast") ?
object.send(@method_name + "_before_type_cast") :
object.send(@method_name)
end
@ -218,23 +238,23 @@ def value_before_type_cast
private
def add_default_name_and_id(options)
if options.has_key? "index"
options['name'] = tag_name_with_index(options["index"]) unless options.has_key? "name"
options['id'] = tag_id_with_index(options["index"]) unless options.has_key? "id"
if options.has_key?("index")
options["name"] ||= tag_name_with_index(options["index"])
options["id"] ||= tag_id_with_index(options["index"])
options.delete("index")
elsif @auto_index
options['name'] = tag_name_with_index(@auto_index) unless options.has_key? "name"
options['id'] = tag_id_with_index(@auto_index) unless options.has_key? "id"
options["name"] ||= tag_name_with_index(@auto_index)
options["id"] ||= tag_id_with_index(@auto_index)
else
options['name'] = tag_name unless options.has_key? "name"
options['id'] = tag_id unless options.has_key? "id"
options["name"] ||= tag_name
options["id"] ||= tag_id
end
end
def tag_name
"#{@object_name}[#{@method_name}]"
end
def tag_name_with_index(index)
"#{@object_name}[#{index}][#{@method_name}]"
end

@ -25,7 +25,7 @@ module FormOptionsHelper
# 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.
#
#
# 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.
@ -34,17 +34,17 @@ module FormOptionsHelper
def select(object, method, choices, options = {}, html_options = {})
InstanceTag.new(object, method, self).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).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).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.
#
@ -57,11 +57,11 @@ def time_zone_select(object, method, priority_zones = nil, options = {}, html_op
InstanceTag.new(object, method, self).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
# 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.
# may also be an array of values to be selected when using a multiple select.
#
# Examples (call, result):
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
@ -79,8 +79,8 @@ def time_zone_select(object, method, priority_zones = nil, options = {}, html_op
# 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|
options_for_select = container.inject([]) do |options, element|
if element.respond_to?(:first) && element.respond_to?(:last)
is_selected = ( (selected.respond_to?(:include?) ? selected.include?(element.last) : element.last == selected) )
if is_selected
@ -93,11 +93,11 @@ def options_for_select(container, selected = nil)
options << ((is_selected) ? "<option selected=\"selected\">#{html_escape(element.to_s)}</option>" : "<option>#{html_escape(element.to_s)}</option>")
end
end
options_for_select.join("\n")
end
# Returns a string of option tags that has been compiled by iterating over the +collection+ and assigning the
# Returns a string of option tags that has 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_value+ is specified, the element returning a match on +value_method+ will get the selected option tag.
#
@ -108,7 +108,7 @@ def options_for_select(container, selected = nil)
# 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_value = nil)
options_for_select(
collection.inject([]) { |options, object| options << [ object.send(text_method), object.send(value_method) ] },
collection.inject([]) { |options, object| options << [ object.send(text_method), object.send(value_method) ] },
selected_value
)
end
@ -135,18 +135,18 @@ def options_from_collection_for_select(collection, value_method, text_method, se
#
# with objects of the following classes:
# class Continent
# def initialize(p_name, p_countries) @continent_name = p_name; @countries = p_countries; end
# def continent_name() @continent_name; end
# def countries() @countries; end
# def initialize(p_name, p_countries) @continent_name = p_name; @countries = p_countries; end
# def continent_name() @continent_name; end
# def countries() @countries; end
# end
# class Country
# def initialize(id, name) @id = id; @name = name end
# def country_id() @id; end
# def country_name() @name; end
# def initialize(id, name) @id = id; @name = name end
# def country_id() @id; end
# def country_name() @name; end
# end
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def option_groups_from_collection_for_select(collection, group_method, group_label_method,
def option_groups_from_collection_for_select(collection, group_method, group_label_method,
option_key_method, option_value_method, selected_key = nil)
collection.inject("") do |options_for_select, group|
group_label_string = eval("group.#{group_label_method}")
@ -154,16 +154,16 @@ def option_groups_from_collection_for_select(collection, group_method, group_lab
options_for_select += options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key)
options_for_select += '</optgroup>'
end
end
# Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to
end
# Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to
# have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so
# that they will be listed above the rest of the (long) list.
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
def country_options_for_select(selected = nil, priority_countries = nil)
country_options = ""
if priority_countries
country_options += options_for_select(priority_countries, selected)
country_options += "<option>-------------</option>\n"
@ -264,23 +264,27 @@ class InstanceTag #:nodoc:
include FormOptionsHelper
def to_select_tag(choices, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
content_tag("select", add_blank_option(options_for_select(choices, value), options[:include_blank]), html_options)
end
def to_collection_select_tag(collection, value_method, text_method, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
content_tag(
"select", add_blank_option(options_from_collection_for_select(collection, value_method, text_method, value), options[:include_blank]), html_options
)
end
def to_country_select_tag(priority_countries, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
content_tag("select", add_blank_option(country_options_for_select(value, priority_countries), options[:include_blank]), html_options)
end
def to_time_zone_select_tag(priority_zones, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
content_tag("select",
add_blank_option(

@ -6,21 +6,20 @@ module Helpers
# Provides a number of methods for creating form tags that doesn't rely on conventions with an object assigned to the template like
# FormHelper does. With the FormTagHelper, you provide the names and values yourself.
module FormTagHelper
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
# ActionController::Base#url_for. The method for the form defaults to POST.
#
# Options:
# * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
def form_tag(url_for_options = {}, options = {}, *parameters_for_url)
html_options = { "method" => "post" }.merge(options)
html_options = { "method" => "post" }.merge(options.stringify_keys)
if html_options[:multipart]
html_options["enctype"] = "multipart/form-data"
html_options.delete(:multipart)
end
html_options["action"] = url_for(url_for_options, *parameters_for_url)
tag("form", html_options, true)
end
@ -32,48 +31,49 @@ def end_form_tag
end
def select_tag(name, option_tags = nil, options = {})
content_tag("select", option_tags, { "name" => name, "id" => name }.update(options))
content_tag("select", option_tags, { "name" => name, "id" => name }.update(options.stringify_keys))
end
def text_field_tag(name, value = nil, options = {})
tag("input", {"type" => "text", "name" => name, "id" => name, "value" => value}.update(options))
tag("input", { "type" => "text", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys))
end
def hidden_field_tag(name, value = nil, options = {})
text_field_tag(name, value, options.update("type" => "hidden"))
text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
end
def file_field_tag(name, options = {})
text_field_tag(name, nil, options.update("type" => "file"))
text_field_tag(name, nil, options.stringify_keys.update("type" => "file"))
end
def password_field_tag(name = "password", value = nil, options = {})
text_field_tag(name, value, options.update("type" => "password"))
text_field_tag(name, value, options.stringify_keys.update("type" => "password"))
end
def text_area_tag(name, content = nil, options = {})
if options[:size]
options["cols"], options["rows"] = options[:size].split("x")
options.delete(:size)
options = options.stringify_keys
if options["size"]
options["cols"], options["rows"] = options["size"].split("x")
options.delete("size")
end
content_tag("textarea", content, { "name" => name, "id" => name }.update(options))
content_tag("textarea", content, { "name" => name, "id" => name }.update(options.stringify_keys))
end
def check_box_tag(name, value = "1", checked = false, options = {})
html_options = {"type" => "checkbox", "name" => name, "id" => name, "value" => value}.update(options)
html_options = { "type" => "checkbox", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
html_options["checked"] = "checked" if checked
tag("input", html_options)
end
def radio_button_tag(name, value, checked = false, options = {})
html_options = {"type" => "radio", "name" => name, "id" => name, "value" => value}.update(options)
html_options = { "type" => "radio", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
html_options["checked"] = "checked" if checked
tag("input", html_options)
end
def submit_tag(value = "Save changes", options = {})
tag("input", {"type" => "submit", "name" => "submit", "value" => value}.update(options))
tag("input", { "type" => "submit", "name" => "submit", "value" => value }.update(options.stringify_keys))
end
end
end

@ -7,14 +7,14 @@ module Helpers
module TagHelper
include ERB::Util
# Examples:
# Examples:
# * <tt>tag("br") => <br /></tt>
# * <tt>tag("input", { "type" => "text"}) => <input type="text" /></tt>
def tag(name, options = {}, open = false)
"<#{name}#{tag_options(options)}" + (open ? ">" : " />")
end
# Examples:
# Examples:
# * <tt>content_tag("p", "Hello world!") => <p>Hello world!</p></tt>
# * <tt>content_tag("div", content_tag("p", "Hello world!"), "class" => "strong") => </tt>
# <tt><div class="strong"><p>Hello world!</p></div></tt>
@ -26,7 +26,7 @@ def content_tag(name, content, options = {})
def tag_options(options)
unless options.empty?
" " + options.map { |key, value|
%(#{key}="#{html_escape(value)}")
%(#{key}="#{html_escape(value.to_s)}")
}.sort.join(" ")
end
end

@ -1,7 +1,7 @@
module ActionView
module Helpers #:nodoc:
# Provides a set of methods for working with text strings that can help unburden the level of inline Ruby code in the
# templates. In the example below we iterate over a collection of posts provided to the template and prints each title
# templates. In the example below we iterate over a collection of posts provided to the template and prints each title
# after making sure it doesn't run longer than 20 characters:
# <% for post in @posts %>
# Title: <%= truncate(post.title, 20) %>
@ -29,14 +29,14 @@ def highlight(text, phrase, highlighter = '<strong class="highlight">\1</strong>
if text.nil? || phrase.nil? then return end
text.gsub(/(#{escape_regexp(phrase)})/i, highlighter) unless text.nil?
end
# Extracts an excerpt from the +text+ surrounding the +phrase+ with a number of characters on each side determined
# by +radius+. If the phrase isn't found, nil is returned. Ex:
# by +radius+. If the phrase isn't found, nil is returned. Ex:
# excerpt("hello my world", "my", 3) => "...lo my wo..."
def excerpt(text, phrase, radius = 100, excerpt_string = "...")
if text.nil? || phrase.nil? then return end
phrase = escape_regexp(phrase)
if found_pos = text =~ /(#{phrase})/i
start_pos = [ found_pos - radius, 0 ].max
end_pos = [ found_pos + phrase.length + radius, text.length ].min
@ -58,7 +58,7 @@ def pluralize(count, singular, plural = nil)
plural
elsif Object.const_defined?("Inflector")
Inflector.pluralize(singular)
else
else
singular + "s"
end
end
@ -66,13 +66,13 @@ def pluralize(count, singular, plural = nil)
begin
require "redcloth"
# Returns the text with all the Textile codes turned into HTML-tags.
# Returns the text with all the Textile codes turned into HTML-tags.
# <i>This method is only available if RedCloth can be required</i>.
def textilize(text)
text.empty? ? "" : RedCloth.new(text, [ :hard_breaks ]).to_html
end
# Returns the text with all the Textile codes turned into HTML-tags, but without the regular bounding <p> tag.
# Returns the text with all the Textile codes turned into HTML-tags, but without the regular bounding <p> tag.
# <i>This method is only available if RedCloth can be required</i>.
def textilize_without_paragraph(text)
textiled = textilize(text)
@ -87,7 +87,7 @@ def textilize_without_paragraph(text)
begin
require "bluecloth"
# Returns the text with all the Markdown codes turned into HTML-tags.
# Returns the text with all the Markdown codes turned into HTML-tags.
# <i>This method is only available if BlueCloth can be required</i>.
def markdown(text)
text.empty? ? "" : BlueCloth.new(text).to_html
@ -101,7 +101,7 @@ def markdown(text)
#
# Example:
# auto_link("Go to http://www.rubyonrails.com and say hello to david@loudthinking.com") =>
# Go to <a href="http://www.rubyonrails.com">http://www.rubyonrails.com</a> and
# Go to <a href="http://www.rubyonrails.com">http://www.rubyonrails.com</a> and
# say hello to <a href="mailto:david@loudthinking.com">david@loudthinking.com</a>
def auto_link(text, link = :all)
case link
@ -115,7 +115,7 @@ def auto_link(text, link = :all)
def strip_links(text)
text.gsub(/<a.*>(.*)<\/a>/m, '\1')
end
private
# Returns a version of the text that's safe to use in a regular expression without triggering engine features.
def escape_regexp(text)
@ -133,4 +133,4 @@ def auto_link_email_addresses(text)
end
end
end
end
end

@ -7,26 +7,27 @@ module Helpers
module UrlHelper
# Returns the URL for the set of +options+ provided. See the valid options in link:classes/ActionController/Base.html#M000021
def url_for(options = {}, *parameters_for_method_reference)
if Hash === options then options = { :only_path => true }.merge(options) end
if Hash === options then options = { :only_path => true }.update(options.stringify_keys) end
@controller.send(:url_for, options, *parameters_for_method_reference)
end
# Creates a link tag of the given +name+ using an URL created by the set of +options+. See the valid options in
# link:classes/ActionController/Base.html#M000021. It's also possible to pass a string instead of an options hash to
# get a link tag that just points without consideration. If nil is passed as a name, the link itself will become the name.
# The html_options have a special feature for creating javascript confirm alerts where if you pass :confirm => 'Are you sure?',
# get a link tag that just points without consideration. If nil is passed as a name, the link itself will become the name.
# The html_options have a special feature for creating javascript confirm alerts where if you pass :confirm => 'Are you sure?',
# the link will be guarded with a JS popup asking that question. If the user accepts, the link is processed, otherwise not.
#
# Example:
# link_to "Delete this page", { :action => "destroy", :id => @page.id }, :confirm => "Are you sure?"
def link_to(name, options = {}, html_options = {}, *parameters_for_method_reference)
convert_confirm_option_to_javascript!(html_options) unless html_options.nil?
def link_to(name, options = {}, html_options = nil, *parameters_for_method_reference)
html_options = (html_options || {}).stringify_keys
convert_confirm_option_to_javascript!(html_options)
if options.is_a?(String)
content_tag "a", name || options, (html_options || {}).merge({ "href" => options })
content_tag "a", name || options, (html_options || {}).merge("href" => options)
else
content_tag(
"a", name || url_for(options, *parameters_for_method_reference),
(html_options || {}).merge({ "href" => url_for(options, *parameters_for_method_reference) })
"a", name || url_for(options, *parameters_for_method_reference),
(html_options || {}).merge("href" => url_for(options, *parameters_for_method_reference))
)
end
end
@ -41,7 +42,7 @@ def link_to(name, options = {}, html_options = {}, *parameters_for_method_refere
# * <tt>:border</tt> - Is set to 0 by default
# * <tt>:align</tt> - Sets the alignment, no special features
#
# The +src+ can be supplied as a...
# The +src+ can be supplied as a...
# * full path, like "/my_images/image.gif"
# * file name, like "rss.gif", that gets expanded to "/images/rss.gif"
# * file name without extension, like "logo", that gets expanded to "/images/logo.png"
@ -51,8 +52,9 @@ def link_to(name, options = {}, html_options = {}, *parameters_for_method_refere
# link_image_to "delete", { :action => "destroy" }, :size => "10x10", :confirm => "Are you sure?", "class" => "admin"
def link_image_to(src, options = {}, html_options = {}, *parameters_for_method_reference)
image_options = { "src" => src.include?("/") ? src : "/images/#{src}" }
image_options["src"] = image_options["src"] + ".png" unless image_options["src"].include?(".")
image_options["src"] += ".png" unless image_options["src"].include?(".")
html_options = html_options.stringify_keys
if html_options["alt"]
image_options["alt"] = html_options["alt"]
html_options.delete "alt"
@ -71,7 +73,7 @@ def link_image_to(src, options = {}, html_options = {}, *parameters_for_method_r
else
image_options["border"] = "0"
end
if html_options["align"]
image_options["align"] = html_options["align"]
html_options.delete "align"
@ -82,9 +84,9 @@ def link_image_to(src, options = {}, html_options = {}, *parameters_for_method_r
alias_method :link_to_image, :link_image_to # deprecated name
# Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current
# Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current
# request uri is the same as the link's, in which case only the name is returned (or the
# given block is yielded, if one exists). This is useful for creating link bars where you don't want to link
# given block is yielded, if one exists). This is useful for creating link bars where you don't want to link
# to the page currently being viewed.
def link_to_unless_current(name, options = {}, html_options = {}, *parameters_for_method_reference)
if current_page?(options)
@ -107,8 +109,8 @@ def link_to_unless_current(name, options = {}, html_options = {}, *parameters_fo
# mail_to "me@domain.com", "My email", :encode => "hex" # =>
# <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
def mail_to(email_address, name = nil, html_options = {})
encode = html_options[:encode]
html_options.delete(:encode)
html_options = html_options.stringify_keys
encode = html_options.delete("encode")
string = ''
if encode == 'javascript'
tmp = "document.write('#{content_tag("a", name || email_address, html_options.merge({ "href" => "mailto:"+email_address.to_s }))}');"
@ -137,9 +139,8 @@ def current_page?(options)
private
def convert_confirm_option_to_javascript!(html_options)
if html_options.include?(:confirm)
html_options["onclick"] = "return confirm('#{html_options[:confirm]}');"
html_options.delete(:confirm)
if confirm = html_options.delete("confirm")
html_options["onclick"] = "return confirm('#{confirm}');"
end
end
end

@ -48,7 +48,7 @@ def Post.content_columns() [ Column.new(:string, "title", "Title"), Column.new(:
@controller = Class.new do
def url_for(options, *parameters_for_method_reference)
options[:action]
options[:action] || options["action"]
end
end
@controller = @controller.new

@ -15,7 +15,7 @@ class FormHelperTest < Test::Unit::TestCase
$VERBOSE = old_verbose
def setup
@post = Post.new
@post = Post.new
def @post.errors() Class.new{ def on(field) field == "author_name" end }.new end
def @post.id; 123; end
@ -47,31 +47,29 @@ def test_text_field_with_escapes
end
def test_text_field_with_options
assert_equal(
'<input id="post_title" name="post[title]" size="35" type="text" value="Hello World" />',
text_field("post", "title", "size" => "35")
)
expected = '<input id="post_title" name="post[title]" size="35" type="text" value="Hello World" />'
assert_equal expected, text_field("post", "title", "size" => 35)
assert_equal expected, text_field("post", "title", :size => 35)
end
def test_text_field_assuming_size
assert_equal(
'<input id="post_title" maxlength="35" name="post[title]" size="35" type="text" value="Hello World" />',
text_field("post", "title", "maxlength" => 35)
)
expected = '<input id="post_title" maxlength="35" name="post[title]" size="35" type="text" value="Hello World" />'
assert_equal expected, text_field("post", "title", "maxlength" => 35)
assert_equal expected, text_field("post", "title", :maxlength => 35)
end
def test_check_box
assert_equal(
'<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
check_box("post", "secret")
)
@post.secret = 0
assert_equal(
'<input id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
check_box("post", "secret")
check_box("post", "secret")
)
@post.secret = true
assert_equal(
'<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
@ -81,20 +79,20 @@ def test_check_box
def test_radio_button
assert_equal('<input checked="checked" id="post_title" name="post[title]" size="30" type="radio" value="Hello World" />',
radio_button("post", "title", "Hello World")
radio_button("post", "title", "Hello World")
)
assert_equal('<input id="post_title" name="post[title]" size="30" type="radio" value="Goodbye World" />',
radio_button("post", "title", "Goodbye World")
radio_button("post", "title", "Goodbye World")
)
end
def test_text_area
assert_equal(
'<textarea cols="40" id="post_body" name="post[body]" rows="20" wrap="virtual">Back to the hill and over it again!</textarea>',
text_area("post", "body")
)
end
def test_text_area_with_escapes
@post.body = "Back to <i>the</i> hill and over it again!"
assert_equal(
@ -109,12 +107,11 @@ def test_date_selects
text_area("post", "body")
)
end
def test_explicit_name
assert_equal(
'<input id="post_title" name="dont guess" size="30" type="text" value="Hello World" />', text_field("post", "title", "name" => "dont guess")
)
)
assert_equal(
'<textarea cols="40" id="post_body" name="really!" rows="20" wrap="virtual">Back to the hill and over it again!</textarea>',
text_area("post", "body", "name" => "really!")
@ -123,12 +120,18 @@ def test_explicit_name
'<input checked="checked" id="post_secret" name="i mean it" type="checkbox" value="1" /><input name="i mean it" type="hidden" value="0" />',
check_box("post", "secret", "name" => "i mean it")
)
assert_equal text_field("post", "title", "name" => "dont guess"),
text_field("post", "title", :name => "dont guess")
assert_equal text_area("post", "body", "name" => "really!"),
text_area("post", "body", :name => "really!")
assert_equal check_box("post", "secret", "name" => "i mean it"),
check_box("post", "secret", :name => "i mean it")
end
def test_explicit_id
assert_equal(
'<input id="dont guess" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title", "id" => "dont guess")
)
)
assert_equal(
'<textarea cols="40" id="really!" name="post[body]" rows="20" wrap="virtual">Back to the hill and over it again!</textarea>',
text_area("post", "body", "id" => "really!")
@ -137,6 +140,12 @@ def test_explicit_id
'<input checked="checked" id="i mean it" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />',
check_box("post", "secret", "id" => "i mean it")
)
assert_equal text_field("post", "title", "id" => "dont guess"),
text_field("post", "title", :id => "dont guess")
assert_equal text_area("post", "body", "id" => "really!"),
text_area("post", "body", :id => "really!")
assert_equal check_box("post", "secret", "id" => "i mean it"),
check_box("post", "secret", :id => "i mean it")
end
def test_auto_index
@ -159,7 +168,5 @@ def test_auto_index
assert_equal("<input id=\"post_#{pid}_title\" name=\"post[#{pid}][title]\" size=\"30\" type=\"radio\" value=\"Goodbye World\" />",
radio_button("post[]", "title", "Goodbye World")
)
end
end

@ -46,7 +46,7 @@ def test_collection_options
)
end
def test_collection_options_with_preselected_value
@posts = [
Post.new("<Abe> went home", "<Abe>", "To a little house", "shh!"),
@ -75,14 +75,14 @@ def test_collection_options_with_preselected_value_array
def test_array_options_for_select
assert_equal(
"<option>&lt;Denmark&gt;</option>\n<option>USA</option>\n<option>Sweden</option>",
"<option>&lt;Denmark&gt;</option>\n<option>USA</option>\n<option>Sweden</option>",
options_for_select([ "<Denmark>", "USA", "Sweden" ])
)
end
def test_array_options_for_select_with_selection
assert_equal(
"<option>Denmark</option>\n<option selected=\"selected\">&lt;USA&gt;</option>\n<option>Sweden</option>",
"<option>Denmark</option>\n<option selected=\"selected\">&lt;USA&gt;</option>\n<option>Sweden</option>",
options_for_select([ "Denmark", "<USA>", "Sweden" ], "<USA>")
)
end
@ -96,21 +96,21 @@ def test_array_options_for_select_with_selection_array
def test_hash_options_for_select
assert_equal(
"<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\">$</option>",
"<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\">$</option>",
options_for_select({ "$" => "Dollar", "<DKR>" => "<Kroner>" })
)
end
def test_hash_options_for_select_with_selection
assert_equal(
"<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
"<option value=\"&lt;Kroner&gt;\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
options_for_select({ "$" => "Dollar", "<DKR>" => "<Kroner>" }, "Dollar")
)
end
def test_hash_options_for_select_with_selection
assert_equal(
"<option value=\"&lt;Kroner&gt;\" selected=\"selected\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
"<option value=\"&lt;Kroner&gt;\" selected=\"selected\">&lt;DKR&gt;</option>\n<option value=\"Dollar\" selected=\"selected\">$</option>",
options_for_select({ "$" => "Dollar", "<DKR>" => "<Kroner>" }, [ "Dollar", "<Kroner>" ])
)
end
@ -197,7 +197,7 @@ def test_select
@post = Post.new
@post.category = "<mus>"
assert_equal(
"<select id=\"post_category\" name=\"post[category]\"><option>abe</option>\n<option selected=\"selected\">&lt;mus&gt;</option>\n<option>hest</option></select>",
"<select id=\"post_category\" name=\"post[category]\"><option>abe</option>\n<option selected=\"selected\">&lt;mus&gt;</option>\n<option>hest</option></select>",
select("post", "category", %w( abe <mus> hest))
)
end
@ -206,7 +206,7 @@ def test_select_with_blank
@post = Post.new
@post.category = "<mus>"
assert_equal(
"<select id=\"post_category\" name=\"post[category]\"><option></option>\n<option>abe</option>\n<option selected=\"selected\">&lt;mus&gt;</option>\n<option>hest</option></select>",
"<select id=\"post_category\" name=\"post[category]\"><option></option>\n<option>abe</option>\n<option selected=\"selected\">&lt;mus&gt;</option>\n<option>hest</option></select>",
select("post", "category", %w( abe <mus> hest), :include_blank => true)
)
end
@ -222,7 +222,7 @@ def test_collection_select
@post.author_name = "Babe"
assert_equal(
"<select id=\"post_author_name\" name=\"post[author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
"<select id=\"post_author_name\" name=\"post[author_name]\"><option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
collection_select("post", "author_name", @posts, "author_name", "author_name")
)
end
@ -238,7 +238,7 @@ def test_collection_select_with_blank_and_style
@post.author_name = "Babe"
assert_equal(
"<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
"<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option></option>\n<option value=\"&lt;Abe&gt;\">&lt;Abe&gt;</option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>",
collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true }, "style" => "width: 200px")
)
end
@ -291,6 +291,8 @@ def test_time_zone_select_with_style
"<option value=\"E\">E</option>" +
"</select>",
html
assert_equal html, time_zone_select("firm", "time_zone", nil, {},
:style => "color: red")
end
def test_time_zone_select_with_blank_and_style
@ -306,6 +308,8 @@ def test_time_zone_select_with_blank_and_style
"<option value=\"E\">E</option>" +
"</select>",
html
assert_equal html, time_zone_select("firm", "time_zone", nil,
{ :include_blank => true }, :style => "color: red")
end
def test_time_zone_select_with_priority_zones

@ -11,12 +11,13 @@ class FormTagHelperTest < Test::Unit::TestCase
%(hidden_field_tag "id", 3) => %(<input id="id" name="id" type="hidden" value="3" />),
%(password_field_tag) => %(<input id="password" name="password" type="password" value="" />),
%(text_area_tag("body", "hello world", :size => "20x40")) => %(<textarea cols="20" id="body" name="body" rows="40">hello world</textarea>),
%(text_area_tag("body", "hello world", "size" => "20x40")) => %(<textarea cols="20" id="body" name="body" rows="40">hello world</textarea>),
%(check_box_tag("admin")) => %(<input id="admin" name="admin" type="checkbox" value="1" />),
%(radio_button_tag("people", "david")) => %(<input id="people" name="people" type="radio" value="david" />),
%(select_tag("people", "<option>david</option>")) => %(<select id="people" name="people"><option>david</option></select>),
}
def test_tags
MethodToTag.each { |method, tag| assert_equal(eval(method), tag) }
MethodToTag.each { |method, tag| assert_equal(tag, eval(method)) }
end
end
end

@ -8,23 +8,26 @@ class TagHelperTest < Test::Unit::TestCase
def test_tag
assert_equal "<p class=\"show\" />", tag("p", "class" => "show")
assert_equal tag("p", "class" => "show"), tag("p", :class => "show")
end
def test_content_tag
assert_equal "<a href=\"create\">Create</a>", content_tag("a", "Create", "href" => "create")
assert_equal content_tag("a", "Create", "href" => "create"),
content_tag("a", "Create", :href => "create")
end
def test_mail_to_with_javascript
assert_equal "<script type=\"text/javascript\" language=\"javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
assert_equal "<script type=\"text/javascript\" language=\"javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript")
end
def test_mail_to_with_hex
assert_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex")
assert_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex")
end
def test_mail_to
assert_equal "<a href=\"mailto:me@domain.com\">My email</a>", mail_to("me@domain.com", "My email")
assert_equal "<a href=\"mailto:me@domain.com\">My email</a>", mail_to("me@domain.com", "My email")
end
# FIXME: Test form tag
end
end

@ -8,11 +8,11 @@ def test_truncate
assert_equal "Hello World!", truncate("Hello World!", 12)
assert_equal "Hello Worl...", truncate("Hello World!!", 12)
end
def test_strip_links
assert_equal "on my mind", strip_links("<a href='almost'>on my mind</a>")
end
def test_highlighter
assert_equal(
"This is a <strong class=\"highlight\">beautiful</strong> morning",
@ -29,7 +29,7 @@ def test_highlighter
highlight("This is a beautiful morning, but also a beautiful day", "beautiful", '<b>\1</b>')
)
end
def test_highlighter_with_regexp
assert_equal(
"This is a <strong class=\"highlight\">beautiful!</strong> morning",
@ -46,7 +46,7 @@ def test_highlighter_with_regexp
highlight("This is a beautiful? morning", "beautiful? morning")
)
end
def test_excerpt
assert_equal("...is a beautiful morni...", excerpt("This is a beautiful morning", "beautiful", 5))
assert_equal("This is a...", excerpt("This is a beautiful morning", "this", 5))
@ -54,16 +54,16 @@ def test_excerpt
assert_equal("...iful morning", excerpt("This is a beautiful morning", "morning", 5))
assert_nil excerpt("This is a beautiful morning", "day")
end
def test_pluralization
assert_equal("1 count", pluralize(1, "count"))
assert_equal("2 counts", pluralize(2, "count"))
end
def test_auto_linking
assert_equal %(hello <a href="mailto:david@loudthinking.com">david@loudthinking.com</a>), auto_link("hello david@loudthinking.com", :email_addresses)
assert_equal %(Go to <a href="http://www.rubyonrails.com">http://www.rubyonrails.com</a>), auto_link("Go to http://www.rubyonrails.com", :urls)
assert_equal %(Go to http://www.rubyonrails.com), auto_link("Go to http://www.rubyonrails.com", :email_addresses)
assert_equal %(Go to <a href="http://www.rubyonrails.com">http://www.rubyonrails.com</a> and say hello to <a href="mailto:david@loudthinking.com">david@loudthinking.com</a>), auto_link("Go to http://www.rubyonrails.com and say hello to david@loudthinking.com")
end
end
end

@ -21,26 +21,31 @@ def url_for(options, *parameters_for_method_reference)
def test_link_tag_with_straight_url
assert_equal "<a href=\"http://www.world.com\">Hello</a>", link_to("Hello", "http://www.world.com")
end
def test_link_tag_with_javascript_confirm
assert_equal(
"<a href=\"http://www.world.com\" onclick=\"return confirm('Are you sure?');\">Hello</a>",
"<a href=\"http://www.world.com\" onclick=\"return confirm('Are you sure?');\">Hello</a>",
link_to("Hello", "http://www.world.com", :confirm => "Are you sure?")
)
end
def test_link_to_image
assert_equal(
"<a href=\"http://www.world.com\"><img alt=\"Rss\" border=\"0\" height=\"45\" src=\"/images/rss.png\" width=\"30\" /></a>",
"<a href=\"http://www.world.com\"><img alt=\"Rss\" border=\"0\" height=\"45\" src=\"/images/rss.png\" width=\"30\" /></a>",
link_to_image("rss", "http://www.world.com", "size" => "30x45")
)
assert_equal(
"<a class=\"admin\" href=\"http://www.world.com\"><img alt=\"Feed\" border=\"0\" height=\"45\" src=\"/images/rss.gif\" width=\"30\" /></a>",
"<a class=\"admin\" href=\"http://www.world.com\"><img alt=\"Feed\" border=\"0\" height=\"45\" src=\"/images/rss.gif\" width=\"30\" /></a>",
link_to_image("rss.gif", "http://www.world.com", "size" => "30x45", "alt" => "Feed", "class" => "admin")
)
assert_equal link_to_image("rss", "http://www.world.com", "size" => "30x45"),
link_to_image("rss", "http://www.world.com", :size => "30x45")
assert_equal link_to_image("rss.gif", "http://www.world.com", "size" => "30x45", "alt" => "Feed", "class" => "admin"),
link_to_image("rss.gif", "http://www.world.com", :size => "30x45", :alt => "Feed", :class => "admin")
end
def test_link_unless_current
@request = RequestMock.new("http://www.world.com")
assert_equal "Showing", link_to_unless_current("Showing", :action => "show", :controller => "weblog")
@ -55,11 +60,13 @@ def test_mail_to
assert_equal "<a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>", mail_to("david@loudthinking.com")
assert_equal "<a href=\"mailto:david@loudthinking.com\">David Heinemeier Hansson</a>", mail_to("david@loudthinking.com", "David Heinemeier Hansson")
assert_equal(
"<a class=\"admin\" href=\"mailto:david@loudthinking.com\">David Heinemeier Hansson</a>",
"<a class=\"admin\" href=\"mailto:david@loudthinking.com\">David Heinemeier Hansson</a>",
mail_to("david@loudthinking.com", "David Heinemeier Hansson", "class" => "admin")
)
assert_equal mail_to("david@loudthinking.com", "David Heinemeier Hansson", "class" => "admin"),
mail_to("david@loudthinking.com", "David Heinemeier Hansson", :class => "admin")
end
def test_link_with_nil_html_options
assert_equal "<a href=\"http://www.world.com\">Hello</a>", link_to("Hello", {:action => 'myaction'}, nil)
end