Ruby on Railsでは、すべてのコントローラーがApplicationControllerを継承しています。このため、ApplicationControllerに定義したメソッドやコールバックは、アプリケーション内のすべてのコントローラーで自動的に利用できます。認証処理やロケール設定など、アプリケーション全体で共通する処理を集約するのに最適な場所です。
基本的な使い方
ApplicationControllerにbefore_actionやメソッドを定義すると、すべてのコントローラーで自動的に適用されます。
class ApplicationController < ActionController::Base
before_action :set_locale
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end上記の例では、すべてのリクエストでset_localeメソッドが実行され、言語設定が適用されます。個別のコントローラーにはこの処理を書く必要がありません。
認証処理の定義
最も一般的な使い方は、ログインユーザーの取得と認証チェックの定義です。
class ApplicationController < ActionController::Base
helper_method :current_user, :logged_in?
private
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
def logged_in?
current_user.present?
end
def require_login
unless logged_in?
redirect_to login_path, alert: "ログインが必要です"
end
end
endhelper_methodでcurrent_userとlogged_in?を宣言しているため、ビューでもこれらのメソッドを使えます。各コントローラーでは必要に応じてbefore_action :require_loginを追加するだけで認証チェックが有効になります。
class PostsController < ApplicationController
before_action :require_login, except: [:index, :show]
def index
@posts = Post.all
end
def create
@post = current_user.posts.build(post_params)
# current_userはApplicationControllerで定義済み
end
endエラーハンドリング
アプリケーション全体で共通のエラーハンドリングを定義できます。
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
rescue_from ActionController::ParameterMissing, with: :bad_request
private
def record_not_found
render file: Rails.root.join("public/404.html"),
status: :not_found, layout: false
end
def bad_request(exception)
render json: { error: exception.message }, status: :bad_request
end
endrescue_fromを使うと、特定の例外が発生したときにカスタムの処理を実行できます。個別のコントローラーでbegin/rescueを書く必要がなくなります。
コントローラーの継承階層
管理画面など、特定のコントローラー群で共通する処理がある場合は、中間の基底コントローラーを作成できます。
class Admin::BaseController < ApplicationController
before_action :require_admin
layout "admin"
private
def require_admin
unless current_user&.admin?
redirect_to root_path, alert: "管理者権限が必要です"
end
end
endclass Admin::UsersController < Admin::BaseController
# ApplicationControllerとAdmin::BaseControllerの両方の処理を継承
def index
@users = User.all
end
end継承チェーンはAdmin::UsersController → Admin::BaseController → ApplicationController → ActionController::Baseとなり、すべてのコールバックが順番に適用されます。
ApplicationControllerには本当にすべてのコントローラーで必要な処理のみを定義しましょう。特定のコントローラー群でのみ必要な処理は、中間の基底コントローラーやConcernsに分離するのがベストプラクティスです。
ApplicationControllerにAPIコントローラーと通常のコントローラーの両方に適用されるコールバックを書くと、API側でCSRFトークン検証エラーなどが発生することがあります。APIコントローラーはActionController::APIを継承するなど、適切に分離してください。
まとめ
ApplicationControllerに定義した処理はすべてのコントローラーで有効- 認証(
current_user)やロケール設定など共通処理を集約する rescue_fromでアプリケーション全体のエラーハンドリングを定義できるhelper_methodでビューからもメソッドを呼び出せるようにする- 管理画面用など中間の基底コントローラーで階層的に共通処理を管理する
- すべてのコントローラーに必要な処理のみを定義し、肥大化を避ける