LoginSignup
3
3

More than 5 years have passed since last update.

複雑な動作をするRailsのコールバックを書きたい場合に

Posted at

RailsのActiveRecordには、特定のタイミングでコールバックを実行する仕組みがあります。とはいえ、複雑な処理を行おうとすると少し悩むことになるかもしれません。

コールバックとは

詳細はRails Guideに譲りますが、before_save/after_saveなど、ActiveRecordが特定の動作をした段階で動くルーチンを仕掛けることができます。

コールバックとして設定できるもの

コールバックはbefore_save 動作というような形で定義しますが、この「動作」に指定できるものも何種類かあります。なお、Railsのコールバックは内部的にActiveSupport::Callbacks詳細)を使っているので、指定できるものはこれに準じます。

ブロック、proc、lambda

「ルーチン」でいちばんイメージしやすいものですが、これらはinstance_evalでそのインスタンスのコンテキストで実行されます1。比較的わかりやすいとは思いますが、複雑な処理を書くには向かない書式です。

  • メリット: ごくかんたんな処理を行う場合、シンプルに書ける
  • デメリット:複雑なものを書くには向かない
before_validation do
  # 中身は省略
end

シンボル

シンボルを渡した場合、インスタンスに対してシンボルの名前のメソッドが実行されます。

  • メリット:モデルのインスタンス変数も使える
  • デメリット:コールバック専用のコードだった場合、インスタンスにメソッドとして持たせるのはじゃまになる
before_validation :convert_name

その他のオブジェクト

その他のオブジェクトを渡すと、そのオブジェクトの、コールバック名と同じメソッドを、モデルインスタンスを引数にして呼び出します。コールバックを別なところへ分離できて便利です。

コールバックオブジェクトのインスタンスについて

この形で指定する場合、after_save SomeObject.newのようにも書けますが、クラス内に書くことからもわかるように、インスタンスはモデル全体で共通ですので、引数指定でインスタンスを作り分ける、というような状況でなければわざわざインスタンスを作る意味もそこまで大きくありません。そのメソッドをモジュールメソッドとして実装して、Moduleオブジェクトそのものを指定する、という手段もありです。

インスタンス変数を引き回したい!

ただ、モジュールメソッドで実装してしまうと、CallBackModule.after_xxx(model)のような形で呼ばれることとなります。下請けメソッドが必要な規模のコールバックを書こうとすれば、モデル内の値を参照するのに、いちいちmodelを引き回す必要が出てきてしまいます。

これでは不便ですし、モデル以外にインスタンス変数やメソッドを持たせても便利ですので、「外から見ればクラスメソッド、内部処理はインスタンスを作ってそこで処理」というような形で書けば、使う方も中身も比較的わかりやすくなります。

class SomeCallback
  # インスタンス変数の準備
  def initialize(model)
    @model = model
    @some_children = @model.some_children
  end

  # インスタンス変数を使って作業
  def all_children?
    @some_children.all? do
      # 略
    end
  end

  # メインルーチン
  def after_save
    some_value = all_children ? @model.some_value : 5
    # 後略
  end

  # フロントエンド用メソッド
  def self.after_save(model)
    new(model).after_save
  end

  # 外部から直接newはしない
  private_class_method :new
end


# モデル側
after_save SomeCallback

  1. 文字列もinstance_evalされますが、これは非推奨とのことです。 

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3