Ruby on Rails

Rails4.2から5.0でコールバックの仕様が変わってた

個人開発で使うGemにseedfuを使おうとしたのですが、5年前から更新がされておらず自分がプロダクトで使っているRails7にはもちろん対応していなかったので、forkして更新することにしました。その際にハマったことについて、今度同じことではまらないように備忘録として残しておきたいと思います。

コールバックで戻り値をfalseにしたときの挙動が変更

seedfuのGemの中ではbefore_save { false if fail_to_save }が書かれていました。Railsガイドによると、Rails4ではコールバックのブロックの中でfalseを返すと、すべてのコールバックチェインが停止する仕様とありました。

つまり、ロールバックされ、record.saveはfalseになります。実際に戻り値はfalseになっていました。(ロールバックされるかについて実際に試したわけではないので間違っていたらすいません‍♂️)

しかしRails5以降では、コールバックチェーンは停止しないようになっていて、停止するには、throw(:abort)で明示的に書く必要があるようです。

下のように書くことで、意図した挙動になりました。マージはされてませんが(というよりメンテナンスされてない。。)、こちらでパッチが当てられていました。

  if ActiveRecord::VERSION::MAJOR < 5
    before_save { false if fail_to_save }
  else
    before_save { throw :abort if fail_to_save }
  end

Rails5.0ではコールバックの中でfalseを返していると以下のようにWarningが出るようになってました。

WARNING: Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in Rails 5.1. To explicitly halt the callback chain, please use `throw :abort` instead.

さいごに

このBreaking Changeは知らなかったので、何でだということでハマってしまいました。

ここまで読んでいただきありがとうございました。
Twitterもやってますので、フォローしていただけるとうれしいです。

ABOUT ME
sakai
三重出身の28歳。前職はメーカーで働いていて、プログラミングスクールに通って未経験からWeb業界に転職しました。Railsをメインで使っていて、AWSも少しできます。音楽を聞くこととYoutubeを見るのが好きです。最近はへきトラ劇場にハマってます