【Rails】コントローラコールバックによる変数の初期化について

に公開

はじめに

初めまして!現在プログラミング学習中のるやんと申します。
学習中にコントローラコールバックの呼び出しタイミングに疑問を感じたため、実験して挙動を確認しました。その内容をまとめます。

概要

プログラミングの学習中に、とあるプログラムのApplicationControllerにて、変数@hogehogeを初期化するコントローラコールバックbefore_action :init_hogehogeの記述を見つけました。
これを見て、「ApplicationControllerが呼び出される度に毎回@hogehogeは初期化されてしまうのではないか?」と疑問に思い、挙動を確認することにしました。

対象読者

  • Ruby on Rails初学者の方
  • コントローラコールバックの動作について学びたい方

コントローラコールバックとは

コントローラコールバックとは、コントローラのアクションが実行される、直前、直後などに実行されるメソッドです。
よく使用されるのが before_action になるかと思います。
なお、コントローラコールバックは、Railsのバージョンアップに伴いその名前が変わってきたようです。

  • ~Rails 7.1 …フィルタ
  • Rails 7.2 … アクションコールバック
  • Rails 8.0 …コントローラコールバック

参考…Railsガイド コントローラコールバック

本記事では、最新であるコントローラコールバックで統一して表現しています。

筆者環境

以下の環境にて、動作させています。

  • Docker環境
  • Windows11
  • Rails 7.0.3.1
  • ruby 3.1.4

疑問

コントローラコールバック before_action は、ApplicationControllerのメソッドを呼び出すたびに毎回実行されるのではないか?

結論

コントローラコールバックは、1つのリクエストの中でアクションが呼び出された際にのみ動作する

実験

以下のコントローラ・ビューファイルを用意して、hoge#fugaアクションを実行しました。

  • app/controllers/application_controller.rb

    class ApplicationController < ActionController::Base
      before_action :init_test
    
      def add_1
        @test += 1
      end
    
      def add_2
        @test += 2
      end
    
      def add_3
        @test += 3
      end
    
      private
    
      def init_test
        @test = 0
      end
    end
    
  • app/controllers/hoge_controller.rb

    class HogesController < ApplicationController
      def fuga
        @test = -1
        add_1
        add_2
        add_3
      end
    end
    
    
  • app/views/hoges/fuga.html.erb

    <%= @test %>
    

結果

画面には「5」が出力される

考察

デバッガでhoges#fugaが呼ばれた際の流れを追いました。

  • まず、fuga アクションの前にinit_testメソッドが実行される。
    (ApplicationControllerにコントローラコールバックを記載しているため、ApplicationControllerを継承しているHogesControllerでも実行される)
    • before_action :init_test のコントローラコールバックを実行
      • init_test を実行⇒ @test=0
  • 次にHogesControllerの fuga アクションが実行される
    • @test = -1を実行⇒@test = -1で上書き
    • add_1メソッド実行
      • before_action は実行されない
      • add_1 メソッド実行…+1する⇒@test = 0
    • add_2メソッド実行
      • before_action は実行されない
      • add_2 メソッド実行…+2する⇒@test = 2
    • add_3メソッド実行
      • before_action は実行されない
      • add_3 メソッド実行…+3する⇒@test = 5
    • fuga.html.erb@test = 5が渡される

まとめ

コントローラコールバックはメソッドが呼び出される度に実行される訳では無く、アクションが呼び出された際にのみ実行されることが分かりました。
この特性を利用して、before_action :init_testにて、変数@testを初期化することができ、かつinit_testメソッドで何度も上書きされないことを学びました。

この記事が、皆様のお役に立てれば幸いです。

Discussion