Railsで、同一のbefore_actionを別オプションで複数定義した場合、最後の定義が有効になる

Railsbefore_action などのフィルタでは、 only などのオプションを追加することで対象のActionを制限できます。
8 フィルタ | Action Controller の概要 - Railsガイド

 
そんな中、同一の before_action を別オプションで複数定義してしまったことがあったため、挙動をメモしておきます。

 
目次

 

環境

 

Controllerの場合

Controllerで before_action を実装する時、

class BeforeActionFilterController < ApplicationController
  before_action :set_template_value, except: [:index3]
  before_action :set_template_value
  before_action :set_template_value, only: [:index3]

  def index1
  end

  def index2
  end

  def index3
  end

  private

  def set_template_value
    @template_value = 'foo'
  end
end

と定義してしまったとします。

この場合は、 index3 のみ before_action が動作します。

index1の場合

index2の場合

index3の場合

 
この挙動は

同じフィルタを異なるオプションで複数回呼び出しても期待どおりに動作しません。最後に呼び出されたフィルタ定義によって、それまでのフィルタ定義は上書きされます。

8 フィルタ | Action Controller の概要 - Railsガイド

のためです。

 

Mailerの場合

Mailerにも before_action などのコールバックがあります。

こちらも挙動は同じです。

そのため、

class BeforeActionCallbackMailer < ApplicationMailer
  before_action :set_template_value, except: [:welcome3]
  before_action :set_template_value
  before_action :set_template_value, only: [:welcome3]

  def welcome1
    mail(to: 'welcome1@example.com')
  end

  def welcome2
    mail(to: 'welcome2@example.com')
  end

  def welcome3
    mail(to: 'welcome3@example.com')
  end

  private

  def set_template_value
    @template_value = 'bar'
  end
end

というMailerを使い

class BeforeActionFilterController < ApplicationController
  def send_mail
    BeforeActionCallbackMailer.welcome1.deliver_now
    BeforeActionCallbackMailer.welcome2.deliver_now
    BeforeActionCallbackMailer.welcome3.deliver_now
  end

のようにメールを送信したとします。

この場合も、 welcome3 のみ before_action が動作しテンプレートに値が渡されます。

 
welcome1の場合

welcome2の場合

welcome3の場合

 

ソースコード

Githubに上げました。
https://github.com/thinkAmi-sandbox/rails_miscellaneous_app

今回のプルリクはこちらです。
https://github.com/thinkAmi-sandbox/rails_miscellaneous_app/pull/1