diff options
author | Dmitry Polushkin <dmitry.polushkin@gmail.com> | 2011-12-31 01:10:42 +0000 |
---|---|---|
committer | Dmitry Polushkin <dmitry.polushkin@gmail.com> | 2011-12-31 01:10:42 +0000 |
commit | 04bc40ff501b1bf81bec7ce3937cb06c896ffc69 (patch) | |
tree | 88a33663195900df8a7307aefa2c9aaa561c3973 /actionpack/lib/action_view | |
parent | 84eece0a823e9c601ea99a8709f24605a19bcbfd (diff) | |
parent | ed17983ec56dec689a0311c7f8fcbeba9874e5a4 (diff) | |
download | rails-04bc40ff501b1bf81bec7ce3937cb06c896ffc69.tar.gz rails-04bc40ff501b1bf81bec7ce3937cb06c896ffc69.tar.bz2 rails-04bc40ff501b1bf81bec7ce3937cb06c896ffc69.zip |
Merge branch 'master' of git://github.com/rails/rails
Diffstat (limited to 'actionpack/lib/action_view')
32 files changed, 325 insertions, 234 deletions
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb index 1d16e34df6..aa5db0d7bc 100644 --- a/actionpack/lib/action_view/asset_paths.rb +++ b/actionpack/lib/action_view/asset_paths.rb @@ -103,8 +103,8 @@ module ActionView if host.respond_to?(:call) args = [source] arity = arity_of(host) - if arity > 1 && !has_request? - invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request.") + if (arity > 1 || arity < -2) && !has_request? + invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.") end args << current_request if (arity > 1 || arity < 0) && has_request? host.call(*args) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 36c49d9c91..66a5d59857 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -9,8 +9,8 @@ require 'active_support/core_ext/module/deprecation' module ActionView #:nodoc: # = Action View Base # - # Action View templates can be written in several 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. + # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERb + # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> extension then Jim Weirich's Builder::XmlMarkup library is used. # # == ERB # @@ -94,10 +94,10 @@ module ActionView #:nodoc: # # Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following: # - # xml.div { + # xml.div do # xml.h1(@person.name) # xml.p(@person.bio) - # } + # end # # would produce something like: # diff --git a/actionpack/lib/action_view/buffers.rb b/actionpack/lib/action_view/buffers.rb index be7f65c2ce..2372d3c433 100644 --- a/actionpack/lib/action_view/buffers.rb +++ b/actionpack/lib/action_view/buffers.rb @@ -4,7 +4,7 @@ module ActionView class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc: def initialize(*) super - encode! if encoding_aware? + encode! end def <<(value) diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb index 96c3eec337..56f15604a6 100644 --- a/actionpack/lib/action_view/helpers/active_model_helper.rb +++ b/actionpack/lib/action_view/helpers/active_model_helper.rb @@ -1,4 +1,3 @@ -require 'action_view/helpers/form_helper' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/enumerable' require 'active_support/core_ext/object/blank' @@ -47,9 +46,5 @@ module ActionView options['type'] != 'hidden' end end - - class InstanceTag - include ActiveModelInstanceTag - end end end diff --git a/actionpack/lib/action_view/helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_paths.rb deleted file mode 100644 index fae2e4fc1c..0000000000 --- a/actionpack/lib/action_view/helpers/asset_paths.rb +++ /dev/null @@ -1,7 +0,0 @@ -ActiveSupport::Deprecation.warn "ActionView::Helpers::AssetPaths is deprecated. Please use ActionView::AssetPaths instead." - -module ActionView - module Helpers - 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 7d01e5ddb8..653e12c7d8 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -228,23 +228,19 @@ module ActionView ) end - # Web browsers cache favicons. If you just throw a <tt>favicon.ico</tt> into the document - # root of your application and it changes later, clients that have it in their cache - # won't see the update. Using this helper prevents that because it appends an asset ID: - # # <%= favicon_link_tag %> # # generates # - # <link href="/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" /> + # <link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> # # You may specify a different file in the first argument: # - # <%= favicon_link_tag 'favicon.ico' %> + # <%= favicon_link_tag '/myicon.ico' %> # # That's passed to +path_to_image+ as is, so it gives # - # <link href="/images/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" /> + # <link href="/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> # # The helper accepts an additional options hash where you can override "rel" and "type". # diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb index 09700bd0c5..d9f1f88ade 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb @@ -119,10 +119,10 @@ module ActionView # # <script type="text/javascript" src="/elsewhere/cools.js?1423139606"></script> # # javascript_include_tag "http://www.example.com/xmlhr" - # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script> + # # => <script type="text/javascript" src="http://www.example.com/xmlhr"></script> # # javascript_include_tag "http://www.example.com/xmlhr.js" - # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script> + # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js"></script> # # javascript_include_tag :defaults # # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script> 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 343153c8c5..41958c6559 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 @@ -68,6 +68,9 @@ module ActionView # Returns a stylesheet link tag for the sources specified as arguments. If # you don't specify an extension, <tt>.css</tt> will be appended automatically. # You can modify the link attributes by passing a hash as the last argument. + # For historical reasons, the 'media' attribute will always be present and defaults + # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to + # apply to all media types. # # ==== Examples # stylesheet_link_tag "style" # => diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index 39c37b25dc..73824dc1f8 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -32,7 +32,7 @@ module ActionView # app/views/posts/index.atom.builder: # atom_feed do |feed| # feed.title("My great blog!") - # feed.updated(@posts.first.created_at) + # feed.updated(@posts[0].created_at) if @posts.length > 0 # # @posts.each do |post| # feed.entry(post) do |entry| diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index f81ce3e31c..850dd5f448 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -54,7 +54,7 @@ module ActionView output_safe = output_buffer.html_safe? fragment = output_buffer.slice!(pos..-1) if output_safe - self.output_buffer = output_buffer.html_safe + self.output_buffer = output_buffer.class.new(output_buffer) end controller.write_fragment(name, fragment, options) end diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 8abd85c3a3..17bbfe2efd 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -81,8 +81,8 @@ module ActionView # <%# This is the layout %> # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> # <head> - # <title>My Website</title> - # <%= yield :script %> + # <title>My Website</title> + # <%= yield :script %> # </head> # <body> # <%= yield %> @@ -110,7 +110,7 @@ module ActionView # That will place +script+ tags for your default set of JavaScript files on the page; # this technique is useful if you'll only be using these scripts in a few views. # - # Note that content_for concatenates the blocks it is given for a particular + # Note that content_for concatenates (default) the blocks it is given for a particular # identifier in order. For example: # # <% content_for :navigation do %> @@ -127,16 +127,37 @@ module ActionView # # <ul><%= content_for :navigation %></ul> # + # If the flush parameter is true content_for replaces the blocks it is given. For example: + # + # <% content_for :navigation do %> + # <li><%= link_to 'Home', :action => 'index' %></li> + # <% end %> + # + # <%# Add some other content, or use a different template: %> + # + # <% content_for :navigation, true do %> + # <li><%= link_to 'Login', :action => 'login' %></li> + # <% end %> + # + # Then, in another template or layout, this code would render only the last link: + # + # <ul><%= content_for :navigation %></ul> + # # Lastly, simple content can be passed as a parameter: # # <% content_for :script, javascript_include_tag(:defaults) %> # # WARNING: content_for is ignored in caches. So you shouldn't use it # for elements that will be fragment cached. - def content_for(name, content = nil, &block) + def content_for(name, content = nil, flush = false, &block) if content || block_given? - content = capture(&block) if block_given? - @view_flow.append(name, content) if content + if block_given? + flush = content if content + content = capture(&block) + end + if content + flush ? @view_flow.set(name, content) : @view_flow.append(name, content) + end nil else @view_flow.get(name) @@ -164,8 +185,8 @@ module ActionView # <%# This is the layout %> # <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> # <head> - # <title>My Website</title> - # <%= yield :script %> + # <title>My Website</title> + # <%= yield :script %> # </head> # <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>"> # <%= yield %> @@ -181,7 +202,7 @@ module ActionView def with_output_buffer(buf = nil) #:nodoc: unless buf buf = ActionView::OutputBuffer.new - buf.force_encoding(output_buffer.encoding) if output_buffer.respond_to?(:encoding) && buf.respond_to?(:force_encoding) + buf.force_encoding(output_buffer.encoding) if output_buffer end self.output_buffer, old_buffer = buf, output_buffer yield diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index e850c258ce..2806348337 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -422,7 +422,7 @@ module ActionView end # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected. - # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer. + # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer. # Override the field name using the <tt>:field_name</tt> option, 'second' by default. # # ==== Examples @@ -448,7 +448,7 @@ module ActionView # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected. # Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute - # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer. + # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer. # Override the field name using the <tt>:field_name</tt> option, 'minute' by default. # # ==== Examples @@ -473,7 +473,7 @@ module ActionView end # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected. - # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer. + # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer. # Override the field name using the <tt>:field_name</tt> option, 'hour' by default. # # ==== Examples @@ -736,7 +736,7 @@ module ActionView if @options[:use_hidden] || @options[:discard_day] build_hidden(:day, day) else - build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false) + build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false, :use_two_digit_numbers => @options[:use_two_digit_numbers]) end end @@ -765,11 +765,16 @@ module ActionView if @options[:use_hidden] || @options[:discard_year] build_hidden(:year, val) else - options = {} - options[:start] = @options[:start_year] || middle_year - 5 - options[:end] = @options[:end_year] || middle_year + 5 - options[:step] = options[:start] < options[:end] ? 1 : -1 - options[:leading_zeros] = false + options = {} + options[:start] = @options[:start_year] || middle_year - 5 + options[:end] = @options[:end_year] || middle_year + 5 + options[:step] = options[:start] < options[:end] ? 1 : -1 + options[:leading_zeros] = false + options[:max_years_allowed] = @options[:max_years_allowed] || 1000 + + if (options[:end] - options[:start]).abs > options[:max_years_allowed] + raise ArgumentError, "There're too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter" + end build_options_and_select(:year, val, options) end @@ -817,6 +822,8 @@ module ActionView def month_name(number) if @options[:use_month_numbers] number + elsif @options[:use_two_digit_numbers] + sprintf "%02d", number elsif @options[:add_month_numbers] "#{number} - #{month_names[number]}" else @@ -852,7 +859,7 @@ module ActionView start = options.delete(:start) || 0 stop = options.delete(:end) || 59 step = options.delete(:step) || 1 - options.reverse_merge!({:leading_zeros => true, :ampm => false}) + options.reverse_merge!({:leading_zeros => true, :ampm => false, :use_two_digit_numbers => false}) leading_zeros = options.delete(:leading_zeros) select_options = [] @@ -860,7 +867,8 @@ module ActionView value = leading_zeros ? sprintf("%02d", i) : i tag_options = { :value => value } tag_options[:selected] = "selected" if selected == i - text = options[:ampm] ? AMPM_TRANSLATION[i] : value + text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value + text = options[:ampm] ? AMPM_TRANSLATION[i] : text select_options << content_tag(:option, text, tag_options) end (select_options.join("\n") + "\n").html_safe @@ -939,8 +947,9 @@ module ActionView # and join them with their appropriate separators. def build_selects_from_types(order) select = '' + first_visible = order.find { |type| !@options[:"discard_#{type}"] } order.reverse.each do |type| - separator = separator(type) unless type == order.first # don't add on last field + separator = separator(type) unless type == first_visible # don't add before first visible field select.insert(0, separator.to_s + send("select_#{type}").to_s) end select.html_safe @@ -965,7 +974,7 @@ module ActionView end end - class InstanceTag #:nodoc: + module DateHelperInstanceTag def to_date_select_tag(options = {}, html_options = {}) datetime_selector(options, html_options).select_date.html_safe end @@ -1021,6 +1030,10 @@ module ActionView end end + class InstanceTag #:nodoc: + include DateHelperInstanceTag + end + class FormBuilder def date_select(method, options = {}, html_options = {}) @template.date_select(@object_name, method, objectify_options(options), html_options) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index f22c466666..5a301ca9b1 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -2,9 +2,9 @@ require 'cgi' require 'action_view/helpers/date_helper' require 'action_view/helpers/tag_helper' require 'action_view/helpers/form_tag_helper' +require 'action_view/helpers/active_model_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' @@ -158,6 +158,9 @@ module ActionView # * <tt>:url</tt> - The URL the form is submitted to. It takes the same # fields you pass to +url_for+ or +link_to+. In particular you may pass # here a named route directly as well. Defaults to the current action. + # * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of + # id attributes on form elements. The namespace attribute will be prefixed + # with underscore on the generated HTML id. # * <tt>:html</tt> - Optional HTML attributes for the form tag. # # Also note that +form_for+ doesn't create an exclusive scope. It's still @@ -384,8 +387,8 @@ module ActionView as = options[:as] action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post] options[:html].reverse_merge!( - :class => as ? "#{as}_#{action}" : dom_class(object, action), - :id => as ? "#{as}_#{action}" : dom_id(object, action), + :class => as ? "#{action}_#{as}" : dom_class(object, action), + :id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence, :method => method ) @@ -649,7 +652,7 @@ module ActionView # # => <label for="post_privacy_public">Public Post</label> # # label(:post, :terms) do - # 'Accept <a href="/terms">Terms</a>.' + # 'Accept <a href="/terms">Terms</a>.'.html_safe # end def label(object_name, method, content_or_options = nil, options = nil, &block) content_is_options = content_or_options.is_a?(Hash) @@ -738,7 +741,7 @@ module ActionView # # => <input type="file" id="user_avatar" name="user[avatar]" /> # # file_field(:post, :attached, :accept => 'text/html') - # # => <input type="file" id="post_attached" name="post[attached]" /> + # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" /> # # file_field(:attachment, :file, :class => 'file_input') # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" /> @@ -960,7 +963,7 @@ module ActionView end class InstanceTag - include Helpers::CaptureHelper, Context, Helpers::TagHelper, Helpers::FormTagHelper + include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper attr_reader :object, :method_name, :object_name @@ -971,6 +974,7 @@ module ActionView def initialize(object_name, method_name, template_object, object = nil) @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup @template_object = template_object + @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]") @object = retrieve_object(object) @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match @@ -989,14 +993,23 @@ module ActionView add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") + options.delete("namespace") options["for"] ||= name_and_id["id"] if block_given? - label_tag(name_and_id["id"], options, &block) + @template_object.label_tag(name_and_id["id"], options, &block) else content = if text.blank? + object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1') method_and_value = tag_value.present? ? "#{method_name}.#{tag_value}" : method_name - I18n.t("helpers.label.#{object_name}.#{method_and_value}", :default => "").presence + + if object.respond_to?(:to_model) + key = object.class.model_name.i18n_key + i18n_default = ["#{key}.#{method_and_value}".to_sym, ""] + end + + i18n_default ||= "" + I18n.t("#{object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence else text.to_s end @@ -1027,6 +1040,8 @@ module ActionView def to_number_field_tag(field_type, options = {}) options = options.stringify_keys + options['size'] ||= nil + if range = options.delete("in") || options.delete("within") options.update("min" => range.min, "max" => range.max) end @@ -1185,6 +1200,7 @@ module ActionView options["name"] ||= tag_name + (options['multiple'] ? '[]' : '') options["id"] = options.fetch("id"){ tag_id } end + options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence end def tag_name @@ -1215,7 +1231,7 @@ module ActionView class FormBuilder # The methods which wrap a form helper call. class_attribute :field_helpers - self.field_helpers = FormHelper.instance_method_names - %w(form_for convert_to_model) + self.field_helpers = FormHelper.instance_methods - [:form_for, :convert_to_model] attr_accessor :object_name, :object, :options @@ -1243,7 +1259,7 @@ module ActionView @nested_child_index = {} @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc @parent_builder = options[:parent_builder] - @default_options = @options ? @options.slice(:index) : {} + @default_options = @options ? @options.slice(:index, :namespace) : {} if @object_name.to_s.match(/\[\]$/) if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param) @auto_index = object.to_param @@ -1270,6 +1286,7 @@ module ActionView 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 + fields_options[:namespace] = fields_options[:parent_builder].options[:namespace] case record_name when String, Symbol @@ -1347,6 +1364,39 @@ module ActionView @template.submit_tag(value, options) end + # Add the submit button for the given form. When no value is given, it checks + # if the object is a new resource or not to create the proper label: + # + # <%= form_for @post do |f| %> + # <%= f.button %> + # <% 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". + # + # Those labels can be customized using I18n, under the helpers.submit key and accept + # the %{model} as translation interpolation: + # + # en: + # helpers: + # button: + # create: "Create a %{model}" + # update: "Confirm changes to %{model}" + # + # It also searches for a key specific for the given object: + # + # en: + # helpers: + # button: + # post: + # create: "Add %{model}" + # + def button(value=nil, options={}) + value, options = nil, value if value.is_a?(Hash) + value ||= submit_default_value + @template.button_tag(value, options) + end + def emitted_hidden_id? @emitted_hidden_id ||= nil end diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index c677257d60..3ee0d8ebc5 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -579,7 +579,12 @@ module ActionView def to_select_tag(choices, options, html_options) selected_value = options.has_key?(:selected) ? options[:selected] : value(object) - if !choices.empty? && choices.try(:first).try(:second).respond_to?(:each) + # Grouped choices look like this: + # + # [nil, []] + # { nil => [] } + # + if !choices.empty? && choices.first.respond_to?(:last) && Array === choices.first.last option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]) else option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 13b9dc8553..bb753ae27a 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -393,21 +393,17 @@ module ActionView # submit_tag "Save edits", :disabled => true # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" /> # - # # submit_tag "Complete sale", :disable_with => "Please wait..." - # # => <input name="commit" data-disable-with="Please wait..." - # # type="submit" value="Complete sale" /> + # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" /> # # submit_tag nil, :class => "form_submit" # # => <input class="form_submit" name="commit" type="submit" /> # # submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button" - # # => <input class="edit_button" data-disable_with="Editing..." - # # name="commit" type="submit" value="Edit" /> + # # => <input class="edit_button" data-disable_with="Editing..." name="commit" type="submit" value="Edit" /> # # submit_tag "Save", :confirm => "Are you sure?" - # # => <input name='commit' type='submit' value='Save' - # data-confirm="Are you sure?" /> + # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" /> # def submit_tag(value = "Save changes", options = {}) options = options.stringify_keys @@ -451,12 +447,11 @@ module ActionView # content_tag(:strong, 'Ask me!') # end # # => <button name="button" type="button"> - # <strong>Ask me!</strong> - # </button> + # # <strong>Ask me!</strong> + # # </button> # # button_tag "Checkout", :disable_with => "Please wait..." - # # => <button data-disable-with="Please wait..." name="button" - # type="submit">Checkout</button> + # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button> # def button_tag(content_or_options = nil, options = nil, &block) options = content_or_options if block_given? && content_or_options.is_a?(Hash) @@ -582,7 +577,7 @@ module ActionView # # ==== Examples # number_field_tag 'quantity', nil, :in => 1...10 - # => <input id="quantity" name="quantity" min="1" max="9" /> + # => <input id="quantity" name="quantity" min="1" max="9" type="number" /> def number_field_tag(name, value = nil, options = {}) options = options.stringify_keys options["type"] ||= "number" diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 1adcd716f8..309923490c 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -14,11 +14,7 @@ module ActionView "'" => "\\'" } - if "ruby".encoding_aware? - JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' - else - JS_ESCAPE_MAP["\342\200\250"] = '
' - end + JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' # Escapes carriage returns and single and double quotes for JavaScript segments. # @@ -94,7 +90,7 @@ module ActionView # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all # the JavaScript is set, the helper appends "; return false;". # - # The +href+ attribute of the tag is set to "#" unles +html_options+ has one. + # The +href+ attribute of the tag is set to "#" unless +html_options+ has one. # # link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link" # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a> diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index ec6c2c8db3..1c0fdb8ff9 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -1,7 +1,6 @@ # encoding: utf-8 require 'active_support/core_ext/big_decimal/conversions' -require 'active_support/core_ext/float/rounding' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' @@ -69,7 +68,7 @@ module ActionView number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3") else number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3") - number.slice!(0, 1) if number.starts_with?('-') + number.slice!(0, 1) if number.starts_with?(delimiter) && !delimiter.blank? end str = [] @@ -100,10 +99,10 @@ module ActionView # number_to_currency(1234567890.50) # => $1,234,567,890.50 # number_to_currency(1234567890.506) # => $1,234,567,890.51 # number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506 - # number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,506 € + # number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,51 € # # number_to_currency(-1234567890.50, :negative_format => "(%u%n)") - # # => ($1,234,567,890.51) + # # => ($1,234,567,890.50) # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "") # # => £1234567890,50 # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u") @@ -193,7 +192,8 @@ module ActionView # number_with_delimiter(12345678) # => 12,345,678 # number_with_delimiter(12345678.05) # => 12,345,678.05 # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678 - # number_with_delimiter(12345678, :separator => ",") # => 12,345,678 + # number_with_delimiter(12345678, :delimiter => ",") # => 12,345,678 + # number_with_delimiter(12345678.05, :separator => " ") # => 12,345,678 05 # number_with_delimiter(12345678.05, :locale => :fr) # => 12 345 678,05 # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",") # # => 98 765 432,98 diff --git a/actionpack/lib/action_view/helpers/rendering_helper.rb b/actionpack/lib/action_view/helpers/rendering_helper.rb index 47efdded42..626e1a1ab7 100644 --- a/actionpack/lib/action_view/helpers/rendering_helper.rb +++ b/actionpack/lib/action_view/helpers/rendering_helper.rb @@ -8,7 +8,7 @@ module ActionView module RenderingHelper # Returns the result of a render that's dictated by the options hash. The primary options are: # - # * <tt>:partial</tt> - See ActionView::Partials. + # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>. # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those. # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller. # * <tt>:text</tt> - Renders the text passed in out. @@ -87,4 +87,4 @@ module ActionView end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index bcc8f6fbcb..7768c8c151 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/object/try' require 'action_controller/vendor/html-scanner' -require 'action_view/helpers/tag_helper' module ActionView # = Action View Sanitize Helpers diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 21074efe86..209360ee82 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/filters' -require 'action_view/helpers/tag_helper' module ActionView # = Action View Text Helpers @@ -31,6 +30,7 @@ module ActionView extend ActiveSupport::Concern include SanitizeHelper + include TagHelper # The preferred method of outputting text in your views is to use the # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods # do not operate as expected in an eRuby code block. If you absolutely must @@ -120,7 +120,7 @@ module ActionView text else match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') - text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter]) + text.gsub(/(#{match})(?![^<]*?>)/i, options[:highlighter]) end.html_safe end diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index be64dc823e..cc74eff53a 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -45,11 +45,18 @@ module ActionView # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) - translation = I18n.translate(scope_key_by_partial(key), options) - if html_safe_translation_key?(key) && translation.respond_to?(:html_safe) - translation.html_safe + if html_safe_translation_key?(key) + html_safe_options = options.dup + options.except(*I18n::RESERVED_KEYS).each do |name, value| + unless name == :count && value.is_a?(Numeric) + html_safe_options[name] = ERB::Util.html_escape(value.to_s) + end + end + translation = I18n.translate(scope_key_by_partial(key), html_safe_options) + + translation.respond_to?(:html_safe) ? translation.html_safe : translation else - translation + I18n.translate(scope_key_by_partial(key), options) end end alias :t :translate diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index eb816b9446..f2a83b92a9 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -152,3 +152,9 @@ update: 'Update %{model}' submit: 'Save %{model}' + # Default translation keys for button FormHelper + button: + create: 'Create %{model}' + update: 'Update %{model}' + submit: 'Save %{model}' + diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index fa4bf70f77..3f07314dda 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -20,7 +20,7 @@ module ActionView def self.register_detail(name, options = {}, &block) self.registered_details << name - initialize = registered_details.map { |n| "self.#{n} = details[:#{n}]" } + initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" } Accessors.send :define_method, :"default_#{name}", &block Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1 @@ -29,7 +29,7 @@ module ActionView end def #{name}=(value) - value = Array.wrap(value.presence || default_#{name}) + value = value.present? ? Array.wrap(value) : default_#{name} _set_detail(:#{name}, value) if value != @details[:#{name}] end @@ -44,7 +44,7 @@ module ActionView module Accessors #:nodoc: end - register_detail(:locale) { [I18n.locale, I18n.default_locale] } + register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq } register_detail(:formats) { Mime::SET.symbols } register_detail(:handlers){ Template::Handlers.extensions } @@ -56,7 +56,11 @@ module ActionView @details_keys = Hash.new def self.get(details) - @details_keys[details.freeze] ||= new + @details_keys[details] ||= new + end + + def self.clear + @details_keys.clear end def initialize @@ -85,9 +89,9 @@ module ActionView protected def _set_detail(key, value) + @details = @details.dup if @details_key @details_key = nil - @details = @details.dup if @details.frozen? - @details[key] = value.freeze + @details[key] = value end end @@ -147,27 +151,17 @@ module ActionView # as well as incorrectly putting part of the path in the template # name instead of the prefix. def normalize_name(name, prefixes) #:nodoc: - name = name.to_s.sub(handlers_regexp) do |match| - ActiveSupport::Deprecation.warn "Passing a template handler in the template name is deprecated. " \ - "You can simply remove the handler name or pass render :handlers => [:#{match[1..-1]}] instead.", caller - "" - end + prefixes = nil if prefixes.blank? + parts = name.to_s.split('/') + name = parts.pop - parts = name.split('/') - name = parts.pop + return name, prefixes || [""] if parts.empty? - prefixes = if prefixes.blank? - [parts.join('/')] - else - prefixes.map { |prefix| [prefix, *parts].compact.join('/') } - end + parts = parts.join('/') + prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts] return name, prefixes end - - def handlers_regexp #:nodoc: - @@handlers_regexp ||= /\.(?:#{default_handlers.join('|')})$/ - end end include Accessors @@ -227,7 +221,6 @@ module ActionView end # A method which only uses the first format in the formats array for layout lookup. - # This method plays straight with instance variables for performance reasons. def with_layout_format if formats.size == 1 yield diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb index c0936441ac..5a611e9f63 100644 --- a/actionpack/lib/action_view/renderer/abstract_renderer.rb +++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb @@ -22,18 +22,6 @@ module ActionView details end - def extract_format(value, details) - if value.is_a?(String) && value.sub!(formats_regexp, "") - ActiveSupport::Deprecation.warn "Passing the format in the template name is deprecated. " \ - "Please pass render with :formats => [:#{$1}] instead.", caller - details[:formats] ||= [$1.to_sym] - end - end - - def formats_regexp - @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/ - end - def instrument(name, options={}) ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield } end diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index e808fa3415..e231aade01 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -27,7 +27,7 @@ module ActionView # # == The :as and :object options # - # By default <tt>ActionView::Partials::PartialRenderer</tt> doesn't have any local variables. + # By default <tt>ActionView::PartialRenderer</tt> doesn't have any local variables. # The <tt>:object</tt> option can be used to pass an object to the partial. For instance: # # <%= render :partial => "account", :object => @buyer %> @@ -82,16 +82,18 @@ module ActionView # # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from. # - # == Rendering objects with the RecordIdentifier + # == Rendering objects that respond to `to_partial_path` # - # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if - # you're following its conventions for RecordIdentifier#partial_path. Examples: + # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work + # and pick the proper path by checking `to_proper_path` method. If the object passed to render is a collection, + # all objects must return the same path. # - # # @account is an Account instance, so it uses the RecordIdentifier to replace + # # @account.to_partial_path returns 'accounts/account', so it can be used to replace: # # <%= render :partial => "accounts/account", :locals => { :account => @account} %> # <%= render :partial => @account %> # - # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace + # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`, + # # that's why we can replace: # # <%= render :partial => "posts/post", :collection => @posts %> # <%= render :partial => @posts %> # @@ -106,13 +108,14 @@ module ActionView # # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %> # <%= render "account", :account => @buyer %> # - # # @account is an Account instance, so it uses the RecordIdentifier to replace - # # <%= render :partial => "accounts/account", :locals => { :account => @account } %> - # <%= render(@account) %> + # # @account.to_partial_path returns 'accounts/account', so it can be used to replace: + # # <%= render :partial => "accounts/account", :locals => { :account => @account} %> + # <%= render @account %> # - # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace + # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`, + # # that's why we can replace: # # <%= render :partial => "posts/post", :collection => @posts %> - # <%= render(@posts) %> + # <%= render @posts %> # # == Rendering partials with layouts # @@ -205,7 +208,7 @@ module ActionView # Deadline: <%= user.deadline %> # <%- end -%> # <% end %> - class PartialRenderer < AbstractRenderer #:nodoc: + class PartialRenderer < AbstractRenderer PARTIAL_NAMES = Hash.new { |h,k| h[k] = {} } def initialize(*) @@ -297,7 +300,6 @@ module ActionView "and is followed by any combinations of letters, numbers, or underscores.") end - extract_format(@path, @details) self end @@ -366,27 +368,26 @@ module ActionView path = if object.respond_to?(:to_partial_path) object.to_partial_path else - ActiveSupport::Deprecation.warn "ActiveModel-compatible objects whose classes return a #model_name that responds to #partial_path are deprecated. Please respond to #to_partial_path directly instead." - object.class.model_name.partial_path + raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.") end - @partial_names[path] ||= path.dup.tap do |object_path| - merge_prefix_into_object_path(@context_prefix, object_path) - end + @partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup) end def merge_prefix_into_object_path(prefix, object_path) if prefix.include?(?/) && object_path.include?(?/) - overlap = [] + prefixes = [] prefix_array = File.dirname(prefix).split('/') object_path_array = object_path.split('/')[0..-3] # skip model dir & partial prefix_array.each_with_index do |dir, index| - overlap << dir if dir == object_path_array[index] + break if dir == object_path_array[index] + prefixes << dir end - object_path.gsub!(/^#{overlap.join('/')}\//,'') - object_path.insert(0, "#{File.dirname(prefix)}/") + (prefixes << object_path).join("/") + else + object_path end end diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb index 1ccf5a8ddb..4b66f66e2d 100644 --- a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb @@ -1,7 +1,7 @@ # 1.9 ships with Fibers but we need to require the extra # methods explicitly. We only load those extra methods if # Fiber is available in the first place. -require 'fiber' if defined?(Fiber) +require 'fiber' module ActionView # == TODO diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index ac91d333ba..06148ccc98 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -6,7 +6,6 @@ module ActionView def render(context, options) @view = context @details = extract_details(options) - extract_format(options[:file] || options[:template], @details) template = determine_template(options) freeze_formats(template.formats, true) render_template(template, options[:layout], options[:locals]) @@ -26,6 +25,8 @@ module ActionView elsif options.key?(:template) options[:template].respond_to?(:render) ? options[:template] : find_template(options[:template], options[:prefixes], false, keys, @details) + else + raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option." end end @@ -58,14 +59,28 @@ module ActionView # 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, keys) - begin - with_layout_format do - layout =~ /^\// ? - with_fallbacks { find_template(layout, nil, false, keys, @details) } : find_template(layout, nil, false, keys, @details) + with_layout_format { resolve_layout(layout, keys) } + end + + def resolve_layout(layout, keys) + case layout + when String + begin + if layout =~ /^\// + with_fallbacks { find_template(layout, nil, false, keys, @details) } + else + find_template(layout, nil, false, keys, @details) + end + rescue ActionView::MissingTemplate + all_details = @details.merge(:formats => @lookup_context.default_formats) + raise unless template_exists?(layout, nil, false, keys, all_details) end - rescue ActionView::MissingTemplate - all_details = @details.merge(:formats => @lookup_context.default_formats) - raise unless template_exists?(layout, nil, false, keys, all_details) + when Proc + resolve_layout(layout.call, keys) + when FalseClass + nil + else + layout end end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 10797c010f..2d9fc3df7a 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -173,6 +173,50 @@ module ActionView @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier end + # This method is responsible for properly setting the encoding of the + # source. Until this point, we assume that the source is BINARY data. + # If no additional information is supplied, we assume the encoding is + # the same as <tt>Encoding.default_external</tt>. + # + # The user can also specify the encoding via a comment on the first + # line of the template (# encoding: NAME-OF-ENCODING). This will work + # with any template engine, as we process out the encoding comment + # before passing the source on to the template engine, leaving a + # blank line in its stead. + def encode! + return unless source.encoding == Encoding::BINARY + + # Look for # encoding: *. If we find one, we'll encode the + # String in that encoding, otherwise, we'll use the + # default external encoding. + if source.sub!(/\A#{ENCODING_FLAG}/, '') + encoding = magic_encoding = $1 + else + encoding = Encoding.default_external + end + + # Tag the source with the default external encoding + # or the encoding specified in the file + source.force_encoding(encoding) + + # If the user didn't specify an encoding, and the handler + # handles encodings, we simply pass the String as is to + # the handler (with the default_external tag) + if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding? + source + # Otherwise, if the String is valid in the encoding, + # encode immediately to default_internal. This means + # that if a handler doesn't handle encodings, it will + # always get Strings in the default_internal + elsif source.valid_encoding? + source.encode! + # Otherwise, since the String is invalid in the encoding + # specified, raise an exception + else + raise WrongEncodingError.new(source, encoding) + end + end + protected # Compile a template. This method ensures a template is compiled @@ -195,15 +239,7 @@ module ActionView end # Among other things, this method is responsible for properly setting - # the encoding of the source. Until this point, we assume that the - # source is BINARY data. If no additional information is supplied, - # we assume the encoding is the same as <tt>Encoding.default_external</tt>. - # - # The user can also specify the encoding via a comment on the first - # line of the template (# encoding: NAME-OF-ENCODING). This will work - # with any template engine, as we process out the encoding comment - # before passing the source on to the template engine, leaving a - # blank line in its stead. + # the encoding of the compiled template. # # If the template engine handles encodings, we send the encoded # String to the engine without further processing. This allows @@ -215,40 +251,8 @@ module ActionView # In general, this means that templates will be UTF-8 inside of Rails, # regardless of the original source encoding. def compile(view, mod) #:nodoc: + encode! method_name = self.method_name - - if source.encoding_aware? - # Look for # encoding: *. If we find one, we'll encode the - # String in that encoding, otherwise, we'll use the - # default external encoding. - if source.sub!(/\A#{ENCODING_FLAG}/, '') - encoding = magic_encoding = $1 - else - encoding = Encoding.default_external - end - - # Tag the source with the default external encoding - # or the encoding specified in the file - source.force_encoding(encoding) - - # If the user didn't specify an encoding, and the handler - # handles encodings, we simply pass the String as is to - # the handler (with the default_external tag) - if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding? - source - # Otherwise, if the String is valid in the encoding, - # encode immediately to default_internal. This means - # that if a handler doesn't handle encodings, it will - # always get Strings in the default_internal - elsif source.valid_encoding? - source.encode! - # Otherwise, since the String is invalid in the encoding - # specified, raise an exception - else - raise WrongEncodingError.new(source, encoding) - end - end - code = @handler.call(self) # Make sure that the resulting String to be evalled is in the @@ -261,20 +265,18 @@ module ActionView end end_src - if source.encoding_aware? - # Make sure the source is in the encoding of the returned code - source.force_encoding(code.encoding) + # Make sure the source is in the encoding of the returned code + source.force_encoding(code.encoding) - # In case we get back a String from a handler that is not in - # BINARY or the default_internal, encode it to the default_internal - source.encode! + # In case we get back a String from a handler that is not in + # BINARY or the default_internal, encode it to the default_internal + source.encode! - # Now, validate that the source we got back from the template - # handler is valid in the default_internal. This is for handlers - # that handle encoding but screw up - unless source.valid_encoding? - raise WrongEncodingError.new(@source, Encoding.default_internal) - end + # Now, validate that the source we got back from the template + # handler is valid in the default_internal. This is for handlers + # that handle encoding but screw up + unless source.valid_encoding? + raise WrongEncodingError.new(@source, Encoding.default_internal) end begin @@ -297,7 +299,11 @@ module ActionView raise e else assigns = view.respond_to?(:assigns) ? view.assigns : {} - template = @virtual_path ? refresh(view) : self + template = self + unless template.source + template = refresh(view) + template.encode! + end raise Template::Error.new(template, assigns, e) end end diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index 2c52cfd90e..34397c3bcf 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -6,12 +6,21 @@ module ActionView self.default_format = Mime::XML def call(template) - require 'builder' + require_engine "xml = ::Builder::XmlMarkup.new(:indent => 2);" + "self.output_buffer = xml.target!;" + template.source + ";xml.target!;" end + + protected + + def require_engine + @required ||= begin + require "builder" + true + end + end end end end diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index 77720e2bc8..323df67c97 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -1,5 +1,5 @@ require 'action_dispatch/http/mime_type' -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/class/attribute' require 'erubis' module ActionView @@ -67,23 +67,19 @@ module ActionView end def call(template) - if template.source.encoding_aware? - # First, convert to BINARY, so in case the encoding is - # wrong, we can still find an encoding tag - # (<%# encoding %>) inside the String using a regular - # expression - template_source = template.source.dup.force_encoding("BINARY") + # First, convert to BINARY, so in case the encoding is + # wrong, we can still find an encoding tag + # (<%# encoding %>) inside the String using a regular + # expression + template_source = template.source.dup.force_encoding("BINARY") - erb = template_source.gsub(ENCODING_TAG, '') - encoding = $2 + erb = template_source.gsub(ENCODING_TAG, '') + encoding = $2 - erb.force_encoding valid_encoding(template.source.dup, encoding) + erb.force_encoding valid_encoding(template.source.dup, encoding) - # Always make sure we return a String in the default_internal - erb.encode! - else - erb = template.source.dup - end + # Always make sure we return a String in the default_internal + erb.encode! self.class.erb_implementation.new( erb, diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index f855ea257c..7fa86866a7 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -1,6 +1,5 @@ require "pathname" require "active_support/core_ext/class" -require "active_support/core_ext/io" require "action_view/template" module ActionView diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 1db079d0e3..9ebe498192 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -50,7 +50,12 @@ module ActionView module ClassMethods def tests(helper_class) - self.helper_class = helper_class + case helper_class + when String, Symbol + self.helper_class = "#{helper_class.to_s.underscore}_helper".camelize.safe_constantize + when Module + self.helper_class = helper_class + end end def determine_default_helper_class(name) |