# frozen_string_literal: true
# :markup: markdown
module ActionView
module Helpers # :nodoc:
# # Action View Rendering Helpers
#
# Implements methods that allow rendering from a view context. In order to use
# this module, all you need is to implement view_renderer that returns an
# ActionView::Renderer object.
module RenderingHelper
# Renders a template and returns the result.
#
# Pass the template to render as the first argument. This is shorthand
# syntax for partial rendering, so the template filename should be
# prefixed with an underscore. The partial renderer looks for the partial
# template in the directory of the calling template first.
#
# <% # app/views/posts/new.html.erb %>
# <%= render "form" %>
# # => renders app/views/posts/_form.html.erb
#
# Use the complete view path to render a partial from another directory.
#
# <% # app/views/posts/show.html.erb %>
# <%= render "comments/form" %>
# # => renders app/views/comments/_form.html.erb
#
# Without the rendering mode, the second argument can be a Hash of local
# variable assignments for the template.
#
# <% # app/views/posts/new.html.erb %>
# <%= render "form", post: Post.new %>
# # => renders app/views/posts/_form.html.erb
#
# If the first argument responds to `render_in`, the template will be rendered
# by calling `render_in` with the current view context.
#
# class Greeting
# def render_in(view_context)
# view_context.render html: "
Hello, World
"
# end
#
# def format
# :html
# end
# end
#
# <%= render Greeting.new %>
# # => "Hello, World
"
#
# #### Rendering Mode
#
# Pass the rendering mode as first argument to override it.
#
# `:partial`
# : See ActionView::PartialRenderer for details.
#
# <%= render partial: "form", locals: { post: Post.new } %>
# # => renders app/views/posts/_form.html.erb
#
# `:file`
# : Renders the contents of a file. This option should **not** be used with
# unsanitized user input.
#
# <%= render file: "/path/to/some/file" %>
# # => renders /path/to/some/file
#
# `:inline`
# : Renders an ERB template string.
#
# <% name = "World" %>
# <%= render inline: "Hello, <%= name %>!
" %>
# # => renders "Hello, World!
"
#
# `:body`
# : Renders the provided text, and sets the format as `:text`.
#
# <%= render body: "Hello, World!" %>
# # => renders "Hello, World!"
#
# `:plain`
# : Renders the provided text, and sets the format as `:text`.
#
# <%= render plain: "Hello, World!" %>
# # => renders "Hello, World!"
#
# `:html`
# : Renders the provided HTML string, and sets the format as
# `:html`. If the string is not `html_safe?`, performs HTML escaping on
# the string before rendering.
#
# <%= render html: "Hello, World!
".html_safe %>
# # => renders "Hello, World!
"
#
# <%= render html: "Hello, World!
" %>
# # => renders "<h1>Hello, World!</h1>"
#
# `:renderable`
# : Renders the provided object by calling `render_in` with the current view
# context. The format is determined by calling `format` on the
# renderable if it responds to `format`, falling back to `:html` by
# default.
#
# <%= render renderable: Greeting.new %>
# # => renders "Hello, World
"
#
#
# #### Options
#
# `:locals`
# : Hash of local variable assignments for the template.
#
# <%= render inline: "Hello, <%= name %>!
", locals: { name: "World" } %>
# # => renders "Hello, World!
"
#
# `:formats`
# : Override the current format to render a template for a different format.
#
# <% # app/views/posts/show.html.erb %>
# <%= render template: "posts/content", formats: [:text] %>
# # => renders app/views/posts/content.text.erb
#
# `:variants`
# : Render a template for a different variant.
#
# <% # app/views/posts/show.html.erb %>
# <%= render template: "posts/content", variants: [:tablet] %>
# # => renders app/views/posts/content.html+tablet.erb
#
# `:handlers`
# : Render a template for a different handler.
#
# <% # app/views/posts/show.html.erb %>
# <%= render template: "posts/content", handlers: [:builder] %>
# # => renders app/views/posts/content.html.builder
def render(options = {}, locals = {}, &block)
case options
when Hash
in_rendering_context(options) do |renderer|
if block_given?
view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
else
view_renderer.render(self, options)
end
end
else
if options.respond_to?(:render_in)
options.render_in(self, &block)
else
view_renderer.render_partial(self, partial: options, locals: locals, &block)
end
end
end
# Overrides _layout_for in the context object so it supports the case a block is
# passed to a partial. Returns the contents that are yielded to a layout, given
# a name or a block.
#
# You can think of a layout as a method that is called with a block. If the user
# calls `yield :some_name`, the block, by default, returns
# `content_for(:some_name)`. If the user calls simply `yield`, the default block
# returns `content_for(:layout)`.
#
# The user can override this default by passing a block to the layout:
#
# # The template
# <%= render layout: "my_layout" do %>
# Content
# <% end %>
#
# # The layout
#
# <%= yield %>
#
#
# In this case, instead of the default block, which would return `content_for(:layout)`,
# this method returns the block that was passed in to `render :layout`, and the response
# would be
#
#
# Content
#
#
# Finally, the block can take block arguments, which can be passed in by
# `yield`:
#
# # The template
# <%= render layout: "my_layout" do |customer| %>
# Hello <%= customer.name %>
# <% end %>
#
# # The layout
#
# <%= yield Struct.new(:name).new("David") %>
#
#
# In this case, the layout would receive the block passed into `render :layout`,
# and the struct specified would be passed into the block as an argument. The result
# would be
#
#
# Hello David
#
#
def _layout_for(*args, &block)
name = args.first
if block && !name.is_a?(Symbol)
capture(*args, &block)
else
super
end
end
end
end
end