DIGGLE開発者ブログ

予実管理クラウドDIGGLEのプロダクトチームが、技術や製品開発について発信します

Rails7.1に上げようとしたらハマりまくった話

DIGGLEエンジニアのreenjです。
2024年4月下旬ごろにDIGGLEで使用しているRailsを7.0から7.1へアップデートする作業を行いました。 マイナーバージョンのアップデートであるため、当初それほど問題なくアップデートできるだろうと考えていました。
実際に、簡単にアップデートできたという記事を多く見かけます。

しかしDIGGLEでのアップデートの場合は、思いの外いろいろとハマってしまい苦戦しました。
対応内容と変更箇所、そしてハマってしまったことについてこちらの記事で紹介していきます。

対話形式によるファイル変更

基本的には「Railsアップグレードガイド」の内容に従い、アップデートします。

railsguides.jp

まずは、Gemfileを編集しRailsのバージョンを7.1へアップデートします。その後、以下のコマンドを実行します。

$ bin/rails app:update

コマンドを実行すると、対話形式で変更するファイルの確認が行われます。
ここではとりあえずすべてyesと返答し、すべての変更を加えます。

差分が出るため、各差分に問題がないかを確認します。

差分となっている箇所の多くは、リリースノートやガイド、設定項目などで説明されています。
問題があるかどうかは各説明などから判断します。

railsguides.jp

railsguides.jp

例えば、環境固有の設定ファイルに以下のような差分があります。

- config.cache_classes = false
+ config.enable_reloading = true

config.enable_reloadingは新しく追加された設定項目です。!config.cache_classes と同等であり置き換わっています。
設定内容としては、これまでと同等であるため特に問題ありません。

一通り差分の確認をし、ガイドやリリースノートの内容で修正が必要な箇所は適宜修正します。
その後は自動テストを行います。
もし自動テストでエラーが発生していれば、エラー箇所を修正します。

自動テストがすべて通った後は、念のため本番同等の環境で各機能の確認をざっと行います。
これで問題が発生しなければリリースへ向けた流れとなります。
しかし、実際に上記の流れで進めていったところいくつもエラーが発生し、アップデートが難航しました…...。

ハマった点1 サードパーティのライブラリがRails7.1に対応していない

DIGGLEではros-apartmentactive-hashというライブラリを使用しています。

しかし、これらサードパーティのライブラリは2024年の4月下旬ではRails7.1に対応したリリースを出しておらず、これらライブラリを使用した機能を使うとエラーが発生してしまいました。

ros-apartmentへの対応

ros-apartmentは、2022年2月7日に2.11.0がリリースされて以降、2年以上リリースがされていませんでした。しかしながら、全く更新されていないということではなく、Rails 7.1への対応はすでにマージされており、リリースはされていないといった状況でした。
そのため、Gemfileを以下のような特定コミットを指定して使用するように変更しました。(この方法は、もしrebaseが行われるとハッシュ値が変わりライブラリが取得できなくなるため、少し注意が必要な方法です。)

gem 'ros-apartment', git: 'https://github.com/rails-on-services/apartment.git', ref: 'xxxxxxx', require: 'apartment'

※DIGGLEでRailsのアップデートを行った直後(2024月5月21日)にRails7.1に対応した3.0.0がリリースされました。
そのため、現在はライブラリのバージョンを更新すれば解決します。

active-hashへの対応

active-hashも同様に、2024年4月下旬時点では、Rails7.1のサポート対応が入っているものの、リリースされていませんでした。必要だったRails7.1のサポート対応としては、こちらのPRの内容です。
大きな対応ではないため、こちらはパッチで対応することとし、以下のモンキーパッチを作成しました。

module ActiveHashMonkeyPatch
  def composite_primary_key?
    false
  end
end

require 'active_hash/version'
require 'active_hash/base'
if ActiveHash::Gem::VERSION == '3.2.1'
  ActiveHash::Base.singleton_class.prepend(ActiveHashMonkeyPatch)
end

※こちらも現在は最新リリースに、Rails7.1の対応が入っているため、パッチの対応は不要です。

ハマった点2 Zeitwerkへの対応

RailsではZeitwerkというオートローダーを使用しています。
オートローダーは、プログラム実行時に必要に応じて自動的にソースを読み込む機能です。
自動読み込み対象のクラスやモジュールはrequireincludeせずに使用できます。

Rails 7.1からはlib配下がデフォルトの自動読み込み先として追加されています。DIGGLEでは、Zeitwerkで自動読み込みすることを想定しておらず、lib配下にZeitwerkの規則に従っていないディレクトリやファイルなどを多数配置していたため、これらが問題になりました。
そのため、それらを適宜Zeitwerkで自動読み込みできるようにするか、ディレクトリ自体を除外するかを整理する必要がありました。

techracho.bpsinc.jp

自動読み込みからの除外

Rails 7.1から設定項目config.autoload_lib()が追加されています。
これにより、lib配下が自動読み込みされます。

lib配下で自動読み込みされることを想定していないディレクトリは、以下のように除外する対象に追加します。

config.autoload_lib(ignore: %w([除外するディレクトリ] [除外するディレクトリ] ...))

また、lib直下に格納されているファイルで自動読み込みしないものは、適宜ディレクトリを作成し移します。作成したディレクトリも除外対象に追加します。

この操作によりパスが変わるため、移したファイルを使用している箇所は適宜修正が必要になります。

Zeitwerkの形式に修正

Zeitwerkでファイルを読み込む際にはクラス、モジュール名とファイル名が以下のように対応している必要があります。

lib/my_gem.rb         -> MyGem
lib/my_gem/foo.rb     -> MyGem::Foo
lib/my_gem/bar_baz.rb -> MyGem::BarBaz
lib/my_gem/woo/zoo.rb -> MyGem::Woo::Zoo

参考: GitHub - fxn/zeitwerk: Efficient and thread-safe code loader for Ruby

Zeitwerkにより自動読み込みされるようになったら、不要なrequireは削除します。

その他の修正内容

  • Rails.env.local?メソッドが追加されました
    Rails.env.development? || Rails.env.test?と等価です。 使用していたので置き換えました。

  • #to_s(:format)メソッドが非推奨化され、#to_fs(:format)に置き換わりました (参考)
    こちら一部#to_s(:format)使用していたため、置き換えました。

  • ActionController::Parametersの変更点への対応
    アプリケーションでparamsを呼び出すと、ハッシュではなくオブジェクトが返されるようになりました。
    paramsを呼び出しオブジェクトが返されるようになった箇所にto_hを追加しました。

  • Active Support変更点への対応
    ログをbroadcastするpublic APIが追加されました。
    この変更の影響で、エラーが発生していたためloggerの設定を修正しました。

まとめ

マイナーバージョンのアップデートにしては、思いの外修正する内容が多く、またサードパーティのライブラリがRails 7.1に対応したリリースを出していない、といったところでどうしようかと悩まされたアップデートでした。
現時点では、すでにRails7.1への対応がリリースされているため、ros-apartmentactive-hashのライブラリの問題をどうこうする必要はなくなっていますが、本記事が同じようにRailsのアップデートをする方の助けになれば幸いです!

最後にチームメンバーの方々、エラー原因が特定できずに行き詰まっていた際や対応方針に困っていた際に助けていただき、また、本記事をレビューし文章を改善いただきありがとうございました!