From dfac1cea3d851000116a23ab14c2b1ae981f7a12 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 6 Mar 2005 11:50:41 +0000 Subject: 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 --- actionpack/CHANGELOG | 2 + .../action_view/helpers/active_record_helper.rb | 28 ++--- .../lib/action_view/helpers/asset_tag_helper.rb | 12 +- actionpack/lib/action_view/helpers/date_helper.rb | 52 ++++----- actionpack/lib/action_view/helpers/form_helper.rb | 128 ++++++++++++--------- .../lib/action_view/helpers/form_options_helper.rb | 50 ++++---- .../lib/action_view/helpers/form_tag_helper.rb | 36 +++--- actionpack/lib/action_view/helpers/tag_helper.rb | 8 +- actionpack/lib/action_view/helpers/text_helper.rb | 22 ++-- actionpack/lib/action_view/helpers/url_helper.rb | 39 ++++--- .../test/template/active_record_helper_test.rb | 2 +- actionpack/test/template/form_helper_test.rb | 57 +++++---- .../test/template/form_options_helper_test.rb | 24 ++-- actionpack/test/template/form_tag_helper_test.rb | 5 +- actionpack/test/template/tag_helper_test.rb | 13 ++- actionpack/test/template/text_helper_test.rb | 14 +-- actionpack/test/template/url_helper_test.rb | 23 ++-- 17 files changed, 283 insertions(+), 232 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index b901012044..18ea6b9b4c 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -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) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 45a5aff8bd..090ca33cef 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -15,7 +15,7 @@ module ActionView 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") => # def input(record_name, method) InstanceTag.new(record_name, method, self).to_tag @@ -41,7 +41,7 @@ module ActionView # 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)}
" }) => # #
@@ -56,16 +56,17 @@ module ActionView # 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 = %(#{id_field}) + all_input_tags(record, record_name, options) yield formtag if block_given? formtag + %(
) @@ -73,7 +74,7 @@ module ActionView # 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 @@ module ActionView "
#{prepend_text + (errors.is_a?(Array) ? errors.first : errors) + append_text}
" 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 object_name. This div can be tailored by the following options: # # * header_tag - Used for the header of the error div (default: h2) # * id - The id of the error div (default: errorExplanation) # * class - 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 @@ module ActionView ) end end - + private def all_input_tags(record, record_name, options) input_block = options[:input_block] || default_input_block @@ -116,7 +118,7 @@ module ActionView def default_input_block Proc.new { |record, column| "


#{input(record, column.name)}

" } - end + end end class InstanceTag #:nodoc: diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 33914b2144..2f3bd71d5a 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -10,15 +10,15 @@ module ActionView # either be :rss (default) or :atom and the +options+ follow the url_for style of declaring a link target. # # Examples: - # auto_discovery_link_tag # => + # auto_discovery_link_tag # => # - # auto_discovery_link_tag(:atom) # => + # auto_discovery_link_tag(:atom) # => # - # auto_discovery_link_tag(:rss, :action => "feed") # => + # auto_discovery_link_tag(:rss, :action => "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 @@ module ActionView 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 @@ module ActionView # # 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) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 03a34c93f1..7ec24f2428 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -5,7 +5,7 @@ module ActionView # 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: # - # * :prefix - overwrites the default prefix of "date" used for the select names. So specifying "birthday" would give + # * :prefix - 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. # * :include_blank - set to true if it should be possible to set an empty date. # * :discard_type - 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 ActionView 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 @@ module ActionView else "#{(distance_in_minutes / 1440).round} days" end end - + # Like distance_of_time_in_words, but where to_time is fixed to Time.now. def distance_of_time_in_words_to_now(from_time) distance_of_time_in_words(from_time, Time.now) @@ -48,7 +48,7 @@ module ActionView # # 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 @@ module ActionView 0.upto(59) do |minute| minute_options << ((datetime.kind_of?(Fixnum) ? datetime : datetime.min) == minute ? - "\n" : + "\n" : "\n" ) end @@ -122,7 +122,7 @@ module ActionView 0.upto(23) do |hour| hour_options << ((datetime.kind_of?(Fixnum) ? datetime : datetime.hour) == hour ? - "\n" : + "\n" : "\n" ) end @@ -137,18 +137,18 @@ module ActionView 1.upto(31) do |day| day_options << ((date.kind_of?(Fixnum) ? date : date.day) == day ? - "\n" : + "\n" : "\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 :use_month_numbers key in +options+ to true for this to happen. If you want both numbers and names, + # set the :use_month_numbers key in +options+ to true for this to happen. If you want both numbers and names, # set the :add_month_numbers key in +options+ to true. Examples: # # select_month(Date.today) # Will use keys like "January", "March" @@ -158,7 +158,7 @@ module ActionView 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 @@ module ActionView end month_options << ((date.kind_of?(Fixnum) ? date : date.month) == month_number ? - %(\n) : + %(\n) : %(\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 :start_year and :end_year keys in the +options+. The date can also be substituted + # can be changed using the :start_year and :end_year keys in the +options+. The date 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 @@ module ActionView (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 ? - "\n" : + "\n" : "\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 = %( # - # Password: - # # # Single?: @@ -43,7 +43,7 @@ module ActionView # # # - # + # # # 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 ActionView # <%= text_field "person", "name", "index" => 1 %> # # becomes - # + # # # - # 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 ActionView # Examples (call, result): # text_field("post", "title", "size" => 20) # - 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 @@ module ActionView 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 @@ module ActionView # 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") # # - # + # 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 @@ module ActionView 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 @@ module ActionView @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) - 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) - end - + 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 = {}) + 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 = "#{html_escape(element.to_s)}" : "") 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 @@ module ActionView # 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 @@ module ActionView # # 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 @@ module ActionView options_for_select += options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key) options_for_select += '' 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 += "\n" @@ -264,23 +264,27 @@ module ActionView 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( diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index a5b18fe7f0..60e3bbb67b 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -6,21 +6,20 @@ module ActionView # 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 url_for_options just like + # Starts a form tag that points the action to an url configured with url_for_options just like # ActionController::Base#url_for. The method for the form defaults to POST. # # Options: # * :multipart - 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 @@ module ActionView 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 diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 5ebfecb8bd..fa74e3f4ee 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -7,14 +7,14 @@ module ActionView module TagHelper include ERB::Util - # Examples: + # Examples: # * tag("br") =>
# * tag("input", { "type" => "text"}) => def tag(name, options = {}, open = false) "<#{name}#{tag_options(options)}" + (open ? ">" : " />") end - - # Examples: + + # Examples: # * content_tag("p", "Hello world!") =>

Hello world!

# * content_tag("div", content_tag("p", "Hello world!"), "class" => "strong") => #

Hello world!

@@ -26,7 +26,7 @@ module ActionView 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 diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 26a8b74faa..1c67e3efb4 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -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 @@ module ActionView 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 @@ module ActionView plural elsif Object.const_defined?("Inflector") Inflector.pluralize(singular) - else + else singular + "s" end end @@ -66,13 +66,13 @@ module ActionView 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. # This method is only available if RedCloth can be required. 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

tag. + # Returns the text with all the Textile codes turned into HTML-tags, but without the regular bounding

tag. # This method is only available if RedCloth can be required. def textilize_without_paragraph(text) textiled = textilize(text) @@ -87,7 +87,7 @@ module ActionView 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. # This method is only available if BlueCloth can be required. def markdown(text) text.empty? ? "" : BlueCloth.new(text).to_html @@ -101,7 +101,7 @@ module ActionView # # Example: # auto_link("Go to http://www.rubyonrails.com and say hello to david@loudthinking.com") => - # Go to http://www.rubyonrails.com and + # Go to http://www.rubyonrails.com and # say hello to david@loudthinking.com def auto_link(text, link = :all) case link @@ -115,7 +115,7 @@ module ActionView def strip_links(text) text.gsub(/(.*)<\/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 @@ module ActionView end end end -end \ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 951760b3ba..901374d877 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -7,26 +7,27 @@ module ActionView 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 @@ module ActionView # * :border - Is set to 0 by default # * :align - 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 @@ module ActionView # 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 @@ module ActionView else image_options["border"] = "0" end - + if html_options["align"] image_options["align"] = html_options["align"] html_options.delete "align" @@ -82,9 +84,9 @@ module ActionView 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 @@ module ActionView # mail_to "me@domain.com", "My email", :encode => "hex" # => # My email 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 @@ module ActionView 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 diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index e650b4f0b8..67001082b8 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -48,7 +48,7 @@ class ActiveRecordHelperTest < Test::Unit::TestCase @controller = Class.new do def url_for(options, *parameters_for_method_reference) - options[:action] + options[:action] || options["action"] end end @controller = @controller.new diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 6b1deb575e..7f7f473564 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -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 @@ class FormHelperTest < Test::Unit::TestCase end def test_text_field_with_options - assert_equal( - '', - text_field("post", "title", "size" => "35") - ) + expected = '' + 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( - '', - text_field("post", "title", "maxlength" => 35) - ) + expected = '' + 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( '', check_box("post", "secret") ) - + @post.secret = 0 assert_equal( '', - check_box("post", "secret") + check_box("post", "secret") ) - + @post.secret = true assert_equal( '', @@ -81,20 +79,20 @@ class FormHelperTest < Test::Unit::TestCase def test_radio_button assert_equal('', - radio_button("post", "title", "Hello World") + radio_button("post", "title", "Hello World") ) assert_equal('', - radio_button("post", "title", "Goodbye World") + radio_button("post", "title", "Goodbye World") ) end - + def test_text_area assert_equal( '', text_area("post", "body") ) end - + def test_text_area_with_escapes @post.body = "Back to the hill and over it again!" assert_equal( @@ -109,12 +107,11 @@ class FormHelperTest < Test::Unit::TestCase text_area("post", "body") ) end - - + def test_explicit_name assert_equal( '', text_field("post", "title", "name" => "dont guess") - ) + ) assert_equal( '', text_area("post", "body", "name" => "really!") @@ -123,12 +120,18 @@ class FormHelperTest < Test::Unit::TestCase '', 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( '', text_field("post", "title", "id" => "dont guess") - ) + ) assert_equal( '', text_area("post", "body", "id" => "really!") @@ -137,6 +140,12 @@ class FormHelperTest < Test::Unit::TestCase '', 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 @@ class FormHelperTest < Test::Unit::TestCase assert_equal("", radio_button("post[]", "title", "Goodbye World") ) - end - end diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 4203ab7079..a94f81a728 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -46,7 +46,7 @@ class FormOptionsHelperTest < Test::Unit::TestCase ) end - + def test_collection_options_with_preselected_value @posts = [ Post.new(" went home", "", "To a little house", "shh!"), @@ -75,14 +75,14 @@ class FormOptionsHelperTest < Test::Unit::TestCase def test_array_options_for_select assert_equal( - "\n\n", + "\n\n", options_for_select([ "", "USA", "Sweden" ]) ) end def test_array_options_for_select_with_selection assert_equal( - "\n\n", + "\n\n", options_for_select([ "Denmark", "", "Sweden" ], "") ) end @@ -96,21 +96,21 @@ class FormOptionsHelperTest < Test::Unit::TestCase def test_hash_options_for_select assert_equal( - "\n", + "\n", options_for_select({ "$" => "Dollar", "" => "" }) ) end def test_hash_options_for_select_with_selection assert_equal( - "\n", + "\n", options_for_select({ "$" => "Dollar", "" => "" }, "Dollar") ) end def test_hash_options_for_select_with_selection assert_equal( - "\n", + "\n", options_for_select({ "$" => "Dollar", "" => "" }, [ "Dollar", "" ]) ) end @@ -197,7 +197,7 @@ class FormOptionsHelperTest < Test::Unit::TestCase @post = Post.new @post.category = "" assert_equal( - "", + "", select("post", "category", %w( abe hest)) ) end @@ -206,7 +206,7 @@ class FormOptionsHelperTest < Test::Unit::TestCase @post = Post.new @post.category = "" assert_equal( - "", + "", select("post", "category", %w( abe hest), :include_blank => true) ) end @@ -222,7 +222,7 @@ class FormOptionsHelperTest < Test::Unit::TestCase @post.author_name = "Babe" assert_equal( - "", + "", collection_select("post", "author_name", @posts, "author_name", "author_name") ) end @@ -238,7 +238,7 @@ class FormOptionsHelperTest < Test::Unit::TestCase @post.author_name = "Babe" assert_equal( - "", + "", collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true }, "style" => "width: 200px") ) end @@ -291,6 +291,8 @@ class FormOptionsHelperTest < Test::Unit::TestCase "" + "", 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 @@ class FormOptionsHelperTest < Test::Unit::TestCase "" + "", 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 diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 6036dd4b50..8fd6300b82 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -11,12 +11,13 @@ class FormTagHelperTest < Test::Unit::TestCase %(hidden_field_tag "id", 3) => %(), %(password_field_tag) => %(), %(text_area_tag("body", "hello world", :size => "20x40")) => %(), + %(text_area_tag("body", "hello world", "size" => "20x40")) => %(), %(check_box_tag("admin")) => %(), %(radio_button_tag("people", "david")) => %(), %(select_tag("people", "")) => %(), } def test_tags - MethodToTag.each { |method, tag| assert_equal(eval(method), tag) } + MethodToTag.each { |method, tag| assert_equal(tag, eval(method)) } end -end \ No newline at end of file +end diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb index 2db1bac238..9d8d2644e8 100644 --- a/actionpack/test/template/tag_helper_test.rb +++ b/actionpack/test/template/tag_helper_test.rb @@ -8,23 +8,26 @@ class TagHelperTest < Test::Unit::TestCase def test_tag assert_equal "

", tag("p", "class" => "show") + assert_equal tag("p", "class" => "show"), tag("p", :class => "show") end - + def test_content_tag assert_equal "Create", 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 "", mail_to("me@domain.com", "My email", :encode => "javascript") + assert_equal "", mail_to("me@domain.com", "My email", :encode => "javascript") end def test_mail_to_with_hex - assert_equal "My email", mail_to("me@domain.com", "My email", :encode => "hex") + assert_equal "My email", mail_to("me@domain.com", "My email", :encode => "hex") end def test_mail_to - assert_equal "My email", mail_to("me@domain.com", "My email") + assert_equal "My email", mail_to("me@domain.com", "My email") end # FIXME: Test form tag -end \ No newline at end of file +end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 2082f4aae8..aba0d99699 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -8,11 +8,11 @@ class TextHelperTest < Test::Unit::TestCase 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("on my mind") end - + def test_highlighter assert_equal( "This is a beautiful morning", @@ -29,7 +29,7 @@ class TextHelperTest < Test::Unit::TestCase highlight("This is a beautiful morning, but also a beautiful day", "beautiful", '\1') ) end - + def test_highlighter_with_regexp assert_equal( "This is a beautiful! morning", @@ -46,7 +46,7 @@ class TextHelperTest < Test::Unit::TestCase 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 @@ class TextHelperTest < Test::Unit::TestCase 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 david@loudthinking.com), auto_link("hello david@loudthinking.com", :email_addresses) assert_equal %(Go to http://www.rubyonrails.com), 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 http://www.rubyonrails.com and say hello to david@loudthinking.com), auto_link("Go to http://www.rubyonrails.com and say hello to david@loudthinking.com") end -end \ No newline at end of file +end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 6e94d98ebe..379db0b9b7 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -21,26 +21,31 @@ class UrlHelperTest < Test::Unit::TestCase def test_link_tag_with_straight_url assert_equal "Hello", link_to("Hello", "http://www.world.com") end - + def test_link_tag_with_javascript_confirm assert_equal( - "Hello", + "Hello", link_to("Hello", "http://www.world.com", :confirm => "Are you sure?") ) end - + def test_link_to_image assert_equal( - "\"Rss\"", + "\"Rss\"", link_to_image("rss", "http://www.world.com", "size" => "30x45") ) assert_equal( - "\"Feed\"", + "\"Feed\"", 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 @@ class UrlHelperTest < Test::Unit::TestCase assert_equal "david@loudthinking.com", mail_to("david@loudthinking.com") assert_equal "David Heinemeier Hansson", mail_to("david@loudthinking.com", "David Heinemeier Hansson") assert_equal( - "David Heinemeier Hansson", + "David Heinemeier Hansson", 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 "Hello", link_to("Hello", {:action => 'myaction'}, nil) end -- cgit v1.2.3