diff options
Diffstat (limited to 'actionpack/lib/action_view/helpers')
21 files changed, 158 insertions, 214 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb index 1bc5c9e003..fae2e4fc1c 100644 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_paths.rb @@ -1,78 +1,7 @@ -require 'active_support/core_ext/file' +ActiveSupport::Deprecation.warn "ActionView::Helpers::AssetPaths is deprecated. Please use ActionView::AssetPaths instead." module ActionView module Helpers - - class AssetPaths #:nodoc: - attr_reader :config, :controller - - def initialize(config, controller) - @config = config - @controller = controller - end - - # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched. - # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL - # roots. Rewrite the asset path for cache-busting asset ids. Include - # asset host, if configured, with the correct request protocol. - def compute_public_path(source, dir, ext = nil, include_host = true) - source = source.to_s - return source if is_uri?(source) - - source = rewrite_extension(source, dir, ext) if ext - source = rewrite_asset_path(source, dir) - - if controller && include_host - has_request = controller.respond_to?(:request) - source = rewrite_host_and_protocol(source, has_request) - end - - source - end - - def is_uri?(path) - path =~ %r{^[-a-z]+://|^cid:|^//} - end - - private - - def rewrite_extension(source, dir, ext) - raise NotImplementedError - end - - def rewrite_asset_path(source, path = nil) - raise NotImplementedError - end - - def rewrite_host_and_protocol(source, has_request) - host = compute_asset_host(source) - if has_request && host && !is_uri?(host) - host = "#{controller.request.protocol}#{host}" - end - "#{host}#{source}" - end - - # Pick an asset host for this source. Returns +nil+ if no host is set, - # the host if no wildcard is set, the host interpolated with the - # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4), - # or the value returned from invoking call on an object responding to call - # (proc or otherwise). - def compute_asset_host(source) - if host = config.asset_host - if host.respond_to?(:call) - case host.is_a?(Proc) ? host.arity : host.method(:call).arity - when 2 - request = controller.respond_to?(:request) && controller.request - host.call(source, request) - else - host.call(source) - end - else - (host =~ /%d/) ? host % (source.hash % 4) : host - end - end - end - end - + AssetPaths = ::ActionView::AssetPaths end end
\ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 7970176d37..509c29844a 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -1,6 +1,7 @@ require 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers' require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers' require 'action_view/helpers/asset_tag_helpers/asset_paths' +require 'action_view/helpers/tag_helper' module ActionView # = Action View Asset Tag Helpers @@ -191,6 +192,7 @@ module ActionView # RewriteEngine On # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L] module AssetTagHelper + include TagHelper include JavascriptTagHelpers include StylesheetTagHelpers # Returns a link tag that browsers and news readers can use to auto-detect @@ -347,7 +349,7 @@ module ActionView src = options[:src] = path_to_image(source) unless src =~ /^cid:/ - options[:alt] = options.fetch(:alt){ File.basename(src, '.*').capitalize } + options[:alt] = options.fetch(:alt){ image_alt(src) } end if size = options.delete(:size) @@ -362,6 +364,10 @@ module ActionView tag("img", options) end + def image_alt(src) + File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize + end + # Returns an html video tag for the +sources+. If +sources+ is a string, # a single video tag will be returned. If +sources+ is an array, a video # tag with nested source tags for each source will be returned. The diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb index e4662a2919..3c05173a1b 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb @@ -60,12 +60,16 @@ module ActionView private - def path_to_asset(source, include_host = true) - asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host) + def path_to_asset(source, include_host = true, protocol = nil) + asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host, protocol) + end + + def path_to_asset_source(source) + asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension) end def compute_paths(*args) - expand_sources(*args).collect { |source| asset_paths.compute_public_path(source, asset_name.pluralize, extension, false) } + expand_sources(*args).collect { |source| path_to_asset_source(source) } end def expand_sources(sources, recursive) @@ -92,7 +96,7 @@ module ActionView def ensure_sources!(sources) sources.each do |source| - asset_file_path!(path_to_asset(source, false)) + asset_file_path!(path_to_asset_source(source)) end end @@ -123,19 +127,14 @@ module ActionView # Set mtime to the latest of the combined files to allow for # consistent ETag without a shared filesystem. - mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max + mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max File.utime(mt, mt, joined_asset_path) end - def asset_file_path(path) - File.join(config.assets_dir, path.split('?').first) - end - - def asset_file_path!(path, error_if_file_is_uri = false) - if asset_paths.is_uri?(path) + def asset_file_path!(absolute_path, error_if_file_is_uri = false) + if asset_paths.is_uri?(absolute_path) raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri else - absolute_path = asset_file_path(path) raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path) return absolute_path end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index cd0f8c8878..8b35aa8896 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -1,11 +1,11 @@ +require 'thread' require 'active_support/core_ext/file' -require 'action_view/helpers/asset_paths' module ActionView module Helpers module AssetTagHelper - class AssetPaths < ActionView::Helpers::AssetPaths #:nodoc: + class AssetPaths < ::ActionView::AssetPaths #:nodoc: # You can enable or disable the asset tag ids cache. # With the cache enabled, the asset tag helper methods will make fewer # expensive file system calls (the default implementation checks the file @@ -85,17 +85,8 @@ module ActionView end end end - - def rewrite_relative_url_root(source, relative_url_root) - relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source - end - - def rewrite_host_and_protocol(source, has_request) - source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request - super(source, has_request) - end end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index e4f11c9bc7..8c25d38bbd 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -16,7 +16,8 @@ module ActionView end def asset_tag(source, options) - tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source)) }.merge(options), false, false) + # We force the :request protocol here to avoid a double-download bug in IE7 and IE8 + tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source, true, :request)) }.merge(options), false, false) end def custom_dir @@ -52,7 +53,7 @@ module ActionView # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs). # Full paths from the document root will be passed through. # Used internally by +stylesheet_link_tag+ to build the stylesheet path. - # + # # ==== Examples # stylesheet_path "style" # => /stylesheets/style.css # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css @@ -60,11 +61,7 @@ module ActionView # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css def stylesheet_path(source) - if config.use_sprockets - asset_path(source, 'css') - else - asset_paths.compute_public_path(source, 'stylesheets', 'css') - end + asset_paths.compute_public_path(source, 'stylesheets', 'css', true, :request) end alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index a087688a2c..39c37b25dc 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -20,7 +20,7 @@ module ActionView # # GET /posts.html # # GET /posts.atom # def index - # @posts = Post.find(:all) + # @posts = Post.all # # respond_to do |format| # format.html diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index e81d03b537..f81ce3e31c 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -51,7 +51,11 @@ module ActionView # This dance is needed because Builder can't use capture pos = output_buffer.length yield + output_safe = output_buffer.html_safe? fragment = output_buffer.slice!(pos..-1) + if output_safe + self.output_buffer = output_buffer.html_safe + end controller.write_fragment(name, fragment, options) end end diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 3b5f4e694f..62f95379cd 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -27,7 +27,7 @@ module ActionView # "The current timestamp is #{Time.now}." # end # - # You can then use that variable anywhere else. For example: + # You can then use that variable anywhere else. For example: # # <html> # <head><title><%= @greeting %></title></head> @@ -76,7 +76,7 @@ module ActionView # # <%= stored_content %> # - # You can use the <tt>yield</tt> syntax alongside an existing call to <tt>yield</tt> in a layout. For example: + # You can use the <tt>yield</tt> syntax alongside an existing call to <tt>yield</tt> in a layout. For example: # # <%# This is the layout %> # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> diff --git a/actionpack/lib/action_view/helpers/controller_helper.rb b/actionpack/lib/action_view/helpers/controller_helper.rb index e22331cb3c..1a583e62ae 100644 --- a/actionpack/lib/action_view/helpers/controller_helper.rb +++ b/actionpack/lib/action_view/helpers/controller_helper.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/attr_internal' + module ActionView module Helpers # This module keeps all methods and behavior in ActionView @@ -18,4 +20,4 @@ module ActionView end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index c78c03a5eb..6a724749f4 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -517,7 +517,7 @@ module ActionView # # that is named 'due' rather than 'day'. # select_day(my_time, :field_name => 'due') # - # # Generates a select field for days with a custom prompt. Use <tt>:prompt => true</tt> for a + # # Generates a select field for days with a custom prompt. Use <tt>:prompt => true</tt> for a # # generic prompt. # select_day(5, :prompt => 'Choose day') # @@ -621,7 +621,6 @@ module ActionView end class DateTimeSelector #:nodoc: - extend ActiveSupport::Memoizable include ActionView::Helpers::TagHelper DEFAULT_PREFIX = 'date'.freeze @@ -786,11 +785,12 @@ module ActionView # Returns translated month names, but also ensures that a custom month # name array has a leading nil element. def month_names - month_names = @options[:use_month_names] || translated_month_names - month_names.unshift(nil) if month_names.size < 13 - month_names + @month_names ||= begin + month_names = @options[:use_month_names] || translated_month_names + month_names.unshift(nil) if month_names.size < 13 + month_names + end end - memoize :month_names # Returns translated month names. # => [nil, "January", "February", "March", @@ -825,9 +825,8 @@ module ActionView end def date_order - @options[:order] || translated_date_order + @date_order ||= @options[:order] || translated_date_order end - memoize :date_order def translated_date_order I18n.translate(:'date.order', :locale => @options[:locale]) || [] diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb index cd67851642..c0cc7d347c 100644 --- a/actionpack/lib/action_view/helpers/debug_helper.rb +++ b/actionpack/lib/action_view/helpers/debug_helper.rb @@ -30,7 +30,7 @@ module ActionView begin Marshal::dump(object) "<pre class='debug_dump'>#{h(object.to_yaml).gsub(" ", " ")}</pre>".html_safe - rescue Exception => e # errors from Marshal or YAML + rescue Exception # errors from Marshal or YAML # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback "<code class='debug_dump'>#{h(object.inspect)}</code>".html_safe end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index cb1c13912a..3a30263b49 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -4,6 +4,7 @@ require 'action_view/helpers/tag_helper' require 'action_view/helpers/form_tag_helper' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/module/method_names' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/array/extract_options' @@ -202,13 +203,13 @@ module ActionView # # is equivalent to something like: # - # <%= form_for @post, :as => :post, :url => post_path(@post), :html => { :class => "new_post", :id => "new_post" } do |f| %> + # <%= form_for @post, :as => :post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %> # ... # <% end %> # # You can also overwrite the individual conventions, like this: # - # <%= form_for(@post, :url => super_post_path(@post)) do |f| %> + # <%= form_for(@post, :url => super_posts_path) do |f| %> # ... # <% end %> # @@ -219,9 +220,9 @@ module ActionView # <% end %> # # If you have an object that needs to be represented as a different - # parameter, like a Client that acts as a Person: + # parameter, like a Person that acts as a Client: # - # <%= form_for(@post, :as => :client) do |f| %> + # <%= form_for(@person, :as => :client) do |f| %> # ... # <% end %> # @@ -290,7 +291,7 @@ module ActionView # # Example: # - # <%= form(@post) do |f| %> + # <%= form_for(@post) do |f| %> # <% f.fields_for(:comments, :include_id => false) do |cf| %> # ... # <% end %> @@ -517,6 +518,18 @@ module ActionView # end # end # + # Note that the <tt>projects_attributes=</tt> writer method is in fact + # required for fields_for to correctly identify <tt>:projects</tt> as a + # collection, and the correct indices to be set in the form markup. + # + # When projects is already an association on Person you can use + # +accepts_nested_attributes_for+ to define the writer method for you: + # + # class Person < ActiveRecord::Base + # has_many :projects + # accepts_nested_attributes_for :projects + # end + # # This model can now be used with a nested fields_for. The block given to # the nested fields_for call will be repeated for each instance in the # collection: @@ -555,19 +568,6 @@ module ActionView # ... # <% end %> # - # In addition, you may want to have access to the current iteration index. - # In that case, you can use a similar method called fields_for_with_index - # which receives a block with an extra parameter: - # - # <%= form_for @person do |person_form| %> - # ... - # <%= person_form.fields_for_with_index :projects do |project_fields, index| %> - # Position: <%= index %> - # Name: <%= project_fields.text_field :name %> - # <% end %> - # ... - # <% end %> - # # When projects is already an association on Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # @@ -1149,7 +1149,7 @@ module ActionView options["name"] ||= tag_name_with_index(@auto_index) options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) } else - options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '') + options["name"] ||= tag_name + (options['multiple'] ? '[]' : '') options["id"] = options.fetch("id"){ tag_id } end end @@ -1229,15 +1229,8 @@ module ActionView RUBY_EVAL end - # Check +fields_for+ for docs and examples. - def fields_for_with_index(record_name, record_object = nil, fields_options = {}, &block) - index = fields_options[:index] || options[:child_index] || nested_child_index(@object_name) - block_with_index = Proc.new{ |obj| block.call(obj, index) } - fields_for(record_name, record_object, fields_options, &block_with_index) - end - def fields_for(record_name, record_object = nil, fields_options = {}, &block) - fields_options, record_object = record_object, nil if record_object.is_a?(Hash) + fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options? fields_options[:builder] ||= options[:builder] fields_options[:parent_builder] = self diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 0aaa690129..3dc6d65432 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -125,9 +125,31 @@ module ActionView # This allows the user to submit a form page more than once with the expected results of creating multiple records. # In addition, this allows a single partial to be used to generate form inputs for both edit and create forms. # - # By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection + # By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection # or <tt>:selected => nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option # tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled. + # + # ==== Gotcha + # + # The HTML specification says when +multiple+ parameter passed to select and all options got deselected + # web browsers do not send any value to server. Unfortunately this introduces a gotcha: + # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user + # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So, + # any mass-assignment idiom like + # + # @user.update_attributes(params[:user]) + # + # wouldn't update roles. + # + # To prevent this the helper generates an auxiliary hidden field before + # every multiple select. The hidden field has the same name as multiple select and blank value. + # + # This way, the client either sends only the hidden field (representing + # the deselected multiple select box), or both fields. Since the HTML specification + # says key/value pairs have to be sent in the same order they appear in the + # form, and parameters extraction gets the last occurrence of any repeated + # key in the query string, that works for ordinary forms. + # def select(object, method, choices, options = {}, html_options = {}) InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options) end @@ -255,7 +277,7 @@ module ActionView # 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+ + # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+ # may also be an array of values to be selected when using a multiple select. # # Examples (call, result): @@ -406,13 +428,13 @@ module ActionView # wraps them with <tt><optgroup></tt> tags. # # Parameters: - # * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the + # * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the # <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a # nested array of text-value pairs. See <tt>options_for_select</tt> for more info. # Ex. ["North America",[["United States","US"],["Canada","CA"]]] # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags, # which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options - # as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>. + # as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>. # * +prompt+ - set to true or a prompt string. When the select element doesn't have a value yet, this # prepends an option with a generic prompt - "Please select" - or the given prompt string. # @@ -427,7 +449,7 @@ module ActionView # # Sample usage (Hash): # grouped_options = { - # 'North America' => [['United States','US], 'Canada'], + # 'North America' => [['United States','US'], 'Canada'], # 'Europe' => ['Denmark','Germany','France'] # } # grouped_options_for_select(grouped_options) @@ -552,43 +574,26 @@ 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) - value = value(object) - selected_value = options.has_key?(:selected) ? options[:selected] : value - disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil - content_tag("select", add_options(options_for_select(choices, :selected => selected_value, :disabled => disabled_value), options, selected_value), html_options) + selected_value = options.has_key?(:selected) ? options[:selected] : value(object) + select_content_tag(options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]), options, 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) - value = value(object) - disabled_value = options.has_key?(:disabled) ? options[:disabled] : nil - selected_value = options.has_key?(:selected) ? options[:selected] : value - content_tag( - "select", add_options(options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => disabled_value), options, value), html_options + selected_value = options.has_key?(:selected) ? options[:selected] : value(object) + select_content_tag( + options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options ) end def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) - html_options = html_options.stringify_keys - add_default_name_and_id(html_options) - value = value(object) - content_tag( - "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options + select_content_tag( + option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, 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) - value = value(object) - content_tag("select", - add_options( - time_zone_options_for_select(value || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), - options, value - ), html_options + select_content_tag( + time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options ) end @@ -603,6 +608,17 @@ module ActionView end option_tags.html_safe end + + def select_content_tag(option_tags, options, html_options) + html_options = html_options.stringify_keys + add_default_name_and_id(html_options) + select = content_tag("select", add_options(option_tags, options, value(object)), html_options) + if html_options["multiple"] + tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select + else + select + end + end end class FormBuilder diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 65a98fb27a..72bc4510b5 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -204,7 +204,7 @@ module ActionView text_field_tag(name, value, options.stringify_keys.update("type" => "hidden")) end - # Creates a file upload field. If you are using file uploads then you will also need + # Creates a file upload field. If you are using file uploads then you will also need # to set the multipart option for the form tag: # # <%= form_tag '/upload', :multipart => true do %> @@ -597,6 +597,12 @@ module ActionView number_field_tag(name, value, options.stringify_keys.update("type" => "range")) end + # Creates the hidden UTF8 enforcer tag. Override this method in a helper + # to customize the tag. + def utf8_enforcer_tag + tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe) + end + private def html_options_for_form(url_for_options, options, *parameters_for_url) options.stringify_keys.tap do |html_options| @@ -611,9 +617,6 @@ module ActionView end def extra_tags_for_form(html_options) - snowman_tag = tag(:input, :type => "hidden", - :name => "utf8", :value => "✓".html_safe) - authenticity_token = html_options.delete("authenticity_token") method = html_options.delete("method").to_s @@ -629,7 +632,7 @@ module ActionView tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token) end - tags = snowman_tag << method_tag + tags = utf8_enforcer_tag << method_tag content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline') end diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index d7228bab67..4484390fde 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -18,7 +18,8 @@ module ActionView # $('some_element').replaceWith('<%=j render 'some/element_template' %>'); def escape_javascript(javascript) if javascript - javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] } + result = javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {|match| JS_ESCAPE_MAP[match] } + javascript.html_safe? ? result.html_safe : result else '' end diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 63d13a0f0b..fe0288521f 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -211,7 +211,7 @@ module ActionView defaults = I18n.translate(:'number.format', :locale => options[:locale], :default => {}) options = options.reverse_merge(defaults) - parts = number.to_s.split('.') + parts = number.to_s.to_str.split('.') parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}") parts.join(options[:separator]).html_safe @@ -342,7 +342,7 @@ module ActionView options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros) storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true) - + base = options[:prefix] == :si ? 1000 : 1024 if number.to_i < base diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index 841be0a567..bcc8f6fbcb 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -14,13 +14,13 @@ module ActionView # # It also strips href/src tags with invalid protocols, like javascript: especially. # It does its best to counter any tricks that hackers may use, like throwing in - # unicode/ascii/hex values to get past the javascript: filters. Check out + # unicode/ascii/hex values to get past the javascript: filters. Check out # the extensive test suite. # # <%= sanitize @article.body %> # # You can add or remove tags/attributes if you want to customize it a bit. - # See ActionView::Base for full docs on the available options. You can add + # See ActionView::Base for full docs on the available options. You can add # tags/attributes for single uses of +sanitize+ by passing either the # <tt>:attributes</tt> or <tt>:tags</tt> options: # @@ -66,7 +66,7 @@ module ActionView self.class.white_list_sanitizer.sanitize_css(style) end - # Strips all HTML tags from the +html+, including comments. This uses the + # Strips all HTML tags from the +html+, including comments. This uses the # html-scanner tokenizer and so its HTML parsing ability is limited by # that of html-scanner. # @@ -142,7 +142,7 @@ module ActionView white_list_sanitizer.protocol_separator = value end - # Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with + # Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with # any object that responds to +sanitize+. # # class Application < Rails::Application @@ -153,7 +153,7 @@ module ActionView @full_sanitizer ||= HTML::FullSanitizer.new end - # Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with + # Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with # any object that responds to +sanitize+. # # class Application < Rails::Application diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 786af5ca58..8c33ef09fa 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -94,7 +94,7 @@ module ActionView end end - # Returns a CDATA section with the given +content+. CDATA sections + # Returns a CDATA section with the given +content+. CDATA sections # are used to escape blocks of text containing characters which would # otherwise be recognized as markup. CDATA sections begin with the string # <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>. diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index ca09c77b5c..ae71ade588 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -232,7 +232,7 @@ module ActionView # considered as a linebreak and a <tt><br /></tt> tag is appended. This # method does not remove the newlines from the +text+. # - # You can pass any HTML attributes into <tt>html_options</tt>. These + # You can pass any HTML attributes into <tt>html_options</tt>. These # will be added to all created paragraphs. # # ==== Options @@ -255,9 +255,11 @@ module ActionView # simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false) # # => "<p><span>I'm allowed!</span> It's true.</p>" def simple_format(text, html_options={}, options={}) - text = ''.html_safe if text.nil? + text = '' if text.nil? + text = text.dup if text.frozen? start_tag = tag('p', html_options, true) text = sanitize(text) unless options[:sanitize] == false + text = text.to_str text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br @@ -267,7 +269,7 @@ module ActionView # Creates a Cycle object whose _to_s_ method cycles through elements of an # array every time it is called. This can be used for example, to alternate - # classes for table rows. You can use named cycles to allow nesting in loops. + # classes for table rows. You can use named cycles to allow nesting in loops. # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a # named cycle. The default name for a cycle without a +:name+ key is # <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle @@ -279,7 +281,7 @@ module ActionView # @items = [1,2,3,4] # <table> # <% @items.each do |item| %> - # <tr class="<%= cycle("even", "odd") -%>"> + # <tr class="<%= cycle("odd", "even") -%>"> # <td>item</td> # </tr> # <% end %> diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index fd8fe417d0..26b6e8b599 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -5,7 +5,7 @@ module I18n class ExceptionHandler include Module.new { def call(exception, locale, key, options) - exception.is_a?(MissingTranslation) ? super.html_safe : super + exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super end } end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 5488c752cc..d70ae4196b 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -55,9 +55,9 @@ module ActionView # # ==== Relying on named routes # - # If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter, - # you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing - # a Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as + # Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will + # trigger the named route for that record. The lookup will happen on the name of the class. So passing a + # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route). # # ==== Examples @@ -112,13 +112,13 @@ module ActionView end end - # Creates a link tag of the given +name+ using a URL created by the set - # of +options+. See the valid options in the documentation for - # +url_for+. It's also possible to pass a string instead - # of an options hash to get a link tag that uses the value of the string as the - # href for the link, or use <tt>:back</tt> to link to the referrer - a JavaScript back - # link will be used in place of a referrer if none exists. If +nil+ is passed as - # a name, the link itself will become the name. + # Creates a link tag of the given +name+ using a URL created by the set of +options+. + # See the valid options in the documentation for +url_for+. It's also possible to + # pass a String instead of an options hash, which generates a link tag that uses the + # value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead + # of an options hash will generate a link to the referrer (a JavaScript back link + # will be used in place of a referrer if none exists). If +nil+ is passed as the name + # the value of the link itself will become the name. # # ==== Signatures # @@ -160,7 +160,7 @@ module ActionView # # ==== Examples # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments - # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base + # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base # your application on resources and use # # link_to "Profile", profile_path(@profile) @@ -348,7 +348,7 @@ module ActionView # Creates a link tag of the given +name+ using a URL created by the set of # +options+ unless the current request URI is the same as the links, in # which case only the name is returned (or the given block is yielded, if - # one exists). You can give +link_to_unless_current+ a block which will + # one exists). You can give +link_to_unless_current+ a block which will # specialize the default behavior (e.g., show a "Start Here" link rather # than the link's text). # @@ -375,7 +375,7 @@ module ActionView # </ul> # # The implicit block given to +link_to_unless_current+ is evaluated if the current - # action is the action given. So, if we had a comments page and wanted to render a + # action is the action given. So, if we had a comments page and wanted to render a # "Go Back" link instead of a link to the comments page, we could do something like this... # # <%= @@ -497,14 +497,14 @@ module ActionView }.compact extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&')) - email_address_obfuscated = email_address.dup + email_address_obfuscated = email_address.to_str email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at") email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot") case encode when "javascript" string = '' html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)) - html = escape_javascript(html) + html = escape_javascript(html.to_str) "document.write('#{html}');".each_byte do |c| string << sprintf("%%%x", c) end @@ -579,7 +579,7 @@ module ActionView url_string = url_for(options) # We ignore any extra parameters in the request_uri if the - # submitted url doesn't have any either. This lets the function + # submitted url doesn't have any either. This lets the function # work with things like ?order=asc if url_string.index("?") request_uri = request.fullpath @@ -619,7 +619,9 @@ module ActionView end def add_method_to_attributes!(html_options, method) - html_options["rel"] = "nofollow" if method.to_s.downcase != "get" + if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/ + html_options["rel"] = "#{html_options["rel"]} nofollow".strip + end html_options["data-method"] = method end @@ -641,7 +643,7 @@ module ActionView # Processes the +html_options+ hash, converting the boolean # attributes from true/false form into the form required by - # HTML/XHTML. (An attribute is considered to be boolean if + # HTML/XHTML. (An attribute is considered to be boolean if # its name is listed in the given +bool_attrs+ array.) # # More specifically, for each boolean attribute in +html_options+ @@ -651,7 +653,7 @@ module ActionView # # if the associated +bool_value+ evaluates to true, it is # replaced with the attribute's name; otherwise the attribute is - # removed from the +html_options+ hash. (See the XHTML 1.0 spec, + # removed from the +html_options+ hash. (See the XHTML 1.0 spec, # section 4.5 "Attribute Minimization" for more: # http://www.w3.org/TR/xhtml1/#h-4.5) # |