こんにちは、広告技術部の倉澤です。
普段は広告配信のための管理画面を作ったりしています。
今回はその管理画面をRails5にアップデートした話を書きたいと思います。
この管理画面の first commit は2013年9月17日、 Rails Wayにそってないコードもあり、それなりにレガシー感のあるシステムです。
きっかけは以下プルリク
Railsの4.2がメンテされなくなるとのことだったので
そろそろ重い腰をあげる時がきたということで取りかかりました。
やったことを簡単にまとめると以下な感じです。
- 1. Rails以外のGemをアップデートする
- 2. Rails5にアップデート
- 3. アップデートタスクを実行
- 4. Rails 4.2からRails 5.0へのアップグレードを眺めて必要そうな変更を入れる
- 5. テストを通す
- 6. deprecation warning が大量に出たので対応する
- 7. 不安なので画面からもテスト
では、詳細を書いていこうと思います。
1. Rails以外のGemをアップデートする
はじめはそのまま Rails のアップデートを試みたんですが、依存関係の問題で簡単には上げられず。 1個ずつ依存解決していっても終わる気しなかったので Gemfile.lock を削除してRockにアップデートすることにしました。
しかし、これをすると変更が大き過ぎるのでRails以外のGemアップデートだけ先にリリースしました。 もちろん問題ないかしっかり確認する必要があり、やることが結構増えましたが、 氷漬けにされたGemをアップデートできたのでよかったと思います。
2. Rails5にアップデート
他のGemをアップデートしたのであとはRails5にアップデートするだけです。
ただし、些末なGemでRails5に対応していないものもあったのでそれは一旦forkして対応しました。
その他、deprecation warning がでる ので shoulda-matchers gem を rails-5
ブランチに変更。
Rails5にすることによって不要になる2つのGemも削除しました。
- quiet_assets
- Rails5ではデフォルトの挙動になっているため
- activerecord-in_batches
- Rails5 の
ActiveRecord::Relation#in_batches
を Rails4へ backport するためのgemであるため
- Rails5 の
今までありがとう。
3. アップデートタスクを実行
アップデートタスク rails app:update
で新しいバージョンでのファイル作成や既存ファイルの変更を対話形式で行えます。
既存ファイルは結構コンフリクトしたので頑張って修正しました。
また new_framework_defaults.rb
が作成され、Rails 5の新機能はオフにしています。
一気に変えずに済むのはありがたいですね。
また、ここで以下の設定も変更しました。
- activerecord-refresh_connection gem の 設定をRails5に対応させる
config.raise_in_transactional_callbacks = true
の設定がいらなくなったため削除
4. Rails 4.2からRails 5.0へのアップグレードを眺めて必要そうな変更を入れる
ここでやった大きなことは以下3つです。
- ApplicationRecord を作成して Active Record モデルから継承するように変更
- ApplicationJob を作成して Active Job から継承するように変更
- rails-controller-testing gem を追加
また「 ActionController::Parameters は今後 HashWithIndifferentAccess を継承しない 」による変更箇所は数多くあったのでこれは次に書くテストで落ちた箇所を修正していきました。
5. テストを通す
テストを通しました。
落ちたところを頑張ってデバッグするだけです。300個くらい落ちてました。
全てではないですが少し列挙してみます。
- 前述した「 ActionController::Parameters は今後 HashWithIndifferentAccess を継承しない 」対応
- 一番多かった修正はこれ
- params を Hashとして利用している箇所で
to_h
orto_unsafe_h
で変換to_h
はpermit
されている値しか変換できないのでpermit
していない箇所はto_unsafe_h
を使用しました。- (もちろん
permit
すべきだけど、この対応ではto_unsafe_h
を選択しました。)
- view の
link_to
にparams
がそのまま渡されている箇所があったのでto_unsafe_h
で変換- そのまま渡すとエラーが出るようになっていた。
- (行儀が悪いけど同上)
- ActiveRecordの collection_singular_ids=(ids), collection<<(object, ...) で引数に不正な値を入れた場合のExceptionが変わったので対応
- 以前 ActiveRecord::RecordNotFound が出ていた箇所で ActiveRecord::AssociationTypeMismatch が出ていた
- Relation#to_a が
dup
した値を返すようになっていたので対応: https://github.com/rails/rails/pull/23794
6. deprecation warning が大量に出たので対応する
deprecation warning を直しました。
テスト時のログが大変なことになってしまうので頑張って直しました。
こちらもいくつか列挙します。
- ActionController::Parameters の非推奨メソッドの使用箇所で
to_h
で Hashに変換 - ActiveRecord の
delete_all
に 条件を渡していた箇所をwhere(conditions).delete_all
に変更 render text:
をrender plain:
に変更render :nothing
をrender body:nil
に変更- Active Record の query に Class を渡していた箇所を Class名の文字列を渡すように変更: https://github.com/rails/rails/commit/c0609dd0f0f0de604ac1ffeffdf5e3e0e7356b35
- Active Record の
uniq
をdistinct
に変更 env
をrequest.env
に変更- テストコードでもかなり出てたけど割愛
7. 不安なので画面からもテスト
Rspec は通ってますがそれだけでは万全だとは思えなかったので画面からもテストしました。 結果から言うとやってよかったです。
Rails5から submit_tag にデフォルトで data-disable-with がつくことなり、画面がリロードされるまでボタンがdisabledになっていました。
基本大丈夫だしありがたい機能なんですが、CSV出力に submit_tag を使っている箇所があり、そこでは画面リロードがおこらず disabled のままになっていました。
一旦は config.action_view.automatically_disable_submit_tag = false
の設定を application.rb に書いてデフォルトのこの挙動を無効にしています。
また、いくつかエッジケースでバグが見つかりました。 この辺は Rspec でテストできるようにしないといけない点ですね。
気をつけた点
以上でやったことはだいたい書けたかなと思います。
注意した点を書くとすると
- commit 粒度に気をつける
- リファクタリングをしない
- こまめに rebase
ですかね。
どうしても変更点が多くなるため、レビュアーが大変だと思ったので commit粒度には気をつけました。 だいたいは上に列挙した粒度よりちょい細かいくらいのコミットログにしています。
また、機能開発は並行して進んでいたのでそれとコンフリクトしないようにも注意して、 変更点をできるだけ少なくする、できるだけ早くリリースするためにリファクタリングはしないようにしました。 変更点が少なければレビューもしやすいですしね。
(この考えから行けば Rails5以外のgem update はやらない方がよかったのですが、先に別プルリクとしてリリースしたのでOKとしました。)
おまけ
今回でやっぱり Gemはこまめにアップデートしておかないと大変だと学んだので 今後は Gem のアップデートをできるだけ自動化できるように dependabot を導入する予定です。
また、フロントでまだ CoffeeScript が使われている状態なので
sprockets-es6 を使って とりあえず es6を書けるようにしました。
今後改善していきたいです。
そして
Rails6の開発も始まったのでRails6が出たらすぐアップデートできるように頑張って行きましょう!
そんな広告技術部ではRails管理画面に限らず広くサーバーサイド開発してくれる仲間を募集中です!
ご興味あるエンジニアの方は下記からぜひ応募してみてください!