Rails controllers are the central nervous system of your web application, handling the flow of data between models and views. Understanding how to implement controllers effectively is crucial for building maintainable and scalable Rails applications. This article explores the key concepts and best practices for working with Rails controllers.
The Role of Controllers
In the Model-View-Controller (MVC) pattern, controllers act as the intermediary between models and views. They:
- Process incoming requests
- Interact with models to fetch or modify data
- Prepare data for views
- Handle redirects and responses
Basic Controller Structure
A typical Rails controller follows RESTful conventions:
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article, notice: 'Article was successfully created.'
else
render :new
end
end
private
def article_params
params.require(:article).permit(:title, :content)
end
end
Controller Best Practices
1. Keep Controllers Thin
Controllers should be focused on their primary responsibility: coordinating the flow of data. Business logic should live in models or service objects:
# Good
def create
@article = Article.create(article_params)
redirect_to @article
end
# Bad
def create
@article = Article.new(article_params)
@article.published_at = Time.current
@article.author = current_user
@article.categories = params[:categories]
if @article.save
NotificationService.notify_subscribers(@article)
redirect_to @article
else
render :new
end
end
2. Use Strong Parameters
Always use Strong Parameters to whitelist attributes that can be mass-assigned:
def article_params
params.require(:article).permit(:title, :content, :category_id)
end
3. Implement Proper Error Handling
Handle errors gracefully and provide meaningful feedback:
def show
@article = Article.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to articles_path, alert: 'Article not found'
end
4. Use Before Actions
DRY up your controller code using before_action
:
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
private
def set_article
@article = Article.find(params[:id])
end
end
Advanced Controller Patterns
1. Concerns
Use concerns to share functionality between controllers:
# app/controllers/concerns/authenticatable.rb
module Authenticatable
extend ActiveSupport::Concern
included do
before_action :authenticate_user!
end
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
include Authenticatable
end
2. Service Objects
For complex operations, consider using service objects:
class ArticlePublishingService
def initialize(article)
@article = article
end
def publish
@article.update(published: true, published_at: Time.current)
NotificationService.notify_subscribers(@article)
end
end
# In controller
def publish
ArticlePublishingService.new(@article).publish
redirect_to @article
end
Conclusion
Rails controllers are powerful tools that, when used correctly, can make your application more maintainable and easier to understand. By following these best practices and patterns, you can write controllers that are:
- Focused on their primary responsibility
- Easy to test
- Secure
- Maintainable
- Scalable
Remember that controllers are just one part of your application's architecture. They work best when combined with well-designed models, views, and supporting services. Keep your controllers thin, your models fat, and your code will be much easier to maintain and extend.