mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Return rendered template information instead of just strings
This commit introduces "rendered template" and "rendered collection" objects. The template renderers can now return a more complex object than just strings. This allows the framework to get more information about the templates that were rendered. In this commit we use the rendered template object to set the "rendered_format" on the lookup context in the controller rather than all the way in the template renderer. That means we don't need to check the "rendered_format" every time we render a template, we just do it once after all templates have been rendered.
This commit is contained in:
parent
d0733ba8c0
commit
1bc0a59d6e
7 changed files with 92 additions and 31 deletions
|
@ -27,6 +27,46 @@ module ActionView
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class RenderedCollection # :nodoc:
|
||||||
|
attr_reader :rendered_templates
|
||||||
|
|
||||||
|
def initialize(rendered_templates, spacer)
|
||||||
|
@rendered_templates = rendered_templates
|
||||||
|
@spacer = spacer
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
@rendered_templates.map(&:body).join(@spacer.body).html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def format
|
||||||
|
rendered_templates.first.format
|
||||||
|
end
|
||||||
|
|
||||||
|
class EmptyCollection
|
||||||
|
def format; nil; end
|
||||||
|
def body; nil; end
|
||||||
|
end
|
||||||
|
|
||||||
|
EMPTY = EmptyCollection.new
|
||||||
|
end
|
||||||
|
|
||||||
|
class RenderedTemplate # :nodoc:
|
||||||
|
attr_reader :body, :layout, :template
|
||||||
|
|
||||||
|
def initialize(body, layout, template)
|
||||||
|
@body = body
|
||||||
|
@layout = layout
|
||||||
|
@template = template
|
||||||
|
end
|
||||||
|
|
||||||
|
def format
|
||||||
|
template.formats.first
|
||||||
|
end
|
||||||
|
|
||||||
|
EMPTY_SPACER = Struct.new(:body).new
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def extract_details(options) # :doc:
|
def extract_details(options) # :doc:
|
||||||
|
@ -49,5 +89,13 @@ module ActionView
|
||||||
|
|
||||||
@lookup_context.formats = formats | @lookup_context.formats
|
@lookup_context.formats = formats | @lookup_context.formats
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_rendered_template(content, layout, template)
|
||||||
|
RenderedTemplate.new content, layout, template
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_rendered_collection(templates, spacer)
|
||||||
|
RenderedCollection.new(templates, spacer)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -314,14 +314,6 @@ module ActionView
|
||||||
template = nil
|
template = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@lookup_context.rendered_format ||= begin
|
|
||||||
if template && template.formats.first
|
|
||||||
template.formats.first
|
|
||||||
else
|
|
||||||
formats.first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if @collection
|
if @collection
|
||||||
render_collection(context, template)
|
render_collection(context, template)
|
||||||
else
|
else
|
||||||
|
@ -334,10 +326,13 @@ module ActionView
|
||||||
def render_collection(view, template)
|
def render_collection(view, template)
|
||||||
identifier = (template && template.identifier) || @path
|
identifier = (template && template.identifier) || @path
|
||||||
instrument(:collection, identifier: identifier, count: @collection.size) do |payload|
|
instrument(:collection, identifier: identifier, count: @collection.size) do |payload|
|
||||||
return nil if @collection.blank?
|
return RenderedCollection::EMPTY if @collection.blank?
|
||||||
|
|
||||||
if @options.key?(:spacer_template)
|
spacer = if @options.key?(:spacer_template)
|
||||||
spacer = find_template(@options[:spacer_template], @locals.keys).render(view, @locals)
|
spacer_template = find_template(@options[:spacer_template], @locals.keys)
|
||||||
|
build_rendered_template(spacer_template.render(view, @locals), nil, spacer_template)
|
||||||
|
else
|
||||||
|
RenderedTemplate::EMPTY_SPACER
|
||||||
end
|
end
|
||||||
|
|
||||||
collection_body = if template
|
collection_body = if template
|
||||||
|
@ -347,7 +342,7 @@ module ActionView
|
||||||
else
|
else
|
||||||
collection_without_template(view)
|
collection_without_template(view)
|
||||||
end
|
end
|
||||||
collection_body.join(spacer).html_safe
|
build_rendered_collection(collection_body, spacer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -369,7 +364,7 @@ module ActionView
|
||||||
|
|
||||||
content = layout.render(view, locals) { content } if layout
|
content = layout.render(view, locals) { content } if layout
|
||||||
payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
|
payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
|
||||||
content
|
build_rendered_template(content, layout, template)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -460,7 +455,7 @@ module ActionView
|
||||||
content = template.render(view, locals)
|
content = template.render(view, locals)
|
||||||
content = layout.render(view, locals) { content } if layout
|
content = layout.render(view, locals) { content } if layout
|
||||||
partial_iteration.iterate!
|
partial_iteration.iterate!
|
||||||
content
|
build_rendered_template(content, layout, template)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -482,7 +477,7 @@ module ActionView
|
||||||
template = (cache[path] ||= find_template(path, keys + [as, counter, iteration]))
|
template = (cache[path] ||= find_template(path, keys + [as, counter, iteration]))
|
||||||
content = template.render(view, locals)
|
content = template.render(view, locals)
|
||||||
partial_iteration.iterate!
|
partial_iteration.iterate!
|
||||||
content
|
build_rendered_template(content, nil, template)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ module ActionView
|
||||||
rendered_partials = @collection.empty? ? [] : yield
|
rendered_partials = @collection.empty? ? [] : yield
|
||||||
|
|
||||||
index = 0
|
index = 0
|
||||||
fetch_or_cache_partial(cached_partials, order_by: keyed_collection.each_key) do
|
fetch_or_cache_partial(cached_partials, template, order_by: keyed_collection.each_key) do
|
||||||
# This block is called once
|
# This block is called once
|
||||||
# for every cache miss while preserving order.
|
# for every cache miss while preserving order.
|
||||||
rendered_partials[index].tap { index += 1 }
|
rendered_partials[index].tap { index += 1 }
|
||||||
|
@ -81,11 +81,13 @@ module ActionView
|
||||||
#
|
#
|
||||||
# If the partial is not already cached it will also be
|
# If the partial is not already cached it will also be
|
||||||
# written back to the underlying cache store.
|
# written back to the underlying cache store.
|
||||||
def fetch_or_cache_partial(cached_partials, order_by:)
|
def fetch_or_cache_partial(cached_partials, template, order_by:)
|
||||||
order_by.map do |cache_key|
|
order_by.map do |cache_key|
|
||||||
cached_partials.fetch(cache_key) do
|
if content = cached_partials[cache_key]
|
||||||
|
build_rendered_template(content, nil, template)
|
||||||
|
else
|
||||||
yield.tap do |rendered_partial|
|
yield.tap do |rendered_partial|
|
||||||
collection_cache.write(cache_key, rendered_partial)
|
collection_cache.write(cache_key, rendered_partial.body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,10 +19,14 @@ module ActionView
|
||||||
|
|
||||||
# Main render entry point shared by Action View and Action Controller.
|
# Main render entry point shared by Action View and Action Controller.
|
||||||
def render(context, options)
|
def render(context, options)
|
||||||
|
render_to_object(context, options).body
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_to_object(context, options) # :nodoc:
|
||||||
if options.key?(:partial)
|
if options.key?(:partial)
|
||||||
render_partial(context, options)
|
render_partial_to_object(context, options)
|
||||||
else
|
else
|
||||||
render_template(context, options)
|
render_template_to_object(context, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,16 +45,24 @@ module ActionView
|
||||||
|
|
||||||
# Direct access to template rendering.
|
# Direct access to template rendering.
|
||||||
def render_template(context, options) #:nodoc:
|
def render_template(context, options) #:nodoc:
|
||||||
TemplateRenderer.new(@lookup_context).render(context, options)
|
render_template_to_object(context, options).body
|
||||||
end
|
end
|
||||||
|
|
||||||
# Direct access to partial rendering.
|
# Direct access to partial rendering.
|
||||||
def render_partial(context, options, &block) #:nodoc:
|
def render_partial(context, options, &block) #:nodoc:
|
||||||
PartialRenderer.new(@lookup_context).render(context, options, block)
|
render_partial_to_object(context, options, &block).body
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_hits # :nodoc:
|
def cache_hits # :nodoc:
|
||||||
@cache_hits ||= {}
|
@cache_hits ||= {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render_template_to_object(context, options) #:nodoc:
|
||||||
|
TemplateRenderer.new(@lookup_context).render(context, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_partial_to_object(context, options, &block) #:nodoc:
|
||||||
|
PartialRenderer.new(@lookup_context).render(context, options, block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,7 @@ module ActionView
|
||||||
# object that responds to each. This object is initialized with a block
|
# object that responds to each. This object is initialized with a block
|
||||||
# that knows how to render the template.
|
# that knows how to render the template.
|
||||||
def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
|
def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
|
||||||
return [super] unless layout_name && template.supports_streaming?
|
return [super.body] unless layout_name && template.supports_streaming?
|
||||||
|
|
||||||
locals ||= {}
|
locals ||= {}
|
||||||
layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])
|
layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])
|
||||||
|
|
|
@ -10,8 +10,6 @@ module ActionView
|
||||||
|
|
||||||
prepend_formats(template.formats)
|
prepend_formats(template.formats)
|
||||||
|
|
||||||
@lookup_context.rendered_format ||= (template.formats.first || formats.first)
|
|
||||||
|
|
||||||
render_template(context, template, options[:layout], options[:locals] || {})
|
render_template(context, template, options[:layout], options[:locals] || {})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,23 +44,24 @@ module ActionView
|
||||||
# Renders the given template. A string representing the layout can be
|
# Renders the given template. A string representing the layout can be
|
||||||
# supplied as well.
|
# supplied as well.
|
||||||
def render_template(view, template, layout_name, locals)
|
def render_template(view, template, layout_name, locals)
|
||||||
render_with_layout(view, layout_name, locals) do |layout|
|
render_with_layout(view, layout_name, template, locals) do |layout|
|
||||||
instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
|
instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
|
||||||
template.render(view, locals) { |*name| view._layout_for(*name) }
|
template.render(view, locals) { |*name| view._layout_for(*name) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_with_layout(view, path, locals)
|
def render_with_layout(view, path, template, locals)
|
||||||
layout = path && find_layout(path, locals.keys, [formats.first])
|
layout = path && find_layout(path, locals.keys, [formats.first])
|
||||||
content = yield(layout)
|
content = yield(layout)
|
||||||
|
|
||||||
if layout
|
body = if layout
|
||||||
view.view_flow.set(:layout, content)
|
view.view_flow.set(:layout, content)
|
||||||
layout.render(view, locals) { |*name| view._layout_for(*name) }
|
layout.render(view, locals) { |*name| view._layout_for(*name) }
|
||||||
else
|
else
|
||||||
content
|
content
|
||||||
end
|
end
|
||||||
|
build_rendered_template(body, layout, template)
|
||||||
end
|
end
|
||||||
|
|
||||||
# This is the method which actually finds the layout using details in the lookup
|
# This is the method which actually finds the layout using details in the lookup
|
||||||
|
|
|
@ -109,10 +109,15 @@ module ActionView
|
||||||
context = view_context
|
context = view_context
|
||||||
|
|
||||||
context.assign assigns if assigns
|
context.assign assigns if assigns
|
||||||
lookup_context.rendered_format = nil if options[:formats]
|
|
||||||
lookup_context.variants = variant if variant
|
lookup_context.variants = variant if variant
|
||||||
|
|
||||||
context.view_renderer.render(context, options)
|
rendered_template = context.in_rendering_context(options) do |renderer|
|
||||||
|
renderer.render_to_object(context, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
lookup_context.rendered_format = rendered_template.format || lookup_context.formats.first
|
||||||
|
|
||||||
|
rendered_template.body
|
||||||
end
|
end
|
||||||
|
|
||||||
# Assign the rendered format to look up context.
|
# Assign the rendered format to look up context.
|
||||||
|
|
Loading…
Reference in a new issue