Controllers & Actions
🎮 Controllers & Actions
resources :posts pointed seven routes at seven controller actions. This chapter writes those actions — and covers the single biggest behavioral surprise coming from Express: a Rails action doesn't have to explicitly send a response at all.
🏛️ ApplicationController
Every Rails controller inherits from ApplicationController, itself a subclass of ActionController::Base — real Ruby classes using the inheritance and class syntax from both Ruby courses. Express has no equivalent concept at all: a "controller" there (as Chapter 1's example showed) is just a plain object of exported functions, with no shared base class or built-in framework hooks.
🎬 The Seven Actions
Every method name matches an action from Chapter 2's route table exactly — that's not a coincidence, it's the same naming convention linking routes to controllers that Chapter 1 introduced.
👻 Implicit Rendering — the Biggest Surprise Coming From Express
In Express, forgetting to call res.send/res.json/res.render leaves the request hanging with no response at all — the framework never assumes what you meant to send back. Rails does the opposite: if an action doesn't explicitly call render or redirect_to, Rails automatically renders the view matching the action's name — index implicitly renders app/views/posts/index.html.erb, show implicitly renders app/views/posts/show.html.erb. This is convention over configuration applied to the controller/view boundary itself, and it's genuinely disorienting the first time a page renders correctly despite the action "doing nothing" to send a response.
📥 params — One Hash for Everything
Rails merges route parameters, query string parameters, and request body parameters into a single params hash-like object — no need to know or care which source a given value came from. Express keeps these genuinely separate: req.params (route), req.query (query string), and req.body (parsed body, requiring middleware like express.json() to populate at all).
🔀 render vs redirect_to
render — same request, different content
Renders a view (or a different one than the action's default) for the current request — no new HTTP round trip, the URL in the browser doesn't change. Used above in create's failure branch, to redisplay the form with validation errors still in place.
redirect_to — a brand new request
Sends an HTTP redirect response, telling the browser to make an entirely new GET request to a different URL — the browser's address bar changes. Used above in create's success branch, sending the user to the new post's show page.
This maps directly onto Express's res.render(...) vs res.redirect(...) — same underlying HTTP distinction, just spelled with Rails' own method names.
📜 Handling a Request: Express vs Rails
Side by Side
| Concept | Express | Rails |
|---|---|---|
| Handler shape | A plain function: (req, res) => { ... } | An instance method inside a class inheriting ApplicationController |
| Route params | req.params | params (merged with query + body) |
| Query string | req.query | params (same hash, no separate access) |
| Request body | req.body (needs body-parsing middleware) | params (parsed automatically) |
| Must you send a response? | Yes, always, explicitly | No — implicit rendering falls back to a matching view |
💻 Coding Challenges
Challenge 1: Write a Full Controller
Write a CommentsController with all seven actions, following the same pattern as this chapter's PostsController (you can use placeholder Active Record calls like Comment.find(params[:id]) even before Chapter 5 covers Active Record in depth). Comment above index and show explaining specifically which view file Rails will implicitly render for each.
Goal: Get the shape of all seven action methods, and the implicit-render convention, under your fingers.
Challenge 2: Reading From params
Given a request to GET /posts/12/comments?sort=newest, with route resources :posts { resources :comments } (Chapter 2), write the index action for CommentsController and show exactly how you'd read both the post's ID and the sort query parameter out of params.
Goal: Practice pulling values from Rails' unified params hash regardless of where they originated.
Challenge 3: render vs redirect_to
Write an update action for a ProductsController that, on a successful save, redirects to the product's show page, and on failure, re-renders the :edit view so the user sees their validation errors. Add a comment explaining what would go wrong (from the user's perspective) if redirect_to were used in the failure branch instead of render.
Goal: Internalize exactly when each of the two response methods is the correct choice.
No routing table, no folder structure — just a controller action's name silently determining what gets rendered if you don't say otherwise. It's the single clearest example in this course so far of Chapter 1's warning made concrete: extremely little code, and genuinely confusing the first time a page renders correctly despite an action that appears to do nothing at all.
🎯 What's Next
Next chapter: Views & ERB — the templates those implicitly-rendered actions actually render, embedding Ruby directly in HTML, and how ERB compares to the EJS templating already covered in Express.