Controllers coordinate every request, and knowing render versus redirect and callbacks avoids many beginner bugs.
A controller is a class inheriting from ApplicationController; each public method is an action. After an action runs, Rails renders the template with the same name (articles/show.html.erb for show) unless you call render or redirect_to yourself. Instance variables set in the action (@article) are visible in the view.
params holds route segments, query-string values and form data in one hash-like object with indifferent access: params[:id] and params["id"] both work. Values are strings.
Responding:
render :new, status: :unprocessable_entityrenders another template in the same request (used to redisplay a form with errors). Turbo expects a 4xx status for failed form submissions.redirect_to @article, notice: "Saved"sends a 302 to a new URL; the notice is stored in the flash for the next request. Usestatus: :see_other(303) after a DELETE.render json: @article,head :no_content,render plain: "ok".
A common bug is calling render or redirect_to twice in one action, which raises DoubleRenderError; add return after an early redirect.
Callbacks run around actions: before_action :set_article, only: %i[show edit update destroy] loads the record once for several actions. rescue_from handles exceptions per controller.
Model.find(params[:id]) raises ActiveRecord::RecordNotFound when missing, which Rails turns into a 404 in production. session[:key] and cookies persist data across requests.
Example
class ArticlesController < ApplicationController
before_action :set_article, only: %i[show edit update destroy]
def index
@articles = Article.order(created_at: :desc).limit(20)
end
def show; end # renders app/views/articles/show.html.erb
def update
if @article.update(article_params)
redirect_to @article, notice: "Article updated"
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@article.destroy!
redirect_to articles_path, notice: "Deleted", status: :see_other
end
private
def set_article = @article = Article.find(params[:id]) # 404 if missing
def article_params = params.expect(article: %i[title body])
endRemember
- Actions render the template of the same name unless you render or redirect
- render redisplays in the same request; redirect_to starts a new one
- before_action shares setup like loading the record
Watch out for
- Calling redirect_to and then continuing to code that renders, causing DoubleRenderError; return after redirecting.