actionview 4.2.11.3 → 5.0.0.beta1
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 +5 -5
- data/CHANGELOG.md +136 -255
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/action_view.rb +1 -1
- data/lib/action_view/base.rb +14 -2
- data/lib/action_view/dependency_tracker.rb +46 -15
- data/lib/action_view/digestor.rb +13 -9
- data/lib/action_view/flows.rb +1 -1
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +15 -5
- data/lib/action_view/helpers/asset_url_helper.rb +51 -12
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -4
- data/lib/action_view/helpers/cache_helper.rb +75 -20
- data/lib/action_view/helpers/capture_helper.rb +3 -2
- data/lib/action_view/helpers/controller_helper.rb +1 -0
- data/lib/action_view/helpers/date_helper.rb +39 -10
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +81 -35
- data/lib/action_view/helpers/form_options_helper.rb +74 -35
- data/lib/action_view/helpers/form_tag_helper.rb +46 -19
- data/lib/action_view/helpers/javascript_helper.rb +4 -4
- data/lib/action_view/helpers/number_helper.rb +10 -12
- data/lib/action_view/helpers/record_tag_helper.rb +12 -99
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +1 -2
- data/lib/action_view/helpers/tag_helper.rb +20 -13
- data/lib/action_view/helpers/tags/base.rb +33 -28
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +2 -30
- data/lib/action_view/helpers/tags/collection_helpers.rb +28 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -9
- data/lib/action_view/helpers/tags/file_field.rb +15 -0
- data/lib/action_view/helpers/tags/label.rb +1 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
- data/lib/action_view/helpers/tags/search_field.rb +12 -9
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +25 -9
- data/lib/action_view/helpers/translation_helper.rb +56 -26
- data/lib/action_view/helpers/url_helper.rb +40 -65
- data/lib/action_view/layouts.rb +11 -10
- data/lib/action_view/lookup_context.rb +14 -40
- data/lib/action_view/model_naming.rb +1 -1
- data/lib/action_view/path_set.rb +15 -18
- data/lib/action_view/railtie.rb +20 -3
- data/lib/action_view/record_identifier.rb +44 -19
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/partial_renderer.rb +27 -26
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +70 -0
- data/lib/action_view/renderer/renderer.rb +2 -6
- data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
- data/lib/action_view/renderer/template_renderer.rb +12 -11
- data/lib/action_view/rendering.rb +8 -5
- data/lib/action_view/routing_url_for.rb +18 -6
- data/lib/action_view/template.rb +50 -13
- data/lib/action_view/template/error.rb +14 -7
- data/lib/action_view/template/handlers.rb +3 -3
- data/lib/action_view/template/handlers/erb.rb +25 -0
- data/lib/action_view/template/handlers/raw.rb +1 -1
- data/lib/action_view/template/resolver.rb +36 -58
- data/lib/action_view/template/types.rb +1 -1
- data/lib/action_view/test_case.rb +13 -8
- data/lib/action_view/testing/resolvers.rb +3 -4
- data/lib/action_view/view_paths.rb +6 -22
- metadata +17 -14
    
        data/lib/action_view/layouts.rb
    CHANGED
    
    | @@ -228,7 +228,7 @@ module ActionView | |
| 228 228 | 
             
                    # set by the <tt>layout</tt> method.
         | 
| 229 229 | 
             
                    #
         | 
| 230 230 | 
             
                    # ==== Returns
         | 
| 231 | 
            -
                    # * <tt> | 
| 231 | 
            +
                    # * <tt>Boolean</tt> - True if the action has a layout definition, false otherwise.
         | 
| 232 232 | 
             
                    def _conditional_layout?
         | 
| 233 233 | 
             
                      return unless super
         | 
| 234 234 |  | 
| @@ -262,7 +262,7 @@ module ActionView | |
| 262 262 | 
             
                  def layout(layout, conditions = {})
         | 
| 263 263 | 
             
                    include LayoutConditions unless conditions.empty?
         | 
| 264 264 |  | 
| 265 | 
            -
                    conditions.each {|k, v| conditions[k] = Array(v).map | 
| 265 | 
            +
                    conditions.each {|k, v| conditions[k] = Array(v).map(&:to_s) }
         | 
| 266 266 | 
             
                    self._layout_conditions = conditions
         | 
| 267 267 |  | 
| 268 268 | 
             
                    self._layout = layout
         | 
| @@ -277,7 +277,7 @@ module ActionView | |
| 277 277 | 
             
                    remove_possible_method(:_layout)
         | 
| 278 278 |  | 
| 279 279 | 
             
                    prefixes    = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
         | 
| 280 | 
            -
                    default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}).first || super"
         | 
| 280 | 
            +
                    default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
         | 
| 281 281 | 
             
                    name_clause = if name
         | 
| 282 282 | 
             
                      default_behavior
         | 
| 283 283 | 
             
                    else
         | 
| @@ -316,7 +316,7 @@ module ActionView | |
| 316 316 | 
             
                    end
         | 
| 317 317 |  | 
| 318 318 | 
             
                    self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
         | 
| 319 | 
            -
                      def _layout
         | 
| 319 | 
            +
                      def _layout(formats)
         | 
| 320 320 | 
             
                        if _conditional_layout?
         | 
| 321 321 | 
             
                          #{layout_definition}
         | 
| 322 322 | 
             
                        else
         | 
| @@ -372,7 +372,7 @@ module ActionView | |
| 372 372 | 
             
                end
         | 
| 373 373 |  | 
| 374 374 | 
             
                # This will be overwritten by _write_layout_method
         | 
| 375 | 
            -
                def _layout; end
         | 
| 375 | 
            +
                def _layout(*); end
         | 
| 376 376 |  | 
| 377 377 | 
             
                # Determine the layout for a given name, taking into account the name type.
         | 
| 378 378 | 
             
                #
         | 
| @@ -382,8 +382,8 @@ module ActionView | |
| 382 382 | 
             
                  case name
         | 
| 383 383 | 
             
                  when String     then _normalize_layout(name)
         | 
| 384 384 | 
             
                  when Proc       then name
         | 
| 385 | 
            -
                  when true       then Proc.new { _default_layout(true)  }
         | 
| 386 | 
            -
                  when :default   then Proc.new { _default_layout(false) }
         | 
| 385 | 
            +
                  when true       then Proc.new { |formats| _default_layout(formats, true)  }
         | 
| 386 | 
            +
                  when :default   then Proc.new { |formats| _default_layout(formats, false) }
         | 
| 387 387 | 
             
                  when false, nil then nil
         | 
| 388 388 | 
             
                  else
         | 
| 389 389 | 
             
                    raise ArgumentError,
         | 
| @@ -399,14 +399,15 @@ module ActionView | |
| 399 399 | 
             
                # Optionally raises an exception if the layout could not be found.
         | 
| 400 400 | 
             
                #
         | 
| 401 401 | 
             
                # ==== Parameters
         | 
| 402 | 
            +
                # * <tt>formats</tt> - The formats accepted to this layout
         | 
| 402 403 | 
             
                # * <tt>require_layout</tt> - If set to true and layout is not found,
         | 
| 403 | 
            -
                #   an ArgumentError exception is raised (defaults to false)
         | 
| 404 | 
            +
                #   an +ArgumentError+ exception is raised (defaults to false)
         | 
| 404 405 | 
             
                #
         | 
| 405 406 | 
             
                # ==== Returns
         | 
| 406 407 | 
             
                # * <tt>template</tt> - The template object for the default layout (or nil)
         | 
| 407 | 
            -
                def _default_layout(require_layout = false)
         | 
| 408 | 
            +
                def _default_layout(formats, require_layout = false)
         | 
| 408 409 | 
             
                  begin
         | 
| 409 | 
            -
                    value = _layout if action_has_layout?
         | 
| 410 | 
            +
                    value = _layout(formats) if action_has_layout?
         | 
| 410 411 | 
             
                  rescue NameError => e
         | 
| 411 412 | 
             
                    raise e, "Could not render layout: #{e.message}"
         | 
| 412 413 | 
             
                  end
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            require ' | 
| 1 | 
            +
            require 'concurrent/map'
         | 
| 2 2 | 
             
            require 'active_support/core_ext/module/remove_method'
         | 
| 3 3 | 
             
            require 'active_support/core_ext/module/attribute_accessors'
         | 
| 4 4 | 
             
            require 'action_view/template/resolver'
         | 
| @@ -6,10 +6,11 @@ require 'action_view/template/resolver' | |
| 6 6 | 
             
            module ActionView
         | 
| 7 7 | 
             
              # = Action View Lookup Context
         | 
| 8 8 | 
             
              #
         | 
| 9 | 
            -
              # LookupContext is the object responsible  | 
| 10 | 
            -
              # templates, i.e. view paths and details. | 
| 11 | 
            -
              #  | 
| 12 | 
            -
              #  | 
| 9 | 
            +
              # <tt>LookupContext</tt> is the object responsible for holding all information
         | 
| 10 | 
            +
              # required for looking up templates, i.e. view paths and details.
         | 
| 11 | 
            +
              # <tt>LookupContext</tt> is also responsible for generating a key, given to
         | 
| 12 | 
            +
              # view paths, used in the resolver cache lookup. Since this key is generated
         | 
| 13 | 
            +
              # only once during the request, it speeds up all cache accesses.
         | 
| 13 14 | 
             
              class LookupContext #:nodoc:
         | 
| 14 15 | 
             
                attr_accessor :prefixes, :rendered_format
         | 
| 15 16 |  | 
| @@ -19,7 +20,7 @@ module ActionView | |
| 19 20 | 
             
                mattr_accessor :registered_details
         | 
| 20 21 | 
             
                self.registered_details = []
         | 
| 21 22 |  | 
| 22 | 
            -
                def self.register_detail(name,  | 
| 23 | 
            +
                def self.register_detail(name, &block)
         | 
| 23 24 | 
             
                  self.registered_details << name
         | 
| 24 25 | 
             
                  initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
         | 
| 25 26 |  | 
| @@ -54,14 +55,14 @@ module ActionView | |
| 54 55 | 
             
                end
         | 
| 55 56 | 
             
                register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css,  :xml, :json] }
         | 
| 56 57 | 
             
                register_detail(:variants) { [] }
         | 
| 57 | 
            -
                register_detail(:handlers){ Template::Handlers.extensions }
         | 
| 58 | 
            +
                register_detail(:handlers) { Template::Handlers.extensions }
         | 
| 58 59 |  | 
| 59 60 | 
             
                class DetailsKey #:nodoc:
         | 
| 60 61 | 
             
                  alias :eql? :equal?
         | 
| 61 62 | 
             
                  alias :object_hash :hash
         | 
| 62 63 |  | 
| 63 64 | 
             
                  attr_reader :hash
         | 
| 64 | 
            -
                  @details_keys =  | 
| 65 | 
            +
                  @details_keys = Concurrent::Map.new
         | 
| 65 66 |  | 
| 66 67 | 
             
                  def self.get(details)
         | 
| 67 68 | 
             
                    if details[:formats]
         | 
| @@ -122,15 +123,11 @@ module ActionView | |
| 122 123 | 
             
                  end
         | 
| 123 124 | 
             
                  alias :find_template :find
         | 
| 124 125 |  | 
| 125 | 
            -
                  def find_file(name, prefixes = [], partial = false, keys = [], options = {})
         | 
| 126 | 
            -
                    @view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
         | 
| 127 | 
            -
                  end
         | 
| 128 | 
            -
             | 
| 129 126 | 
             
                  def find_all(name, prefixes = [], partial = false, keys = [], options = {})
         | 
| 130 127 | 
             
                    @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
         | 
| 131 128 | 
             
                  end
         | 
| 132 129 |  | 
| 133 | 
            -
                  def exists?(name, prefixes = [], partial = false, keys = [], options | 
| 130 | 
            +
                  def exists?(name, prefixes = [], partial = false, keys = [], **options)
         | 
| 134 131 | 
             
                    @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
         | 
| 135 132 | 
             
                  end
         | 
| 136 133 | 
             
                  alias :template_exists? :exists?
         | 
| @@ -176,13 +173,13 @@ module ActionView | |
| 176 173 | 
             
                  # name instead of the prefix.
         | 
| 177 174 | 
             
                  def normalize_name(name, prefixes) #:nodoc:
         | 
| 178 175 | 
             
                    prefixes = prefixes.presence
         | 
| 179 | 
            -
                    parts    = name.to_s.split('/')
         | 
| 176 | 
            +
                    parts    = name.to_s.split('/'.freeze)
         | 
| 180 177 | 
             
                    parts.shift if parts.first.empty?
         | 
| 181 178 | 
             
                    name     = parts.pop
         | 
| 182 179 |  | 
| 183 180 | 
             
                    return name, prefixes || [""] if parts.empty?
         | 
| 184 181 |  | 
| 185 | 
            -
                    parts    = parts.join('/')
         | 
| 182 | 
            +
                    parts    = parts.join('/'.freeze)
         | 
| 186 183 | 
             
                    prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
         | 
| 187 184 |  | 
| 188 185 | 
             
                    return name, prefixes
         | 
| @@ -195,7 +192,6 @@ module ActionView | |
| 195 192 |  | 
| 196 193 | 
             
                def initialize(view_paths, details = {}, prefixes = [])
         | 
| 197 194 | 
             
                  @details, @details_key = {}, nil
         | 
| 198 | 
            -
                  @skip_default_locale = false
         | 
| 199 195 | 
             
                  @cache = true
         | 
| 200 196 | 
             
                  @prefixes = prefixes
         | 
| 201 197 | 
             
                  @rendered_format = nil
         | 
| @@ -208,7 +204,7 @@ module ActionView | |
| 208 204 | 
             
                # add :html as fallback to :js.
         | 
| 209 205 | 
             
                def formats=(values)
         | 
| 210 206 | 
             
                  if values
         | 
| 211 | 
            -
                    values.concat(default_formats) if values.delete "*/*"
         | 
| 207 | 
            +
                    values.concat(default_formats) if values.delete "*/*".freeze
         | 
| 212 208 | 
             
                    if values == [:js]
         | 
| 213 209 | 
             
                      values << :html
         | 
| 214 210 | 
             
                      @html_fallback_for_js = true
         | 
| @@ -217,12 +213,6 @@ module ActionView | |
| 217 213 | 
             
                  super(values)
         | 
| 218 214 | 
             
                end
         | 
| 219 215 |  | 
| 220 | 
            -
                # Do not use the default locale on template lookup.
         | 
| 221 | 
            -
                def skip_default_locale!
         | 
| 222 | 
            -
                  @skip_default_locale = true
         | 
| 223 | 
            -
                  self.locale = nil
         | 
| 224 | 
            -
                end
         | 
| 225 | 
            -
             | 
| 226 216 | 
             
                # Override locale to return a symbol instead of array.
         | 
| 227 217 | 
             
                def locale
         | 
| 228 218 | 
             
                  @details[:locale].first
         | 
| @@ -237,23 +227,7 @@ module ActionView | |
| 237 227 | 
             
                    config.locale = value
         | 
| 238 228 | 
             
                  end
         | 
| 239 229 |  | 
| 240 | 
            -
                  super( | 
| 241 | 
            -
                end
         | 
| 242 | 
            -
             | 
| 243 | 
            -
                # Uses the first format in the formats array for layout lookup.
         | 
| 244 | 
            -
                def with_layout_format
         | 
| 245 | 
            -
                  if formats.size == 1
         | 
| 246 | 
            -
                    yield
         | 
| 247 | 
            -
                  else
         | 
| 248 | 
            -
                    old_formats = formats
         | 
| 249 | 
            -
                    _set_detail(:formats, formats[0,1])
         | 
| 250 | 
            -
             | 
| 251 | 
            -
                    begin
         | 
| 252 | 
            -
                      yield
         | 
| 253 | 
            -
                    ensure
         | 
| 254 | 
            -
                      _set_detail(:formats, old_formats)
         | 
| 255 | 
            -
                    end
         | 
| 256 | 
            -
                  end
         | 
| 230 | 
            +
                  super(default_locale)
         | 
| 257 231 | 
             
                end
         | 
| 258 232 | 
             
              end
         | 
| 259 233 | 
             
            end
         | 
    
        data/lib/action_view/path_set.rb
    CHANGED
    
    | @@ -46,35 +46,32 @@ module ActionView #:nodoc: | |
| 46 46 | 
             
                  find_all(*args).first || raise(MissingTemplate.new(self, *args))
         | 
| 47 47 | 
             
                end
         | 
| 48 48 |  | 
| 49 | 
            -
                def find_file(path, prefixes = [], *args)
         | 
| 50 | 
            -
                  _find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
         | 
| 51 | 
            -
                end
         | 
| 52 | 
            -
             | 
| 53 49 | 
             
                def find_all(path, prefixes = [], *args)
         | 
| 54 | 
            -
                   | 
| 50 | 
            +
                  prefixes = [prefixes] if String === prefixes
         | 
| 51 | 
            +
                  prefixes.each do |prefix|
         | 
| 52 | 
            +
                    paths.each do |resolver|
         | 
| 53 | 
            +
                      templates = resolver.find_all(path, prefix, *args)
         | 
| 54 | 
            +
                      return templates unless templates.empty?
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                  []
         | 
| 55 58 | 
             
                end
         | 
| 56 59 |  | 
| 57 60 | 
             
                def exists?(path, prefixes, *args)
         | 
| 58 61 | 
             
                  find_all(path, prefixes, *args).any?
         | 
| 59 62 | 
             
                end
         | 
| 60 63 |  | 
| 61 | 
            -
                 | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
                  prefixes.each do |prefix|
         | 
| 66 | 
            -
                    paths.each do |resolver|
         | 
| 67 | 
            -
                      if outside_app
         | 
| 68 | 
            -
                        templates = resolver.find_all_anywhere(path, prefix, *args)
         | 
| 69 | 
            -
                      else
         | 
| 70 | 
            -
                        templates = resolver.find_all(path, prefix, *args)
         | 
| 71 | 
            -
                      end
         | 
| 72 | 
            -
                      return templates unless templates.empty?
         | 
| 73 | 
            -
                    end
         | 
| 64 | 
            +
                def find_all_with_query(query) # :nodoc:
         | 
| 65 | 
            +
                  paths.each do |resolver|
         | 
| 66 | 
            +
                    templates = resolver.find_all_with_query(query)
         | 
| 67 | 
            +
                    return templates unless templates.empty?
         | 
| 74 68 | 
             
                  end
         | 
| 69 | 
            +
             | 
| 75 70 | 
             
                  []
         | 
| 76 71 | 
             
                end
         | 
| 77 72 |  | 
| 73 | 
            +
                private
         | 
| 74 | 
            +
             | 
| 78 75 | 
             
                def typecast(paths)
         | 
| 79 76 | 
             
                  paths.map do |path|
         | 
| 80 77 | 
             
                    case path
         | 
    
        data/lib/action_view/railtie.rb
    CHANGED
    
    | @@ -6,6 +6,7 @@ module ActionView | |
| 6 6 | 
             
              class Railtie < Rails::Railtie # :nodoc:
         | 
| 7 7 | 
             
                config.action_view = ActiveSupport::OrderedOptions.new
         | 
| 8 8 | 
             
                config.action_view.embed_authenticity_token_in_remote_forms = false
         | 
| 9 | 
            +
                config.action_view.debug_missing_translation = true
         | 
| 9 10 |  | 
| 10 11 | 
             
                config.eager_load_namespaces << ActionView
         | 
| 11 12 |  | 
| @@ -36,14 +37,30 @@ module ActionView | |
| 36 37 | 
             
                  end
         | 
| 37 38 | 
             
                end
         | 
| 38 39 |  | 
| 40 | 
            +
                initializer "action_view.collection_caching" do |app|
         | 
| 41 | 
            +
                  ActiveSupport.on_load(:action_controller) do
         | 
| 42 | 
            +
                    PartialRenderer.collection_cache = app.config.action_controller.cache_store
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                initializer "action_view.per_request_digest_cache" do |app|
         | 
| 47 | 
            +
                  ActiveSupport.on_load(:action_view) do
         | 
| 48 | 
            +
                    if app.config.consider_all_requests_local
         | 
| 49 | 
            +
                      app.middleware.use ActionView::Digestor::PerRequestDigestCacheExpiry
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 39 54 | 
             
                initializer "action_view.setup_action_pack" do |app|
         | 
| 40 55 | 
             
                  ActiveSupport.on_load(:action_controller) do
         | 
| 41 | 
            -
                    ActionView::RoutingUrlFor. | 
| 56 | 
            +
                    ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor)
         | 
| 42 57 | 
             
                  end
         | 
| 43 58 | 
             
                end
         | 
| 44 59 |  | 
| 45 | 
            -
                rake_tasks do
         | 
| 46 | 
            -
                   | 
| 60 | 
            +
                rake_tasks do |app|
         | 
| 61 | 
            +
                  unless app.config.api_only
         | 
| 62 | 
            +
                    load "action_view/tasks/dependencies.rake"
         | 
| 63 | 
            +
                  end
         | 
| 47 64 | 
             
                end
         | 
| 48 65 | 
             
              end
         | 
| 49 66 | 
             
            end
         | 
| @@ -2,29 +2,54 @@ require 'active_support/core_ext/module' | |
| 2 2 | 
             
            require 'action_view/model_naming'
         | 
| 3 3 |  | 
| 4 4 | 
             
            module ActionView
         | 
| 5 | 
            -
              #  | 
| 6 | 
            -
              #  | 
| 7 | 
            -
              # a higher logical level.
         | 
| 5 | 
            +
              # RecordIdentifier encapsulates methods used by various ActionView helpers
         | 
| 6 | 
            +
              # to associate records with DOM elements.
         | 
| 8 7 | 
             
              #
         | 
| 9 | 
            -
              # | 
| 10 | 
            -
              #   resources :posts
         | 
| 8 | 
            +
              # Consider for example the following code that displays the body of a post:
         | 
| 11 9 | 
             
              #
         | 
| 12 | 
            -
              #    | 
| 13 | 
            -
              # | 
| 14 | 
            -
              # | 
| 15 | 
            -
              #   <% end %>                  </div>
         | 
| 10 | 
            +
              #   <%= div_for(post) do %>
         | 
| 11 | 
            +
              #     <%= post.body %>
         | 
| 12 | 
            +
              #   <% end %>
         | 
| 16 13 | 
             
              #
         | 
| 17 | 
            -
              # | 
| 18 | 
            -
              # | 
| 19 | 
            -
              #     post = Post.find(params[:id])
         | 
| 20 | 
            -
              #     post.update(params[:post])
         | 
| 14 | 
            +
              # When +post+ is a new, unsaved ActiveRecord::Base instance, the resulting HTML
         | 
| 15 | 
            +
              # is:
         | 
| 21 16 | 
             
              #
         | 
| 22 | 
            -
              # | 
| 23 | 
            -
              # | 
| 17 | 
            +
              #    <div id="new_post" class="post">
         | 
| 18 | 
            +
              #    </div>
         | 
| 19 | 
            +
              #
         | 
| 20 | 
            +
              # When +post+ is a persisted ActiveRecord::Base instance, the resulting HTML
         | 
| 21 | 
            +
              # is:
         | 
| 22 | 
            +
              #
         | 
| 23 | 
            +
              #   <div id="post_42" class="post">
         | 
| 24 | 
            +
              #     What a wonderful world!
         | 
| 25 | 
            +
              #   </div>
         | 
| 26 | 
            +
              #
         | 
| 27 | 
            +
              # In both cases, the +id+ and +class+ of the wrapping DOM element are
         | 
| 28 | 
            +
              # automatically generated, following naming conventions encapsulated by the
         | 
| 29 | 
            +
              # RecordIdentifier methods #dom_id and #dom_class:
         | 
| 30 | 
            +
              #
         | 
| 31 | 
            +
              #   dom_id(Post.new)         # => "new_post"
         | 
| 32 | 
            +
              #   dom_class(Post.new)      # => "post"
         | 
| 33 | 
            +
              #   dom_id(Post.find 42)     # => "post_42"
         | 
| 34 | 
            +
              #   dom_class(Post.find 42)  # => "post"
         | 
| 24 35 | 
             
              #
         | 
| 25 | 
            -
              #  | 
| 26 | 
            -
              #  | 
| 27 | 
            -
              #  | 
| 36 | 
            +
              # Note that these methods do not strictly require +Post+ to be a subclass of
         | 
| 37 | 
            +
              # ActiveRecord::Base.
         | 
| 38 | 
            +
              # Any +Post+ class will work as long as its instances respond to +to_key+
         | 
| 39 | 
            +
              # and +model_name+, given that +model_name+ responds to +param_key+.
         | 
| 40 | 
            +
              # For instance:
         | 
| 41 | 
            +
              #
         | 
| 42 | 
            +
              #   class Post
         | 
| 43 | 
            +
              #     attr_accessor :to_key
         | 
| 44 | 
            +
              #
         | 
| 45 | 
            +
              #     def model_name
         | 
| 46 | 
            +
              #       OpenStruct.new param_key: 'post'
         | 
| 47 | 
            +
              #     end
         | 
| 48 | 
            +
              #
         | 
| 49 | 
            +
              #     def self.find(id)
         | 
| 50 | 
            +
              #       new.tap { |post| post.to_key = [id] }
         | 
| 51 | 
            +
              #     end
         | 
| 52 | 
            +
              #   end
         | 
| 28 53 | 
             
              module RecordIdentifier
         | 
| 29 54 | 
             
                extend self
         | 
| 30 55 | 
             
                extend ModelNaming
         | 
| @@ -78,7 +103,7 @@ module ActionView | |
| 78 103 | 
             
                # make sure yourself that your dom ids are valid, in case you overwrite this method.
         | 
| 79 104 | 
             
                def record_key_for_dom_id(record)
         | 
| 80 105 | 
             
                  key = convert_to_model(record).to_key
         | 
| 81 | 
            -
                  key ? key.join( | 
| 106 | 
            +
                  key ? key.join(JOIN) : key
         | 
| 82 107 | 
             
                end
         | 
| 83 108 | 
             
              end
         | 
| 84 109 | 
             
            end
         | 
| @@ -15,7 +15,7 @@ module ActionView | |
| 15 15 | 
             
              # that new object is called in turn. This abstracts the setup and rendering
         | 
| 16 16 | 
             
              # into a separate classes for partials and templates.
         | 
| 17 17 | 
             
              class AbstractRenderer #:nodoc:
         | 
| 18 | 
            -
                delegate :find_template, : | 
| 18 | 
            +
                delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
         | 
| 19 19 |  | 
| 20 20 | 
             
                def initialize(lookup_context)
         | 
| 21 21 | 
             
                  @lookup_context = lookup_context
         | 
| @@ -1,4 +1,5 @@ | |
| 1 | 
            -
            require ' | 
| 1 | 
            +
            require 'action_view/renderer/partial_renderer/collection_caching'
         | 
| 2 | 
            +
            require 'concurrent/map'
         | 
| 2 3 |  | 
| 3 4 | 
             
            module ActionView
         | 
| 4 5 | 
             
              class PartialIteration
         | 
| @@ -73,7 +74,7 @@ module ActionView | |
| 73 74 | 
             
              #
         | 
| 74 75 | 
             
              #   <%= render partial: "account", locals: { user: @buyer } %>
         | 
| 75 76 | 
             
              #
         | 
| 76 | 
            -
              # == Rendering a collection of partials
         | 
| 77 | 
            +
              # == \Rendering a collection of partials
         | 
| 77 78 | 
             
              #
         | 
| 78 79 | 
             
              # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
         | 
| 79 80 | 
             
              # render a sub template for each of the elements. This pattern has been implemented as a single method that
         | 
| @@ -105,7 +106,7 @@ module ActionView | |
| 105 106 | 
             
              # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
         | 
| 106 107 | 
             
              # just keep domain objects, like Active Records, in there.
         | 
| 107 108 | 
             
              #
         | 
| 108 | 
            -
              # == Rendering shared partials
         | 
| 109 | 
            +
              # == \Rendering shared partials
         | 
| 109 110 | 
             
              #
         | 
| 110 111 | 
             
              # Two controllers can share a set of partials and render them like this:
         | 
| 111 112 | 
             
              #
         | 
| @@ -113,7 +114,7 @@ module ActionView | |
| 113 114 | 
             
              #
         | 
| 114 115 | 
             
              # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
         | 
| 115 116 | 
             
              #
         | 
| 116 | 
            -
              # == Rendering objects that respond to `to_partial_path`
         | 
| 117 | 
            +
              # == \Rendering objects that respond to `to_partial_path`
         | 
| 117 118 | 
             
              #
         | 
| 118 119 | 
             
              # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work
         | 
| 119 120 | 
             
              # and pick the proper path by checking `to_partial_path` method.
         | 
| @@ -127,7 +128,7 @@ module ActionView | |
| 127 128 | 
             
              #  # <%= render partial: "posts/post", collection: @posts %>
         | 
| 128 129 | 
             
              #  <%= render partial: @posts %>
         | 
| 129 130 | 
             
              #
         | 
| 130 | 
            -
              # == Rendering the default case
         | 
| 131 | 
            +
              # == \Rendering the default case
         | 
| 131 132 | 
             
              #
         | 
| 132 133 | 
             
              # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
         | 
| 133 134 | 
             
              # defaults of render to render partials. Examples:
         | 
| @@ -147,29 +148,29 @@ module ActionView | |
| 147 148 | 
             
              #  # <%= render partial: "posts/post", collection: @posts %>
         | 
| 148 149 | 
             
              #  <%= render @posts %>
         | 
| 149 150 | 
             
              #
         | 
| 150 | 
            -
              # == Rendering partials with layouts
         | 
| 151 | 
            +
              # == \Rendering partials with layouts
         | 
| 151 152 | 
             
              #
         | 
| 152 153 | 
             
              # Partials can have their own layouts applied to them. These layouts are different than the ones that are
         | 
| 153 154 | 
             
              # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
         | 
| 154 155 | 
             
              # of users:
         | 
| 155 156 | 
             
              #
         | 
| 156 | 
            -
              #   <%# app/views/users/index.html.erb  | 
| 157 | 
            +
              #   <%# app/views/users/index.html.erb %>
         | 
| 157 158 | 
             
              #   Here's the administrator:
         | 
| 158 159 | 
             
              #   <%= render partial: "user", layout: "administrator", locals: { user: administrator } %>
         | 
| 159 160 | 
             
              #
         | 
| 160 161 | 
             
              #   Here's the editor:
         | 
| 161 162 | 
             
              #   <%= render partial: "user", layout: "editor", locals: { user: editor } %>
         | 
| 162 163 | 
             
              #
         | 
| 163 | 
            -
              #   <%# app/views/users/_user.html.erb  | 
| 164 | 
            +
              #   <%# app/views/users/_user.html.erb %>
         | 
| 164 165 | 
             
              #   Name: <%= user.name %>
         | 
| 165 166 | 
             
              #
         | 
| 166 | 
            -
              #   <%# app/views/users/_administrator.html.erb  | 
| 167 | 
            +
              #   <%# app/views/users/_administrator.html.erb %>
         | 
| 167 168 | 
             
              #   <div id="administrator">
         | 
| 168 169 | 
             
              #     Budget: $<%= user.budget %>
         | 
| 169 170 | 
             
              #     <%= yield %>
         | 
| 170 171 | 
             
              #   </div>
         | 
| 171 172 | 
             
              #
         | 
| 172 | 
            -
              #   <%# app/views/users/_editor.html.erb  | 
| 173 | 
            +
              #   <%# app/views/users/_editor.html.erb %>
         | 
| 173 174 | 
             
              #   <div id="editor">
         | 
| 174 175 | 
             
              #     Deadline: <%= user.deadline %>
         | 
| 175 176 | 
             
              #     <%= yield %>
         | 
| @@ -232,7 +233,7 @@ module ActionView | |
| 232 233 | 
             
              #
         | 
| 233 234 | 
             
              # You can also apply a layout to a block within any template:
         | 
| 234 235 | 
             
              #
         | 
| 235 | 
            -
              #   <%# app/views/users/_chief.html.erb  | 
| 236 | 
            +
              #   <%# app/views/users/_chief.html.erb %>
         | 
| 236 237 | 
             
              #   <%= render(layout: "administrator", locals: { user: chief }) do %>
         | 
| 237 238 | 
             
              #     Title: <%= chief.title %>
         | 
| 238 239 | 
             
              #   <% end %>
         | 
| @@ -249,13 +250,13 @@ module ActionView | |
| 249 250 | 
             
              # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
         | 
| 250 251 | 
             
              # an array to layout and treat it as an enumerable.
         | 
| 251 252 | 
             
              #
         | 
| 252 | 
            -
              #   <%# app/views/users/_user.html.erb  | 
| 253 | 
            +
              #   <%# app/views/users/_user.html.erb %>
         | 
| 253 254 | 
             
              #   <div class="user">
         | 
| 254 255 | 
             
              #     Budget: $<%= user.budget %>
         | 
| 255 256 | 
             
              #     <%= yield user %>
         | 
| 256 257 | 
             
              #   </div>
         | 
| 257 258 | 
             
              #
         | 
| 258 | 
            -
              #   <%# app/views/users/index.html.erb  | 
| 259 | 
            +
              #   <%# app/views/users/index.html.erb %>
         | 
| 259 260 | 
             
              #   <%= render layout: @users do |user| %>
         | 
| 260 261 | 
             
              #     Title: <%= user.title %>
         | 
| 261 262 | 
             
              #   <% end %>
         | 
| @@ -264,14 +265,14 @@ module ActionView | |
| 264 265 | 
             
              #
         | 
| 265 266 | 
             
              # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
         | 
| 266 267 | 
             
              #
         | 
| 267 | 
            -
              #   <%# app/views/users/_user.html.erb  | 
| 268 | 
            +
              #   <%# app/views/users/_user.html.erb %>
         | 
| 268 269 | 
             
              #   <div class="user">
         | 
| 269 270 | 
             
              #     <%= yield user, :header %>
         | 
| 270 271 | 
             
              #     Budget: $<%= user.budget %>
         | 
| 271 272 | 
             
              #     <%= yield user, :footer %>
         | 
| 272 273 | 
             
              #   </div>
         | 
| 273 274 | 
             
              #
         | 
| 274 | 
            -
              #   <%# app/views/users/index.html.erb  | 
| 275 | 
            +
              #   <%# app/views/users/index.html.erb %>
         | 
| 275 276 | 
             
              #   <%= render layout: @users do |user, section| %>
         | 
| 276 277 | 
             
              #     <%- case section when :header -%>
         | 
| 277 278 | 
             
              #       Title: <%= user.title %>
         | 
| @@ -280,8 +281,10 @@ module ActionView | |
| 280 281 | 
             
              #     <%- end -%>
         | 
| 281 282 | 
             
              #   <% end %>
         | 
| 282 283 | 
             
              class PartialRenderer < AbstractRenderer
         | 
| 283 | 
            -
                 | 
| 284 | 
            -
             | 
| 284 | 
            +
                include CollectionCaching
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                PREFIXED_PARTIAL_NAMES = Concurrent::Map.new do |h, k|
         | 
| 287 | 
            +
                  h[k] = Concurrent::Map.new
         | 
| 285 288 | 
             
                end
         | 
| 286 289 |  | 
| 287 290 | 
             
                def initialize(*)
         | 
| @@ -321,8 +324,9 @@ module ActionView | |
| 321 324 | 
             
                    spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
         | 
| 322 325 | 
             
                  end
         | 
| 323 326 |  | 
| 324 | 
            -
                   | 
| 325 | 
            -
             | 
| 327 | 
            +
                  cache_collection_render do
         | 
| 328 | 
            +
                    @template ? collection_with_template : collection_without_template
         | 
| 329 | 
            +
                  end.join(spacer).html_safe
         | 
| 326 330 | 
             
                end
         | 
| 327 331 |  | 
| 328 332 | 
             
                def render_partial
         | 
| @@ -334,7 +338,7 @@ module ActionView | |
| 334 338 | 
             
                  end
         | 
| 335 339 |  | 
| 336 340 | 
             
                  object = locals[as] if object.nil? # Respect object when object is false
         | 
| 337 | 
            -
                  locals[as] = object
         | 
| 341 | 
            +
                  locals[as] = object if @has_object
         | 
| 338 342 |  | 
| 339 343 | 
             
                  content = @template.render(view, locals) do |*name|
         | 
| 340 344 | 
             
                    view._layout_for(*name, &block)
         | 
| @@ -344,8 +348,6 @@ module ActionView | |
| 344 348 | 
             
                  content
         | 
| 345 349 | 
             
                end
         | 
| 346 350 |  | 
| 347 | 
            -
                private
         | 
| 348 | 
            -
             | 
| 349 351 | 
             
                # Sets up instance variables needed for rendering a partial. This method
         | 
| 350 352 | 
             
                # finds the options and details and extracts them. The method also contains
         | 
| 351 353 | 
             
                # logic that handles the type of object passed in as the partial.
         | 
| @@ -518,8 +520,8 @@ module ActionView | |
| 518 520 |  | 
| 519 521 | 
             
                def retrieve_variable(path, as)
         | 
| 520 522 | 
             
                  variable = as || begin
         | 
| 521 | 
            -
                    base = path[-1] == "/" ? "" : File.basename(path)
         | 
| 522 | 
            -
                    raise_invalid_identifier(path) unless base =~ /\A_?( | 
| 523 | 
            +
                    base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
         | 
| 524 | 
            +
                    raise_invalid_identifier(path) unless base =~ /\A_?(.*)(?:\.\w+)*\z/
         | 
| 523 525 | 
             
                    $1.to_sym
         | 
| 524 526 | 
             
                  end
         | 
| 525 527 | 
             
                  if @collection
         | 
| @@ -530,8 +532,7 @@ module ActionView | |
| 530 532 | 
             
                end
         | 
| 531 533 |  | 
| 532 534 | 
             
                IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +
         | 
| 533 | 
            -
                                           "make sure your partial name starts with underscore | 
| 534 | 
            -
                                           "and is followed by any combination of letters, numbers and underscores."
         | 
| 535 | 
            +
                                           "make sure your partial name starts with underscore."
         | 
| 535 536 |  | 
| 536 537 | 
             
                OPTION_AS_ERROR_MESSAGE  = "The value (%s) of the option `as` is not a valid Ruby identifier; " +
         | 
| 537 538 | 
             
                                           "make sure it starts with lowercase letter, " +
         |