| @技術/プログラミング

あるモデルがあって、#save が実行されたときに同一モデル内で複製したインスタンスも一緒に保存したかった。一個目の #save が走る前にコールバックメソッドを使って複製したインスタンスを保存するようにした。コードだと以下のような感じ。

class Model
  before_validation :method_one
  before_create :method_two

  def method_one
    ...
  end

  def mothod_two
    Model.reset_callbacks(:validation)
    Model.reset_callbacks(:create)

    @model = Model.new
    @model.save
  end
end

なんで reset_callbacks 呼んでるのかというと二回コールバックメソッドを走らせないため。一回目の #save (コントローラーから呼ばれる)が呼ばれたときだけコールバックメソッドを実行して、二回目の #save (モデルのコールバックメソッド内で呼ばれる)では実行したくないから。

しかしここではまってしまった。なんとコールバックメソッドで複製したドキュメントを DB 内で確認すると created_at が空になっている。なんじゃこりゃ。

どうも reset_callbacks(:create) がいかんかったみたい。ORM が実行する create 周辺のコールバックメソッドも軒並みリセットされてしまう模様。

そういうわけで以下の様にして解決した。

class Model
  before_validation :method_one
  before_create :method_two

  def method_one
    ...
  end

  def hoge
    Model.skip_callback :create, :before, :sell
    Model.skip_callback :create, :after, :send_notification
    Model.reset_callbacks(:validation)

    @model = Model.new
    @model.save
  end
end

callback、便利だけど奥が深い。ちなみにこれら skipp_callback とか reset_callbacks とかは ActiveModel や ActiveRecord (僕はMongoidで開発してます)などの OR マッパーのメソッドではなく、 ActiveSupport::Callbacks のメソッドだったりします。ActiveSupport も奥が深い。