初めまして。株式会社iCAREのサーバーサイドエンジニアのyotubaです。
前回の記事ではRails5.0から5.1へのバージョンアップについて書きましたが、無事にリリースが出来ました。
今回の記事では、Railsの5.2へのバージョンアップについて備忘録を書きたいと思います。
現在弊社のCarelyというサービスは、長いこと、Railsの5.0.7.2で稼働していて、現在はRails5.1.7まで上がりましたが、今回5.2系へバージョンアップを弊社技術顧問のwillnetさんと連携してやらせて頂いています。
今回の記事では、5.2系へバージョンアップを現在しようとしている際にぶつかった問題を共有させて頂きたいと思います。
5.2系へ上げた際に落ちたテストからわかる変更点
バージョンアップする際には、まず、こちらのRailsガイドのリリースノートに書いてあるものなので、そちらを参考にして頂くのが一番良いと思います。
まず
Railsのバージョンをあげる前に関連するgemをあげるべきだと色々な記事で書かれていますが、これを身をもって知ることになりました。
なんとbundle update railsでバージョンをあげようとすると、gemの依存関係にひっかかってしまって上がらないのです!(数年アップデートが止まってしまっていました・・・)
具体的には様々なgemの依存関係が
(3.0 < hoge)
とか
(5.1 > hoge)
のようにエラー文として吐き出されます。
Rails以外のgemを全て上げていきたいところですが、量が膨大なこともあり、上のエラー文を読み解いて必要なgemを上げていきます。
具体的には(5.1 > hoge)となっているようなgemはrailsや関連gemの5.1以下を指定していますので、アップデートする必要があります。
つぎに
またgemの話になります。上記を解決してバージョンアップしたPRを作ることができました。
ただ、gemは大体の場合、RubyやRailsのどのバージョンをサポートしているかが書いてあります。
新しいRubyやRailsが出ると、gemを作ってくれてる人たちが新しいRubyやRailsで動くように修正してくれるわけですね。(更新が止まってしまっているgemを使わない方がいいというのは、こういった対応がされなくなるためでもあります)
gemのバージョンが古いままだと、Rails5.2をサポートしていなかったりします。gemのバージョンで何を変えたかはchangelogというファイルに書いてあり、そこにsupport rails 5.2などと書いてある場合は、それ未満のバージョンのままrails5.2にあげると問題が起きます。(起きたから修正したバージョンが出たわけです)
なので、bundle outdatedなどのコマンドで古くなっているgemを確認し、1個づつ上げていく必要があります。
これが非常に大変です。。。
具体的にはRansackやPublicActivityなどがひっかかりました。
本題
gemの話が続いてしまったので、Railsのバージョンアップにより動かなくなってしまったコードについて解説します。
ActiveModel::Dirtyのchanged?の挙動
DEPRECATION WARNING: The behavior of `attribute_changed?` inside of after callbacks will be changing in the next version of Rails.
というDEPRECATION WARNINGが5.1で出ます。
これは何かというと、
class Model < AR::Base
after_save :callback
def callback
p changed?
end
end
model = Model.new
model.body = '変更した'
model.save
#=> trueが出力される
class Model < AR::Base
def callback
changed?
end
end
model = Model.new
model.body = '変更した'
model.save
p model.callback #=> falseが出力される
上記二つのコードが5.1までは別の挙動をしていましたが、わかりづらいということで、5.2で両者の挙動は後者の挙動で統一されました。
こちらの記事にまとめて頂いている方がいますが、5.1の挙動を維持できるメソッドへ変更して解決しました。。
callbackの際にfalseの場合、callbackチェーンを終了させない設定
callback処理でfalseをreturnしても、callback chainを終了させない設定を行うinitializerが、こちらもdeprecatedになり、削除して対応しました。
こちらのブログに書いて頂いている方がいますので確認ください。
5.2からは、callback chainを終了させたい場合は、明示的にthrow :abortを使う必要があります。
モデルのclass_nameに渡すのをStringにする
このようなエラーが出ました。
A class was passed to `:class_name` but we are expecting a string. (ArgumentError)
こちらでマージされたものですが、単純な話で、モデルのclass_nameにStringを渡す必要があるので修正しました。
モデルのCallbackの際に、if/unlessにStringを渡せなくなった。
このようなエラーが出ました。
ArgumentError: Passing string to be evaluated in :if and :unless conditional options is not supported. Pass a symbol for an instance method, or a lambda, proc or block, instead.
☓ if: 'first_name.blank?'
○ if: proc { |s| s.first_name.blank? }
Procで書くやり方に修正しました。
終わりに
今回もいかがでしたでしょうか。
実際に動いているプロダクトで起きた問題に絞って記事を書かせてもらいました。
長年gemのアップデートを放置していると大変なことになるというのが教訓です。。。みなさん気をつけてくださいね!