1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionview/lib/action_view/base.rb

282 lines
11 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require "active_support/core_ext/module/attr_internal"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/ordered_options"
require "action_view/log_subscriber"
require "action_view/helpers"
require "action_view/context"
require "action_view/template"
require "action_view/lookup_context"
2009-05-13 04:10:37 -04:00
module ActionView #:nodoc:
# = Action View Base
#
# Action View templates can be written in several ways.
# If the template file has a <tt>.erb</tt> extension, then it uses the erubi[https://rubygems.org/gems/erubi]
# template system which can embed Ruby into an HTML document.
# If the template file has a <tt>.builder</tt> extension, then Jim Weirich's Builder::XmlMarkup library is used.
#
# == ERB
#
# You trigger ERB by using embeddings such as <tt><% %></tt>, <tt><% -%></tt>, and <tt><%= %></tt>. The <tt><%= %></tt> tag set is used when you want output. Consider the
# following loop for names:
#
# <b>Names of all the people</b>
2010-12-24 14:41:50 -05:00
# <% @people.each do |person| %>
# Name: <%= person.name %><br/>
# <% end %>
#
2019-08-09 17:30:15 -04:00
# The loop is set up in regular embedding tags <tt><% %></tt>, and the name is written using the output embedding tag <tt><%= %></tt>. Note that this
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
#
2010-04-05 05:51:26 -04:00
# <%# WRONG %>
# Hi, Mr. <% puts "Frodo" %>
#
2010-04-05 05:51:26 -04:00
# If you absolutely must write from within a function use +concat+.
#
# When on a line that only contains whitespaces except for the tag, <tt><% %></tt> suppresses leading and trailing whitespace,
# including the trailing newline. <tt><% %></tt> and <tt><%- -%></tt> are the same.
# Note however that <tt><%= %></tt> and <tt><%= -%></tt> are different: only the latter removes trailing whitespaces.
#
# === Using sub templates
#
# Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
# classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
#
# <%= render "shared/header" %>
# Something really specific and terrific
# <%= render "shared/footer" %>
#
# As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
# result of the rendering. The output embedding writes it to the current template.
#
# But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
# variables defined using the regular embedding tags. Like this:
#
# <% @page_title = "A Wonderful Hello" %>
# <%= render "shared/header" %>
#
Merge docrails: commit e6afd8b2736364322b673bbdcca3e9b38b6d3da0 Author: Xavier Noria <fxn@hashref.com> Date: Thu May 8 23:49:36 2008 +0200 Overall documentation improvement and markup corrections. Zillion changes. commit 2fead68b3192332eee27945ed95a94a64ca73f70 Author: Austin Putman <austin@emmanuel.local> Date: Wed May 7 19:35:46 2008 -0700 Documented class methods on ActionController::Routing. These are dangerous, and mostly used for testing. commit f5b84182dbc39bea79c8ee319c688d00fa99f9d1 Author: Teflon Ted <github@rudiment.net> Date: Wed May 7 16:08:49 2008 -0400 Added explanation about errant inflections not being patched in the future in order to avoid breaking legacy applications. commit 370f4f51722cec49ace17093d29e9ce9e8f15cfb Author: Sunny Ripert <negatif@gmail.com> Date: Wed May 7 14:00:59 2008 +0200 Applied list conventions in AR::Base commit 5bd18429f09d44e75191bec42a6db04bd33f3030 Author: Sunny Ripert <negatif@gmail.com> Date: Wed May 7 13:53:35 2008 +0200 Renamed Options list to Attributes list whenever they weren't option hashes in AR::Base commit d912bd5672316454457ae83f6e9dda5197beeb6f Author: Yaroslav Markin <yaroslav@markin.net> Date: Wed May 7 13:50:28 2008 +0400 Add a filter_parameter_logging usage hint to generated ApplicationController. This may help to remind the developer to filter sensitive information from application logs. Closes #11578 commit b243de0db3c2605121e055079854af5090d06374 Author: Jack Danger Canty <git@6brand.com> Date: Tue May 6 23:39:47 2008 -0700 doc: disambiguating an example ActiveRecord class commit f81d771f0657ae8375b84a77a059812cce5d6fd9 Author: Jack Danger Canty <git@6brand.com> Date: Tue May 6 23:35:05 2008 -0700 doc: ActiveRecord::Reflection::AssociationReflection#through_reflection Added documentation demonstrating the use of #through_reflection for finding intervening reflection objects for HasManyThrough and HasOneThrough. commit ae6b46f00b5b8b2939c6b37ce3329c83de7e71db Author: Cheah Chu Yeow <chuyeow@gmail.com> Date: Wed May 7 13:47:41 2008 +0800 Document AttributeAssignmentError and MultiparameterAssignmentErrors. commit 8f463550b597db2156b67733f31aed13487fbc3a Author: John Barnette <jbarnette@gmail.com> Date: Tue May 6 22:46:44 2008 -0700 Killing/fixing a bunch of outdated language in the AR README. commit aca44bcd92ef783abdf484b58abdde6786db0f89 Author: Cheah Chu Yeow <chuyeow@gmail.com> Date: Wed May 7 13:34:52 2008 +0800 Make a note about ActiveResource::Timeouterror being raised when ARes calls timeout. commit 284a930a93fbee16e25d06392779dbf2f03e9e12 Author: Jonathan Dance <jd@wuputah.com> Date: Tue May 6 14:58:26 2008 -0400 improvements to the page caching docs commit 9482da621390c874da7c921c8bd6230caae7035a Author: Sunny Ripert <negatif@gmail.com> Date: Mon May 5 18:13:40 2008 +0200 validates_numericality_of() "integer" option really is "only_integer" commit e9afd6790a8f530528f6597a7f59bb283be754f6 Author: Sunny Ripert <negatif@gmail.com> Date: Mon May 5 12:11:59 2008 +0200 Harmonized hash notation in AR::Base commit 67ebf14a91ffd970b582be4ff2991d691a9cf3e1 Author: Sunny Ripert <negatif@gmail.com> Date: Mon May 5 12:06:19 2008 +0200 Turned options into rdoc-lists in AR::Base commit 0ec7c0a41d889d4e5382b9dff72f1aaba89bf297 Author: Marshall Huss <mwhuss@Macbook.local> Date: Sun May 4 23:21:33 2008 -0400 Added information of how to set element_name in the case the user has a name confliction with an existing model Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2008-05-09 05:38:02 -04:00
# Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
#
# <title><%= @page_title %></title>
#
# === Passing local variables to sub templates
#
# You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
#
2012-10-29 07:10:53 -04:00
# <%= render "shared/header", { headline: "Welcome", person: person } %>
#
Merge docrails: commit e6afd8b2736364322b673bbdcca3e9b38b6d3da0 Author: Xavier Noria <fxn@hashref.com> Date: Thu May 8 23:49:36 2008 +0200 Overall documentation improvement and markup corrections. Zillion changes. commit 2fead68b3192332eee27945ed95a94a64ca73f70 Author: Austin Putman <austin@emmanuel.local> Date: Wed May 7 19:35:46 2008 -0700 Documented class methods on ActionController::Routing. These are dangerous, and mostly used for testing. commit f5b84182dbc39bea79c8ee319c688d00fa99f9d1 Author: Teflon Ted <github@rudiment.net> Date: Wed May 7 16:08:49 2008 -0400 Added explanation about errant inflections not being patched in the future in order to avoid breaking legacy applications. commit 370f4f51722cec49ace17093d29e9ce9e8f15cfb Author: Sunny Ripert <negatif@gmail.com> Date: Wed May 7 14:00:59 2008 +0200 Applied list conventions in AR::Base commit 5bd18429f09d44e75191bec42a6db04bd33f3030 Author: Sunny Ripert <negatif@gmail.com> Date: Wed May 7 13:53:35 2008 +0200 Renamed Options list to Attributes list whenever they weren't option hashes in AR::Base commit d912bd5672316454457ae83f6e9dda5197beeb6f Author: Yaroslav Markin <yaroslav@markin.net> Date: Wed May 7 13:50:28 2008 +0400 Add a filter_parameter_logging usage hint to generated ApplicationController. This may help to remind the developer to filter sensitive information from application logs. Closes #11578 commit b243de0db3c2605121e055079854af5090d06374 Author: Jack Danger Canty <git@6brand.com> Date: Tue May 6 23:39:47 2008 -0700 doc: disambiguating an example ActiveRecord class commit f81d771f0657ae8375b84a77a059812cce5d6fd9 Author: Jack Danger Canty <git@6brand.com> Date: Tue May 6 23:35:05 2008 -0700 doc: ActiveRecord::Reflection::AssociationReflection#through_reflection Added documentation demonstrating the use of #through_reflection for finding intervening reflection objects for HasManyThrough and HasOneThrough. commit ae6b46f00b5b8b2939c6b37ce3329c83de7e71db Author: Cheah Chu Yeow <chuyeow@gmail.com> Date: Wed May 7 13:47:41 2008 +0800 Document AttributeAssignmentError and MultiparameterAssignmentErrors. commit 8f463550b597db2156b67733f31aed13487fbc3a Author: John Barnette <jbarnette@gmail.com> Date: Tue May 6 22:46:44 2008 -0700 Killing/fixing a bunch of outdated language in the AR README. commit aca44bcd92ef783abdf484b58abdde6786db0f89 Author: Cheah Chu Yeow <chuyeow@gmail.com> Date: Wed May 7 13:34:52 2008 +0800 Make a note about ActiveResource::Timeouterror being raised when ARes calls timeout. commit 284a930a93fbee16e25d06392779dbf2f03e9e12 Author: Jonathan Dance <jd@wuputah.com> Date: Tue May 6 14:58:26 2008 -0400 improvements to the page caching docs commit 9482da621390c874da7c921c8bd6230caae7035a Author: Sunny Ripert <negatif@gmail.com> Date: Mon May 5 18:13:40 2008 +0200 validates_numericality_of() "integer" option really is "only_integer" commit e9afd6790a8f530528f6597a7f59bb283be754f6 Author: Sunny Ripert <negatif@gmail.com> Date: Mon May 5 12:11:59 2008 +0200 Harmonized hash notation in AR::Base commit 67ebf14a91ffd970b582be4ff2991d691a9cf3e1 Author: Sunny Ripert <negatif@gmail.com> Date: Mon May 5 12:06:19 2008 +0200 Turned options into rdoc-lists in AR::Base commit 0ec7c0a41d889d4e5382b9dff72f1aaba89bf297 Author: Marshall Huss <mwhuss@Macbook.local> Date: Sun May 4 23:21:33 2008 -0400 Added information of how to set element_name in the case the user has a name confliction with an existing model Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2008-05-09 05:38:02 -04:00
# These can now be accessed in <tt>shared/header</tt> with:
#
# Headline: <%= headline %>
# First name: <%= person.first_name %>
#
# The local variables passed to sub templates can be accessed as a hash using the <tt>local_assigns</tt> hash. This lets you access the
# variables as:
#
# Headline: <%= local_assigns[:headline] %>
#
# This is useful in cases where you aren't sure if the local variable has been assigned. Alternatively, you could also use
# <tt>defined? headline</tt> to first check if the variable has been assigned before using it.
#
# === Template caching
#
# By default, Rails will compile each template to a method in order to render it. When you alter a template,
# Rails will check the file's modification time and recompile it in development mode.
#
# == Builder
#
# Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
# named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
#
# Here are some basic examples:
#
2011-05-10 19:55:31 -04:00
# xml.em("emphasized") # => <em>emphasized</em>
# xml.em { xml.b("emph & bold") } # => <em><b>emph &amp; bold</b></em>
# xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
# xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\>
# # NOTE: order of attributes is not specified.
#
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
#
2011-12-05 16:11:20 -05:00
# xml.div do
# xml.h1(@person.name)
# xml.p(@person.bio)
2011-12-05 16:11:20 -05:00
# end
#
# would produce something like:
#
# <div>
# <h1>David Heinemeier Hansson</h1>
# <p>A product of Danish Design during the Winter of '79...</p>
# </div>
#
# Here is a full-length RSS example actually used on Basecamp:
#
# xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
# xml.channel do
# xml.title(@feed_title)
# xml.link(@url)
# xml.description "Basecamp: Recent items"
# xml.language "en-us"
# xml.ttl "40"
#
# @recent_items.each do |item|
# xml.item do
# xml.title(item_title(item))
# xml.description(item_description(item)) if item_description(item)
# xml.pubDate(item_pubDate(item))
# xml.guid(@person.firm.account.url + @recent_items.url(item))
# xml.link(@person.firm.account.url + @recent_items.url(item))
#
# xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
# end
# end
# end
# end
#
2014-11-29 16:52:21 -05:00
# For more information on Builder please consult the {source
# code}[https://github.com/jimweirich/builder].
class Base
include Helpers, ::ERB::Util, Context
# Specify the proc used to decorate input tags that refer to attributes with errors.
cattr_accessor :field_error_proc, default: Proc.new { |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
# How to complete the streaming when an exception occurs.
# This is our best guess: first try to close the attribute, then the tag.
cattr_accessor :streaming_completion_on_exception, default: %("><script>window.location = "/500.html"</script></html>)
# Specify whether rendering within namespaced controllers should prefix
# the partial paths for ActiveModel objects with the namespace.
# (e.g., an Admin::PostsController would render @post using /admin/posts/_post.erb)
class_attribute :prefix_partial_path_with_controller_namespace, default: true
# Specify default_formats that can be rendered.
cattr_accessor :default_formats
# Specify whether an error should be raised for missing translations
cattr_accessor :raise_on_missing_translations, default: false
# Specify whether submit_tag should automatically disable on click
cattr_accessor :automatically_disable_submit_tag, default: true
# Annotate rendered view with file names
cattr_accessor :annotate_rendered_view_with_filenames, default: false
class_attribute :_routes
2012-01-18 12:05:36 -05:00
class_attribute :logger
class << self
2016-08-06 13:36:34 -04:00
delegate :erb_trim_mode=, to: "ActionView::Template::Handlers::ERB"
def cache_template_loading
ActionView::Resolver.caching?
end
def cache_template_loading=(value)
ActionView::Resolver.caching = value
end
2011-05-04 06:04:29 -04:00
def xss_safe? #:nodoc:
true
end
def with_empty_template_cache # :nodoc:
subclass = Class.new(self) {
# We can't implement these as self.class because subclasses will
# share the same template cache as superclasses, so "changed?" won't work
# correctly.
define_method(:compiled_method_container) { subclass }
define_singleton_method(:compiled_method_container) { subclass }
def inspect
"#<ActionView::Base:#{'%#016x' % (object_id << 1)}>"
end
}
end
def changed?(other) # :nodoc:
compiled_method_container != other.compiled_method_container
end
end
attr_reader :view_renderer, :lookup_context
2011-05-04 06:04:29 -04:00
attr_internal :config, :assigns
2016-08-06 13:36:34 -04:00
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, to: :lookup_context
def assign(new_assigns) # :nodoc:
@_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
# :stopdoc:
def self.empty
with_view_paths([])
end
def self.with_view_paths(view_paths, assigns = {}, controller = nil)
with_context ActionView::LookupContext.new(view_paths), assigns, controller
end
def self.with_context(context, assigns = {}, controller = nil)
new context, assigns, controller
end
# :startdoc:
def initialize(lookup_context, assigns, controller) #:nodoc:
@_config = ActiveSupport::InheritableOptions.new
@lookup_context = lookup_context
@view_renderer = ActionView::Renderer.new @lookup_context
@current_template = nil
2011-05-04 06:04:29 -04:00
assign(assigns)
assign_controller(controller)
_prepare_context
end
Ensure cache fragment digests include all templates A Rails view may rely on several templates (e.g. layouts and partials) in addition to the template for the action being rendered (e.g. "show.html.erb"). To track which view file is currently being rendered for the purpose of generating template tree digests used in cache fragment keys, Action View uses a stack, the top item of which is accessed via the @current_template variable (introduced in 1581cab). Consider the following template: <!-- home.html.erb --> <%= render layout: "wrapper" do %> <%= cache "foo" %> HOME <%= end %> <%= end %> Inside the block passed to the render helper, @current_template corresponds to the wrapper.html.erb template instead of home.html.erb. As wrapper.html.erb is then used as the root node for generating the template tree digest used in the cache fragment key, the cache fragment fails to expire upon changes to home.html.erb. Additionally, should a second template use the wrapper.html.erb layout and contain a cache fragment with the same key, the cache fragment keys for both templates will be identical - causing cached content to "leak" from one view to another (as described in #38984). This commit skips adding templates to the stack when rendered as a layout with a block via the render helper, ensuring correct and unique cache fragment digests. Additionally, the virtual_path keyword arguments found in CacheHelper and all references to the are removed as they no longer possess any function. (Following the introduction of @current_template, virtual_path is accessed via @current_template.virtual_path rather than as a standalone variable.)
2020-04-24 17:29:00 -04:00
def _run(method, template, locals, buffer, add_to_stack: true, &block)
_old_output_buffer, _old_template = @output_buffer, @current_template
Ensure cache fragment digests include all templates A Rails view may rely on several templates (e.g. layouts and partials) in addition to the template for the action being rendered (e.g. "show.html.erb"). To track which view file is currently being rendered for the purpose of generating template tree digests used in cache fragment keys, Action View uses a stack, the top item of which is accessed via the @current_template variable (introduced in 1581cab). Consider the following template: <!-- home.html.erb --> <%= render layout: "wrapper" do %> <%= cache "foo" %> HOME <%= end %> <%= end %> Inside the block passed to the render helper, @current_template corresponds to the wrapper.html.erb template instead of home.html.erb. As wrapper.html.erb is then used as the root node for generating the template tree digest used in the cache fragment key, the cache fragment fails to expire upon changes to home.html.erb. Additionally, should a second template use the wrapper.html.erb layout and contain a cache fragment with the same key, the cache fragment keys for both templates will be identical - causing cached content to "leak" from one view to another (as described in #38984). This commit skips adding templates to the stack when rendered as a layout with a block via the render helper, ensuring correct and unique cache fragment digests. Additionally, the virtual_path keyword arguments found in CacheHelper and all references to the are removed as they no longer possess any function. (Following the introduction of @current_template, virtual_path is accessed via @current_template.virtual_path rather than as a standalone variable.)
2020-04-24 17:29:00 -04:00
@current_template = template if add_to_stack
@output_buffer = buffer
public_send(method, locals, buffer, &block)
ensure
@output_buffer, @current_template = _old_output_buffer, _old_template
end
def compiled_method_container
raise NotImplementedError, <<~msg.squish
Subclasses of ActionView::Base must implement `compiled_method_container`
or use the class method `with_empty_template_cache` for constructing
an ActionView::Base subclass that has an empty cache.
msg
end
2019-02-13 18:35:20 -05:00
def in_rendering_context(options)
old_view_renderer = @view_renderer
old_lookup_context = @lookup_context
if !lookup_context.html_fallback_for_js && options[:formats]
formats = Array(options[:formats])
if formats == [:js]
formats << :html
end
@lookup_context = lookup_context.with_prepended_formats(formats)
@view_renderer = ActionView::Renderer.new @lookup_context
end
yield @view_renderer
ensure
@view_renderer = old_view_renderer
@lookup_context = old_lookup_context
end
ActiveSupport.run_load_hooks(:action_view, self)
end
end