Controllers in Rails
Controllers are the C in MVC. They receive HTTP requests, interact with models to get data, and render views or redirect users.
Creating a Controller
Using the Generator
bash
1rails generate controller Articles index show new create edit update destroy
2# Creates:
3# app/controllers/articles_controller.rb
4# app/views/articles/ (with view files)
5# test/controllers/articles_controller_test.rb
6# app/helpers/articles_helper.rbManual Creation
ruby
1# app/controllers/articles_controller.rb
2class ArticlesController < ApplicationController
3 def index
4 @articles = Article.all
5 end
6
7 def show
8 @article = Article.find(params[:id])
9 end
10endController Actions
Each public method in a controller is an action that can respond to a route:
ruby
1class ArticlesController < ApplicationController
2 # GET /articles
3 def index
4 @articles = Article.published.recent
5 end
6
7 # GET /articles/:id
8 def show
9 @article = Article.find(params[:id])
10 end
11
12 # GET /articles/new
13 def new
14 @article = Article.new
15 end
16
17 # POST /articles
18 def create
19 @article = Article.new(article_params)
20
21 if @article.save
22 redirect_to @article, notice: 'Article created!'
23 else
24 render :new, status: :unprocessable_entity
25 end
26 end
27
28 # GET /articles/:id/edit
29 def edit
30 @article = Article.find(params[:id])
31 end
32
33 # PATCH/PUT /articles/:id
34 def update
35 @article = Article.find(params[:id])
36
37 if @article.update(article_params)
38 redirect_to @article, notice: 'Article updated!'
39 else
40 render :edit, status: :unprocessable_entity
41 end
42 end
43
44 # DELETE /articles/:id
45 def destroy
46 @article = Article.find(params[:id])
47 @article.destroy
48 redirect_to articles_path, notice: 'Article deleted!'
49 end
50
51 private
52
53 def article_params
54 params.require(:article).permit(:title, :body, :published)
55 end
56endWorking with Parameters
Accessing Parameters
ruby
1# URL: /articles?category=ruby&page=2
2params[:category] # => "ruby"
3params[:page] # => "2"
4
5# URL: /articles/5
6params[:id] # => "5"
7
8# Form data (POST/PATCH)
9params[:article][:title] # => "My Title"Strong Parameters
Strong parameters protect against mass assignment vulnerabilities:
ruby
1class ArticlesController < ApplicationController
2 private
3
4 # Basic usage
5 def article_params
6 params.require(:article).permit(:title, :body, :published)
7 end
8
9 # With nested attributes
10 def article_params
11 params.require(:article).permit(
12 :title,
13 :body,
14 :category_id,
15 tag_ids: [], # Array of values
16 images_attributes: [:id, :url, :_destroy], # Nested
17 metadata: {} # Hash (permit all keys)
18 )
19 end
20
21 # Conditional parameters
22 def article_params
23 permitted = [:title, :body]
24 permitted << :published if current_user.admin?
25 params.require(:article).permit(permitted)
26 end
27endRendering Responses
Render Views
ruby
1class ArticlesController < ApplicationController
2 def show
3 @article = Article.find(params[:id])
4 # Automatically renders app/views/articles/show.html.erb
5 end
6
7 def index
8 @articles = Article.all
9 render :index # Explicit render (same as default)
10 end
11
12 def special
13 @article = Article.find(params[:id])
14 render :show # Render a different template
15 end
16
17 def from_other
18 render 'other_controller/other_action' # Render from another controller
19 end
20endRender with Options
ruby
1# Render with status code
2render :new, status: :unprocessable_entity # 422
3render :not_found, status: :not_found # 404
4
5# Render different formats
6render json: @article
7render xml: @article
8render plain: 'Hello World'
9render html: '<h1>Hello</h1>'.html_safe
10
11# Render with layout
12render :show, layout: 'admin'
13render :show, layout: false # No layout
14
15# Render partial
16render partial: 'form'
17render partial: 'article', collection: @articlesRedirects
ruby
1class ArticlesController < ApplicationController
2 def create
3 @article = Article.new(article_params)
4
5 if @article.save
6 # Redirect to the article's show page
7 redirect_to @article
8
9 # Or with a flash message
10 redirect_to @article, notice: 'Created!'
11
12 # Or with alert
13 redirect_to articles_path, alert: 'Something went wrong'
14 else
15 render :new, status: :unprocessable_entity
16 end
17 end
18
19 def old_action
20 # Redirect to another URL
21 redirect_to 'https://example.com'
22
23 # Redirect back to previous page
24 redirect_back(fallback_location: root_path)
25
26 # Redirect with status
27 redirect_to @article, status: :see_other # 303
28 end
29endFlash Messages
Flash messages display information to users after redirects:
ruby
1class ArticlesController < ApplicationController
2 def create
3 @article = Article.new(article_params)
4
5 if @article.save
6 flash[:notice] = 'Article created successfully!'
7 # Or shorthand:
8 redirect_to @article, notice: 'Article created!'
9 else
10 flash.now[:alert] = 'Please fix the errors'
11 render :new, status: :unprocessable_entity
12 end
13 end
14endDisplay flash messages in layouts:
erb
1<!-- app/views/layouts/application.html.erb -->
2<body>
3 <% if flash[:notice] %>
4 <div class="alert alert-success"><%= flash[:notice] %></div>
5 <% end %>
6
7 <% if flash[:alert] %>
8 <div class="alert alert-danger"><%= flash[:alert] %></div>
9 <% end %>
10
11 <%= yield %>
12</body>Filters (Callbacks)
Run code before, after, or around controller actions:
Before Action
ruby
1class ArticlesController < ApplicationController
2 before_action :authenticate_user!
3 before_action :set_article, only: [:show, :edit, :update, :destroy]
4 before_action :authorize_user, only: [:edit, :update, :destroy]
5
6 def show
7 # @article is already set by before_action
8 end
9
10 private
11
12 def set_article
13 @article = Article.find(params[:id])
14 end
15
16 def authorize_user
17 unless @article.user == current_user
18 redirect_to articles_path, alert: 'Not authorized'
19 end
20 end
21endAfter and Around Actions
ruby
1class ArticlesController < ApplicationController
2 after_action :track_view, only: [:show]
3 around_action :log_request
4
5 private
6
7 def track_view
8 @article.increment!(:view_count)
9 end
10
11 def log_request
12 Rails.logger.info "Starting request..."
13 yield # Execute the action
14 Rails.logger.info "Finished request"
15 end
16endSkip Filters
ruby
1class Admin::ArticlesController < ApplicationController
2 skip_before_action :authenticate_user!, only: [:public_page]
3endRespond to Different Formats
ruby
1class ArticlesController < ApplicationController
2 def index
3 @articles = Article.all
4
5 respond_to do |format|
6 format.html # renders index.html.erb
7 format.json { render json: @articles }
8 format.xml { render xml: @articles }
9 format.csv { send_data @articles.to_csv }
10 end
11 end
12
13 def show
14 @article = Article.find(params[:id])
15
16 respond_to do |format|
17 format.html
18 format.json { render json: @article }
19 format.pdf do
20 pdf = ArticlePdf.new(@article)
21 send_data pdf.render, filename: "article.pdf"
22 end
23 end
24 end
25endSending Files
ruby
1class DownloadsController < ApplicationController
2 def show
3 # Send a file
4 send_file Rails.root.join('files', 'document.pdf'),
5 filename: 'document.pdf',
6 type: 'application/pdf',
7 disposition: 'attachment' # or 'inline' to display
8
9 # Send data directly
10 send_data generate_pdf,
11 filename: 'report.pdf',
12 type: 'application/pdf'
13 end
14endStreaming
ruby
1class ReportsController < ApplicationController
2 def large_report
3 # Stream large data
4 self.response.headers['Content-Type'] = 'text/csv'
5 self.response.headers['Content-Disposition'] = 'attachment; filename="report.csv"'
6
7 self.response_body = Enumerator.new do |yielder|
8 Article.find_each do |article|
9 yielder << article.to_csv_row
10 end
11 end
12 end
13endControllers are the orchestrators of your Rails app - keep them clean and focused!
