はじめに
本記事では、Ruby on Rails 6.1から7.0へアップグレードする手順を解説します。
公式サイトのアップグレードガイドに加えて、現場で遭遇しがちな問題の解決策や、より詳細な解説を盛り込みました。ぜひ、アップグレード作業の際にお役立てください。
目次
- アップグレード前に行なうこと
- 自動テストのカバレッジ向上と修正
- Rubyのバージョンアップ
- アップグレード手順
- Gemfileの更新
rails app:updateタスクの実行
- デフォルト設定適用とコード修正
config.load_defaults 7.0への段階的な移行- レンダリングでファイル拡張子が含まれているとエラーになる問題
- オープンリダイレクトの例外
ActionView::Helpers::UrlHelper#button_toの挙動変更- Spring gemの非デフォルト化
- Sprocketsの使用継続または削除
- Zeitwerkオートローダーの必須化
- ハッシュ関数の変更に伴うクッキーローテーターの設置
ActiveSupport::Digestのダイジェストクラス変更- スキーマダンプへのRailsバージョン記載
stylesheet_link_tagのmedia属性- Cookie serializerフォーマットの変更
- Ruby 3.1/3.2エラー対策用Gemの削除
- 本番環境へのリリース
- まとめ
1. アップグレード前に行うこと
自動テストのカバレッジ向上と修正
どのバージョンのアップグレードでも同様ですが、まず自動テストのカバレージを可能な限り高めておきましょう。テストが失敗している箇所があれば、アップグレード前に修正します。テストが不完全な状態では、アップグレードによって生じた不具合を検知するのが困難になります。
Rubyのバージョンアップ
Rails 7.0は、Ruby 2.7.0以上を要求します。お使いのRubyが2.7.0未満の場合は、先にRubyのバージョンアップが必要です。
本記事の執筆時点(2025年6月)では、Ruby 3.1以前のバージョンはすべてEOL(End of Life)となっています。セキュリティの観点からも、Ruby 3.2以上へのアップグレードを強く推奨します。
なお、影響範囲を限定するため、RubyのバージョンアップとRailsのバージョンアップは、それぞれ独立したブランチで作業し、個別にリリースすることをおすすめします。
2. アップグレード手順
Gemfileの更新
Gemfileに記載されているRailsのバージョンを7.0系に書き換えます。
その後、bundle updateを実行します。
# Gemfile
gem 'rails', '~> 7.0.0
# Bash
bundle update
bundle updateが終わらない場合
bundle updateが完了しない場合は、以下の方法で解決できることがあります。
- verboseオプションを付ける:
bundle update --verboseを実行すると、処理の詳細が出力され、問題箇所を特定できる場合があります。 - Railsのみアップデートする:
bundle update railsを実行し、まずRails本体と、その直接の依存Gemのみをアップデートします。
他のGemがRails 7.0未満に依存している場合
特定のGemがRails 7.0未満のバージョンに依存していると、以下のような依存関係のエラーでアップデートが失敗します。
Bundler could not find compatible versions for gem "activemodel":
In Gemfile:
rails (~> 7.0.0) was resolved to 7.0.0, which depends on
activemodel (= 7.0.0)
validates_timeliness was resolved to 6.0.1, which depends on
activemodel (>= 6.0.0, < 7)
この場合、Gemfileで該当Gemのバージョン指定を一旦緩めます。
-gem 'validates_timeliness', '~> 6.0.0'
+gem 'validates_timeliness'
そして、bundle updateでRailsと同時にアップデート対象として指定します。
# Bash
bundle update rails validates_timeliness
rails app:updateタスクの実行
Railsにはバージョンアップ用のapp:updateタスクが用意されています。これを実行して、設定ファイルを更新します。
# Bash
rails app:update
Loggerエラーが発生する場合
このタスクの実行中に、以下のLogger関連のエラーが発生することがあります。
uninitialized constant ActiveSupport::LoggerThreadSafeLevel::Logger
これは、concurrent-rubyの特定のバージョンでloggerの依存関係が変更されたことに起因します。解決策はいくつかありますが、concurrent-rubyのバージョンをエラーが発生しないバージョン(例:1.3.4など)に固定する方法が確実です。
# Gemfile
gem 'concurrent-ruby', '1.3.4'
# Bash
bundle
もう一つの方法は、config/boot.rbの先頭でloggerをrequireすることですが、Sprocketsを使い続ける場合はこの方法ではassets:precompile時に問題が再発する可能性があります。
# config/boot.rb
require "logger" # この行を追加
require "bundler/setup"
require "bootsnap/setup
このエラーはRails 7.1では解消されているため、7.1へアップグレードするまでの一時的な対応となります。
ファイルの更新
app:updateタスクは、新しいバージョンのデフォルトファイルと現在のファイルを比較し、上書きするかどうかをファイルごとに尋ねてきます。
Overwrite config/boot.rb? (enter "h" for help) [Ynaqdhm]
各キーの意味は以下の通りです。dキーで差分を必ず確認し、判断することをおすすめします。
Y: 上書きする
n: スキップする
a: このファイル以降、すべて上書きする
q: 終了する
h: ヘルプを表示する
d: 差分を表示する
m: マージツールを起動する
もし誤ってファイルを上書きしてしまっても、このタスクは何回でも実行できます。git restore <file>でファイルを元に戻し、再度タスクを実行してください。
rails app:updateで更新対象となるファイルは以下の通りです。
- config/boot.rb
- config/application.rb
- config/environments/development.rb
- config/environments/production.rb
- config/environments/test.rb
- config/initializers/assets.rb
- config/initializers/content_security_policy.rb
- config/initializers/cors.rb
- config/initializers/filter_parameter_logging.rb
- config/initializers/inflections.rb
- config/initializers/permissions_policy.rb
- bin/rails
- bin/rake
- bin/setup
- db/schema.rb
なお、本コマンドで比較や上書きで使われるRailsのデフォルトファイル(tt拡張子のテンプレートファイル)は、以下のディレクトリに格納されています。必要であればコマンドを一度中止し、現在のファイルとテンプレートファイルをじっくり比較してどうするか検討しても良いでしょう。
# Bash
cd `bundle show railties`
cd lib/rails/generators/rails/app/templates
また、自身がRails6.1でどのようなカスタマイズを行ったかを確認する場合は、デフォルトファイルがRails 6.1の上記と同様のフォルダに格納されていますので、比較してみましょう。
このタスクの一環でrails active_storage:updateも実行され、マイグレーションファイルが生成されることがあります。忘れずにrails db:migrateを実行しましょう。
# db/migrate/remove_not_null_on_active_storage_blobs_checksum.active_storage.rb
class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
def change
return unless table_exists?(:active_storage_blobs)
change_column_null(:active_storage_blobs, :checksum, true)
end
end
rails db:migrate
3. デフォルト設定適用とコード修正
config.load_defaults 7.0 への段階的な移行
rails app:updateを実行すると、config/initializers/new_framework_defaults_7_0.rbというファイルが生成されます。この時点では、アプリケーション全体の設定はまだ6.1のままです。
# config/application.rb
config.load_defaults 6.1
安全にアップグレードを進めるため、この設定は6.1のまま維持し、new_framework_defaults_7_0.rb内のコメントアウトされた設定を一つずつ有効化して動作確認と修正を進めるのが推奨されるアプローチです。
レンダリングでファイル拡張子が含まれているとエラーになる問題
Rails 7.0では、renderやrender_to_stringメソッドで渡すパスに.html.erbのような拡張子が含まれているとActionView::MissingTemplateエラーが発生します。
# Rails 6.1では非推奨警告、7.0でエラー
render(partial: 'example.html.erb')
# => ActionView::MissingTemplate
このエラーは、パスから拡張子を削除し、代わりにformatsオプションでファイルタイプをシンボルで指定することで解決できます。
render(partial: 'example', formats: :html)
render_to_string(
layout: 'application',
template: "examples/report",
formats: :pdf
)
オープンリダイレクトの例外
安全でない外部サイトへのリダイレクト(オープンリダイレクト)防止のため、redirect_to メソッドを使った外部サイトへのリダイレクトは例外が発生するようになりました。
redirect_to 'https://some_external_website'
=> ActionController::Redirecting::UnsafeRedirectError
この変更はnew_framework_defaults_7_0.rb内の以下の設定で有効になります。
Rails.application.config.action_controller.raise_on_open_redirects = true
例外を回避するためには、allow_other_hostオプションをtrueに設定する必要があります。
redirect_to 'https://some_external_website', allow_other_host: true
但し、paramsなどユーザー入力値をリダイレクト先にしている場合は脆弱性に該当しますので、許可された外部サイトのホワイトリストを作成して照合するなど、注意が必要です。
ActionView::Helpers::UrlHelper#button_toの挙動変更
button_toメソッドの挙動が2点変更されました。
- HTTPメソッドの変更: 永続化されたレコード(DBに保存済みのオブジェクト)に対して使うと、HTTPメソッドが
POSTからPATCHに変わります。 - HTMLタグの変更: 出力されるHTMLが
<input type="submit">から<button type="submit">に変わります。
# Rails 6.1
= button_to 'Post my object', Example.first
=> <form class="button_to" method="post" action="/examples/1"><input type="submit" value="Post my object"><input type="hidden" name="authenticity_token" value="my_token"></form>
Rails 7.0
= button_to 'Post my object', Example.first
=> <form class="button_to" method="post" action="/examples/1"><input type="hidden" name="_method" value="patch" autocomplete="off"><button type="submit">Post my object</button><input type="hidden" name="authenticity_token" value="my_token" autocomplete="off"></form>
この変更はnew_framework_defaults_7_0.rb内の以下の設定で有効になります。
Rails.application.config.action_view.button_to_generates_button_tag = true
RESTの観点からはPATCHが適切ですが、もしPOSTを維持したい場合は、method: :postを明示的に指定してください。
# Rails 7.0
= button_to 'Post my object', Example.first, method: :post
=> <form class="button_to" method="post" action="/examples/1"><button type="submit">Post my object</button><input type="hidden" name="authenticity_token" value="my_token" autocomplete="off"></form>
Spring gemの非デフォルト化
近年のPCハードウェア性能向上により、Springは新規Railsアプリケーションにデフォルトで含まれなくなりました。特別な理由がなければ、既存アプリケーションからも削除してよいでしょう。
Springを削除する場合
Gemfileからspringと関連するlisten、spring-watcher-listenを削除します。
- gem 'spring'
- gem 'spring-watcher-listen', '~> 2.0.0'
- gem 'listen', '>= 3.0.5', '< 3.2' # springが依存していた場合
bin/railsとbin/rakeからload File.expand_path("spring", __dir__)の行を削除し、config/spring.rbを削除します。
config/environments/test.rbではクラスをキャッシングする設定にします。
config.cache_classes = true
listen gemの削除に伴い、以下のファイル監視コードをconfig/environments/development.rbから削除します。
# config/environments/development.rb
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
Springを使い続ける場合
springのバージョンが3.0.0未満だとエラーが発生するため、bundle update springでアップデートしてください。
undefined method `mechanism=' for ActiveSupport::Dependencies:Module (NoMethodError)
設定ファイルで、test環境のキャッシング設定を変更します。
# config/environments/test.rb
config.cache_classes = false
config.action_view.cache_template_loading = true
また、rails app:updateタスクでSpringをロードするコードをbin/railsとbin/rakeから削除してしまわないように気をつけましょう。
# bin/rails
load File.expand_path("spring", __dir__)
# bin/rake
load File.expand_path("spring", __dir__)
Sprocketsの使用継続または削除
Rails 7.0はsprockets-rails gemに依存しなくなりました。もしアセットパイプラインでSprocketsを使い続ける場合は、Gemfileに明示的に追加する必要があります。
# Gemfile
gem "sprockets-rails"
# Bash
bundle
使い続ける場合は、config/application.rbのrequire "sprockets/railtie"やconfig/initializers/assets.rbなどの関連設定を維持してください。逆にSprocketsを使わないのであれば、これらの設定は削除します。
# config/application.rb
require "sprockets/railtie"
# config/environments/development.rb
config.assets.debug = true
config.assets.quiet = true
# config/environments/production.rb
config.assets.css_compressor = :sass
config.assets.compile = false
# config/initializers/assets.rbの中身全て
Zeitwerkオートローダーの必須化
Rails 6.xでは従来のclassicモードも選択できましたが、Rails 7.0ではzeitwerkモードが必須となりました。そのため、config/application.rbでオートローダーを明示的に指定していたコードは不要になるので削除します。
# 以下の設定はどちらも不要
- config.autoloader = :classic
- config.autoloader = :zeitwerk
ハッシュ関数の変更に伴うクッキーローテーターの設置
セキュリティ向上のため、クッキーの署名などに使われるハッシュ関数がSHA1からSHA256に変更されました。
# config/initializers/new_framework_defaults_7_0.rb
Rails.application.config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA256
この設定を有効にすると、既存のSHA1で生成されたクッキーが読めなくなってしまいます。これを防ぐため、古いクッキーと新しいクッキーの両方を読めるようにする「ローテーター」をinitializerに設置します。
# config/initializers/cookie_rotator.rb
Rails.application.config.after_initialize do Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
authenticated_encrypted_cookie_salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
signed_cookie_salt = Rails.application.config.action_dispatch.signed_cookie_salt
secret_key_base = Rails.application.secret_key_base
key_generator = ActiveSupport::KeyGenerator.new(
secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
)
key_len = ActiveSupport::MessageEncryptor.key_len
old_encrypted_secret = key_generator.generate_key(authenticated_encrypted_cookie_salt, key_len)
old_signed_secret = key_generator.generate_key(signed_cookie_salt)
cookies.rotate :encrypted, old_encrypted_secret
cookies.rotate :signed, old_signed_secret
end
end
ActiveSupport::Digest のダイジェストクラス変更
キャッシュキーの生成などに使われるActiveSupport::Digestのデフォルトダイジェストクラスも、SHA1からSHA256に変更されました。
# config/initializers/new_framework_defaults_7_0.rb
Rails.application.config.active_support.hash_digest_class = OpenSSL::Digest::SHA256
この設定を適用すると、既存のキャッシュは全て無効になります。デプロイ直後のパフォーマンスへの影響に注意してください。
スキーマダンプへのRailsバージョン記載
Rails 7.0から、db/schema.rbにスキーマを生成したRailsのバージョンが記録されるようになりました。これにより、バージョンの異なるスキーマが誤ってロードされるのを防ぎます。
# Rails 6.1
ActiveRecord::Schema.define(version: 2025_06_06_000000) do
# ...
end
# Rails 7.0
ActiveRecord::Schema[7.0].define(version: 2025_06_06_000000) do
# ...
end
stylesheet_link_tag のmedia属性
Rails 7.0では、stylesheet_link_tagヘルパーがmedia="screen"属性を自動で追加しなくなりました。
#config/initializers/new_framework_defaults_7_0.rb
Rails.application.config.action_view.apply_stylesheet_media_default = false
これまでは自動で付与されていましたが、今後は必要に応じて明示的に指定する必要があります。
# Rails6.1
stylesheet_link_tag 'application
=> <link rel="stylesheet" media="screen" href="/assets/application.css>
# Rails7.0では自動追加はなし
= stylesheet_link_tag 'application'
=> <link rel="stylesheet" href="/assets/application.css>
# Rails7.0では明示的に追加する
stylesheet_link_tag 'application', media: 'screen'
=> <link rel="stylesheet" media="screen" href="/assets/application.css>
Cookie serializerフォーマットの変更
Rails 7.0では、クッキーのデフォルトserializerが:marshalから:jsonに変更されました。既存のアプリケーションを:jsonへ移行するには、一時的に:hybridモードを使用します。
# config/initializers/cookies_serializer.rb
Rails.application.config.action_dispatch.cookies_serializer = :hybrid
:hybridモードは、:marshalと:json両方のフォーマットのクッキーを読み込めます。さらに、:marshal形式で書かれたクッキーを読み込んだ際に、自動的に:json形式で書き直してくれます。
十分な移行期間を経て、全ユーザーのクッキーがJSONに置き換わったと判断できたら、設定を:jsonに変更するか、設定ファイル自体を削除してデフォルト(:json)に準拠させましょう。
もし何らかの理由でJsonフォーマットに移行できない場合は、Marshalフォーマットを明示的に指定します。
# config/initializers/cookies_serializer.rb
Rails.application.config.action_dispatch.cookies_serializer = :marshal
逆に、6.1からすでにinitializerでjsonフォーマットを指定していた場合は、initializerを削除してしまってOKです。
# config/initializers/cookies_serializer.rb
Rails.application.config.action_dispatch.cookies_serializer = :json
git rm config/initializers/cookies_serializer.rb
Ruby 3.1/3.2エラー対策用Gemの削除
Rails 6.1とRuby 3.1もしくはRuby 3.2の組み合わせで起こるエラー対策として、net-smtpなどのGemをGemfileに追加していた場合、Rails 7.0では不要になるため削除してください。
# Gemfile
-gem 'net-smtp', require: false
-gem 'net-pop', require: false
-gem 'net-imap', require: false
-gem 'digest', '3.1.1', require: false
# Bash
bundle
4. 本番環境へのリリース
config.active_support.hash_digest_classの変更のように、一度適用するとロールバックが困難になる設定は、初回のリリースに含めないようにしましょう。
まず、安全な変更のみを適用してリリースし、数日間〜数週間運用して問題がないことを確認します。その後、ロールバックの難しい設定を個別に有効化し、再度リリースするという段階的な手法が最も安全です。
すべての設定を適用し終えたら、config/application.rbでアプリケーション全体のデフォルト設定を7.0に変更し、config/initializers/new_framework_defaults_7_0.rbは削除します。
# config/application.rb
config.load_defaults 7.0
# bash
git rm config/initializers/new_framework_defaults_7_0.rb
5. まとめ
Railsのメジャーバージョンアップは、アプリケーション全体に影響を及ぼす大きな変更です。開発段階では、新しいデフォルト設定を一つずつ適用し、丁寧な確認と修正が不可欠です。また、本番リリースも段階的に行い、万一の際に迅速にロールバックできるよう計画することで、ユーザーへの影響を最小限に抑えることができます。
