actionview 5.2.4.1 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +193 -79
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -2
- data/lib/action_view.rb +3 -2
- data/lib/action_view/base.rb +107 -10
- data/lib/action_view/buffers.rb +15 -0
- data/lib/action_view/cache_expiry.rb +54 -0
- data/lib/action_view/context.rb +5 -9
- data/lib/action_view/digestor.rb +12 -20
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers.rb +0 -2
- data/lib/action_view/helpers/asset_tag_helper.rb +7 -30
- data/lib/action_view/helpers/asset_url_helper.rb +4 -3
- data/lib/action_view/helpers/cache_helper.rb +18 -10
- data/lib/action_view/helpers/capture_helper.rb +4 -0
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +69 -25
- data/lib/action_view/helpers/form_helper.rb +238 -6
- data/lib/action_view/helpers/form_options_helper.rb +27 -18
- data/lib/action_view/helpers/form_tag_helper.rb +12 -9
- data/lib/action_view/helpers/javascript_helper.rb +9 -8
- data/lib/action_view/helpers/number_helper.rb +5 -0
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/rendering_helper.rb +6 -4
- data/lib/action_view/helpers/sanitize_helper.rb +12 -18
- data/lib/action_view/helpers/tag_helper.rb +7 -6
- data/lib/action_view/helpers/tags/base.rb +9 -5
- data/lib/action_view/helpers/tags/color_field.rb +1 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -6
- data/lib/action_view/helpers/text_helper.rb +3 -3
- data/lib/action_view/helpers/translation_helper.rb +16 -12
- data/lib/action_view/helpers/url_helper.rb +14 -14
- data/lib/action_view/layouts.rb +5 -5
- data/lib/action_view/log_subscriber.rb +6 -6
- data/lib/action_view/lookup_context.rb +73 -31
- data/lib/action_view/path_set.rb +5 -10
- data/lib/action_view/railtie.rb +24 -1
- data/lib/action_view/record_identifier.rb +2 -2
- data/lib/action_view/renderer/abstract_renderer.rb +56 -3
- data/lib/action_view/renderer/partial_renderer.rb +66 -55
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +62 -16
- data/lib/action_view/renderer/renderer.rb +16 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +5 -5
- data/lib/action_view/renderer/template_renderer.rb +24 -18
- data/lib/action_view/rendering.rb +51 -31
- data/lib/action_view/routing_url_for.rb +12 -11
- data/lib/action_view/template.rb +102 -70
- data/lib/action_view/template/error.rb +21 -1
- data/lib/action_view/template/handlers.rb +27 -1
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb.rb +17 -7
- data/lib/action_view/template/handlers/erb/erubi.rb +7 -3
- data/lib/action_view/template/handlers/html.rb +1 -1
- data/lib/action_view/template/handlers/raw.rb +2 -2
- data/lib/action_view/template/html.rb +14 -5
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +28 -0
- data/lib/action_view/template/resolver.rb +136 -133
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/text.rb +5 -3
- data/lib/action_view/test_case.rb +1 -1
- data/lib/action_view/testing/resolvers.rb +33 -20
- data/lib/action_view/unbound_template.rb +32 -0
- data/lib/action_view/view_paths.rb +25 -1
- data/lib/assets/compiled/rails-ujs.js +29 -3
- metadata +25 -17
- data/lib/action_view/helpers/record_tag_helper.rb +0 -23
| @@ -15,8 +15,8 @@ module ActionView | |
| 15 15 | 
             
                    "'"     => "\\'"
         | 
| 16 16 | 
             
                  }
         | 
| 17 17 |  | 
| 18 | 
            -
                  JS_ESCAPE_MAP["\342\200\250". | 
| 19 | 
            -
                  JS_ESCAPE_MAP["\342\200\251". | 
| 18 | 
            +
                  JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "
"
         | 
| 19 | 
            +
                  JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "
"
         | 
| 20 20 |  | 
| 21 21 | 
             
                  # Escapes carriage returns and single and double quotes for JavaScript segments.
         | 
| 22 22 | 
             
                  #
         | 
| @@ -25,12 +25,13 @@ module ActionView | |
| 25 25 | 
             
                  #
         | 
| 26 26 | 
             
                  #   $('some_element').replaceWith('<%= j render 'some/element_template' %>');
         | 
| 27 27 | 
             
                  def escape_javascript(javascript)
         | 
| 28 | 
            -
                     | 
| 29 | 
            -
             | 
| 30 | 
            -
                       | 
| 28 | 
            +
                    javascript = javascript.to_s
         | 
| 29 | 
            +
                    if javascript.empty?
         | 
| 30 | 
            +
                      result = ""
         | 
| 31 31 | 
             
                    else
         | 
| 32 | 
            -
                      " | 
| 32 | 
            +
                      result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) { |match| JS_ESCAPE_MAP[match] }
         | 
| 33 33 | 
             
                    end
         | 
| 34 | 
            +
                    javascript.html_safe? ? result.html_safe : result
         | 
| 34 35 | 
             
                  end
         | 
| 35 36 |  | 
| 36 37 | 
             
                  alias_method :j, :escape_javascript
         | 
| @@ -65,7 +66,7 @@ module ActionView | |
| 65 66 | 
             
                  #   <% end -%>
         | 
| 66 67 | 
             
                  #
         | 
| 67 68 | 
             
                  # If you have a content security policy enabled then you can add an automatic
         | 
| 68 | 
            -
                  # nonce value by passing  | 
| 69 | 
            +
                  # nonce value by passing <tt>nonce: true</tt> as part of +html_options+. Example:
         | 
| 69 70 | 
             
                  #
         | 
| 70 71 | 
             
                  #   <%= javascript_tag nonce: true do -%>
         | 
| 71 72 | 
             
                  #     alert('All is good')
         | 
| @@ -83,7 +84,7 @@ module ActionView | |
| 83 84 | 
             
                      html_options[:nonce] = content_security_policy_nonce
         | 
| 84 85 | 
             
                    end
         | 
| 85 86 |  | 
| 86 | 
            -
                    content_tag("script" | 
| 87 | 
            +
                    content_tag("script", javascript_cdata_section(content), html_options)
         | 
| 87 88 | 
             
                  end
         | 
| 88 89 |  | 
| 89 90 | 
             
                  def javascript_cdata_section(content) #:nodoc:
         | 
| @@ -100,6 +100,9 @@ module ActionView | |
| 100 100 | 
             
                  #   absolute value of the number.
         | 
| 101 101 | 
             
                  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
         | 
| 102 102 | 
             
                  #   the argument is invalid.
         | 
| 103 | 
            +
                  # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
         | 
| 104 | 
            +
                  #   insignificant zeros after the decimal separator (defaults to
         | 
| 105 | 
            +
                  #   +false+).
         | 
| 103 106 | 
             
                  #
         | 
| 104 107 | 
             
                  # ==== Examples
         | 
| 105 108 | 
             
                  #
         | 
| @@ -117,6 +120,8 @@ module ActionView | |
| 117 120 | 
             
                  #   # => R$1234567890,50
         | 
| 118 121 | 
             
                  #   number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
         | 
| 119 122 | 
             
                  #   # => 1234567890,50 R$
         | 
| 123 | 
            +
                  #   number_to_currency(1234567890.50, strip_insignificant_zeros: true)
         | 
| 124 | 
            +
                  #   # => "$1,234,567,890.5"
         | 
| 120 125 | 
             
                  def number_to_currency(number, options = {})
         | 
| 121 126 | 
             
                    delegate_number_helper_method(:number_to_currency, number, options)
         | 
| 122 127 | 
             
                  end
         | 
| @@ -38,7 +38,7 @@ module ActionView #:nodoc: | |
| 38 38 |  | 
| 39 39 | 
             
                  # Converts the array to a comma-separated sentence where the last element is
         | 
| 40 40 | 
             
                  # joined by the connector word. This is the html_safe-aware version of
         | 
| 41 | 
            -
                  # ActiveSupport's {Array#to_sentence}[ | 
| 41 | 
            +
                  # ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
         | 
| 42 42 | 
             
                  #
         | 
| 43 43 | 
             
                  def to_sentence(array, options = {})
         | 
| 44 44 | 
             
                    options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
         | 
| @@ -27,10 +27,12 @@ module ActionView | |
| 27 27 | 
             
                  def render(options = {}, locals = {}, &block)
         | 
| 28 28 | 
             
                    case options
         | 
| 29 29 | 
             
                    when Hash
         | 
| 30 | 
            -
                       | 
| 31 | 
            -
                         | 
| 32 | 
            -
             | 
| 33 | 
            -
                         | 
| 30 | 
            +
                      in_rendering_context(options) do |renderer|
         | 
| 31 | 
            +
                        if block_given?
         | 
| 32 | 
            +
                          view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
         | 
| 33 | 
            +
                        else
         | 
| 34 | 
            +
                          view_renderer.render(self, options)
         | 
| 35 | 
            +
                        end
         | 
| 34 36 | 
             
                      end
         | 
| 35 37 | 
             
                    else
         | 
| 36 38 | 
             
                      view_renderer.render_partial(self, partial: options, locals: locals, &block)
         | 
| @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require "active_support/core_ext/object/try"
         | 
| 4 3 | 
             
            require "rails-html-sanitizer"
         | 
| 5 4 |  | 
| 6 5 | 
             
            module ActionView
         | 
| @@ -10,14 +9,14 @@ module ActionView | |
| 10 9 | 
             
                # These helper methods extend Action View making them callable within your template files.
         | 
| 11 10 | 
             
                module SanitizeHelper
         | 
| 12 11 | 
             
                  extend ActiveSupport::Concern
         | 
| 13 | 
            -
                  # Sanitizes HTML input, stripping all tags and attributes | 
| 12 | 
            +
                  # Sanitizes HTML input, stripping all but known-safe tags and attributes.
         | 
| 14 13 | 
             
                  #
         | 
| 15 14 | 
             
                  # It also strips href/src attributes with unsafe protocols like
         | 
| 16 15 | 
             
                  # <tt>javascript:</tt>, while also protecting against attempts to use Unicode,
         | 
| 17 16 | 
             
                  # ASCII, and hex character references to work around these protocol filters.
         | 
| 18 17 | 
             
                  # All special characters will be escaped.
         | 
| 19 18 | 
             
                  #
         | 
| 20 | 
            -
                  # The default sanitizer is Rails::Html:: | 
| 19 | 
            +
                  # The default sanitizer is Rails::Html::SafeListSanitizer. See {Rails HTML
         | 
| 21 20 | 
             
                  # Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
         | 
| 22 21 | 
             
                  #
         | 
| 23 22 | 
             
                  # Custom sanitization rules can also be provided.
         | 
| @@ -40,7 +39,7 @@ module ActionView | |
| 40 39 | 
             
                  #
         | 
| 41 40 | 
             
                  #   <%= sanitize @comment.body %>
         | 
| 42 41 | 
             
                  #
         | 
| 43 | 
            -
                  # Providing custom  | 
| 42 | 
            +
                  # Providing custom lists of permitted tags and attributes:
         | 
| 44 43 | 
             
                  #
         | 
| 45 44 | 
             
                  #   <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
         | 
| 46 45 | 
             
                  #
         | 
| @@ -80,12 +79,12 @@ module ActionView | |
| 80 79 | 
             
                  #   config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
         | 
| 81 80 | 
             
                  #   config.action_view.sanitized_allowed_attributes = ['href', 'title']
         | 
| 82 81 | 
             
                  def sanitize(html, options = {})
         | 
| 83 | 
            -
                    self.class. | 
| 82 | 
            +
                    self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
         | 
| 84 83 | 
             
                  end
         | 
| 85 84 |  | 
| 86 85 | 
             
                  # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
         | 
| 87 86 | 
             
                  def sanitize_css(style)
         | 
| 88 | 
            -
                    self.class. | 
| 87 | 
            +
                    self.class.safe_list_sanitizer.sanitize_css(style)
         | 
| 89 88 | 
             
                  end
         | 
| 90 89 |  | 
| 91 90 | 
             
                  # Strips all HTML tags from +html+, including comments and special characters.
         | 
| @@ -123,20 +122,18 @@ module ActionView | |
| 123 122 | 
             
                  end
         | 
| 124 123 |  | 
| 125 124 | 
             
                  module ClassMethods #:nodoc:
         | 
| 126 | 
            -
                    attr_writer :full_sanitizer, :link_sanitizer, : | 
| 125 | 
            +
                    attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
         | 
| 127 126 |  | 
| 128 | 
            -
                    # Vendors the full, link and white list sanitizers.
         | 
| 129 | 
            -
                    # Provided strictly for compatibility and can be removed in Rails 5.1.
         | 
| 130 127 | 
             
                    def sanitizer_vendor
         | 
| 131 128 | 
             
                      Rails::Html::Sanitizer
         | 
| 132 129 | 
             
                    end
         | 
| 133 130 |  | 
| 134 131 | 
             
                    def sanitized_allowed_tags
         | 
| 135 | 
            -
                       | 
| 132 | 
            +
                      safe_list_sanitizer.allowed_tags
         | 
| 136 133 | 
             
                    end
         | 
| 137 134 |  | 
| 138 135 | 
             
                    def sanitized_allowed_attributes
         | 
| 139 | 
            -
                       | 
| 136 | 
            +
                      safe_list_sanitizer.allowed_attributes
         | 
| 140 137 | 
             
                    end
         | 
| 141 138 |  | 
| 142 139 | 
             
                    # Gets the Rails::Html::FullSanitizer instance used by +strip_tags+. Replace with
         | 
| @@ -145,7 +142,6 @@ module ActionView | |
| 145 142 | 
             
                    #   class Application < Rails::Application
         | 
| 146 143 | 
             
                    #     config.action_view.full_sanitizer = MySpecialSanitizer.new
         | 
| 147 144 | 
             
                    #   end
         | 
| 148 | 
            -
                    #
         | 
| 149 145 | 
             
                    def full_sanitizer
         | 
| 150 146 | 
             
                      @full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
         | 
| 151 147 | 
             
                    end
         | 
| @@ -156,20 +152,18 @@ module ActionView | |
| 156 152 | 
             
                    #   class Application < Rails::Application
         | 
| 157 153 | 
             
                    #     config.action_view.link_sanitizer = MySpecialSanitizer.new
         | 
| 158 154 | 
             
                    #   end
         | 
| 159 | 
            -
                    #
         | 
| 160 155 | 
             
                    def link_sanitizer
         | 
| 161 156 | 
             
                      @link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
         | 
| 162 157 | 
             
                    end
         | 
| 163 158 |  | 
| 164 | 
            -
                    # Gets the Rails::Html:: | 
| 159 | 
            +
                    # Gets the Rails::Html::SafeListSanitizer instance used by sanitize and +sanitize_css+.
         | 
| 165 160 | 
             
                    # Replace with any object that responds to +sanitize+.
         | 
| 166 161 | 
             
                    #
         | 
| 167 162 | 
             
                    #   class Application < Rails::Application
         | 
| 168 | 
            -
                    #     config.action_view. | 
| 163 | 
            +
                    #     config.action_view.safe_list_sanitizer = MySpecialSanitizer.new
         | 
| 169 164 | 
             
                    #   end
         | 
| 170 | 
            -
                     | 
| 171 | 
            -
             | 
| 172 | 
            -
                      @white_list_sanitizer ||= sanitizer_vendor.white_list_sanitizer.new
         | 
| 165 | 
            +
                    def safe_list_sanitizer
         | 
| 166 | 
            +
                      @safe_list_sanitizer ||= sanitizer_vendor.safe_list_sanitizer.new
         | 
| 173 167 | 
             
                    end
         | 
| 174 168 | 
             
                  end
         | 
| 175 169 | 
             
                end
         | 
| @@ -58,7 +58,7 @@ module ActionView | |
| 58 58 |  | 
| 59 59 | 
             
                    def tag_options(options, escape = true)
         | 
| 60 60 | 
             
                      return if options.blank?
         | 
| 61 | 
            -
                      output = "" | 
| 61 | 
            +
                      output = +""
         | 
| 62 62 | 
             
                      sep    = " "
         | 
| 63 63 | 
             
                      options.each_pair do |key, value|
         | 
| 64 64 | 
             
                        if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
         | 
| @@ -86,11 +86,12 @@ module ActionView | |
| 86 86 |  | 
| 87 87 | 
             
                    def tag_option(key, value, escape)
         | 
| 88 88 | 
             
                      if value.is_a?(Array)
         | 
| 89 | 
            -
                        value = escape ? safe_join(value, " " | 
| 89 | 
            +
                        value = escape ? safe_join(value, " ") : value.join(" ")
         | 
| 90 90 | 
             
                      else
         | 
| 91 | 
            -
                        value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
         | 
| 91 | 
            +
                        value = escape ? ERB::Util.unwrapped_html_escape(value).dup : value.to_s.dup
         | 
| 92 92 | 
             
                      end
         | 
| 93 | 
            -
                       | 
| 93 | 
            +
                      value.gsub!('"', """)
         | 
| 94 | 
            +
                      %(#{key}="#{value}")
         | 
| 94 95 | 
             
                    end
         | 
| 95 96 |  | 
| 96 97 | 
             
                    private
         | 
| @@ -227,10 +228,10 @@ module ActionView | |
| 227 228 | 
             
                  #   tag("img", src: "open & shut.png")
         | 
| 228 229 | 
             
                  #   # => <img src="open & shut.png" />
         | 
| 229 230 | 
             
                  #
         | 
| 230 | 
            -
                  #   tag("img", {src: "open & shut.png"}, false, false)
         | 
| 231 | 
            +
                  #   tag("img", { src: "open & shut.png" }, false, false)
         | 
| 231 232 | 
             
                  #   # => <img src="open & shut.png" />
         | 
| 232 233 | 
             
                  #
         | 
| 233 | 
            -
                  #   tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
         | 
| 234 | 
            +
                  #   tag("div", data: { name: 'Stephen', city_state: %w(Chicago IL) })
         | 
| 234 235 | 
             
                  #   # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
         | 
| 235 236 | 
             
                  def tag(name = nil, options = nil, open = false, escape = true)
         | 
| 236 237 | 
             
                    if name.nil?
         | 
| @@ -109,11 +109,11 @@ module ActionView | |
| 109 109 | 
             
                        # a little duplication to construct less strings
         | 
| 110 110 | 
             
                        case
         | 
| 111 111 | 
             
                        when @object_name.empty?
         | 
| 112 | 
            -
                          "#{sanitized_method_name}#{"[]"  | 
| 112 | 
            +
                          "#{sanitized_method_name}#{multiple ? "[]" : ""}"
         | 
| 113 113 | 
             
                        when index
         | 
| 114 | 
            -
                          "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]"  | 
| 114 | 
            +
                          "#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
         | 
| 115 115 | 
             
                        else
         | 
| 116 | 
            -
                          "#{@object_name}[#{sanitized_method_name}]#{"[]"  | 
| 116 | 
            +
                          "#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
         | 
| 117 117 | 
             
                        end
         | 
| 118 118 | 
             
                      end
         | 
| 119 119 |  | 
| @@ -138,7 +138,7 @@ module ActionView | |
| 138 138 | 
             
                      end
         | 
| 139 139 |  | 
| 140 140 | 
             
                      def sanitized_value(value)
         | 
| 141 | 
            -
                        value.to_s.gsub( | 
| 141 | 
            +
                        value.to_s.gsub(/[\s\.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
         | 
| 142 142 | 
             
                      end
         | 
| 143 143 |  | 
| 144 144 | 
             
                      def select_content_tag(option_tags, options, html_options)
         | 
| @@ -170,7 +170,11 @@ module ActionView | |
| 170 170 | 
             
                          option_tags = tag_builder.content_tag_string("option", options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, value: "") + "\n" + option_tags
         | 
| 171 171 | 
             
                        end
         | 
| 172 172 | 
             
                        if value.blank? && options[:prompt]
         | 
| 173 | 
            -
                           | 
| 173 | 
            +
                          tag_options = { value: "" }.tap do |prompt_opts|
         | 
| 174 | 
            +
                            prompt_opts[:disabled] = true if options[:disabled] == ""
         | 
| 175 | 
            +
                            prompt_opts[:selected] = true if options[:selected] == ""
         | 
| 176 | 
            +
                          end
         | 
| 177 | 
            +
                          option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
         | 
| 174 178 | 
             
                        end
         | 
| 175 179 | 
             
                        option_tags
         | 
| 176 180 | 
             
                      end
         | 
| @@ -16,13 +16,8 @@ module ActionView | |
| 16 16 | 
             
                      translated_attribute || human_attribute_name
         | 
| 17 17 | 
             
                    end
         | 
| 18 18 |  | 
| 19 | 
            -
                    # TODO Change this to private once we've dropped Ruby 2.2 support.
         | 
| 20 | 
            -
                    # Workaround for Ruby 2.2 "private attribute?" warning.
         | 
| 21 | 
            -
                    protected
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                      attr_reader :object_name, :method_and_value, :scope, :model
         | 
| 24 | 
            -
             | 
| 25 19 | 
             
                    private
         | 
| 20 | 
            +
                      attr_reader :object_name, :method_and_value, :scope, :model
         | 
| 26 21 |  | 
| 27 22 | 
             
                      def i18n_default
         | 
| 28 23 | 
             
                        if model
         | 
| @@ -188,7 +188,7 @@ module ActionView | |
| 188 188 |  | 
| 189 189 | 
             
                    unless separator.empty?
         | 
| 190 190 | 
             
                      text.split(separator).each do |value|
         | 
| 191 | 
            -
                        if value.match(regex)
         | 
| 191 | 
            +
                        if value.match?(regex)
         | 
| 192 192 | 
             
                          phrase = value
         | 
| 193 193 | 
             
                          break
         | 
| 194 194 | 
             
                        end
         | 
| @@ -228,7 +228,7 @@ module ActionView | |
| 228 228 | 
             
                  #   pluralize(2, 'Person', locale: :de)
         | 
| 229 229 | 
             
                  #   # => 2 Personen
         | 
| 230 230 | 
             
                  def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
         | 
| 231 | 
            -
                    word = if  | 
| 231 | 
            +
                    word = if count == 1 || count.to_s =~ /^1(\.0+)?$/
         | 
| 232 232 | 
             
                      singular
         | 
| 233 233 | 
             
                    else
         | 
| 234 234 | 
             
                      plural || singular.pluralize(locale)
         | 
| @@ -259,7 +259,7 @@ module ActionView | |
| 259 259 | 
             
                  #   # => Once\r\nupon\r\na\r\ntime
         | 
| 260 260 | 
             
                  def word_wrap(text, line_width: 80, break_sequence: "\n")
         | 
| 261 261 | 
             
                    text.split("\n").collect! do |line|
         | 
| 262 | 
            -
                      line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}"). | 
| 262 | 
            +
                      line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").rstrip : line
         | 
| 263 263 | 
             
                    end * break_sequence
         | 
| 264 264 | 
             
                  end
         | 
| 265 265 |  | 
| @@ -59,11 +59,9 @@ module ActionView | |
| 59 59 | 
             
                  # they can provide HTML values for.
         | 
| 60 60 | 
             
                  def translate(key, options = {})
         | 
| 61 61 | 
             
                    options = options.dup
         | 
| 62 | 
            -
                     | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
                    if has_default && !remaining_defaults.first.kind_of?(Symbol)
         | 
| 66 | 
            -
                      options[:default] = remaining_defaults
         | 
| 62 | 
            +
                    if options.has_key?(:default)
         | 
| 63 | 
            +
                      remaining_defaults = Array.wrap(options.delete(:default)).compact
         | 
| 64 | 
            +
                      options[:default] = remaining_defaults unless remaining_defaults.first.kind_of?(Symbol)
         | 
| 67 65 | 
             
                    end
         | 
| 68 66 |  | 
| 69 67 | 
             
                    # If the user has explicitly decided to NOT raise errors, pass that option to I18n.
         | 
| @@ -85,8 +83,11 @@ module ActionView | |
| 85 83 | 
             
                        end
         | 
| 86 84 | 
             
                      end
         | 
| 87 85 | 
             
                      translation = I18n.translate(scope_key_by_partial(key), html_safe_options.merge(raise: i18n_raise))
         | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 86 | 
            +
                      if translation.respond_to?(:map)
         | 
| 87 | 
            +
                        translation.map { |element| element.respond_to?(:html_safe) ? element.html_safe : element }
         | 
| 88 | 
            +
                      else
         | 
| 89 | 
            +
                        translation.respond_to?(:html_safe) ? translation.html_safe : translation
         | 
| 90 | 
            +
                      end
         | 
| 90 91 | 
             
                    else
         | 
| 91 92 | 
             
                      I18n.translate(scope_key_by_partial(key), options.merge(raise: i18n_raise))
         | 
| 92 93 | 
             
                    end
         | 
| @@ -97,7 +98,7 @@ module ActionView | |
| 97 98 | 
             
                      raise e if raise_error
         | 
| 98 99 |  | 
| 99 100 | 
             
                      keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
         | 
| 100 | 
            -
                      title = "translation missing: #{keys.join('.')}" | 
| 101 | 
            +
                      title = +"translation missing: #{keys.join('.')}"
         | 
| 101 102 |  | 
| 102 103 | 
             
                      interpolations = options.except(:default, :scope)
         | 
| 103 104 | 
             
                      if interpolations.any?
         | 
| @@ -113,7 +114,7 @@ module ActionView | |
| 113 114 |  | 
| 114 115 | 
             
                  # Delegates to <tt>I18n.localize</tt> with no additional functionality.
         | 
| 115 116 | 
             
                  #
         | 
| 116 | 
            -
                  # See  | 
| 117 | 
            +
                  # See https://www.rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
         | 
| 117 118 | 
             
                  # for more information.
         | 
| 118 119 | 
             
                  def localize(*args)
         | 
| 119 120 | 
             
                    I18n.localize(*args)
         | 
| @@ -122,9 +123,12 @@ module ActionView | |
| 122 123 |  | 
| 123 124 | 
             
                  private
         | 
| 124 125 | 
             
                    def scope_key_by_partial(key)
         | 
| 125 | 
            -
                       | 
| 126 | 
            +
                      stringified_key = key.to_s
         | 
| 127 | 
            +
                      if stringified_key.first == "."
         | 
| 126 128 | 
             
                        if @virtual_path
         | 
| 127 | 
            -
                          @ | 
| 129 | 
            +
                          @_scope_key_by_partial_cache ||= {}
         | 
| 130 | 
            +
                          @_scope_key_by_partial_cache[@virtual_path] ||= @virtual_path.gsub(%r{/_?}, ".")
         | 
| 131 | 
            +
                          "#{@_scope_key_by_partial_cache[@virtual_path]}#{stringified_key}"
         | 
| 128 132 | 
             
                        else
         | 
| 129 133 | 
             
                          raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
         | 
| 130 134 | 
             
                        end
         | 
| @@ -134,7 +138,7 @@ module ActionView | |
| 134 138 | 
             
                    end
         | 
| 135 139 |  | 
| 136 140 | 
             
                    def html_safe_translation_key?(key)
         | 
| 137 | 
            -
                      /( | 
| 141 | 
            +
                      /(?:_|\b)html\z/.match?(key.to_s)
         | 
| 138 142 | 
             
                    end
         | 
| 139 143 | 
             
                end
         | 
| 140 144 | 
             
              end
         | 
| @@ -200,9 +200,9 @@ module ActionView | |
| 200 200 | 
             
                    html_options = convert_options_to_data_attributes(options, html_options)
         | 
| 201 201 |  | 
| 202 202 | 
             
                    url = url_for(options)
         | 
| 203 | 
            -
                    html_options["href" | 
| 203 | 
            +
                    html_options["href"] ||= url
         | 
| 204 204 |  | 
| 205 | 
            -
                    content_tag("a" | 
| 205 | 
            +
                    content_tag("a", name || url, html_options, &block)
         | 
| 206 206 | 
             
                  end
         | 
| 207 207 |  | 
| 208 208 | 
             
                  # Generates a form containing a single button that submits to the URL created
         | 
| @@ -308,7 +308,7 @@ module ActionView | |
| 308 308 | 
             
                    params = html_options.delete("params")
         | 
| 309 309 |  | 
| 310 310 | 
             
                    method     = html_options.delete("method").to_s
         | 
| 311 | 
            -
                    method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "". | 
| 311 | 
            +
                    method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
         | 
| 312 312 |  | 
| 313 313 | 
             
                    form_method  = method == "get" ? "get" : "post"
         | 
| 314 314 | 
             
                    form_options = html_options.delete("form") || {}
         | 
| @@ -321,7 +321,7 @@ module ActionView | |
| 321 321 | 
             
                      request_method = method.empty? ? "post" : method
         | 
| 322 322 | 
             
                      token_tag(nil, form_options: { action: url, method: request_method })
         | 
| 323 323 | 
             
                    else
         | 
| 324 | 
            -
                      "" | 
| 324 | 
            +
                      ""
         | 
| 325 325 | 
             
                    end
         | 
| 326 326 |  | 
| 327 327 | 
             
                    html_options = convert_options_to_data_attributes(options, html_options)
         | 
| @@ -487,12 +487,12 @@ module ActionView | |
| 487 487 | 
             
                      option = html_options.delete(item).presence || next
         | 
| 488 488 | 
             
                      "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
         | 
| 489 489 | 
             
                    }.compact
         | 
| 490 | 
            -
                    extras = extras.empty? ? "" | 
| 490 | 
            +
                    extras = extras.empty? ? "" : "?" + extras.join("&")
         | 
| 491 491 |  | 
| 492 492 | 
             
                    encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
         | 
| 493 493 | 
             
                    html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
         | 
| 494 494 |  | 
| 495 | 
            -
                    content_tag("a" | 
| 495 | 
            +
                    content_tag("a", name || email_address, html_options, &block)
         | 
| 496 496 | 
             
                  end
         | 
| 497 497 |  | 
| 498 498 | 
             
                  # True if the current request URI was generated by the given +options+.
         | 
| @@ -553,7 +553,7 @@ module ActionView | |
| 553 553 | 
             
                    url_string = URI.parser.unescape(url_for(options)).force_encoding(Encoding::BINARY)
         | 
| 554 554 |  | 
| 555 555 | 
             
                    # We ignore any extra parameters in the request_uri if the
         | 
| 556 | 
            -
                    # submitted  | 
| 556 | 
            +
                    # submitted URL doesn't have any either. This lets the function
         | 
| 557 557 | 
             
                    # work with things like ?order=asc
         | 
| 558 558 | 
             
                    # the behaviour can be disabled with check_parameters: true
         | 
| 559 559 | 
             
                    request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
         | 
| @@ -575,21 +575,21 @@ module ActionView | |
| 575 575 | 
             
                    def convert_options_to_data_attributes(options, html_options)
         | 
| 576 576 | 
             
                      if html_options
         | 
| 577 577 | 
             
                        html_options = html_options.stringify_keys
         | 
| 578 | 
            -
                        html_options["data-remote"] = "true" | 
| 578 | 
            +
                        html_options["data-remote"] = "true" if link_to_remote_options?(options) || link_to_remote_options?(html_options)
         | 
| 579 579 |  | 
| 580 | 
            -
                        method = html_options.delete("method" | 
| 580 | 
            +
                        method = html_options.delete("method")
         | 
| 581 581 |  | 
| 582 582 | 
             
                        add_method_to_attributes!(html_options, method) if method
         | 
| 583 583 |  | 
| 584 584 | 
             
                        html_options
         | 
| 585 585 | 
             
                      else
         | 
| 586 | 
            -
                        link_to_remote_options?(options) ? { "data-remote" => "true" | 
| 586 | 
            +
                        link_to_remote_options?(options) ? { "data-remote" => "true" } : {}
         | 
| 587 587 | 
             
                      end
         | 
| 588 588 | 
             
                    end
         | 
| 589 589 |  | 
| 590 590 | 
             
                    def link_to_remote_options?(options)
         | 
| 591 591 | 
             
                      if options.is_a?(Hash)
         | 
| 592 | 
            -
                        options.delete("remote" | 
| 592 | 
            +
                        options.delete("remote") || options.delete(:remote)
         | 
| 593 593 | 
             
                      end
         | 
| 594 594 | 
             
                    end
         | 
| 595 595 |  | 
| @@ -618,11 +618,11 @@ module ActionView | |
| 618 618 | 
             
                    end
         | 
| 619 619 |  | 
| 620 620 | 
             
                    def token_tag(token = nil, form_options: {})
         | 
| 621 | 
            -
                      if token != false && protect_against_forgery?
         | 
| 621 | 
            +
                      if token != false && defined?(protect_against_forgery?) && protect_against_forgery?
         | 
| 622 622 | 
             
                        token ||= form_authenticity_token(form_options: form_options)
         | 
| 623 623 | 
             
                        tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
         | 
| 624 624 | 
             
                      else
         | 
| 625 | 
            -
                        "" | 
| 625 | 
            +
                        ""
         | 
| 626 626 | 
             
                      end
         | 
| 627 627 | 
             
                    end
         | 
| 628 628 |  | 
| @@ -636,7 +636,7 @@ module ActionView | |
| 636 636 | 
             
                    #   to_form_params(name: 'David', nationality: 'Danish')
         | 
| 637 637 | 
             
                    #   # => [{name: 'name', value: 'David'}, {name: 'nationality', value: 'Danish'}]
         | 
| 638 638 | 
             
                    #
         | 
| 639 | 
            -
                    #   to_form_params(country: {name: 'Denmark'})
         | 
| 639 | 
            +
                    #   to_form_params(country: { name: 'Denmark' })
         | 
| 640 640 | 
             
                    #   # => [{name: 'country[name]', value: 'Denmark'}]
         | 
| 641 641 | 
             
                    #
         | 
| 642 642 | 
             
                    #   to_form_params(countries: ['Denmark', 'Sweden']})
         |