diff options
Diffstat (limited to 'actionpack/lib/action_view')
39 files changed, 311 insertions, 210 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 4d06ca0d89..956c88a553 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -3,16 +3,19 @@ require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/array/wrap' require 'active_support/ordered_options' +require 'action_view/log_subscriber' module ActionView #:nodoc: class NonConcattingString < ActiveSupport::SafeBuffer end + # = Action View Base + # # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used. # If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator. # - # = ERb + # == ERb # # You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the # following loop for names: @@ -32,7 +35,7 @@ module ActionView #:nodoc: # # <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>. # - # == Using sub templates + # === Using sub templates # # Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The # classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts): @@ -54,7 +57,7 @@ module ActionView #:nodoc: # # <title><%= @page_title %></title> # - # == Passing local variables to sub templates + # === Passing local variables to sub templates # # You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values: # @@ -74,7 +77,7 @@ module ActionView #:nodoc: # # Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction. # - # == Template caching + # === Template caching # # By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will # check the file's modification time and recompile it. @@ -202,8 +205,12 @@ module ActionView #:nodoc: value.dup : ActionView::PathSet.new(Array.wrap(value)) end + def assign(new_assigns) # :nodoc: + self.assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) } + end + def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc: - self.assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } + assign(assigns_for_first_render) self.helpers = self.class.helpers || Module.new if @_controller = controller diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index 88efd4b34f..39d88333e8 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -2,13 +2,12 @@ module ActionView module CompiledTemplates #:nodoc: # holds compiled template code end - - # Action View contexts are supplied to Action Controller - # to render template. The default Action View context - # is ActionView::Base. + # = Action View Context + # + # Action View contexts are supplied to Action Controller to render template. + # The default Action View context is ActionView::Base. # - # In order to work with ActionController, a Context - # must implement: + # In order to work with ActionController, a Context must implement: # # Context#render_partial[options] # - responsible for setting options[:_template] @@ -21,16 +20,14 @@ module ActionView # options<Hash>:: See _render_template_with_layout in ActionView::Base # partial<Boolean>:: Whether or not the template to render is a partial # - # An Action View context can also mix in Action View's - # helpers. In order to mix in helpers, a context must - # implement: + # An Action View context can also mix in Action View's helpers. In order to + # mix in helpers, a context must implement: # # Context#controller # - Returns an instance of AbstractController # - # In any case, a context must mix in ActionView::Context, - # which stores compiled template and provides the output - # buffer. + # In any case, a context must mix in ActionView::Context, which stores compiled + # template and provides the output buffer. module Context include CompiledTemplates attr_accessor :output_buffer diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb index 8054de0af6..6bb0875bc3 100644 --- a/actionpack/lib/action_view/helpers/active_model_helper.rb +++ b/actionpack/lib/action_view/helpers/active_model_helper.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/enumerable' require 'active_support/core_ext/object/blank' module ActionView + # = Active Model Helpers module Helpers module ActiveModelHelper %w(input form error_messages_for error_message_on).each do |method| @@ -35,12 +36,16 @@ module ActionView end end - %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| + %w(content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| module_eval "def #{meth}(*) error_wrapping(super) end", __FILE__, __LINE__ end + def tag(type, options, *) + tag_generate_errors?(options) ? error_wrapping(super) : super + end + def error_wrapping(html_tag) - if object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && object.errors[@method_name].any? + if object_has_errors? Base.field_error_proc.call(html_tag, self) else html_tag @@ -50,6 +55,16 @@ module ActionView def error_message object.errors[@method_name] end + + private + + def object_has_errors? + object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && error_message.any? + end + + def tag_generate_errors?(options) + options['type'] != 'hidden' + end end class FormBuilder diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 25426a5547..a3c43d3e93 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -6,6 +6,7 @@ require 'active_support/core_ext/file' require 'active_support/core_ext/object/blank' module ActionView + # = Action View Asset Tag Helpers module Helpers #:nodoc: # This module provides methods for generating HTML that links views to assets such # as images, javascripts, stylesheets, and feeds. These methods do not verify @@ -193,7 +194,19 @@ module ActionView # RewriteEngine On # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L] module AssetTagHelper - JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) + mattr_reader :javascript_expansions + @@javascript_expansions = { } + + mattr_reader :stylesheet_expansions + @@stylesheet_expansions = {} + + # You can enable or disable the asset tag timestamps cache. + # With the cache enabled, the asset tag helper methods will make fewer + # expensive file system calls. However this prevents you from modifying + # any asset files while the server is running. + # + # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false + mattr_accessor :cache_asset_timestamps # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or @@ -350,8 +363,6 @@ module ActionView end end - @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup } - # Register one or more javascript files to be included when <tt>symbol</tt> # is passed to <tt>javascript_include_tag</tt>. This method is typically intended # to be called from plugin initialization to register javascript files @@ -367,8 +378,6 @@ module ActionView @@javascript_expansions.merge!(expansions) end - @@stylesheet_expansions = {} - # Register one or more stylesheet files to be included when <tt>symbol</tt> # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended # to be called from plugin initialization to register stylesheet files @@ -384,18 +393,6 @@ module ActionView @@stylesheet_expansions.merge!(expansions) end - # Register one or more additional JavaScript files to be included when - # <tt>javascript_include_tag :defaults</tt> is called. This method is - # typically intended to be called from plugin initialization to register additional - # .js files that the plugin installed in <tt>public/javascripts</tt>. - def self.register_javascript_include_default(*sources) - @@javascript_expansions[:defaults].concat(sources) - end - - def self.reset_javascript_include_default #:nodoc: - @@javascript_expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup - end - # Computes the path to a stylesheet asset in the public stylesheets directory. # 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. @@ -706,23 +703,8 @@ module ActionView tag("audio", options) end - def self.cache_asset_timestamps - @@cache_asset_timestamps - end - - # You can enable or disable the asset tag timestamps cache. - # With the cache enabled, the asset tag helper methods will make fewer - # expensive file system calls. However this prevents you from modifying - # any asset files while the server is running. - # - # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false - def self.cache_asset_timestamps=(value) - @@cache_asset_timestamps = value - end - - @@cache_asset_timestamps = true - private + def rewrite_extension?(source, dir, ext) source_ext = File.extname(source)[1..-1] ext && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(config.assets_dir, dir, "#{source}.#{ext}")))) diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index 52806f206a..cb5a1404ff 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -1,10 +1,12 @@ require 'set' -# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other -# template languages). module ActionView + # = Action View Atom Feed Helpers module Helpers #:nodoc: module AtomFeedHelper + # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other + # template languages). + # # Full usage example: # # config/routes.rb: diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index 8251ed18f4..f544a9d147 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -1,8 +1,10 @@ module ActionView + # = Action View Cache Helper module Helpers - # This helper to exposes a method for caching of view fragments. - # See ActionController::Caching::Fragments for usage instructions. module CacheHelper + # This helper to exposes a method for caching of view fragments. + # See ActionController::Caching::Fragments for usage instructions. + # # A method for caching fragments of a view rather than an entire # action or page. This technique is useful caching pieces like # menus, lists of news topics, static HTML fragments, and so on. diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index edc6afc622..f9105ca364 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -1,9 +1,11 @@ require 'active_support/core_ext/object/blank' module ActionView + # = Action View Capture Helper module Helpers # CaptureHelper exposes methods to let you extract generated markup which # can be used in other parts of a template or layout file. + # # It provides a method to capture blocks into variables through capture and # a way to capture a block of markup for use in a layout through content_for. module CaptureHelper @@ -176,9 +178,7 @@ module ActionView def flush_output_buffer #:nodoc: if output_buffer && !output_buffer.empty? response.body_parts << output_buffer - new = '' - new.force_encoding(output_buffer.encoding) if new.respond_to?(:force_encoding) - self.output_buffer = new + self.output_buffer = output_buffer[0,0] nil end end diff --git a/actionpack/lib/action_view/helpers/csrf_helper.rb b/actionpack/lib/action_view/helpers/csrf_helper.rb index 41c6b67f91..3d03f6aac6 100644 --- a/actionpack/lib/action_view/helpers/csrf_helper.rb +++ b/actionpack/lib/action_view/helpers/csrf_helper.rb @@ -1,7 +1,9 @@ module ActionView + # = Action View CSRF Helper module Helpers module CsrfHelper - # Returns a meta tag with the request forgery protection token for forms to use. Put this in your head. + # Returns a meta tag with the cross-site request forgery protection token + # for forms to use. Place this in your head. def csrf_meta_tag if protect_against_forgery? %(<meta name="csrf-param" content="#{Rack::Utils.escape_html(request_forgery_protection_token)}"/>\n<meta name="csrf-token" content="#{Rack::Utils.escape_html(form_authenticity_token)}"/>).html_safe diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 7d846a01dd..f097b9a5a3 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -4,6 +4,8 @@ require 'active_support/core_ext/hash/slice' module ActionView module Helpers + # = Action View Date 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: # @@ -580,10 +582,10 @@ module ActionView extend ActiveSupport::Memoizable include ActionView::Helpers::TagHelper - DEFAULT_PREFIX = 'date'.freeze unless const_defined?('DEFAULT_PREFIX') + DEFAULT_PREFIX = 'date'.freeze POSITION = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 - }.freeze unless const_defined?('POSITION') + }.freeze def initialize(datetime, options = {}, html_options = {}) @options = options.dup @@ -894,8 +896,10 @@ module ActionView # Returns the separator for a given datetime component def separator(type) case type - when :month, :day - @options[:date_separator] + when :month + @options[:discard_month] ? "" : @options[:date_separator] + when :day + @options[:discard_day] ? "" : @options[:date_separator] when :hour (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator] when :minute diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb index e637dc1474..1491cb073f 100644 --- a/actionpack/lib/action_view/helpers/debug_helper.rb +++ b/actionpack/lib/action_view/helpers/debug_helper.rb @@ -1,6 +1,8 @@ module ActionView + # = Action View Debug Helper + # + # Provides a set of methods for making it easier to debug Rails objects. module Helpers - # Provides a set of methods for making it easier to debug Rails objects. module DebugHelper # Returns a YAML representation of +object+ wrapped with <pre> and </pre>. # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead. diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index b3db3151d3..d1b10a9281 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -7,6 +7,7 @@ require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/object/blank' module ActionView + # = Action View Form Helpers module Helpers # Form helpers are designed to make working with resources much easier # compared to using vanilla HTML. @@ -92,7 +93,7 @@ module ActionView # # error handling # end # - # That's how you tipically work with resources. + # That's how you typically work with resources. module FormHelper extend ActiveSupport::Concern @@ -123,7 +124,6 @@ module ActionView # model: # # <%= form_for :person do |f| %> - # <%= f.error_messages %> # First name: <%= f.text_field :first_name %><br /> # Last name : <%= f.text_field :last_name %><br /> # Biography : <%= f.text_area :biography %><br /> @@ -220,15 +220,15 @@ module ActionView # <% end %> # # === Unobtrusive JavaScript - # - # Specifying: - # + # + # Specifying: + # # :remote => true # # in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its - # behaviour. The expected default behaviour is an XMLHttpRequest in the background instead of the regular + # behaviour. The expected default behaviour is an XMLHttpRequest in the background instead of the regular # POST arrangement, but ultimately the behaviour is the choice of the JavaScript driver implementor. - # Even though it's using JavaScript to serialize the form elements, the form submission will work just like + # Even though it's using JavaScript to serialize the form elements, the form submission will work just like # a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>). # # Example: @@ -269,7 +269,7 @@ module ActionView # <tt>labelling_form</tt>. # # The custom FormBuilder class is automatically merged with the options - # of a nested fields_for call, unless it's explicitely set. + # of a nested fields_for call, unless it's explicitly set. # # In many cases you will want to wrap the above in another helper, so you # could do something like the following: @@ -302,7 +302,7 @@ module ActionView args.unshift object end - options[:html][:remote] = true if options.delete(:remote) + (options[:html] ||= {})[:remote] = true if options.delete(:remote) output = form_tag(options.delete(:url) || {}, options.delete(:html) || {}) output << fields_for(object_name, *(args << options), &proc) @@ -717,7 +717,7 @@ module ActionView # # To prevent this the helper generates an auxiliary hidden field before # the very check box. The hidden field has the same name and its - # attributes mimick an unchecked check box. + # attributes mimic an unchecked check box. # # This way, the client either sends only the hidden field (representing # the check box is unchecked), or both fields. Since the HTML specification @@ -838,9 +838,9 @@ module ActionView attr_reader :method_name, :object_name - DEFAULT_FIELD_OPTIONS = { "size" => 30 }.freeze unless const_defined?(:DEFAULT_FIELD_OPTIONS) - DEFAULT_RADIO_OPTIONS = { }.freeze unless const_defined?(:DEFAULT_RADIO_OPTIONS) - DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze unless const_defined?(:DEFAULT_TEXT_AREA_OPTIONS) + DEFAULT_FIELD_OPTIONS = { "size" => 30 }.freeze + DEFAULT_RADIO_OPTIONS = { }.freeze + DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze def initialize(object_name, method_name, template_object, object = nil) @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup @@ -897,7 +897,7 @@ module ActionView options.delete("size") end options["type"] ||= field_type - options["value"] ||= value_before_type_cast(object) unless field_type == "file" + options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file" options["value"] &&= html_escape(options["value"]) add_default_name_and_id(options) tag("input", options) @@ -1030,7 +1030,7 @@ module ActionView private def add_default_name_and_id_for_value(tag_value, options) unless tag_value.nil? - pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase + pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase specified_id = options["id"] add_default_name_and_id(options) options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present? @@ -1042,14 +1042,14 @@ module ActionView def add_default_name_and_id(options) if options.has_key?("index") options["name"] ||= tag_name_with_index(options["index"]) - options["id"] = options.fetch("id", tag_id_with_index(options["index"])) + options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) } options.delete("index") elsif defined?(@auto_index) options["name"] ||= tag_name_with_index(@auto_index) - options["id"] = options.fetch("id", tag_id_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["id"] = options.fetch("id", tag_id) + options["id"] = options.fetch("id"){ tag_id } end end @@ -1180,7 +1180,7 @@ module ActionView # <%= form_for @post do |f| %> # <%= f.submit %> # <% end %> - # + # # In the example above, if @post is a new record, it will use "Create Post" as # submit button label, otherwise, it uses "Update Post". # diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index fe71d2cdf7..6f9d14de8b 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -4,6 +4,7 @@ require 'action_view/helpers/form_helper' require 'active_support/core_ext/object/blank' module ActionView + # = Action View Form Option Helpers module Helpers # Provides a number of methods for turning different kinds of containers into a set of option tags. # == Options @@ -398,7 +399,7 @@ module ActionView options_for_select += "<optgroup label=\"#{html_escape(group_label_string)}\">" 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.html_safe end # Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but @@ -412,8 +413,8 @@ module ActionView # * +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>. - # * +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. + # * +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. # # Sample usage (Array): # grouped_options = [ diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 796268628a..4c1b751160 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/object/returning' require 'active_support/core_ext/object/blank' module ActionView + # = Action View Form Tag Helpers module Helpers # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like # FormHelper does. Instead, you provide the names and values manually. @@ -528,23 +529,34 @@ module ActionView def html_options_for_form(url_for_options, options, *parameters_for_url) returning options.stringify_keys do |html_options| html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart") + # The following URL is unescaped, this is just a hash of options, and it is the + # responsability of the caller to escape all the values. html_options["action"] = url_for(url_for_options, *parameters_for_url) + html_options["accept-charset"] = "UTF-8" html_options["data-remote"] = true if html_options.delete("remote") end end def extra_tags_for_form(html_options) - case method = html_options.delete("method").to_s - when /^get$/i # must be case-insentive, but can't use downcase as might be nil + snowman_tag = tag(:input, :type => "hidden", + :name => "_snowman", :value => "☃".html_safe) + + method = html_options.delete("method").to_s + + method_tag = case method + when /^get$/i # must be case-insensitive, but can't use downcase as might be nil html_options["method"] = "get" '' when /^post$/i, "", nil html_options["method"] = "post" - protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0;display:inline') : '' + token_tag else html_options["method"] = "post" - content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0;display:inline') + tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag end + + tags = snowman_tag << method_tag + content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline') end def form_tag_html(html_options) diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index b0a7718f22..84f53b842c 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -1,6 +1,7 @@ require 'action_view/helpers/tag_helper' module ActionView + # = Action View JavaScript Helpers module Helpers # Provides functionality for working with JavaScript in your views. # diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 3b0dfb561c..37e5d91d8b 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/float/rounding' require 'active_support/core_ext/object/blank' module ActionView + # = Action View Number Helpers module Helpers #:nodoc: # Provides methods for converting numbers into formatted strings. @@ -332,7 +333,7 @@ module ActionView # number_to_human_size(483989, :precision => 2) # => 470 KB # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,2 MB # - # Unsignificant zeros after the fractional separator are stripped out by default (set + # Non-significant zeros after the fractional separator are stripped out by default (set # <tt>:strip_insignificant_zeros</tt> to +false+ to change that): # number_to_human_size(1234567890123, :precision => 5) # => "1.1229 TB" # number_to_human_size(524288000, :precision=>5) # => "500 MB" diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index a798c3eaef..5c0ff5d59c 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1,9 +1,9 @@ require 'set' require 'active_support/json' -require 'active_support/core_ext/object/returning' require 'active_support/core_ext/object/blank' module ActionView + # = Action View Prototype Helpers module Helpers # Prototype[http://www.prototypejs.org/] is a JavaScript library that provides # DOM[http://en.wikipedia.org/wiki/Document_Object_Model] manipulation, @@ -94,14 +94,12 @@ module ActionView # See JavaScriptGenerator for information on updating multiple elements # on the page in an Ajax response. module PrototypeHelper - unless const_defined? :CALLBACKS - CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded, - :interactive, :complete, :failure, :success ] + - (100..599).to_a) - AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url, - :asynchronous, :method, :insertion, :position, - :form, :with, :update, :script, :type ]).merge(CALLBACKS) - end + CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded, + :interactive, :complete, :failure, :success ] + + (100..599).to_a) + AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url, + :asynchronous, :method, :insertion, :position, + :form, :with, :update, :script, :type ]).merge(CALLBACKS) # Returns the JavaScript needed for a remote function. # Takes the same arguments as link_to_remote. @@ -133,7 +131,7 @@ module ActionView url_options = options[:url] url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash) - function << "'#{escape_javascript(url_for(url_options))}'" + function << "'#{html_escape(escape_javascript(url_for(url_options)))}'" function << ", #{javascript_options})" function = "#{options[:before]}; #{function}" if options[:before] @@ -229,7 +227,7 @@ module ActionView # <script> tag. module GeneratorMethods def to_s #:nodoc: - returning javascript = @lines * $/ do + (@lines * $/).tap do |javascript| if ActionView::Base.debug_rjs source = javascript.dup javascript.replace "try {\n#{source}\n} catch (e) " @@ -531,9 +529,9 @@ module ActionView end def record(line) - returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do - self << line - end + line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" + self << line + line end def render(*options) diff --git a/actionpack/lib/action_view/helpers/raw_output_helper.rb b/actionpack/lib/action_view/helpers/raw_output_helper.rb index 8c7f41177d..da7599fa8f 100644 --- a/actionpack/lib/action_view/helpers/raw_output_helper.rb +++ b/actionpack/lib/action_view/helpers/raw_output_helper.rb @@ -1,6 +1,15 @@ module ActionView #:nodoc: + # = Action View Raw Output Helper module Helpers #:nodoc: module RawOutputHelper + # This method outputs without escaping a string. Since escaping tags is + # now default, this can be used when you don't want Rails to automatically + # escape tags. This is not recommended if the data is coming from the user's + # input. + # + # For example: + # + # <%=raw @user.name %> def raw(stringish) stringish.to_s.html_safe end diff --git a/actionpack/lib/action_view/helpers/record_identification_helper.rb b/actionpack/lib/action_view/helpers/record_identification_helper.rb index 6c235bff3d..372f1cb8aa 100644 --- a/actionpack/lib/action_view/helpers/record_identification_helper.rb +++ b/actionpack/lib/action_view/helpers/record_identification_helper.rb @@ -1,4 +1,7 @@ module ActionView + # = Action View Record Identification Helpers + # + # See ActionController::RecordIdentifier for documentation on these methods. module Helpers module RecordIdentificationHelper # See ActionController::RecordIdentifier.partial_path -- this is just a delegate to that for convenient access in the view. diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index a9cf15f418..7433f08777 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -1,4 +1,5 @@ module ActionView + # = Action View Record Tag Helpers module Helpers module RecordTagHelper # Produces a wrapper DIV element with id and class parameters that diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index f173523f6a..b47818a22a 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -2,19 +2,25 @@ require 'action_controller/vendor/html-scanner' require 'action_view/helpers/tag_helper' module ActionView + # = Action View Sanitize Helpers module Helpers #:nodoc: # The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements. # These helper methods extend Action View making them callable within your template files. module SanitizeHelper - # This +sanitize+ helper will html encode all tags and strip all attributes that aren't specifically allowed. - # 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 + # This +sanitize+ helper will html encode all tags and strip all attributes that + # aren't specifically allowed. + # + # 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 # 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 tags/attributes for single uses of +sanitize+ by passing either the <tt>:attributes</tt> or <tt>:tags</tt> options: + # 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 + # tags/attributes for single uses of +sanitize+ by passing either the + # <tt>:attributes</tt> or <tt>:tags</tt> options: # # Normal Use # diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index 37319cca1b..8610c2469e 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -2,9 +2,11 @@ require 'action_view/helpers/javascript_helper' require 'active_support/json' module ActionView + # = Action View Scriptaculous Helpers module Helpers - # Provides a set of helpers for calling Scriptaculous JavaScript - # functions, including those which create Ajax controls and visual effects. + # Provides a set of helpers for calling Scriptaculous[http://script.aculo.us/] + # JavaScript functions, including those which create Ajax controls and visual + # effects. # # To be able to use these helpers, you must include the Prototype # JavaScript framework and the Scriptaculous JavaScript library in your @@ -12,12 +14,11 @@ module ActionView # for more information on including the necessary JavaScript. # # The Scriptaculous helpers' behavior can be tweaked with various options. + # # See the documentation at http://script.aculo.us for more information on # using these helpers in your application. module ScriptaculousHelper - unless const_defined? :TOGGLE_EFFECTS - TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind] - end + TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind] # Returns a JavaScript snippet to be used on the Ajax callbacks for # starting visual effects. diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 66277f79fe..5d032b32a7 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/object/blank' require 'set' module ActionView + # = Action View Tag Helpers module Helpers #:nodoc: # Provides methods to generate HTML tags programmatically when you can't use # a Builder. By default, they output XHTML compliant tags. @@ -121,7 +122,7 @@ module ActionView attrs << %(#{key}="#{key}") if value elsif !value.nil? final_value = value.is_a?(Array) ? value.join(" ") : value - final_value = escape_once(final_value) if escape + final_value = html_escape(final_value) if escape attrs << %(#{key}="#{final_value}") end end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index c7f96597b9..0be8a2c36e 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/string/filters' require 'action_view/helpers/tag_helper' module ActionView + # = Action View Text Helpers module Helpers #:nodoc: # The TextHelper module provides a set of methods for filtering, formatting # and transforming strings, which can reduce the amount of inline Ruby code in @@ -52,7 +53,7 @@ module ActionView # truncate("Once upon a time in a world far far away", :length => 17) # # => "Once upon a ti..." # - # truncate("Once upon a time in a world far far away", :lenght => 17, :separator => ' ') + # truncate("Once upon a time in a world far far away", :length => 17, :separator => ' ') # # => "Once upon a..." # # truncate("And they found that many people were sleeping better.", :length => 25, :omission => '... (continued)') diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 0d2b2aa7b1..dac9c28ab7 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -1,21 +1,28 @@ require 'action_view/helpers/tag_helper' module ActionView + # = Action View Translation Helpers module Helpers module TranslationHelper - # Delegates to I18n#translate but also performs three additional functions. First, it'll catch MissingTranslationData exceptions - # and turn them into inline spans that contains the missing key, such that you can see in a view what is missing where. + # Delegates to I18n#translate but also performs three additional functions. + # First, it'll catch MissingTranslationData exceptions and turn them into + # inline spans that contains the missing key, such that you can see in a + # view what is missing where. # - # Second, it'll scope the key by the current partial if the key starts with a period. So if you call translate(".foo") from the - # people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo"). This makes it less repetitive - # to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't - # prepend the key with a period, nothing is converted. + # Second, it'll scope the key by the current partial if the key starts + # with a period. So if you call <tt>translate(".foo")</tt> from the + # <tt>people/index.html.erb</tt> template, you'll actually be calling + # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive + # to translate many keys within the same partials and gives you a simple framework + # for scoping them consistently. If you don't prepend the key with a period, + # nothing is converted. # - # Third, it’ll mark the translation as safe HTML if the key has the suffix "_html" or the last element of the key is the word - # "html". For example, calling translate("footer_html") or translate("footer.html") will return a safe HTML string that won’t - # be escaped by other HTML helper methods. This naming convention helps to identify translations that include HTML tags so that + # Third, it'll mark the translation as safe HTML if the key has the suffix + # "_html" or the last element of the key is the word "html". For example, + # calling translate("footer_html") or translate("footer.html") will return + # a safe HTML string that won't be escaped by other HTML helper methods. This + # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. - def translate(key, options = {}) translation = I18n.translate(scope_key_by_partial(key), options.merge!(:raise => true)) if html_safe_translation_key?(key) && translation.respond_to?(:html_safe) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 210f148c02..b8d6dc22f2 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/hash/keys' require 'action_dispatch' module ActionView + # = Action View URL Helpers module Helpers #:nodoc: # Provides a set of methods for making links and getting URLs that # depend on the routing subsystem (see ActionDispatch::Routing). @@ -12,7 +13,7 @@ module ActionView module UrlHelper # This helper may be included in any class that includes the # URL helpers of a router (router.url_helpers). Some methods - # provided here will only work in the context of a request + # provided here will only work in the4 context of a request # (link_to_unless_current, for instance), which must be provided # as a method called #request on the context. @@ -37,9 +38,6 @@ module ActionView # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action" # instead of the fully qualified URL like "http://example.com/controller/action". # - # When called from a view, +url_for+ returns an HTML escaped url. If you - # need an unescaped url, pass <tt>:escape => false</tt> in the +options+. - # # ==== Options # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path. # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified). @@ -49,7 +47,6 @@ module ActionView # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided. # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present). # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present). - # * <tt>:escape</tt> - Determines whether the returned URL will be HTML escaped or not (<tt>true</tt> by default). # # ==== Relying on named routes # @@ -71,10 +68,7 @@ module ActionView # <%= url_for(:action => 'play', :anchor => 'player') %> # # => /messages/play/#player # - # <%= url_for(:action => 'checkout', :anchor => 'tax&ship') %> - # # => /testing/jump/#tax&ship - # - # <%= url_for(:action => 'checkout', :anchor => 'tax&ship', :escape => false) %> + # <%= url_for(:action => 'jump', :anchor => 'tax&ship') %> # # => /testing/jump/#tax&ship # # <%= url_for(Workshop.new) %> @@ -99,21 +93,17 @@ module ActionView options ||= {} url = case options when String - escape = true options when Hash options = { :only_path => options[:host].nil? }.update(options.symbolize_keys) - escape = options.key?(:escape) ? options.delete(:escape) : false super when :back - escape = false controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' else - escape = false polymorphic_path(options) end - escape ? escape_once(url).html_safe : url + url end # Creates a link tag of the given +name+ using a URL created by the set @@ -253,8 +243,8 @@ module ActionView tag_options = nil end - href_attr = "href=\"#{url}\"" unless href - "<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe + href_attr = "href=\"#{html_escape(url)}\"" unless href + "<a #{href_attr}#{tag_options}>#{html_escape(name || url)}</a>".html_safe end end @@ -338,7 +328,7 @@ module ActionView html_options.merge!("type" => "submit", "value" => name) - ("<form method=\"#{form_method}\" action=\"#{escape_once url}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" + + ("<form method=\"#{form_method}\" action=\"#{html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" + method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe end @@ -484,24 +474,27 @@ module ActionView # :subject => "This is an example email" # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a> def mail_to(email_address, name = nil, html_options = {}) + email_address = html_escape(email_address) + html_options = html_options.stringify_keys encode = html_options.delete("encode").to_s cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body") - string = '' - extras = '' - extras << "cc=#{Rack::Utils.escape(cc).gsub("+", "%20")}&" unless cc.nil? - extras << "bcc=#{Rack::Utils.escape(bcc).gsub("+", "%20")}&" unless bcc.nil? - extras << "body=#{Rack::Utils.escape(body).gsub("+", "%20")}&" unless body.nil? - extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}&" unless subject.nil? - extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty? - - email_address_obfuscated = html_escape(email_address) + extras = [] + extras << "cc=#{Rack::Utils.escape(cc).gsub("+", "%20")}" unless cc.nil? + extras << "bcc=#{Rack::Utils.escape(bcc).gsub("+", "%20")}" unless bcc.nil? + extras << "body=#{Rack::Utils.escape(body).gsub("+", "%20")}" unless body.nil? + extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}" unless subject.nil? + extras = extras.empty? ? '' : '?' + html_escape(extras.join('&')) + + email_address_obfuscated = email_address.dup email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at") email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot") + string = '' + if encode == "javascript" - "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c| + "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".each_byte do |c| string << sprintf("%%%x", c) end "<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe @@ -518,9 +511,9 @@ module ActionView char = c.chr string << (char =~ /\w/ ? sprintf("%%%x", c) : char) end - content_tag "a", name || email_address_encoded.html_safe, html_options.merge({ "href" => "#{string}#{extras}" }) + content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe) else - content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" }) + content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe) end end @@ -573,7 +566,7 @@ module ActionView "in a #request method" end - url_string = CGI.unescapeHTML(url_for(options)) + 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 diff --git a/actionpack/lib/action_view/log_subscriber.rb b/actionpack/lib/action_view/log_subscriber.rb new file mode 100644 index 0000000000..443a0eafd1 --- /dev/null +++ b/actionpack/lib/action_view/log_subscriber.rb @@ -0,0 +1,28 @@ +module ActionView + # = Action View Log Subscriber + # + # Provides functionality so that Rails can output logs from Action View. + class LogSubscriber < ActiveSupport::LogSubscriber + def render_template(event) + message = "Rendered #{from_rails_root(event.payload[:identifier])}" + message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout] + message << (" (%.1fms)" % event.duration) + info(message) + end + alias :render_partial :render_template + alias :render_collection :render_template + + # TODO: Ideally, ActionView should have its own logger so it does not depend on AC.logger + def logger + ActionController::Base.logger if defined?(ActionController::Base) + end + + protected + + def from_rails_root(string) + string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "") + end + end +end + +ActionView::LogSubscriber.attach_to :action_view diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 823226cb9c..3ea8b86af1 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -2,6 +2,8 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/object/blank' module ActionView + # = Action View Lookup Context + # # LookupContext is the object responsible to hold all information required to lookup # templates, i.e. view paths and details. The LookupContext is also responsible to # generate a key, given to view paths, used in the resolver cache lookup. Since diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 7f5e5d11b8..9857d688d2 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -1,4 +1,5 @@ module ActionView #:nodoc: + # = Action View PathSet class PathSet < Array #:nodoc: %w(initialize << concat insert push unshift).each do |method| class_eval <<-METHOD, __FILE__, __LINE__ + 1 diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb index c606a71e18..33dfcbb803 100644 --- a/actionpack/lib/action_view/railtie.rb +++ b/actionpack/lib/action_view/railtie.rb @@ -2,11 +2,11 @@ require "action_view" require "rails" module ActionView + # = Action View Railtie class Railtie < Rails::Railtie config.action_view = ActiveSupport::OrderedOptions.new - - require "action_view/railties/log_subscriber" - log_subscriber :action_view, ActionView::Railties::LogSubscriber.new + config.action_view.stylesheet_expansions = {} + config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] } initializer "action_view.cache_asset_timestamps" do |app| unless app.config.cache_classes @@ -16,6 +16,18 @@ module ActionView end end + initializer "action_view.javascript_expansions" do |app| + ActiveSupport.on_load(:action_view) do + ActionView::Helpers::AssetTagHelper.register_javascript_expansion( + app.config.action_view.delete(:javascript_expansions) + ) + + ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion( + app.config.action_view.delete(:stylesheet_expansions) + ) + end + end + initializer "action_view.set_configs" do |app| ActiveSupport.on_load(:action_view) do app.config.action_view.each do |k,v| diff --git a/actionpack/lib/action_view/railties/log_subscriber.rb b/actionpack/lib/action_view/railties/log_subscriber.rb deleted file mode 100644 index 9487a10706..0000000000 --- a/actionpack/lib/action_view/railties/log_subscriber.rb +++ /dev/null @@ -1,24 +0,0 @@ -module ActionView - module Railties - class LogSubscriber < Rails::LogSubscriber - def render_template(event) - message = "Rendered #{from_rails_root(event.payload[:identifier])}" - message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout] - message << (" (%.1fms)" % event.duration) - info(message) - end - alias :render_partial :render_template - alias :render_collection :render_template - - def logger - ActionController::Base.logger - end - - protected - - def from_rails_root(string) - string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "") - end - end - end -end
\ No newline at end of file diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb index a9dfc0cced..a474783a20 100644 --- a/actionpack/lib/action_view/render/layouts.rb +++ b/actionpack/lib/action_view/render/layouts.rb @@ -1,4 +1,5 @@ module ActionView + # = Action View Layouts module Layouts # Returns the contents that are yielded to a layout, given a name or a block. # @@ -55,7 +56,7 @@ module ActionView end # This is the method which actually finds the layout using details in the lookup - # context object. If no layout is found, it checkes if at least a layout with + # context object. If no layout is found, it checks if at least a layout with # the given name exists across all details before raising the error. def find_layout(layout) begin diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 85f67d4f14..459aae94a2 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -1,6 +1,8 @@ require 'active_support/core_ext/object/blank' module ActionView + # = Action View Partials + # # There's also a convenience method for rendering sub templates within the current controller that depends on a # single object (we call this kind of sub templates for partials). It relies on the fact that partials should # follow the naming convention of being prefixed with an underscore -- as to separate them from regular @@ -316,7 +318,7 @@ module ActionView object.class.model_name.partial_path.dup.tap do |partial| path = @view.controller_path - partial.insert(0, "#{File.dirname(path)}/") if path.include?(?/) + partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/) end end end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index 4d35296932..5320245173 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/object/try' module ActionView + # = Action View Rendering module Rendering # Returns the result of a render that's dictated by the options hash. The primary options are: # diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 53ad24fdc6..40ff1f2182 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/kernel/singleton_class' module ActionView + # = Action View Template class Template extend ActiveSupport::Autoload @@ -155,11 +156,12 @@ module ActionView end def inspect - if defined?(Rails.root) - identifier.sub("#{Rails.root}/", '') - else - identifier - end + @inspect ||= + if defined?(Rails.root) + identifier.sub("#{Rails.root}/", '') + else + identifier + end end private @@ -266,9 +268,11 @@ module ActionView end def build_method_name(locals) - # TODO: is locals.keys.hash reliably the same? - @method_names[locals.keys.hash] ||= - "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") + @method_names[locals.keys.hash] ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") + end + + def identifier_method_name + @identifier_method_name ||= inspect.gsub(/[^a-z_]/, '_') end end end diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index e50de7e5af..b1839b65e5 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -1,6 +1,7 @@ require "active_support/core_ext/enumerable" module ActionView + # = Action View Errors class ActionViewError < StandardError #:nodoc: end diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb index 6228d7ac39..84d6474dd1 100644 --- a/actionpack/lib/action_view/template/handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -1,4 +1,5 @@ module ActionView #:nodoc: + # = Action View Template Handlers class Template module Handlers #:nodoc: autoload :ERB, 'action_view/template/handlers/erb' diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index ef44925951..c9e20ca14e 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -3,6 +3,7 @@ require "active_support/core_ext/class" require "action_view/template" module ActionView + # = Action View Resolver class Resolver def initialize @cached = Hash.new { |h1,k1| h1[k1] = @@ -98,7 +99,7 @@ module ActionView def initialize(path) raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver) super() - @path = Pathname.new(path).expand_path + @path = File.expand_path(path) end def eql?(resolver) diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index 269340514c..51be831dfb 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -1,4 +1,5 @@ module ActionView #:nodoc: + # = Action View Text Template class Template class Text < String #:nodoc: attr_accessor :mime_type diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 15d424be74..e5614c9e3b 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -4,6 +4,7 @@ require 'action_controller/test_case' require 'action_view' module ActionView + # = Action View Test Case class TestCase < ActiveSupport::TestCase class TestController < ActionController::Base include ActionDispatch::TestProcess @@ -84,6 +85,7 @@ module ActionView def setup_with_controller @controller = ActionView::TestCase::TestController.new + @request = @controller.request @output_buffer = ActiveSupport::SafeBuffer.new @rendered = '' @@ -97,10 +99,15 @@ module ActionView end def render(options = {}, local_assigns = {}, &block) - @rendered << output = _view.render(options, local_assigns, &block) + view.assign(_assigns) + @rendered << output = view.render(options, local_assigns, &block) output end + def locals + @locals ||= {} + end + included do setup :setup_with_controller end @@ -130,36 +137,51 @@ module ActionView end end - def _view - @_view ||= begin - view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller) + module Locals + attr_accessor :locals + + def _render_partial(options) + locals[options[:partial]] = options[:locals] + super(options) + end + end + + # The instance of ActionView::Base that is used by +render+. + def view + @view ||= begin + view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller) view.singleton_class.send :include, _helpers view.singleton_class.send :include, @controller._router.url_helpers view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash" + view.extend(Locals) + view.locals = self.locals view.output_buffer = self.output_buffer view end end + alias_method :_view, :view + EXCLUDE_IVARS = %w{ + @_assertion_wrapped @_result + @controller + @layouts + @locals + @method_name @output_buffer + @partials @rendered + @request + @routes @templates - @view_context_class - @layouts - @partials - @controller - - @method_name - @fixture_cache - @loaded_fixtures @test_passed + @view + @view_context_class } def _instance_variables - instance_variables - EXCLUDE_IVARS - instance_variables + instance_variables.map(&:to_s) - EXCLUDE_IVARS end def _assigns |