Views & ERB

Ruby on Rails Fundamentals
Course 1 ยท Chapter 4 ยท Views & ERB

๐Ÿ“ Views & ERB

Chapter 3's implicit rendering pointed at a view file by convention โ€” this chapter is what's actually inside it. Rails' templating language, ERB (Embedded Ruby), turns out to share a surprising amount of syntax with the EJS templates already covered in Express, which makes this one of the more comfortable transitions in the whole course.

๐Ÿงฌ ERB Tags โ€” <%= %> vs <% %>

<!-- app/views/posts/show.html.erb -->
<h1><%= @post.title %></h1>

<% if @post.published? %>
  <p>Published</p>
<% end %>

ERB vs EJS โ€” A Genuine Syntax Convergence

PurposeEJS (Express)ERB (Rails)
Output a value (escaped)<%= value %><%= value %>
Run logic, no output<% if (x) { %><% if x %>
File extension.ejs.html.erb

This is worth pausing on: <%= %> for output and <% %> for logic-only is identical syntax between EJS and ERB โ€” a genuine convergence, similar in spirit to the spaceship operator and safe navigation operator syntax Ruby Course 2 flagged converging into PHP. The main practical difference: ERB's logic tags use Ruby's own if/end block syntax (Ruby Course 1, Chapter 3) instead of EJS's JavaScript curly braces.

๐ŸŒ‰ Instance Variables Arrive Automatically

# PostsController
def show
  @post = Post.find(params[:id])
end

<!-- app/views/posts/show.html.erb -- @post is just... available -->
<h1><%= @post.title %></h1>

Recall Chapter 3's @post โ€” every instance variable set in a controller action is automatically available inside that action's view, with no explicit data-passing step. Compare this to Express, where res.render("show", { post }) requires the controller to explicitly build and pass a data object into the template every single time.

๐Ÿ–ผ๏ธ Layouts

<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
  <head><title>My Blog</title></head>
  <body>
    <nav>...</nav>
    <%= yield %>   <!-- the current view's content gets inserted here -->
  </body>
</html>

yield here is the same keyword from Ruby Course 1 Chapter 4's blocks โ€” the layout is, in effect, a method that yields to whichever view is currently being rendered. Every view in the app is automatically wrapped in application.html.erb with zero effort on the individual view's part.

โš  EJS has no automatic equivalent of this

Express's EJS setup (Chapter 1's comparisons already flagged this pattern) typically requires each template to explicitly <%- include('partials/header') %> and <%- include('partials/footer') %> itself โ€” shared page structure is opt-in, template by template. Rails' layout wraps every view automatically; you'd have to explicitly opt out (layout false) rather than opt in.

๐Ÿงฉ Partials

<!-- app/views/posts/_form.html.erb -- leading underscore names a partial -->
<%= form_with model: post do |f| %>
  <%= f.text_field :title %>
<% end %>

<!-- app/views/posts/new.html.erb -->
<h1>New Post</h1>
<%= render "form", post: @post %>   <!-- note: no leading underscore when referencing it -->

Partials are reusable view fragments, named with a leading underscore on disk but referenced without it. render "form", post: @post passes post in explicitly as a local variable to the partial โ€” this is the conceptual equivalent of EJS's include(), just with Rails' own naming convention (the underscore) marking a file as a partial rather than a full view.

๐Ÿ› ๏ธ View Helpers

<%= link_to "Edit", edit_post_path(@post) %>
<!-- generates: <a href="/posts/5/edit">Edit</a> -->

link_to is a real Ruby method call โ€” using the named route helpers from Chapter 2 โ€” that generates the HTML for you, rather than hand-writing an <a href="..."> tag with a string-interpolated URL the way an EJS template typically would. Dozens of similar helpers exist for forms, dates, and more.

๐Ÿ›ก๏ธ Automatic Escaping

@comment = "<script>alert('xss')</script>"

<%= @comment %>         <!-- output is escaped -- renders as inert TEXT, not executed -->
<%= raw(@comment) %>    <!-- explicitly opt out -- renders as real, executable HTML -->

<%= %> automatically HTML-escapes its output by default โ€” the exact XSS protection covered in depth in the completed XSS security course, here built into the templating layer itself rather than something you have to remember to apply. EJS's <%= %> auto-escapes too (a real parallel), while EJS's separate <%- %> tag opts out; Rails instead keeps one output tag and requires an explicit raw(...) call (or .html_safe) to bypass escaping.

๐Ÿ“œ Templating, Side by Side

EJS vs ERB

ConceptEJS (Express)ERB (Rails)
Passing data inExplicit object in res.render(view, data)Automatic โ€” controller's instance variables
Shared layoutOpt-in via include() in each templateAutomatic โ€” every view wraps in the layout by default
Reusable fragmentinclude('partials/name')render "name", locals passed explicitly
Escape by default?Yes, with <%= %> (opt out via <%- %>)Yes, with <%= %> (opt out via raw()/.html_safe)

๐Ÿ’ป Coding Challenges

Challenge 1: An index View

Write app/views/posts/index.html.erb that loops over @posts (an array of Post objects, each with .title and .published?) using <% %>, and for each one outputs the title with <%= %>, only showing a "Published" label next to posts where .published? is true.

Goal: Practice correctly distinguishing output tags from logic-only tags in a realistic loop.

โ†’ Solution

Challenge 2: A Shared Navbar Partial

Write a partial app/views/shared/_navbar.html.erb containing a simple nav with two link_to calls, then show the single line you'd add to application.html.erb (above the yield) to render it on every page.

Goal: Practice the underscore-on-disk, no-underscore-when-referenced partial convention.

โ†’ Solution

Challenge 3: Escaping vs raw

Given @bio = "Bold claim" set in a controller, write two lines in a view: one using plain <%= %> and one using raw(...). Add a comment describing exactly how each would actually render in the browser, and which one you'd use for genuinely untrusted, user-submitted content.

Goal: Be certain, not just aware, of when escaping matters.

โ†’ Solution

๐Ÿ’Ž Secure by Default, Not by Discipline

The XSS course already covered why unescaped user content is dangerous. What's worth noticing here is the framework-level design choice: Rails makes the safe behavior (<%= %>, escaped) the default and the dangerous behavior (raw(...)) something you have to explicitly, visibly opt into. That's a meaningfully different security posture than a templating setup where escaping is something a developer has to remember to apply correctly every time.

๐ŸŽฏ What's Next

Next chapter: Active Record Basics โ€” migrations, models, and how Post.find(params[:id]), used casually throughout this chapter and the last, actually talks to the database.