diff options
Diffstat (limited to 'actionview/lib/action_view')
29 files changed, 217 insertions, 136 deletions
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index caade8f43b..455ce531ae 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -1,5 +1,5 @@ require 'active_support/core_ext/module/attr_internal' -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' require 'active_support/ordered_options' require 'action_view/log_subscriber' require 'action_view/helpers' @@ -153,6 +153,10 @@ module ActionView #:nodoc: # Specify default_formats that can be rendered. cattr_accessor :default_formats + # Specify whether an error should be raised for missing translations + cattr_accessor :raise_on_missing_translations + @@raise_on_missing_translations = false + class_attribute :_routes class_attribute :logger diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb index b2e8334077..0ccf2515c5 100644 --- a/actionview/lib/action_view/dependency_tracker.rb +++ b/actionview/lib/action_view/dependency_tracker.rb @@ -1,7 +1,7 @@ require 'thread_safe' module ActionView - class DependencyTracker + class DependencyTracker # :nodoc: @trackers = ThreadSafe::Cache.new def self.find_dependencies(name, template) @@ -23,24 +23,52 @@ module ActionView @trackers.delete(handler) end - class ERBTracker + class ERBTracker # :nodoc: EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/ + # A valid ruby identifier - suitable for class, method and specially variable names + IDENTIFIER = / + [[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore + [[:word:]]* # followed by optional letters, numbers or underscores + /x + + # Any kind of variable name. e.g. @instance, @@class, $global or local. + # Possibly following a method call chain + VARIABLE_OR_METHOD_CHAIN = / + (?:\$|@{1,2})? # optional global, instance or class variable indicator + (?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls + (?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC + /x + + # A simple string literal. e.g. "School's out!" + STRING = / + (?<quote>['"]) # an opening quote + (?<static>.*?) # with anything inside, captured as STATIC + \k<quote> # and a matching closing quote + /x + + # Part of any hash containing the :partial key + PARTIAL_HASH_KEY = / + (?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax + \s* # followed by optional spaces + /x + # Matches: - # render partial: "comments/comment", collection: commentable.comments - # render "comments/comments" - # render 'comments/comments' - # render('comments/comments') + # partial: "comments/comment", collection: @all_comments => "comments/comment" + # (object: @single_comment, partial: "comments/comment") => "comments/comment" # - # render(@topic) => render("topics/topic") - # render(topics) => render("topics/topic") - # render(message.topics) => render("topics/topic") - RENDER_DEPENDENCY = / - render\s* # render, followed by optional whitespace - \(? # start an optional parenthesis for the render call - (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture - ([@a-z"'][@\w\/\."']+) # the template name itself -- 2nd capture - /x + # "comments/comments" + # 'comments/comments' + # ('comments/comments') + # + # (@topic) => "topics/topic" + # topics => "topics/topic" + # (message.topics) => "topics/topic" + RENDER_ARGUMENTS = /\A + (?:\s*\(?\s*) # optional opening paren surrounded by spaces + (?:.*?#{PARTIAL_HASH_KEY})? # optional hash, up to the partial key declaration + (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest + /xm def self.call(name, template) new(name, template).dependencies @@ -68,19 +96,33 @@ module ActionView end def render_dependencies - source.scan(RENDER_DEPENDENCY). - collect(&:second).uniq. + render_dependencies = [] + render_calls = source.split(/\brender\b/).drop(1) - # render(@topic) => render("topics/topic") - # render(topics) => render("topics/topic") - # render(message.topics) => render("topics/topic") - collect { |name| name.sub(/\A@?([a-z_]+\.)*([a-z_]+)\z/) { "#{$2.pluralize}/#{$2.singularize}" } }. + render_calls.each do |arguments| + arguments.scan(RENDER_ARGUMENTS) do + add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic]) + add_static_dependency(render_dependencies, Regexp.last_match[:static]) + end + end - # render("headline") => render("message/headline") - collect { |name| name.include?("/") ? name : "#{directory}/#{name}" }. + render_dependencies.uniq + end + + def add_dynamic_dependency(dependencies, dependency) + if dependency + dependencies << "#{dependency.pluralize}/#{dependency.singularize}" + end + end - # replace quotes from string renders - collect { |name| name.gsub(/["']/, "") } + def add_static_dependency(dependencies, dependency) + if dependency + if dependency.include?('/') + dependencies << dependency + else + dependencies << "#{directory}/#{dependency}" + end + end end def explicit_dependencies diff --git a/actionview/lib/action_view/helpers.rb b/actionview/lib/action_view/helpers.rb index 8a78685ae1..787e9d67b2 100644 --- a/actionview/lib/action_view/helpers.rb +++ b/actionview/lib/action_view/helpers.rb @@ -27,6 +27,12 @@ module ActionView #:nodoc: autoload :TextHelper autoload :TranslationHelper autoload :UrlHelper + autoload :Tags + + def self.eager_load! + super + Tags.eager_load! + end extend ActiveSupport::Concern diff --git a/actionview/lib/action_view/helpers/active_model_helper.rb b/actionview/lib/action_view/helpers/active_model_helper.rb index 901f433c70..d5222e3616 100644 --- a/actionview/lib/action_view/helpers/active_model_helper.rb +++ b/actionview/lib/action_view/helpers/active_model_helper.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/enumerable' module ActionView diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index 2a44ae5d5c..aa49f1edc1 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -103,7 +103,7 @@ module ActionView }.join("\n").html_safe end - # Returns a link tag that browsers and news readers can use to auto-detect + # Returns a link tag that browsers and feed readers can use to auto-detect # an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or # <tt>:atom</tt>. Control the link options in url_for format using the # +url_options+. You can modify the LINK tag itself in +tag_options+. @@ -176,7 +176,7 @@ module ActionView # ==== Options # # You can add HTML attributes using the +options+. The +options+ supports - # three additional keys for convenience and conformance: + # two additional keys for convenience and conformance: # # * <tt>:alt</tt> - If no alt text is given, the file name part of the # +source+ is used (capitalized and without the extension) @@ -207,11 +207,7 @@ module ActionView options[:alt] = options.fetch(:alt){ image_alt(src) } end - if size = options.delete(:size) - options[:width], options[:height] = size.split("x") if size =~ %r{\A\d+x\d+\z} - options[:width] = options[:height] = size if size =~ %r{\A\d+\z} - end - + options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size] tag("img", options) end @@ -248,9 +244,9 @@ module ActionView # # * <tt>:poster</tt> - Set an image (like a screenshot) to be shown # before the video loads. The path is calculated like the +src+ of +image_tag+. - # * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes - # width="30" and height="45". <tt>:size</tt> will be ignored if the - # value is not in the correct format. + # * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes + # width="30" and height="45", and "50" becomes width="50" and height="50". + # <tt>:size</tt> will be ignored if the value is not in the correct format. # # ==== Examples # @@ -264,6 +260,8 @@ module ActionView # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" /> # video_tag("/trailers/hd.avi", size: "16x16") # # => <video src="/trailers/hd.avi" width="16" height="16" /> + # video_tag("/trailers/hd.avi", size: "16") + # # => <video height="16" src="/trailers/hd.avi" width="16" /> # video_tag("/trailers/hd.avi", height: '32', width: '32') # # => <video height="32" src="/trailers/hd.avi" width="32" /> # video_tag("trailer.ogg", "trailer.flv") @@ -275,10 +273,7 @@ module ActionView def video_tag(*sources) multiple_sources_tag('video', sources) do |options| options[:poster] = path_to_image(options[:poster]) if options[:poster] - - if size = options.delete(:size) - options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$} - end + options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size] end end @@ -314,6 +309,14 @@ module ActionView content_tag(type, nil, options) end end + + def extract_dimensions(size) + if size =~ %r{\A\d+x\d+\z} + size.split('x') + elsif size =~ %r{\A\d+\z} + [size, size] + end + end end end end diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 36e4c5725f..3d091c4a00 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -115,7 +115,7 @@ module ActionView # e.g. if there are 20 leap year days between 2 dates having the same day # and month then the based on 365 days calculation # the distance in years will come out to over 80 years when in written - # english it would read better as about 80 years. + # English it would read better as about 80 years. minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year else minutes_with_offset = distance_in_minutes @@ -170,8 +170,12 @@ module ActionView # * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names. # Note: You can also use Rails' i18n functionality for this. # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing). - # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>. - # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>. + # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt>if + # you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to + # the current selected year minus 5. + # * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if + # you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to + # the current selected year plus 5. # * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day # as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the # first of the given month in order to not create invalid dates like 31 February. @@ -1062,7 +1066,7 @@ module ActionView # Wraps ActionView::Helpers::DateHelper#datetime_select for form builders: # # <%= form_for @person do |f| %> - # <%= f.time_select :last_request_at %> + # <%= f.datetime_select :last_request_at %> # <%= f.submit %> # <% end %> # diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 38d969ed0c..5235962f9f 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -3,9 +3,8 @@ 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 'action_view/helpers/tags' require 'action_view/model_naming' -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/string/inflections' @@ -52,7 +51,7 @@ module ActionView # The HTML generated for this would be (modulus formatting): # # <form action="/people" class="new_person" id="new_person" method="post"> - # <div style="margin:0;padding:0;display:inline"> + # <div style="display:none"> # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # </div> # <label for="person_first_name">First name</label>: @@ -82,7 +81,7 @@ module ActionView # the code above as is would yield instead: # # <form action="/people/256" class="edit_person" id="edit_person_256" method="post"> - # <div style="margin:0;padding:0;display:inline"> + # <div style="display:none"> # <input name="_method" type="hidden" value="patch" /> # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # </div> @@ -316,7 +315,7 @@ module ActionView # The HTML generated for this would be: # # <form action='http://www.example.com' method='post' data-remote='true'> - # <div style='margin:0;padding:0;display:inline'> + # <div style='display:none'> # <input name='_method' type='hidden' value='patch' /> # </div> # ... @@ -334,7 +333,7 @@ module ActionView # The HTML generated for this would be: # # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'> - # <div style='margin:0;padding:0;display:inline'> + # <div style='display:none'> # <input name='_method' type='hidden' value='patch' /> # </div> # ... @@ -763,8 +762,8 @@ module ActionView # text_field(:post, :title, class: "create_input") # # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" /> # - # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }") - # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }"/> + # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }") + # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/> # # text_field(:snippet, :code, size: 20, class: 'code_input') # # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" /> diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb index 4347983bad..f625a9ff49 100644 --- a/actionview/lib/action_view/helpers/form_options_helper.rb +++ b/actionview/lib/action_view/helpers/form_options_helper.rb @@ -260,7 +260,7 @@ module ActionView Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render end - # Return select and option tags for the given object and method, using + # Returns select and option tags for the given object and method, using # #time_zone_options_for_select to generate the list of option tags. # # In addition to the <tt>:include_blank</tt> option documented above, diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 142c27ace0..80f066b3be 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -465,7 +465,7 @@ module ActionView # # <strong>Ask me!</strong> # # </button> # - # button_tag "Checkout", data: { disable_with => "Please wait..." } + # button_tag "Checkout", data: { :disable_with => "Please wait..." } # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button> # def button_tag(content_or_options = nil, options = nil, &block) @@ -722,7 +722,7 @@ module ActionView enforce_utf8 = html_options.delete("enforce_utf8") { true } tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag - content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline') + content_tag(:div, tags, :style => 'display:none') end def form_tag_html(html_options) diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb index 9adc2c1a8f..ad825cd1f1 100644 --- a/actionview/lib/action_view/helpers/number_helper.rb +++ b/actionview/lib/action_view/helpers/number_helper.rb @@ -100,10 +100,10 @@ module ActionView # # number_to_currency(-1234567890.50, negative_format: "(%u%n)") # # => ($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") - # # => 1234567890,50 £ + # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "") + # # => R$1234567890,50 + # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u") + # # => 1234567890,50 R$ def number_to_currency(number, options = {}) delegate_number_helper_method(:number_to_currency, number, options) end @@ -394,6 +394,7 @@ module ActionView def escape_unsafe_delimiters_and_separators(options) options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator] && !options[:separator].html_safe? options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter] && !options[:delimiter].html_safe? + options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe? options end diff --git a/actionview/lib/action_view/helpers/tags.rb b/actionview/lib/action_view/helpers/tags.rb index a05e16979a..45c75d10c0 100644 --- a/actionview/lib/action_view/helpers/tags.rb +++ b/actionview/lib/action_view/helpers/tags.rb @@ -3,37 +3,39 @@ module ActionView module Tags #:nodoc: extend ActiveSupport::Autoload - autoload :Base - autoload :CheckBox - autoload :CollectionCheckBoxes - autoload :CollectionRadioButtons - autoload :CollectionSelect - autoload :ColorField - autoload :DateField - autoload :DateSelect - autoload :DatetimeField - autoload :DatetimeLocalField - autoload :DatetimeSelect - autoload :EmailField - autoload :FileField - autoload :GroupedCollectionSelect - autoload :HiddenField - autoload :Label - autoload :MonthField - autoload :NumberField - autoload :PasswordField - autoload :RadioButton - autoload :RangeField - autoload :SearchField - autoload :Select - autoload :TelField - autoload :TextArea - autoload :TextField - autoload :TimeField - autoload :TimeSelect - autoload :TimeZoneSelect - autoload :UrlField - autoload :WeekField + eager_autoload do + autoload :Base + autoload :CheckBox + autoload :CollectionCheckBoxes + autoload :CollectionRadioButtons + autoload :CollectionSelect + autoload :ColorField + autoload :DateField + autoload :DateSelect + autoload :DatetimeField + autoload :DatetimeLocalField + autoload :DatetimeSelect + autoload :EmailField + autoload :FileField + autoload :GroupedCollectionSelect + autoload :HiddenField + autoload :Label + autoload :MonthField + autoload :NumberField + autoload :PasswordField + autoload :RadioButton + autoload :RangeField + autoload :SearchField + autoload :Select + autoload :TelField + autoload :TextArea + autoload :TextField + autoload :TimeField + autoload :TimeSelect + autoload :TimeZoneSelect + autoload :UrlField + autoload :WeekField + end end end end diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb index 787039c82e..991f32cea2 100644 --- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb +++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb @@ -18,7 +18,7 @@ module ActionView end def label(label_html_options={}, &block) - html_options = label_html_options.merge(@input_html_options) + html_options = @input_html_options.slice(:index, :namespace).merge(label_html_options) @template_object.label(@object_name, @sanitized_attribute_name, @text, html_options, &block) end end diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb index 180aa9ac27..35d3ba8434 100644 --- a/actionview/lib/action_view/helpers/tags/label.rb +++ b/actionview/lib/action_view/helpers/tags/label.rb @@ -30,7 +30,6 @@ module ActionView add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") options.delete("namespace") - options.delete("multiple") options["for"] = name_and_id["id"] unless options.key?("for") if block_given? diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb index b0e4aa3cd3..7cfbca5b6f 100644 --- a/actionview/lib/action_view/helpers/text_helper.rb +++ b/actionview/lib/action_view/helpers/text_helper.rb @@ -31,6 +31,8 @@ module ActionView include SanitizeHelper include TagHelper + include OutputSafetyHelper + # 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 @@ -80,6 +82,9 @@ module ActionView # # => "And they f... (continued)" # # truncate("<p>Once upon a time in a world far far away</p>") + # # => "<p>Once upon a time in a wo..." + # + # truncate("<p>Once upon a time in a world far far away</p>", escape: false) # # => "<p>Once upon a time in a wo..." # # truncate("Once upon a time in a world far far away") { link_to "Continue", "#" } @@ -268,7 +273,7 @@ module ActionView content_tag(wrapper_tag, nil, html_options) else paragraphs.map! { |paragraph| - content_tag(wrapper_tag, paragraph, html_options, false) + content_tag(wrapper_tag, raw(paragraph), html_options) }.join("\n\n").html_safe end end @@ -314,7 +319,7 @@ module ActionView options = values.extract_options! name = options.fetch(:name, 'default') - values.unshift(first_value) + values.unshift(*first_value) cycle = get_cycle(name) unless cycle && cycle.values == values diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index ad8eb47f1f..0bc40874d9 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -1,24 +1,14 @@ require 'action_view/helpers/tag_helper' require 'i18n/exceptions' -module I18n - class ExceptionHandler - include Module.new { - def call(exception, locale, key, options) - exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super - end - } - end -end - module ActionView # = Action View Translation Helpers module Helpers module TranslationHelper # Delegates to <tt>I18n#translate</tt> but also performs three additional functions. # - # First, it'll pass the <tt>rescue_format: :html</tt> option to I18n so that any - # thrown +MissingTranslation+ messages will be turned into inline spans that + # First, it will ensure that any thrown +MissingTranslation+ messages will be turned + # into inline spans that: # # * have a "translation-missing" class set, # * contain the missing key as a title attribute and @@ -44,8 +34,17 @@ module ActionView # 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 = {}) - options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) options[:default] = wrap_translate_defaults(options[:default]) if options[:default] + + # If the user has specified rescue_format then pass it all through, otherwise use + # raise and do the work ourselves + options[:raise] ||= ActionView::Base.raise_on_missing_translations + + raise_error = options[:raise] || options.key?(:rescue_format) + unless raise_error + options[:raise] = true + end + if html_safe_translation_key?(key) html_safe_options = options.dup options.except(*I18n::RESERVED_KEYS).each do |name, value| @@ -59,6 +58,11 @@ module ActionView else I18n.translate(scope_key_by_partial(key), options) end + rescue I18n::MissingTranslationData => e + raise e if raise_error + + keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope]) + content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}") end alias :t :translate diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index f9d5b97fe3..76c9890776 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -1,6 +1,7 @@ require 'thread_safe' require 'active_support/core_ext/module/remove_method' require 'active_support/core_ext/module/attribute_accessors' +require 'action_view/template/resolver' module ActionView # = Action View Lookup Context @@ -52,6 +53,7 @@ module ActionView locales end register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] } + register_detail(:variants) { [] } register_detail(:handlers){ Template::Handlers.extensions } class DetailsKey #:nodoc: @@ -62,6 +64,13 @@ module ActionView @details_keys = ThreadSafe::Cache.new def self.get(details) + if details[:formats] + details = details.dup + syms = Set.new Mime::SET.symbols + details[:formats] = details[:formats].select { |v| + syms.include? v + } + end @details_keys[details] ||= new end diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb index c2783f6377..81f9c40b85 100644 --- a/actionview/lib/action_view/railtie.rb +++ b/actionview/lib/action_view/railtie.rb @@ -36,19 +36,12 @@ module ActionView end end - initializer "action_view.setup_action_pack", before: :add_view_paths do |app| + initializer "action_view.setup_action_pack" do |app| ActiveSupport.on_load(:action_controller) do - ActionController::Base.superclass.send(:include, ActionView::Layouts) ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor) end end - initializer "action_view.setup_action_mailer", before: :add_view_paths do |app| - ActiveSupport.on_load(:action_mailer) do - ActionMailer::Base.send(:include, ActionView::Layouts) - end - end - rake_tasks do load "action_view/tasks/dependencies.rake" end diff --git a/actionview/lib/action_view/renderer/streaming_template_renderer.rb b/actionview/lib/action_view/renderer/streaming_template_renderer.rb index 9cf6eb0c65..3ab2cd36fc 100644 --- a/actionview/lib/action_view/renderer/streaming_template_renderer.rb +++ b/actionview/lib/action_view/renderer/streaming_template_renderer.rb @@ -58,7 +58,7 @@ module ActionView def delayed_render(buffer, template, layout, view, locals) # Wrap the given buffer in the StreamingBuffer and pass it to the - # underlying template handler. Now, everytime something is concatenated + # underlying template handler. Now, every time something is concatenated # to the buffer, it is not appended to an array, but streamed straight # to the client. output = ActionView::StreamingBuffer.new(buffer) diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index 82db9e26df..99b95fdfb7 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -88,10 +88,14 @@ module ActionView private - # Find and renders a template based on the options given. + # Find and render a template based on the options given. # :api: private def _render_template(options) #:nodoc: + variant = options[:variant] + lookup_context.rendered_format = nil if options[:formats] + lookup_context.variants = [variant] if variant + view_renderer.render(view_context, options) end diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb index 7b4b5e13e0..743ef6de0a 100644 --- a/actionview/lib/action_view/template/error.rb +++ b/actionview/lib/action_view/template/error.rb @@ -41,6 +41,9 @@ module ActionView 'template' end + if partial && path.present? + path = path.sub(%r{([^/]+)$}, "_\\1") + end searched_paths = prefixes.map { |prefix| [prefix, path].join("/") } out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}. Searched in:\n" diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb index c8a0059596..4523060442 100644 --- a/actionview/lib/action_view/template/handlers/erb.rb +++ b/actionview/lib/action_view/template/handlers/erb.rb @@ -18,7 +18,7 @@ module ActionView src << "@output_buffer.safe_append='" src << "\n" * @newline_pending if @newline_pending > 0 src << escape_text(text) - src << "';" + src << "'.freeze;" @newline_pending = 0 end @@ -67,7 +67,7 @@ module ActionView def flush_newline_if_pending(src) if @newline_pending > 0 - src << "@output_buffer.safe_append='#{"\n" * @newline_pending}';" + src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;" @newline_pending = 0 end end diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 3304605c1a..3a3b74cdd5 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -1,6 +1,6 @@ require "pathname" require "active_support/core_ext/class" -require "active_support/core_ext/class/attribute_accessors" +require "active_support/core_ext/module/attribute_accessors" require "action_view/template" require "thread" require "thread_safe" @@ -162,8 +162,8 @@ module ActionView # An abstract class that implements a Resolver with path semantics. class PathResolver < Resolver #:nodoc: - EXTENSIONS = [:locale, :formats, :handlers] - DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}" + EXTENSIONS = { :locale => ".", :formats => ".", :variants => "+", :handlers => "." } + DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}" def initialize(pattern=nil) @pattern = pattern || DEFAULT_PATTERN @@ -240,7 +240,9 @@ module ActionView end handler = Template.handler_for_extension(extension) - format = pieces.last && Template::Types[pieces.last] + format = pieces.last && pieces.last.split(EXTENSIONS[:variants], 2).first # remove variant from format + format &&= Template::Types[format] + [handler, format] end end @@ -303,12 +305,13 @@ module ActionView # An Optimized resolver for Rails' most common case. class OptimizedFileSystemResolver < FileSystemResolver #:nodoc: def build_query(path, details) - exts = EXTENSIONS.map { |ext| details[ext] } query = escape_entry(File.join(@path, path)) - query + exts.map { |ext| - "{#{ext.compact.uniq.map { |e| ".#{e}," }.join}}" - }.join + exts = EXTENSIONS.map do |ext, prefix| + "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}" + end.join + + query + exts end end diff --git a/actionview/lib/action_view/template/types.rb b/actionview/lib/action_view/template/types.rb index db77cb5d19..b84e0281ae 100644 --- a/actionview/lib/action_view/template/types.rb +++ b/actionview/lib/action_view/template/types.rb @@ -1,5 +1,5 @@ require 'set' -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' module ActionView class Template diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb index 7afa2fa613..af53ad3b25 100644 --- a/actionview/lib/action_view/testing/resolvers.rb +++ b/actionview/lib/action_view/testing/resolvers.rb @@ -21,7 +21,7 @@ module ActionView #:nodoc: def query(path, exts, formats) query = "" - EXTENSIONS.each do |ext| + EXTENSIONS.each_key do |ext| query << '(' << exts[ext].map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)' end query = /^(#{Regexp.escape(path)})#{query}$/ diff --git a/actionview/lib/action_view/vendor/html-scanner/html/node.rb b/actionview/lib/action_view/vendor/html-scanner/html/node.rb index 7e7cd4f7b6..27f0f2f6f8 100644 --- a/actionview/lib/action_view/vendor/html-scanner/html/node.rb +++ b/actionview/lib/action_view/vendor/html-scanner/html/node.rb @@ -71,12 +71,12 @@ module HTML #:nodoc: @line, @position = line, pos end - # Return a textual representation of the node. + # Returns a textual representation of the node. def to_s @children.join() end - # Return false (subclasses must override this to provide specific matching + # Returns false (subclasses must override this to provide specific matching # behavior.) +conditions+ may be of any type. def match(conditions) false diff --git a/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb b/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb index 30b6b8b141..ed34eecf55 100644 --- a/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb +++ b/actionview/lib/action_view/vendor/html-scanner/html/sanitizer.rb @@ -1,6 +1,6 @@ require 'set' require 'cgi' -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' module HTML class Sanitizer diff --git a/actionview/lib/action_view/vendor/html-scanner/html/selector.rb b/actionview/lib/action_view/vendor/html-scanner/html/selector.rb index 7f8609c408..dfdd724b9b 100644 --- a/actionview/lib/action_view/vendor/html-scanner/html/selector.rb +++ b/actionview/lib/action_view/vendor/html-scanner/html/selector.rb @@ -488,7 +488,7 @@ module HTML end - # Return the next element after this one. Skips sibling text nodes. + # Returns the next element after this one. Skips sibling text nodes. # # With the +name+ argument, returns the next element with that name, # skipping other sibling elements. diff --git a/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb b/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb index 8ac8d34430..adf4e45930 100644 --- a/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb +++ b/actionview/lib/action_view/vendor/html-scanner/html/tokenizer.rb @@ -30,7 +30,7 @@ module HTML #:nodoc: @current_line = 1 end - # Return the next token in the sequence, or +nil+ if there are no more tokens in + # Returns the next token in the sequence, or +nil+ if there are no more tokens in # the stream. def next return nil if @scanner.eos? diff --git a/actionview/lib/action_view/version.rb b/actionview/lib/action_view/version.rb index 094dd474df..edb6d8f116 100644 --- a/actionview/lib/action_view/version.rb +++ b/actionview/lib/action_view/version.rb @@ -1,7 +1,7 @@ module ActionView # Returns the version of the currently loaded ActionView as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta" + Gem::Version.new "4.1.0.beta1" end module VERSION #:nodoc: |