Controller Filters
Filters are methods that run before, after, or around controller actions. They help you share common logic across actions and keep your controllers DRY.
Before Action
Run code before an action executes:
ruby
1class ArticlesController < ApplicationController
2 before_action :authenticate_user!
3 before_action :set_article, only: [:show, :edit, :update, :destroy]
4 before_action :authorize_author, only: [:edit, :update, :destroy]
5
6 def show
7 # @article is already set
8 end
9
10 def edit
11 # @article is set and user is authorized
12 end
13
14 def update
15 if @article.update(article_params)
16 redirect_to @article
17 else
18 render :edit
19 end
20 end
21
22 private
23
24 def set_article
25 @article = Article.find(params[:id])
26 end
27
28 def authorize_author
29 unless @article.user == current_user
30 redirect_to articles_path, alert: "Not authorized"
31 end
32 end
33endFilter Options
ruby
1class ArticlesController < ApplicationController
2 # Only for specific actions
3 before_action :authenticate_user!, only: [:new, :create, :edit, :update, :destroy]
4
5 # Except for specific actions
6 before_action :authenticate_user!, except: [:index, :show]
7
8 # With conditions
9 before_action :check_premium, if: :premium_content?
10 before_action :log_request, unless: :admin_user?
11
12 # With lambda
13 before_action :track_view, if: -> { request.format.html? }
14
15 private
16
17 def premium_content?
18 @article&.premium?
19 end
20
21 def admin_user?
22 current_user&.admin?
23 end
24endAfter Action
Run code after an action executes:
ruby
1class ArticlesController < ApplicationController
2 after_action :track_view, only: [:show]
3 after_action :log_response, if: -> { Rails.env.development? }
4
5 def show
6 @article = Article.find(params[:id])
7 end
8
9 private
10
11 def track_view
12 @article.increment!(:views_count)
13 end
14
15 def log_response
16 Rails.logger.info "Response: #{response.status}"
17 end
18endAround Action
Wrap an action with before and after logic:
ruby
1class ArticlesController < ApplicationController
2 around_action :with_timing
3 around_action :catch_exceptions
4
5 def index
6 @articles = Article.all
7 end
8
9 private
10
11 def with_timing
12 start = Time.current
13 yield # Execute the action
14 duration = Time.current - start
15 Rails.logger.info "Action took #{duration}s"
16 end
17
18 def catch_exceptions
19 yield
20 rescue StandardError => e
21 Rails.logger.error "Error: #{e.message}"
22 redirect_to root_path, alert: "Something went wrong"
23 end
24endSkip Filters
Override parent controller filters:
ruby
1class ApplicationController < ActionController::Base
2 before_action :authenticate_user!
3end
4
5class PublicController < ApplicationController
6 skip_before_action :authenticate_user!
7end
8
9class ArticlesController < ApplicationController
10 skip_before_action :authenticate_user!, only: [:index, :show]
11endPrepend and Append
Control the order of filters:
ruby
1class ApplicationController < ActionController::Base
2 before_action :set_locale
3 before_action :authenticate_user!
4end
5
6class ArticlesController < ApplicationController
7 # Runs BEFORE parent filters
8 prepend_before_action :check_maintenance_mode
9
10 # Runs AFTER parent filters (default behavior)
11 before_action :set_article
12 # Same as: append_before_action :set_article
13endCommon Filter Patterns
Authentication
ruby
1class ApplicationController < ActionController::Base
2 before_action :authenticate_user!
3
4 private
5
6 def authenticate_user!
7 unless current_user
8 store_location
9 redirect_to login_path, alert: "Please log in"
10 end
11 end
12
13 def current_user
14 @current_user ||= User.find_by(id: session[:user_id])
15 end
16
17 def store_location
18 session[:return_to] = request.fullpath if request.get?
19 end
20endAuthorization
ruby
1class ArticlesController < ApplicationController
2 before_action :set_article, only: [:show, :edit, :update, :destroy]
3 before_action :require_author, only: [:edit, :update, :destroy]
4
5 private
6
7 def require_author
8 unless @article.user == current_user || current_user.admin?
9 redirect_to @article, alert: "You can't edit this article"
10 end
11 end
12endSetting Instance Variables
ruby
1class ArticlesController < ApplicationController
2 before_action :set_article, only: [:show, :edit, :update, :destroy]
3 before_action :set_categories, only: [:new, :edit, :create, :update]
4
5 private
6
7 def set_article
8 @article = Article.find(params[:id])
9 rescue ActiveRecord::RecordNotFound
10 redirect_to articles_path, alert: "Article not found"
11 end
12
13 def set_categories
14 @categories = Category.all
15 end
16endRequest Format Handling
ruby
1class ApiController < ApplicationController
2 before_action :set_default_format
3 before_action :authenticate_api_key!
4
5 private
6
7 def set_default_format
8 request.format = :json unless params[:format]
9 end
10
11 def authenticate_api_key!
12 api_key = request.headers["X-API-Key"]
13 head :unauthorized unless valid_api_key?(api_key)
14 end
15endTracking and Analytics
ruby
1class ApplicationController < ActionController::Base
2 after_action :track_action
3
4 private
5
6 def track_action
7 Analytics.track(
8 user_id: current_user&.id,
9 action: "#{controller_name}##{action_name}",
10 path: request.path,
11 referrer: request.referrer
12 )
13 end
14endCaching Headers
ruby
1class ArticlesController < ApplicationController
2 before_action :set_cache_headers, only: [:show]
3
4 private
5
6 def set_cache_headers
7 response.headers["Cache-Control"] = "public, max-age=3600"
8 end
9endFilters keep your controller actions focused on their core responsibilities!
