| name | rails-controllers |
| description | Controller actions, routing, REST conventions, filters, and response handling |
| version | 1.0.0 |
| rails_version | >= 7.0 |
| tags | ["controllers","routing","actions","rest"] |
Rails Controllers
Quick Reference
| Pattern | Example |
|---|
| Generate | rails g controller Posts index show |
| Route | resources :posts |
| Action | def show; @post = Post.find(params[:id]); end |
| Render | render :edit |
| Redirect | redirect_to posts_path |
| Filter | before_action :authenticate_user! |
| Strong Params | params.require(:post).permit(:title, :body) |
Controller Structure
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
@posts = Post.all.order(created_at: :desc)
end
def show
end
def new
@post = Post.new
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post, notice: 'Post created successfully.'
else
render :new, status: :unprocessable_entity
end
end
def edit
end
def update
if @post.update(post_params)
redirect_to @post, notice: 'Post updated successfully.'
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@post.destroy
redirect_to posts_path, notice: 'Post deleted successfully.'
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :body, :published)
end
end
Routing
RESTful Routes
Rails.application.routes.draw do
resources :posts
resources :posts, only: [:index, :show]
resources :posts, except: [:destroy]
resources :authors do
resources :posts
end
resources :authors do
resources :posts, shallow: true
end
resources :posts do
member do
post :publish
post :unpublish
end
collection do
get :archived
end
end
resource :profile, only: [:show, :edit, :update]
end
Custom Routes
get 'about', to: 'pages#about', as: :about
root 'posts#index'
get '/old-path', to: redirect('/new-path')
get 'posts/:id', to: 'posts#show', constraints: { id: /\d+/ }
# Namespace
namespace :admin do
resources :posts
end
# URLs: /admin/posts
# Controller: Admin::PostsController
# Scope
scope module: 'admin' do
resources :posts
end
# URLs: /posts
concern :commentable do
resources :comments
end
resources :posts, concerns: :commentable
resources :photos, concerns: :commentable
Filters (Callbacks)
class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :set_locale
around_action :log_request
after_action :track_analytics
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
def log_request
start_time = Time.current
yield
duration = Time.current - start_time
Rails.logger.info "Request took #{duration}s"
end
end
class PostsController < ApplicationController
skip_before_action :authenticate_user!, only: [:index, :show]
before_action :set_post, only: [:show, :edit, :update, :destroy]
before_action :authorize_post, only: [:edit, :update, :destroy]
private
def authorize_post
unless @post.author == current_user
redirect_to root_path, alert: 'Not authorized'
end
end
end
Strong Parameters
class PostsController < ApplicationController
private
def post_params
params.require(:post).permit(:title, :body, :published)
end
def post_params
params.require(:post).permit(:title, :body, tag_ids: [])
end
def post_params
params.require(:post).permit(
:title,
:body,
comments_attributes: [:id, :content, :_destroy]
)
end
def post_params
permitted = [:title, :body]
permitted << :published if current_user.admin?
params.require(:post).permit(permitted)
end
end
Rendering and Redirecting
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
else
render :new, status: :unprocessable_entity
end
end
end
Flash Messages
class PostsController < ApplicationController
def create
@post = Post.new(post_params)
if @post.save
flash[:notice] = 'Post created!'
redirect_to @post, notice: 'Post created!'
else
flash.now[:alert] = 'Could not create post'
render :new
end
end
def update
flash.keep
redirect_to @post
end
end
Response Formats
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html
format.json { render json: @post }
format.xml { render xml: @post }
format.pdf { render pdf: generate_pdf(@post) }
end
end
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Created!' }
format.json { render json: @post, status: :created }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
end
Controller Concerns
module Authenticatable
extend ActiveSupport::Concern
included do
before_action :authenticate_user!
helper_method :current_user, :logged_in?
end
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
def logged_in?
current_user.present?
end
def authenticate_user!
unless logged_in?
redirect_to login_path, alert: 'Please log in'
end
end
end
class PostsController < ApplicationController
include Authenticatable
end
Error Handling
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
rescue_from ActionController::ParameterMissing, with: :parameter_missing
private
def record_not_found
render file: "#{Rails.root}/public/404.html", status: :not_found
end
def parameter_missing
render json: { error: 'Missing parameter' }, status: :bad_request
end
end
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to posts_path, alert: 'Post not found'
end
end
Session and Cookies
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
session[:user_id] = user.id
cookies[:user_name] = user.name
cookies.signed[:user_id] = user.id
cookies.encrypted[:user_data] = { id: user.id, role: user.role }
cookies.permanent[:remember_token] = user.remember_token
redirect_to root_path
else
flash.now[:alert] = 'Invalid credentials'
render :new
end
end
def destroy
session.delete(:user_id)
cookies.delete(:user_name)
redirect_to root_path
end
end
Best Practices
- Keep controllers thin - Move business logic to models or service objects
- Use before_action for common setup code
- Always use strong parameters for security
- Return proper HTTP status codes
- Use concerns for shared controller behavior
- Follow REST conventions when possible
- Handle errors gracefully with rescue_from
- Use flash messages for user feedback
- Set instance variables only for view rendering
- Avoid complex queries in controllers - use scopes or query objects
Common Patterns
Service Objects for Complex Actions
class PostsController < ApplicationController
def create
result = Posts::CreateService.call(
params: post_params,
user: current_user
)
if result.success?
redirect_to result.post, notice: 'Created!'
else
@post = result.post
flash.now[:alert] = result.error
render :new
end
end
end
Query Objects for Complex Queries
class PostsController < ApplicationController
def index
@posts = PostsQuery.new(params).call
end
end
References