Views & ERB

Ruby on Rails Fundamentals — 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.