【Rails】skip_callbackとset_callback

先日ちょっとはまった現象についてのメモ
(いやといっても修整ミスなんだけどね・・)

とあるモデルでの話。

  • saveするときにcallback処理を走らせたい
  • どうしてもsaveを2回走らせたい

というケースがあって、

最初、

class Hoge < ActiveRecord::Base
  after_save :fuga
  
  def piyo
    hoge = Hoge.new
    ##
    #初期保存
    hoge.save
    ##
    #追加処理
    hoge.save
  end

  def fuga
    p "yukkurisiteittene"
  end
end

と書いてたんだけど、これだと当然save時に2回のcallbackが走る。
callbackは1回で良かったので、とりあえず、

class Hoge < ActiveRecord::Base
  after_save :fuga
  
  def piyo
    hoge = Hoge.new
    ##
    #初期保存
    Hoge.skip_callback(:save, :after, :fuga)
    hoge.save
    Hoge.set_callback(:save, :after, :fuga)
    ##
    #追加処理
    hoge.save
  end

  def fuga
    p "yukkurisiteittene"
  end
end

としてみた。

でもHogeモデルは他からも操作されるのと、updateが走ったときはcallback処理したくなかったので、
after_saveじゃなくてafter_createでいいじゃんwと、

class Hoge < ActiveRecord::Base
  after_create :fuga
  
  def piyo
    hoge = Hoge.new
    ##
    #初期保存
    Hoge.skip_callback(:save, :after, :fuga)
    hoge.save
    Hoge.set_callback(:save, :after, :fuga)
    ##
    #追加処理
    hoge.save
  end

  def fuga
    p "yukkurisiteittene"
  end
end

単純にこうしてみたところ、

callbackが2回走った。。(´;ω;`)

その時はafter_createしか仕込んでいないのになぜ2回走ったのかすぐわからず。。
(でもこうやって書いてみるとすぐわかるなぁ。。)


この理由は、

  • 1回目のsaveの前のHoge.skip_callback(:save, :after, :fuga)はそもそもきかない。(after_createなので)
  • ので、通常通りafter_createが走る。
  • 1回目のsaveの後のHoge.set_callback(:save, :after, :fuga)でafter_save :fugaがHogeモデルに追加されてしまっている。
  • 2回目のsaveの時にHoge.set_callback(:save, :after, :fuga)で追加されたcallbackが走る。

という感じ。

そんなこんなで2回callbackが走ったと。

なのでこの場合、1回目のsaveはcreate、2回目のsaveはupdateなので、skip_callbackなどはいらなくて、

class Hoge < ActiveRecord::Base
  after_create :fuga
  
  def piyo
    hoge = Hoge.new
    ##
    #初期保存
    hoge.save
    ##
    #追加処理
    hoge.save
  end

  def fuga
    p "yukkurisiteittene"
  end
end

これでおkw
簡単じゃねーかΣ(゚д゚lll)


こうなんというか、モデルが大きくなって、汎用部分を切りだして親にincludeしだしたり、callbackが多くなってきたりすると、単純なミスもすぐ気づかない。。という(´・ω・`;)


特にcallbackは注意しないと危険ですね。

通常通りbefore_saveとかでフックするのもあるし、
observerでフックするのもあるし、
プラグインがフックするのもあるし。。。

ちゃんと注意しなきゃならんなと・・

というか整理しなきゃならんなと、、改めてオモタ(´・ω・`)