読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Rails4でアクションの前後にフィルタ/処理を挟み込む

Rails初級 Rails Controller

Rails4ではコントローラーでbefore_actionafter_actionaround_actionを宣言することでアクションの前後に処理を挟み込むことができます。

before_filterafter_filteraround_filterという記述も可能ですがRails3までの記述のため、Rails4ではxxx_actionを使いましょう。

また、prepend_before_actionpreprend_after_actionprepend_around_actionを宣言することでbefore_actionなどのアクションの前にアクションを挿入することができます。

これは、この宣言がフィルタ以外にも共通の処理を記述するなど多目的に使われるため、xxx_filterからxxx_actionに変わったという経緯があります。

動作確認

  • Rails 4.1

目次

  1. before_action, after_actionの宣言方法
  2. around_actionの宣言方法
  3. before_action, after_action, around_actionをスキップする
  4. 複数のコントローラーにxxx_actionの処理を追加したい
  5. befor_action, after_action, around_actionの前にアクションを挿入するprepend_xxx_action

1. before_action, after_actionの宣言方法

before_actionを使うことで、アクションの前に処理を実施することができます。
よく認証に使われます。

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  # アクションの処理を実行する前に、認証の確認を行う
  # 認証に失敗したら、リダイレクトされる
  before_action :authenticate_user!

  # 次のように、only, exceptを指定することでアクションを絞れる
  # before_action :authenticate_user!, only: [:show, :edit, :update, :destroy]

  ...
  private
    def authenticate_user!
      # 認証処理をする
      # 認証に失敗したらログインページにリダイレクトする
    end
end


2. around_actionの宣言方法

次のように、around_actionを使うときは、処理内で明示的にyieldを呼び出す必要がることに注意が必要です。

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  around_action :hoge

  ...
  private
    def hoge
      logger.debug "アクション前の処理"
      yield  # アクションが実行される
      logger.debug "アクション後の処理"
    end
end


3. before_action, after_action, around_actionをスキップする

skip_before_action skip_after_action skip_around_actionを使うことでスキップすることが可能です。



4. 複数のコントローラーにxxx_actionの処理を追加したい

複数のコントローラーで同じアクションを指定したい場合は、クラスにすることでDRY原則を守ることが可能です。
クラス内では、xxx_actionxxxに合ったbeforeafteraroundメソッドを定義する必要があります。

# app/controllers/concerns/my_action.rb
class MyAction
  # before_action時に呼び出される
  def before(controller)
    controller.logger.debug "before: #{controller.action_name}"
  end

  # after_action時に呼び出される
  def after(controller)
    controller.logger.debug "after: #{controller.action_name}"
  end

  # around_action時に呼び出される
  def around(controller)
    before(controller)
    yield
    after(controller)
  end
end

コントローラーからは次のようにして使います。

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_aciton MyAction.new   # beforeが呼び出される
  after_action  MyAction.new   # afterが呼び出される
  around_action MyAction.new   # aroundが呼び出される
  ...
end

5. befor_action, after_action, around_actionの前にアクションを挿入するprepend_xxx_action

xxx_actionを複数宣言すると、宣言した順に処理が実行されます。
継承などをした場合に、親クラスで宣言されたフィルタより先に自分で作成したフィルタを実行したい場合には、prepend_xxx_action(prepend_before_actionなど)が使えます。

class API::BaseController < ApplicationController
  before_filter :ensure_login
end

class API::SomethingController < API::BaseController
  preprend_before_filter :do_something

  def show
    # このアクションが実行される前に
    # do_something、ensure_loginの順で
    # フィルタが実行される
    render :show
  end
end

以上です。

参考文献

  • Rails3 レシピブック 190の技
  • Rails API