1
0
Fork 0
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:
Aaron Patterson 2019-02-13 17:59:21 -08:00
parent d0733ba8c0
commit 1bc0a59d6e
No known key found for this signature in database
GPG key ID: 953170BCB4FFAFC6
7 changed files with 92 additions and 31 deletions

View file

@ -27,6 +27,46 @@ module ActionView
raise NotImplementedError
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
def extract_details(options) # :doc:
@ -49,5 +89,13 @@ module ActionView
@lookup_context.formats = formats | @lookup_context.formats
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

View file

@ -314,14 +314,6 @@ module ActionView
template = nil
end
@lookup_context.rendered_format ||= begin
if template && template.formats.first
template.formats.first
else
formats.first
end
end
if @collection
render_collection(context, template)
else
@ -334,10 +326,13 @@ module ActionView
def render_collection(view, template)
identifier = (template && template.identifier) || @path
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 = 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), nil, spacer_template)
else
RenderedTemplate::EMPTY_SPACER
end
collection_body = if template
@ -347,7 +342,7 @@ module ActionView
else
collection_without_template(view)
end
collection_body.join(spacer).html_safe
build_rendered_collection(collection_body, spacer)
end
end
@ -369,7 +364,7 @@ module ActionView
content = layout.render(view, locals) { content } if layout
payload[:cache_hit] = view.view_renderer.cache_hits[template.virtual_path]
content
build_rendered_template(content, layout, template)
end
end
@ -460,7 +455,7 @@ module ActionView
content = template.render(view, locals)
content = layout.render(view, locals) { content } if layout
partial_iteration.iterate!
content
build_rendered_template(content, layout, template)
end
end
@ -482,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, nil, template)
end
end

View file

@ -40,7 +40,7 @@ module ActionView
rendered_partials = @collection.empty? ? [] : yield
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
# for every cache miss while preserving order.
rendered_partials[index].tap { index += 1 }
@ -81,11 +81,13 @@ module ActionView
#
# If the partial is not already cached it will also be
# 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|
cached_partials.fetch(cache_key) do
if content = cached_partials[cache_key]
build_rendered_template(content, nil, template)
else
yield.tap do |rendered_partial|
collection_cache.write(cache_key, rendered_partial)
collection_cache.write(cache_key, rendered_partial.body)
end
end
end

View file

@ -19,10 +19,14 @@ module ActionView
# Main render entry point shared by Action View and Action Controller.
def render(context, options)
render_to_object(context, options).body
end
def render_to_object(context, options) # :nodoc:
if options.key?(:partial)
render_partial(context, options)
render_partial_to_object(context, options)
else
render_template(context, options)
render_template_to_object(context, options)
end
end
@ -41,16 +45,24 @@ module ActionView
# Direct access to template rendering.
def render_template(context, options) #:nodoc:
TemplateRenderer.new(@lookup_context).render(context, options)
render_template_to_object(context, options).body
end
# Direct access to partial rendering.
def render_partial(context, options, &block) #:nodoc:
PartialRenderer.new(@lookup_context).render(context, options, block)
render_partial_to_object(context, options, &block).body
end
def cache_hits # :nodoc:
@cache_hits ||= {}
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

View file

@ -44,7 +44,7 @@ module ActionView
# object that responds to each. This object is initialized with a block
# that knows how to render the template.
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 ||= {}
layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])

View file

@ -10,8 +10,6 @@ module ActionView
prepend_formats(template.formats)
@lookup_context.rendered_format ||= (template.formats.first || formats.first)
render_template(context, template, options[:layout], options[:locals] || {})
end
@ -46,23 +44,24 @@ module ActionView
# Renders the given template. A string representing the layout can be
# supplied as well.
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
template.render(view, locals) { |*name| view._layout_for(*name) }
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])
content = yield(layout)
if layout
body = if layout
view.view_flow.set(:layout, content)
layout.render(view, locals) { |*name| view._layout_for(*name) }
else
content
end
build_rendered_template(body, layout, template)
end
# This is the method which actually finds the layout using details in the lookup

View file

@ -109,10 +109,15 @@ module ActionView
context = view_context
context.assign assigns if assigns
lookup_context.rendered_format = nil if options[:formats]
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
# Assign the rendered format to look up context.