Ruby on RailsのRubyバージョンを2.7から3.0へアップグレードする方法を解説します。
前提条件として、本記事ではRuby on Railsは6.1バージョンを使います。
Ruby3.0をインストール
ここではRVM(Ruby Version Manager)を使ってインストールしますが、rbenvやdockerでも問題ありません。
3.0系の最新バージョンをインストールします。
rvm install 3.0.7
インストールが終わると、rvmがバージョン3.0に自動的に切り替えてくれます。
rvm list
=> ruby-3.0.7 [ x86_64 ]
Gemfile更新
Gemfileのrubyバージョンを更新します。
# Gemfile
-ruby '2.7.8'
+ruby '3.0.7'
bundle installを実行します。
この時、以下のようなエラーがでる場合があります。
$ bundle install
Fetching source index from https://rubygems.org/
Resolving dependencies............................
Bundler found conflicting requirements for the Ruby version:
In Gemfile:
rails (~> 6.1.0) was resolved to 6.1.4.4, which depends on
Ruby (>= 2.5.0)
...
listen (>= 3.0.5, < 3.2) was resolved to 3.1.5, which depends on
Ruby (~> 2.2, >= 2.2.3)
...
Ruby (~> 3.0.7.0)
各GemがどのRubyのバージョンを必要とするかが表示されます。この例では、listenの現行バージョンがruby3.0に対応していないため、エラーが発生しています。
そのため、まずはlisten単独でアップデートします。
bundle update listen
...
Using listen 3.0.8 (was 3.1.5)
...
Note: listen version regressed from 3.1.5 to 3.0.8
Bundle updated!
listenのバージョンを下げることで、全てのGemがRuby3.0で動作するようになりました。
また、使っているffi gemのバージョンによっては以下のようなエラーが出る場合があります。
# エラー
ffi-1.17.0-x86_64-linux requires rubygems version >= 3.3.22, which is
incompatible with the current version, 2.7.6
その場合、Gemfileでffiのバージョンを指定すれば解決します。
# Gemfile
gem "ffi", "< 1.17.0"
bundle install
Dockerファイル更新
もしも開発・テスト環境でDockerを使っている場合は、Dockerfileを更新し、Rubyのバージョンを3.0.7に変更します。
# Dockerfile
-FROM ruby:2.7.5
+FROM ruby:3.0.7
コンテナを再構築します。
# docker composeを使っている場合のコンテナ再構築
docker compose build サービス名
もしもコンテナ再構築の際に以下のような警告が出る場合は、明示的に正しいbundlerバージョンをインストールするようにDockerファイルに記載します。
# コンテナ再構築の際の警告
Warning: the running version of Bundler (2.2.33) is older than the version that created the lockfile (2.3.6). We suggest you to upgrade to the version that created the lockfile by running gem install bundler:2.3.6.
# Dockerfile
RUN gem install bundler:2.3.6
アプリケーション・テストコード更新
次に、アプリケーションやテストコードでRuby 3.0に対応していない箇所を更新します。
アプリケーションをローカル環境で起動し、主な機能をテストすると共に、rspecやminitestなどの自動テストを実行してパスするかどうか確認します。
特に注意が必要なのは、Ruby 3.0でキーワード引数と位置引数の扱いが厳密になった点です。
詳しくは上記の記事を読んでいただきたいのですが、例を上げると以下の通りです。
# メソッド定義。引数はキーワード引数
def foo(**kwargs); end
args = { bar: 'bar' }
# ruby 2.7は以下の呼び出しを許可する。
foo(args)
# ruby 3.0ではArgumentErrorとなる。
foo(args)
=> wrong number of arguments (given 1, expected 0) (ArgumentError)
# ruby 3.0では呼び出しの際にdouble splat演算子を使って明示的にキーワード引数になるように変更する。
foo(**args)
メソッドはキーワード引数で定義されているのに、呼び出しを位置引数で行っているために起こるエラーですので、呼び出しもキーワード引数で行なうように変更すればOKです。
Railsでこの問題が起こる可能性が高いものの1つにI18n.tメソッドがあります。I18n.tメソッドは、Ruby 3.0ではキーワード引数で呼び出す必要があります。
# Ruby 2.7では以下のコードは許可されるが、3.0だとArgumentError
I18n.t('translation_string', { foo: 'bar' })
=> ArgumentError:
wrong number of arguments (given 2, expected 0..1)
# Ruby 3.0
# Hashのままでは位置引数とみなされるので、キーワード引数に変換。
I18n.t('translation_string', foo: 'bar')
なお、上記のような引数エラーはアプリケーションコードだけではなく使っているgemにも起きる可能性があります。もしエラーを発見したら、そのgemをアップデートして問題が解消するか確認しましょう。
# エラー例:validates_timeliness gem
ArgumentError:
wrong number of arguments (given 3, expected 1..2)
# ~/.rvm/gems/ruby-3.0.7/gems/validates_timeliness-5.0.0/lib/validates_timeliness/validator.rb:89:in `add_error'
ステージング環境にデプロイ
ローカル環境での確認が終わったら、ステージング環境にデプロイできるか確認します。
もしエラーになったら、デプロイログを確認して原因を修正します。
エラーになる原因はアプリケーションやデプロイ先の環境によって様々ですが、いくつか例をあげます。
デプロイ先環境のRubyGemsのバージョンが、特定のGemが要求するバージョンと一致しない
この場合は以下のようなエラーが表示されます。
ffi-1.17.0-x86_64-linux requires rubygems version >= 3.3.22, which is
incompatible with the current version, 3.2.33
Bundler Output: Fetching source index from https://rubygems.org/
ffi-1.17.0-x86_64-linux requires rubygems version >= 3.3.22, which is
incompatible with the current version, 3.2.33
解決方法としては、デプロイ先環境のrubygemsをバージョンアップする、問題になっているgemをバージョンダウンする、などがあります。
rake assets:precompileの途中でロードエラーが起きる
この現象では以下のようなエラーが表示されます。
Running: rake assets:precompile
...
LoadError: cannot load such file --
この場合、まずはローカル環境上でデプロイ先と同じ環境モードにてコマンドを実行してエラーにならないかを確認します。
RAILS_ENV=staging rake assets:precompile
解決方法としては、該当GemのGemfileでの記載位置をdevelopmentグループの外側に出し、全ての環境でインストールされるようにする、などが考えられます。
デプロイがうまくいくようになったら、アプリケーションのクリティカルな箇所を手動でテストし、エラーになる箇所があったら修正します。
本番環境にデプロイ
本番環境へのデプロイ手順はステージング環境と同じです。
もしデプロイがうまくいったとしても、これまでのテストで見落としている箇所があるかもしれませんので、改めてクリティカルな箇所をテストするとともに、デプロイ後の数日はエラー監視を強化するようにします。
まとめ
Rubyのバージョンアップは、アプリケーションの性能向上や新しい機能の利用につながる一方で、互換性の問題が発生する可能性もあります。本記事で紹介した手順を参考に、慎重にバージョンアップを進めてください。特に、キーワード引数やパターンマッチングなど、Ruby 3.0で変更された文法には注意が必要です。
