この記事は クラウドワークスAdvent Calendar 2023の9日目の記事です。
はじめに
株式会社クラウドワークスの@nisyuuです。和食作りに日々励んでいます。
エンジニアとしてクラウドテックというフリーランスと企業をマッチングするエージェントサービスを開発しています。
クラウドテックは2023年11月に、Rails6.1からRails7.0へとアップグレードしました。アップグレード時点でRails7.1がリリースされていましたが、既にRails7.0アップグレードのための変更対応を一通り行いレビュー状態になっていたことと、7.1に対応するためには新たな変更が必要になるため7.0までとしました。
本記事では、Rails6.1からRails7.0へ移行するために行なったことをまとめます。
Rails7.0に依存するgemのアップグレード
Gemfile
のRailsバージョンを指定している箇所をgem 'rails', '~> 7.0.5.1’
に変更します。
次にbundle update
をすることでRails7.0と依存するgemのアップデートが可能となりますが、1時間以上経過しても処理が一向に終わらなかったため、手動で一つ一つ依存gemを確認してアップグレードしました。
手動でアップデートした方法
Railsバージョンをgem 'rails', '~> 7.0.5.1’
にした状態で、bundle install
をすると、処理が中断し依存するgemをチェックすることができます。
例
Bundler could not find compatible versions for gem "activemodel":
In snapshot (Gemfile.lock):
activemodel (= 6.1.7.3)
In Gemfile:
public_activity was resolved to 2.0.2, which depends on
activerecord (>= 5.0) was resolved to 6.1.7.3, which depends on
activemodel (= 6.1.7.3)
rails (~> 7.0.5.1) was resolved to 7.0.5.1, which depends on
activemodel (= 7.0.5.1)
Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.
activemodel
のバージョンが依存関係にあるとわかったので、bundle update --conservative activemodel
を実行します。
再びbundle install
実行し、Bundler could not find compatible versions for gem
が表示されなくなるまで繰り返していきます。
Sprocketsへの依存がオプショナルになった
Rails7.0からsprockets-rails
への依存関係がなくなったため、引き続きSprocketを使用したい場合は別途インストールする必要があります。
アップデートタスクの実行
gemのアップグレードが終わった後に、rails app:update
を実行します。app:update
コマンドは、新しいRailsバージョンで必要となるファイルの新規作成や既存ファイルの変更などが行われます。
ソースコードの変更
手始めはRails 6.1からRails 7.0へのアップグレードを参考にチェック作業を行い、変更対応を入れました。
アップグレードガイド以外の変更必要箇所もあったため、下記にまとめていきます。
ActiveRecord::Base.configurations[Rails.env]が使用できない
ActiveRecord::Base.configurations
がカプセル化されたようで、ActiveRecord::Base.configurations[Rails.env]
をActiveRecord::Base.connection_db_config.configuration_hash
へと変更する必要がありました。
Tasks::DatabaseTasks.schema_file が削除された
元々、非推奨になっていたTasks::DatabaseTasks.schema_file
がRails7.0で削除されました。代わりに下記コードへと変更しました。
file = ActiveRecord::Tasks::DatabaseTasks.schema_file_type(:sql)
file_path = File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, file)
ただし、Tasks::DatabaseTasks.schema_file_type
がRails7.0で非推奨化されてしまったため、他の回避方法があればそちらを選択するのが良さそうです。
to_sがto_fsへと変更
to_s
がRails7.0から非推奨となり、to_fs
もしくはto_formatted_s
が推奨されるようになりました。Rails7.1からto_s
は削除となっています。
テストでセッションの値をセットする時はActionController::TestSessionを使用する
Rails7.0にアップグレードしてからallow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return({xxx: yyy})
という書き方ができなくなりました。
実際に実行すると、このようなエラーが表示されます。
NoMethodError: undefined method `enabled?' for {xxx: yyy}:Hash
and_return
にハッシュ値の{xxx: yyy}
をそのまま入れるのではなく、ActionController::TestSession
でセットした値をand_return
に渡すようにしなければなりません。
例
mock_session = ActionController::TestSession.new(test_ids: [1, 2, 3])
allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(mock_session)
ActiveModel::Errorsがハッシュとして使えなくなった
ActiveModel::Errorsはエラーオブジェクトの配列となりました。
エラーメッセージ
NoMethodError: undefined method `to_sym' for #<ActiveModel::Error attribute=others, type=too_long, options={:count=>4096}>
変更はこのようにする必要があります。
- record.errors.each { |k, v| errors.add(k, v) }
+ record.errors.each { |error| errors.add(error.attribute, error.message) }
ActiveModel::Errors#clearが使えなくなる
非推奨であったActiveModel::Errors#clearが使えなくなり、代わりにActiveModel::Errors#deleteが代替の機能として提供されました。
コードはこのような変更が必要になります。
- errors[:base].clear
+ errors.delete(:base)
mergeを使ってのBETWEEN条件を評価してくれなくなる
同じカラム上で条件を複数マージした時に、マージした条件が維持されず常に後者の条件によって置き換わるようになりました。
複数条件を維持させるには、以下のように一つのmergeで完結させるか、rewhere
オプションを使うことで可能なようです。
relation = Author.all
- relation = relation.joins(:book).merge(Book.where(published_at: start_published_at..)) if start_published_at.present?
- relation = relation.joins(:book).merge(Book.where(published_at: ..end_published_at)) if end_published_at.present?
+ relation.joins(:book).merge(Book.where(published_at: start_published_at.presence..end_published_at.presence))
rewhere
についてはRuby on Rails 7.0 リリースノートを参照ください。
検証作業
Rails7.0の変更対応を終え、CIも無事に通るようになったところで、通常のコードレビューに加えドッグフーディングやウォークスルーを実施しました。
ドッグフーディングとは、開発したものを実際に自社組織内で使ってみることを意味します。今回の場合だと、Rails7.0環境の含まれるブランチを各エンジニアに取り込んでもらい、実際にRails7.0上で作業してもらうことでドッグフーディングを実施しました。
ウォークスルーは、評価対象のプロセスを実際に体験をすることを指します。評価対象は影響範囲のある箇所がメインになります。Rails7.0アップグレードはシステム全体に影響があるものなので、システム上のユーザーがよく使用する機能を中心に幅広くウォークスルーを行いました。
おわりに
チームのサポートと入念にチェックを行っていたおかげで、リリースは何事もなく終えられました。