Ruby on Rails Module 4 – Views, forms and assets
ERB views, layouts, partials and helpers
Action View in Rails 8.1: how Rails picks a template and a layout, what a partial accepts as locals, and which output tags escape and which do not.
What you will learn
- Render a page from a template, a layout and a partial, with locals and a strict-locals signature
- Trace how Rails chooses the template, the format and the layout for an action that renders nothing
- Recognise the helpers that skip escaping, and sanitise a field that is meant to hold HTML
- Use the built-in helpers for links, buttons, numbers, dates and DOM ids
Before you start
What you’ll learn · Before you start
What you will learn
- Render a page from a template, a layout and a partial, with locals and a strict-locals signature
- Trace how Rails chooses the template, the format and the layout for an action that renders nothing
- Recognise the helpers that skip escaping, and sanitise a field that is meant to hold HTML
- Use the built-in helpers for links, buttons, numbers, dates and DOM ids
Before you start
On this page
The batch page of Kaksha, the coaching-centre app this track builds, is an action of two lines with no render call
in it, and the HTML that leaves it comes out of four files: a controller, a template, a layout and one partial
rendered three times, plus the helpers called inside them. Two kinds of trouble start in that handover. One is a page that looks
nothing like the one you edited, because Rails picked a different file. The other is a guardian’s typed text turning
into a pop-up, because one output tag was written the wrong way.
This lesson follows one recorded render of that page. You will see which files Rails chose and why, how the layout’s three holes were filled, what a partial refuses when a local is misspelt, and a check counting the script elements that reached the page with the safe tag and with the unsafe one.
Rails picks the template from the controller, the action and the format
Here is the whole controller behind the page. The index action sets one instance variable and ends:
# The action sets one instance variable and renders nothing: Rails then renders app/views/batches/index.html.erb
# wrapped in app/views/layouts/batches.html.erb.
class BatchesController < ApplicationController
def index
@batches = Batch.ordered
end
def show
@batch = Batch.find(params[:id])
end
end Because it calls neither render nor redirect_to, Action View has to work out what to render from two names it
already has: the controller, batches, and the action, index. It looks for app/views/batches/index, then narrows
that by the format the request asked for, html, and the last extension names the library that compiles the file.
index.html.erb matches both. If the file is not there, the search does not stop: Rails walks the controller’s
inheritance chain and tries app/views/application/index.html.erb too, which is why shared partials belong in
app/views/application. Only when nothing is found anywhere does it raise.
Step through the lookup, one stage per step, and watch the names the recorded run of the page later confirms:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The action sets data. BatchesController#index fills @batches and ends. It calls neither render nor redirect_to, so Action View takes over with the controller name and the action name in hand.
- The name comes from the route. The controller is batches and the action is index, so the lookup starts at app/views/batches/index. No file in the app says so: both names come from the route the router matched.
- Format comes from the request. The request asked for HTML, so Rails wants a template whose format is html, and the last extension names the library that compiles it. app/views/batches/index.html.erb matches both.
- A miss walks up the chain. Had that file been missing, Rails would have looked in app/views/application, the view folder of the parent controller, and raised only after that. This is why shared partials live there.
- The layout follows the name. Rails looks for app/views/layouts/batches.html.erb first, because the controller is BatchesController, and falls back to layouts/application.html.erb. This app has the first one.
- The layout wraps the result. The template is rendered first, its result reaches the layout at the unnamed yield, and the blocks the template collected fill the named ones. One HTML body goes back to the controller.
Now the render itself. Action View is asked for the page outside a request, so nothing listens on a port, and the
HTML is printed as it would be sent. Read the comments in the output first: in development a new app sets
config.action_view.annotate_rendered_view_with_filenames to true, so every template, partial and layout marks where
its own output begins and ends.
# The batch page of Kaksha, rendered by the real Action View. ../batchpage holds the files this lesson adds to the
# generated app (the controller, the model, the template, the partial, the layout and the routes); ../render_batch_page.rb
# renders BatchesController#index and prints the HTML.
require_relative "session"
Session.run(<<~SH)
cd ../kaksha
cp -R ../batchpage/app ../batchpage/config .
bin/rails runner ../render_batch_page.rb
SH Output
$ cd ../kaksha
$ cp -R ../batchpage/app ../batchpage/config .
$ bin/rails runner ../render_batch_page.rb
<!-- BEGIN app/views/layouts/batches.html.erb
--><!DOCTYPE html>
<html lang="en">
<head>
<title>Batches | Kaksha</title>
<meta name="description" content="Batches running at the Pune centre">
</head>
<body>
<header>Kaksha, Pune centre</header>
<main><!-- BEGIN app/views/batches/index.html.erb
-->
<h1>3 batches running</h1>
<!-- BEGIN app/views/batches/_batch.html.erb
--><article id="batch_1">
<h2><a href="/batches/1">Class 10 maths, morning</a></h2>
<p>Teacher Asha Rane, 28 seats</p>
<p>Fee ₹12,000 a term</p>
</article>
<!-- END app/views/batches/_batch.html.erb --><!-- BEGIN app/views/batches/_batch.html.erb
--><article id="batch_2">
<h2><a href="/batches/2">Class 12 physics, evening</a></h2>
<p>Teacher Imran Shah, 22 seats</p>
<p>Fee ₹18,000 a term</p>
</article>
<!-- END app/views/batches/_batch.html.erb --><!-- BEGIN app/views/batches/_batch.html.erb
--><article id="batch_3">
<h2><a href="/batches/3">Spoken English, weekend</a></h2>
<p>Teacher Nila Menon, 35 seats</p>
<p>Fee ₹6,000 a term</p>
</article>
<!-- END app/views/batches/_batch.html.erb -->
<!-- END app/views/batches/index.html.erb --></main>
</body>
</html>
<!-- END app/views/layouts/batches.html.erb -->
--- partial rendered 3 times, one per batch
Recorded with ruby 4.0.7 (revision 229531a6cf) +PRISM [arm64-darwin23] on macOS 26 arm64. To run it yourself: ruby batch_page_session.rb
Three files signed their work in those comments: the layout opened the body, batches/index.html.erb produced the
heading, and batches/_batch.html.erb appears three times, once per row of the collection. The two numbers in the output are the
lesson’s own: the heading says three batches, and the last line counts three renders of the partial.
Checkpoint
Check: How Rails finds the template
A layout wraps the template, and named blocks fill its holes
Rails renders the template first and hands the result to a layout, and the layout it picks follows the controller’s
name: app/views/layouts/batches.html.erb for anything BatchesController renders, and
app/views/layouts/application.html.erb when no such file exists. The one in this app is deliberately small:
<%# A layout named after the controller: Rails prefers app/views/layouts/batches.html.erb over application.html.erb
for every action of BatchesController. The unnamed yield receives the whole template; yield :head receives
whatever the template collected under content_for :head. %>
<!DOCTYPE html>
<html lang="en">
<head>
<title><%= content_for(:title) || "Kaksha" %></title>
<%= yield :head %>
</head>
<body>
<header>Kaksha, Pune centre</header>
<main><%= yield %></main>
</body>
</html> A layout: A template that wraps the one an action renders: the plain yield receives the whole template's output and each named yield receives a block the template collected with content_for. has one hole that is always filled and as many named ones as you like. The plain
yield receives the whole template; yield :head receives whatever the template collected under that name with
content_for. The generated application.html.erb of a new Rails 8.1 app uses both ideas already: it has
yield :head inside its head element, and its title element reads content_for(:title) || "Kaksha", which is the
page’s own title when a template set one and the app’s name when none did.
The template fills all three holes and renders nothing else of its own except a heading and the collection:
<%# app/views/batches/index.html.erb: the template Rails renders for BatchesController#index when the action
renders nothing itself. content_for fills the layout's two holes; render @batches renders one partial per row. %>
<% content_for :title, "Batches | Kaksha" %>
<% content_for :head do %>
<meta name="description" content="Batches running at the Pune centre">
<% end %>
<h1><%= pluralize(@batches.size, "batch") %> running</h1>
<%= render @batches %> The order is what makes this work, and the diagram below is worth reading next to the recorded output above: the
template runs before the layout, so by the time the layout asks for :title and :head, both are already collected.
The three holes of the batches layout, and what fills each
Text description of the diagram
The diagram runs from top to bottom.
- At the top sits app/views/batches/index.html.erb, the template Rails renders for this action.
- Three arrows leave it. Two are labelled "collects": one leads to the title that content_for :title holds as a string, the other to the markup that content_for :head holds as a block. The third, labelled "renders", leads to everything the template itself produces: its heading and the three batch partials.
- All three lead into app/views/layouts/batches.html.erb, which has three places to receive them: the title element, yield :head and the plain yield. The arrows are labelled "fills the title", "fills yield :head" and "fills the plain yield".
- The layout sends out one HTML body, which is what reaches the browser.
The order matters: the template is rendered before the layout, which is why the layout can read blocks the template collected.
In the recorded page, Batches | Kaksha is in the title element and the description meta element sits in the head,
neither of them written in the layout. Everything else the template produced landed at the plain yield.
Keep the page furniture in the layout, the page facts in the template
A layout that needs an if for every controller is a sign that the parts differ: give that controller its own
layout file, or move the varying piece into a content_for block that each template fills.
Partials follow the same inheritance rule as templates, so a piece of furniture that two controllers share can live
in app/views/application and be rendered from either template by name.
Key idea
The template is rendered first and the layout second, so a layout can only show what a template has already
collected: the plain yield for the body, a named yield for everything else.
Checkpoint
Check: Layouts and named blocks
Partials take locals, and a strict-locals line turns a typo into an error
A partial: A template whose file name starts with an underscore, rendered from another template and referred to without that underscore, so that one piece of markup can be reused with different data. is a template whose file name starts with an underscore, rendered from another
template and referred to without that underscore: render "batch" renders _batch.html.erb. Partials exist to be
called with different data each time, so what they receive matters more than what they contain. This one declares
what it accepts on its first line:
<%# locals: (batch:, show_fee: true) -%>
<article id="<%= dom_id(batch) %>">
<h2><%= link_to batch.name, batch_path(batch) %></h2>
<p>Teacher <%= batch.teacher %>, <%= batch.seats %> seats</p>
<% if show_fee %>
<p>Fee <%= number_to_currency(batch.fee_paise / 100, unit: "₹", precision: 0) %> a term</p>
<% end %>
</article> That first line is an ERB comment that Action View reads as a signature. Without it a partial accepts any locals at
all, and a misspelt name silently becomes nil, because partials are compiled into Ruby methods and one version is
compiled for every combination of locals passed in. With it, exactly one version is compiled, batch is required,
and show_fee has a default of true. Four renders of the same file show what that buys:
# Four renders of the same partial through its strict-locals signature: both locals given, the default used, a
# required local missing, and a local whose name is misspelt.
require_relative "session"
Session.run(<<~SH)
cd ../kaksha
cp -R ../batchpage/app ../batchpage/config .
bin/rails runner ../strict_locals_probe.rb
SH Output
$ cd ../kaksha $ cp -R ../batchpage/app ../batchpage/config . $ bin/rails runner ../strict_locals_probe.rb both locals given: rendered, fee paragraph left out show_fee left out: rendered, fee paragraph included batch left out : ActionView::StrictLocalsError: missing local: :batch for app/views/batches/_batch.html.erb show_fees misspelt: ActionView::StrictLocalsError: unknown local: :show_fees for app/views/batches/_batch.html.erb
Recorded with ruby 4.0.7 (revision 229531a6cf) +PRISM [arm64-darwin23] on macOS 26 arm64. To run it yourself: ruby strict_locals_session.rb
The first render passed show_fee: false and the fee paragraph is gone. The second passed only batch and the
default put the fee back. The third and fourth raise ActionView::StrictLocalsError and name the file: once for
missing local: :batch, once for unknown local: :show_fees, the misspelling that would otherwise have rendered a
page with no fee on it and no error anywhere.
The template never passed locals by hand, though. render @batches is the collection form: Rails asks each record
for its partial path, renders _batch once per record, and passes the record as a local named after the partial,
which is exactly the batch the signature requires. The table gives that form and the three options worth
knowing beside it:
| Written as | What Rails does |
|---|---|
render @batches |
One _batch partial per record, the record in a local called batch |
render partial: "batch", collection: @batches, as: :row |
The same, with the local renamed to row |
batch_counter inside the partial |
The position of this render, starting at 0 |
render partial: "batch", collection: @batches, cached: true |
Reads and writes each row’s HTML in the cache store, where caching is switched on |
An empty collection makes render return nil, so render(@batches) || "No batches yet." is the usual way to write
the empty case.
Looping instead of rendering the collection
Writing @batches.each do |batch| around render "batch", batch: batch gives the same HTML, but it gives up the
counter, the renaming, the empty-collection result and cached: true, and it reads the partial path once per row.
Let render @batches do it.
Checkpoint
Check: Partials, locals and collections
The default output tag escapes, and raw is where cross-site scripting starts
Everything so far assumed the data is harmless. A coaching centre’s data is typed by people: a batch name arrives
from a form, and someone will eventually type markup into it. The OWASP Cross Site Scripting Prevention Cheat Sheet
asks for encoding at the place where untrusted data is written into the page, and in Action View that place is the
output tag. <%= %> escapes what it prints; <% %> prints nothing at all and is for conditions and loops.
Three renders of one batch whose name is a script element show what each way produces. The check under each render
is Nokogiri parsing the result and counting the script elements a browser would have run, which is the only count
that matters:
# Output escaping in Action View, with a check that proves it. A batch whose name is a <script> element is rendered
# three ways; Nokogiri then counts the script elements that reached the page, which is what a browser would run.
# The raw version is here only to be recognised and never written: it is the usual start of a stored XSS defect.
require "action_view"
require "nokogiri"
NAME = "<script>alert(1)</script>"
NOTICE = "<p>Fees due <b>Friday</b>.</p><script>alert(2)</script>"
view = ActionView::Base.with_empty_template_cache.new(ActionView::LookupContext.new([]), {}, nil)
def check(html)
found = Nokogiri::HTML5.fragment(html).css("script").size
puts "#{html}"
puts " check: #{found} script element(s) reached the page -> #{found.zero? ? "PASS" : "FAIL"}"
end
puts "1. <%= %>, the default:"
check(view.render(inline: "<h2><%= name %></h2>", locals: { name: NAME }))
puts "2. raw, never do this with anything a user typed:"
check(view.render(inline: "<h2><%= raw name %></h2>", locals: { name: NAME }))
puts "3. sanitize with an allowlist, for a field that is meant to hold HTML:"
check(view.render(inline: "<div><%= sanitize notice, tags: %w[p b i a], attributes: %w[href] %></div>",
locals: { notice: NOTICE }))
puts "4. the same three values as Ruby strings:"
puts " html_safe? of the name #{NAME.html_safe?}"
puts " html_safe? after .html_safe #{NAME.html_safe.html_safe?}"
puts " html_safe? of sanitize's result #{view.sanitize(NOTICE).html_safe?}" Output
1. <%= %>, the default: <h2><script>alert(1)</script></h2> check: 0 script element(s) reached the page -> PASS 2. raw, never do this with anything a user typed: <h2><script>alert(1)</script></h2> check: 1 script element(s) reached the page -> FAIL 3. sanitize with an allowlist, for a field that is meant to hold HTML: <div><p>Fees due <b>Friday</b>.</p>alert(2)</div> check: 0 script element(s) reached the page -> PASS 4. the same three values as Ruby strings: html_safe? of the name false html_safe? after .html_safe true html_safe? of sanitize's result true
Recorded with ruby 4.0.7 (revision 229531a6cf) +PRISM [arm64-darwin23] on macOS 26 arm64. To run it yourself: ruby escaping_demo.rb
With <%= %> the angle brackets came out as < and >, the page shows the name as text, and the check found
no script element. With raw the element went straight through and the check found one: that is a stored
cross-site-scripting defect, and the only reason it is in this lesson is so that you can recognise the shape in a
code review. The third render is the case where HTML really is wanted: sanitize keeps an allowlist of tags and
attributes, so the p and b elements survived, the script element did not, and its text was left behind as plain
words.
Four ways of printing a value, the first safe and the other three not, are worth knowing by name:
| Written as | What it does |
|---|---|
<%= %> |
Escapes, unless the value is already marked as HTML-safe. The default, and the right choice |
raw value |
Prints the value unescaped; it is value.to_s.html_safe under another name |
<%== value %> |
The same as raw, in tag form, and easy to mistake for <%= %> |
value.html_safe |
Marks a string as safe, so every later <%= %> prints it unchanged |
The last block of the recorded output is the state they share: Action View asks every string it prints whether it
is HTML-safe: A flag on a string that tells Action View it may be printed unescaped; sanitize sets it on markup it has checked against an allowlist, and html_safe sets it on any string at all.. A plain batch name answers no, so it is escaped. The same name after
.html_safe answers yes, so it is not. The result of sanitize answers yes too, and that answer is earned: the
markup was checked against the allowlist before it was marked. The default sanitizer in Rails 8.1 is Rails::HTML5::SafeListSanitizer, and its
documentation warns that adding tags or attributes to the allowlist may introduce vulnerabilities, so the default
lists are the ones to keep.
Reaching for raw to make a layout look right
raw and html_safe on a value that came from a form, a request or a database column written by a user is how
output escaping: Replacing the characters that would start markup, such as the angle brackets and the ampersand, with character references at the place where a value is printed into a page, so that typed text stays text. gets lost. If markup is genuinely needed, pass the field through
sanitize and let the allowlist decide; if only the look is wrong, fix it in the markup around the value.
Checkpoint
Check: Escaping and the helpers that skip it
The helpers almost every page uses
A view helper: A method available inside templates, such as link_to or number_to_currency, that builds a piece of markup or formats a value, so that templates hold markup rather than logic. is a method available inside templates, and Action View ships with
several hundred. The batch page used four of them: pluralize in the template, and dom_id, link_to and
number_to_currency in the partial. This run prints each on the first batch, so the
output and the page agree value for value:
# The built-in view helpers on one batch of Kaksha. The render happens outside a request, so there is no session and
# the form that button_to builds carries no hidden authenticity_token field; in a real request Rails adds one.
require_relative "session"
Session.run(<<~SH)
cd ../kaksha
cp -R ../batchpage/app ../batchpage/config .
bin/rails runner ../helper_tour.rb
SH Output
$ cd ../kaksha $ cp -R ../batchpage/app ../batchpage/config . $ bin/rails runner ../helper_tour.rb link_to <a href="/batches/1">Class 10 maths, morning</a> dom_id batch_1 and new_batch dom_class batch number_to_currency ₹12,000 number_with_delimiter 12,000 l(:long) May 10, 2031 l(:short) May 10 time_ago_in_words 3 days and 35 minutes pluralize 1 batch / 3 batches truncate Class 10 maths,... button_to builds a form that posts, not a link: <!-- BEGIN inline template --><form class="button_to" method="post" action="/batches/1/attendance"><button type="submit">Mark attendance</button></form> <!-- END inline template -->
Recorded with ruby 4.0.7 (revision 229531a6cf) +PRISM [arm64-darwin23] on macOS 26 arm64. To run it yourself: ruby helper_tour_session.rb
link_to built the anchor, with the path from the route helper rather than a hand-written string, so a change to
config/routes.rb cannot leave a dead link behind. number_to_currency and number_with_delimiter grouped the
amount; l formatted the date twice, in the :long and :short formats the locale file defines; time_ago_in_words
turned two moments into “3 days” and “35 minutes”; pluralize and truncate did the small jobs that otherwise fill
a template with ifs.
Two of the lines deserve a second look. dom_id returned batch_1 for a saved record and new_batch for an unsaved
one, and dom_class returned batch: these are the conventional ids and classes that form_with writes and that
Turbo targets, so using the helper instead of typing "batch_#{batch.id}" keeps your markup and the framework’s in
step. And button_to produced a form element with method="post", not an anchor. That difference is the point: a
state change belongs in a non-GET request, and the Rails security guide’s countermeasure for cross-site request
forgery is a security token in non-GET requests, added automatically because
config.action_controller.default_protect_from_forgery is true in a new app. Rails puts that token in the forms it
builds, so button_to is protected where a styled link is not. The recorded form has no token field only because the
render happened outside a request, where there is no session to tie a token to.
India-specific
The amounts in the run are rupees, printed by number_to_currency with the unit given in the template. The two
fees, 12,000 and 18,000, happen to be grouped the same way in the default en locale as they would be in the
Indian convention; a seven-digit total would not be, coming out as 1,200,000 rather than 12,00,000. The
internationalisation module of this track sets up that grouping and a locale of your own.
One thing about these templates will change in the next minor release, and it is worth building the habit now.
Version note
Rails 8.1 compiles ERB templates with Erubi. In Rails 8.2 config.action_view.erb_implementation also accepts
:herb, and the 8.2 framework defaults choose it for templates whose format is HTML, leaving every other format on
Erubi. Herb parses the HTML as well as the ERB, so markup that is well formed today, with every element closed in
the right order, is markup that needs no fixing after that upgrade.
Checkpoint
Check: The built-in view helpers
Practise: predict what each file renders
These questions are taken from the four runs above: the rendered page, the four sets of locals, the three ways of printing a dangerous name, and the helper output. Answer one and its explanation opens at once:
Practice that adapts to you
Practise this lesson
11 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up: which Action View tag escapes its output, and which three ways of printing a value do not?
<%= %> escapes, unless the value it prints is already marked HTML-safe, so &, ", < and > reach the page as
character references and a typed script element is shown as text. Three ways skip that. raw value prints the
value unchanged and is only value.to_s.html_safe written differently. <%== value %> is the same thing in tag
form, which makes it easy to read as <%= %> in a code review. And value.html_safe marks the string itself, so every
later <%= %> prints it as it is, however far from the template that marking happened. The safe way to print a field
that is meant to carry markup is sanitize, which checks the markup against an allowlist of tags and attributes and
marks only the result as safe.
The module’s question bank takes two longer cases from this page, one on a field that must keep its formatting and one on where shared page furniture belongs. Write down your own answer to both before you read what each question sets out to test:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
A page shows teachers' announcements, which may carry bold text and links, so the template prints the field with the raw helper. A guardian reports a pop-up on that page. How do you make the page safe without losing the formatting, and how do you show that no other page has the same hole?
Flashcards · 6 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
An action renders nothing. Which template does Rails look for, and where does it search before it raises?
The template named after the controller and the action, for the request's format. If that is missing, Rails walks up the controller's parent classes, through app/views/application, before raising an error.
-
Why can a layout show a page title that its template set with content_for?
The template renders first and the layout second. The plain yield takes the body and a named yield takes what content_for collected. Rails picks the layout by the controller's name, then falls back to layouts/application.html.erb.
-
What does a strict-locals line at the top of a partial change?
A local it lists without a default becomes required, one with a default may be left out, and a name it does not list, such as a misspelling, raises ActionView::StrictLocalsError instead of rendering a page with a value silently missing.
-
What does render @batches do?
It renders the _batch partial once for each record, with the record in a local named batch and its position, from 0, in batch_counter. An empty collection makes render return nil, so a fallback text can follow it after ||.
-
The <%= %> tag escapes what it prints. Which three ways of printing skip that?
raw, the <%== %> tag and a string marked with html_safe. In the recorded run only the raw version let a script element into the page, so none of the three may carry text a person typed.
-
Which helper should a state change such as a delete use, and which helper builds the ids Turbo expects?
button_to, which writes a small form that sends a non-GET request; link_to with a route helper stays for plain navigation. dom_id builds the element ids that forms and Turbo already look for.
Key takeaways
- An action that renders nothing gets the template named after its controller and action, narrowed by the request’s
format, and Rails walks the controller’s inheritance chain, through
app/views/application, before it raises. - The template is rendered before the layout, which Rails picks by controller name and then by
layouts/application.html.erb; the plainyieldtakes the body and a namedyieldtakes whatevercontent_forcollected. - A partial’s strict-locals signature makes required locals required and misspelt ones an error, which is why the
recorded run raised
ActionView::StrictLocalsErrorinstead of rendering a page with a missing fee. render @batchesrenders one partial per record with the record in a local named after the partial, and brings the counter, renaming, the empty-collection result andcached: truewith it.<%= %>escapes by default, andraw,<%== %>andhtml_safeare the three ways that defect reports begin: the check on the recorded run found a script element in the page only in therawversion.- The built-in helpers keep markup in step with the framework: route helpers inside
link_to,button_tofor a state change in a non-GET form, anddom_idfor the ids that forms and Turbo already expect.
Terms in this lesson
- HTML-safe
- A flag on a string that tells Action View it may be printed unescaped; sanitize sets it on markup it has checked against an allowlist, and html_safe sets it on any string at all.
- layout
- A template that wraps the one an action renders: the plain yield receives the whole template's output and each named yield receives a block the template collected with content_for.
- output escaping
- Replacing the characters that would start markup, such as the angle brackets and the ampersand, with character references at the place where a value is printed into a page, so that typed text stays text.
- partial
- A template whose file name starts with an underscore, rendered from another template and referred to without that underscore, so that one piece of markup can be reused with different data.
- view helper
- A method available inside templates, such as link_to or number_to_currency, that builds a piece of markup or formats a value, so that templates hold markup rather than logic.
References
- Layouts and Rendering in Rails (Ruby on Rails)
- Action View Overview (Ruby on Rails)
- Action View Helpers (Ruby on Rails)
- Securing Rails Applications (Ruby on Rails)
- ActionView::Helpers::SanitizeHelper (Ruby on Rails)
- ActionView::Helpers::OutputSafetyHelper (Ruby on Rails)
- ActionView::RecordIdentifier (Ruby on Rails)
- This Week in Rails: a new framework default for compiling templates (Ruby on Rails)
- Cross Site Scripting Prevention Cheat Sheet (OWASP Foundation)
Report a problem with this lesson
Kept only in this browser. Your Learn progress