diff options
Diffstat (limited to 'actionview/lib/action_view/renderer/partial_renderer.rb')
-rw-r--r-- | actionview/lib/action_view/renderer/partial_renderer.rb | 120 |
1 files changed, 67 insertions, 53 deletions
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index d7f97c3b50..ed8d5cf54e 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -295,43 +295,60 @@ module ActionView end def render(context, options, block) - setup(context, options, block) - @template = find_partial + as = as_variable(options) + setup(context, options, as, block) - @lookup_context.rendered_format ||= begin - if @template && @template.formats.present? - @template.formats.first + if @path + if @has_object || @collection + @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as) + @template_keys = retrieve_template_keys(@variable) else - formats.first + @template_keys = @locals.keys + end + template = find_partial(@path, @template_keys) + @variable ||= template.variable + else + if options[:cached] + raise NotImplementedError, "render caching requires a template. Please specify a partial when rendering" end + template = nil end if @collection - render_collection + render_collection(context, template) else - render_partial + render_partial(context, template) end end private - def render_collection - instrument(:collection, count: @collection.size) do |payload| - return nil if @collection.blank? + def render_collection(view, template) + identifier = (template && template.identifier) || @path + instrument(:collection, identifier: identifier, count: @collection.size) do |payload| + return RenderedCollection.empty(@lookup_context.formats.first) if @collection.blank? - if @options.key?(:spacer_template) - spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals) + spacer = if @options.key?(:spacer_template) + spacer_template = find_template(@options[:spacer_template], @locals.keys) + build_rendered_template(spacer_template.render(view, @locals), spacer_template) + else + RenderedTemplate::EMPTY_SPACER end - cache_collection_render(payload) do - @template ? collection_with_template : collection_without_template - end.join(spacer).html_safe + collection_body = if template + cache_collection_render(payload, view, template) do + collection_with_template(view, template) + end + else + collection_without_template(view) + end + build_rendered_collection(collection_body, spacer) end end - def render_partial - instrument(:partial) do |payload| - view, locals, block = @view, @locals, @block + def render_partial(view, template) + instrument(:partial, identifier: template.identifier) do |payload| + locals, block = @locals, @block object, as = @object, @variable if !block && (layout = @options[:layout]) @@ -341,13 +358,13 @@ module ActionView object = locals[as] if object.nil? # Respect object when object is false locals[as] = object if @has_object - content = @template.render(view, locals) do |*name| + content = template.render(view, locals) do |*name| view._layout_for(*name, &block) end content = layout.render(view, locals) { content } if layout - payload[:cache_hit] = view.view_renderer.cache_hits[@template.virtual_path] - content + payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path] + build_rendered_template(content, template, layout) end end @@ -358,16 +375,13 @@ module ActionView # If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is # set to that string. Otherwise, the +options[:partial]+ object must # respond to +to_partial_path+ in order to setup the path. - def setup(context, options, block) - @view = context + def setup(context, options, as, block) @options = options @block = block - @locals = options[:locals] ? options[:locals].symbolize_keys : {} + @locals = options[:locals] || {} @details = extract_details(options) - prepend_formats(options[:formats]) - partial = options[:partial] if String === partial @@ -381,26 +395,26 @@ module ActionView @collection = collection_from_object || collection_from_options if @collection - paths = @collection_data = @collection.map { |o| partial_path(o) } - @path = paths.uniq.one? ? paths.first : nil + paths = @collection_data = @collection.map { |o| partial_path(o, context) } + if paths.uniq.length == 1 + @path = paths.first + else + paths.map! { |path| retrieve_variable(path, as).unshift(path) } + @path = nil + end else - @path = partial_path + @path = partial_path(@object, context) end end + self + end + + def as_variable(options) if as = options[:as] raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s) - as = as.to_sym - end - - if @path - @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as) - @template_keys = retrieve_template_keys - else - paths.map! { |path| retrieve_variable(path, as).unshift(path) } + as.to_sym end - - self end def collection_from_options @@ -414,8 +428,8 @@ module ActionView @object.to_ary if @object.respond_to?(:to_ary) end - def find_partial - find_template(@path, @template_keys) if @path + def find_partial(path, template_keys) + find_template(path, template_keys) end def find_template(path, locals) @@ -423,8 +437,8 @@ module ActionView @lookup_context.find_template(path, prefixes, true, locals, @details) end - def collection_with_template - view, locals, template = @view, @locals, @template + def collection_with_template(view, template) + locals = @locals as, counter, iteration = @variable, @variable_counter, @variable_iteration if layout = @options[:layout] @@ -441,12 +455,12 @@ module ActionView content = template.render(view, locals) content = layout.render(view, locals) { content } if layout partial_iteration.iterate! - content + build_rendered_template(content, template, layout) end end - def collection_without_template - view, locals, collection_data = @view, @locals, @collection_data + def collection_without_template(view) + locals, collection_data = @locals, @collection_data cache = {} keys = @locals.keys @@ -463,7 +477,7 @@ module ActionView template = (cache[path] ||= find_template(path, keys + [as, counter, iteration])) content = template.render(view, locals) partial_iteration.iterate! - content + build_rendered_template(content, template) end end @@ -474,7 +488,7 @@ module ActionView # # If +prefix_partial_path_with_controller_namespace+ is true, then this # method will prefix the partial paths with a namespace. - def partial_path(object = @object) + def partial_path(object, view) object = object.to_model if object.respond_to?(:to_model) path = if object.respond_to?(:to_partial_path) @@ -483,7 +497,7 @@ module ActionView raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.") end - if @view.prefix_partial_path_with_controller_namespace + if view.prefix_partial_path_with_controller_namespace prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup) else path @@ -511,9 +525,9 @@ module ActionView end end - def retrieve_template_keys + def retrieve_template_keys(variable) keys = @locals.keys - keys << @variable if @has_object || @collection + keys << variable if @collection keys << @variable_counter keys << @variable_iteration @@ -523,7 +537,7 @@ module ActionView def retrieve_variable(path, as) variable = as || begin - base = path[-1] == "/".freeze ? "".freeze : File.basename(path) + base = path[-1] == "/" ? "" : File.basename(path) raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/ $1.to_sym end |