Controllers and actions

Ruby on Rails · Essentials · 12 min

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_entity renders 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. Use status: :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])
end

Remember

  • 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.

Check yourself

  1. A form submission fails validation. What is the conventional response?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: guides.rubyonrails.org/action_controller_overview.html