Views: ERB Templates & the Rails MVC

Website Rebuild with Ruby on Rails

Chapter 4 · Views: ERB Templates & the Rails MVC

A Rails Controller action renders a view — the same MVC shape as every sibling course. The template layer, ERB, is where this chapter finds its own genuinely distinctive material: not in layouts and partials, which every sibling already has an equivalent of, but in exactly how much of the host language ERB lets through, and in a dedicated architectural layer — Helpers — the other three siblings don't offer quite the same way.

Layouts: The Same Wrapping Shell, ERB's Own Syntax

<!-- app/views/layouts/application.html.erb --> <!DOCTYPE html> <html> <head><title><%= @page&.title || 'Untitled' %></title></head> <body> <%= yield %> </body> </html>

<%= yield %> is Rails' own equivalent of Blade's @yield and Django's {% block %} — the point where the current page's own template content is dropped into the shared shell. <%= %> outputs the expression's result into the page; plain <% %> executes Ruby without outputting anything — used for control flow like loops and conditionals.

Partials: Reusable View Fragments

<%# app/views/pages/show.html.erb --> <%= render partial: 'breadcrumb', locals: { page: @page } %> <h1><%= @page.title %></h1> <%== @page.body %>

<%== %> outputs without HTML-escaping — ERB's own direct equivalent of Blade's {!! !!} and Django's |safe filter, needed here for the same reason: @page.body is trusted, already-formatted HTML content, not user input that needs escaping.

A Third Place for View Logic: Helpers

# app/helpers/pages_helper.rb module PagesHelper def breadcrumb_for(page) ancestors = [] current = page while current ancestors.unshift(current) current = current.parent end ancestors end end

Called from the view as breadcrumb_for(@page).each do |crumb| ... end. Rails ships app/helpers/ by default, and every method defined there is automatically available in every view — a dedicated architectural layer specifically for view-supporting logic that's too presentational for a model method but too structured to sprawl across the template itself.

A real third option, not just a fourth name for the same idea
Django's own Chapter 4 built its breadcrumb walk directly inside the view function — there was nowhere else idiomatic to put it. Laravel's own Chapter 4 built it inside the Controller, by convention rather than necessity, since Blade's @php would technically have allowed it inline. Rails offers a genuinely separate, named layer for exactly this kind of logic — Helpers aren't a workaround; they're a first-class, intended part of the MVC structure.
Set up deliberately, not accidentally
breadcrumb_for walks current.parent in a loop — one query per level, exactly the same N+1 pattern both sibling courses' own Chapter 4/6 built into their breadcrumb code on purpose. It's left as written here, to be caught and fixed live in Chapter 6, matching the running motif across the whole series.

ERB's Permissiveness, Verified Honestly

ERB has no separate template language layered on top of Ruby the way Django's DTL is a genuinely separate, deliberately restricted language — <% %> tags contain real, unrestricted Ruby, full stop, with no curated allow-list of tags/filters standing between the template and the language. That puts ERB closer in spirit to Blade's @php escape hatch and to Next.js's own JSX (real JavaScript/TypeScript by default) than to Django — with one honest nuance: Blade normally chooses not to use raw PHP, reaching for its own sugared directives (@if, @foreach) that compile down to the same plain PHP @php would let through directly, while ERB has no separate sugared layer at all — <% %> already is the whole language, by design, not an escape hatch from something more restricted.

Next.js (JSX)Django (DTL)Laravel (Blade)Rails (ERB)
Full host-language accessYes — JSX is real JS/TSNo — a deliberately restricted separate languageYes, via @php — though sugared directives are the default styleYes — no separate language exists at all
Is there a "restricted mode"?NoYes — the entire point of DTLNo — @php is always availableNo

Hands-On Exercises

Exercise 1

Explain what a Rails Helper module is, and why it's a genuinely different architectural option from both Django's "build it in the view" approach and Laravel's "build it in the Controller by convention" approach.

📄 View solution
Exercise 2

Explain the real difference between ERB's own permissiveness and Blade's @php permissiveness, given that both technically allow full host-language code.

📄 View solution
Exercise 3

Explain why breadcrumb_for is expected to have a performance problem, and where in this course that problem is deliberately meant to be caught and fixed.

📄 View solution

Chapter 4 Quick Reference

  • <%= yield %> — the layout's own insertion point, Rails' equivalent of @yield/{% block %}
  • <%= %> / <% %> / <%== %> — output, execute-only, and unescaped output
  • app/helpers/ — a genuine third architectural layer for view-supporting logic, distinct from both siblings' own approaches
  • ERB's own permissiveness — no separate template language exists; <% %> already is unrestricted Ruby, by design
  • Deliberate setupbreadcrumb_for's N+1 pattern is left in place on purpose, caught in Chapter 6
  • Next chapter: Styling — Dark Theme