Herokuで稼働させていたRailsアプリケーションが Heroku-18
のEOLを間近に控えてバージョンアップを余儀なくされたので対応内容を後世のために記しておきます。
アプリケーションの構成概要
Ruby: 2.6.3
Rails: 5.1.7
DB: PostgreSQL
開発環境: Mac、Windows
本番環境: Heroku (Heroku-18)
なお、Railsは APIモード で実行しています。
[config/application.rb]
config.api_only = true
経緯
Herokuのstackの Heroku-18
が来年の2023年5月1日でデプロイが行えなくなります。
当初は1つ上の Heroku-20
への対応を検討していたのですが2年後にまた Heroku-22
対応するのはしんどいので一気に Heroku-22
に上げることを検討しました。
Heroku-22 に必要なもの
Ruby: 3系 (デフォルトは3.1.x)
Rubyを3系に上げるにはRailsも含め、主要なgemのバージョンアップが必須でした。
以下はバージョンアップを行ったgemの一覧。
変更後のバージョン
Ruby: 3.1.2
Rails: 7.0.4
gemの変更差分
gem | 変更前 | 変更後 |
---|---|---|
bundler | 1.17.3 | 2.3.23 |
pg | 1.1.4 | 1.4.4 |
nokogiri | 1.10.3 | 1.12.5 |
msgpack | 1.3.0 | 1.5.6 |
bootsnap | 1.4.4 | 1.12.0 |
active_model_serializers | 0.10.9 | 0.10.13 |
activerecord-import | 1.0.4 | 1.4.1 |
rspec-rails | 3.8.2 | 5.1.2 |
pry-byebug | 3.7.0 | 3.10.1 |
timecop | 0.9.1 | 0.9.5 |
以下は Ruby 3、および Rails 7 での仕様変更、実装変更へのコードレベルでの対応になります。
1. Ruby 3 対応
大文字の予約語を小文字にする
Ruby 3 では命名規約が厳密に適用されるようで予約語を誤って大文字で記述していたものがエラーになりました。
これはRailsのDBマイグレーションスクリプトで真偽値の false
を誤って FALSE
と記述していたものがエラーになった例です。
[db/migrate/YYYYMMDD_create_hoge.rb]
NameError: uninitialized constant CreateHoge::FALSE
t.boolean :fuga_flg, default: FALSE
命名規約エラー
クラス名、モジュール名はキャメルケースで記述するルールですが Ruby 2.x では以下のような命名が許容されていました。
HTMLGenerator
しかし、Ruby 3.x では読込時にエラーになります。
これはRailsで開発時は動的な読込で気付かなかったのに本番環境に上げたら引っかかるというパターンが多そうです。
Unable to load application: NameError: uninitialized constant HTMLGenerator
このケースでは HtmlGenerator
などにリネームする必要があります。
SQL、HTML、XMLなどアッパーケースで記述される技術要素などを含むクラス、モジュールがこれに引っかかりやすそうです。
キーワード引数の分離
キーワード引数の分離に関しては多くの記事で言及されているので内容自体は触れません。
以下のリンク先などを参照してください。
プロと読み解く Ruby 3.0 NEWS - キーワード引数の分離
基本的には Hash で渡していたものをキーワード引数に置き換える作業になります。
2. Rails 7 対応
トランザクションブロックで return すると commit されない
Rails 7 ではDBのトランザクションブロックで return
すると commit が実行されずに rollback されます。
return
を除き、ifブロックに置き換えるなどの対処が必要です。
ActiveRecord::Base.transaction do
# 前略
return if hoge_condition
# 後略
end
index_exists? の実装変更
特殊なパターンでハマったのが ActiveRecord
が提供している index_exists? メソッドの実装変更。
3番目の引数のオプション設定の定義が変わっているのでHashで渡していたものはエラーになりました。
- Rails 5.x
def index_exists?(table_name, column_name, options = {})
# 略
end
- Rails 7.x
def index_exists?(table_name, column_name, **options)
# 略
end
index_exists?(:hoge, :column_a, { name: 'hoge_index1' })
⇒ ArgumentError: wrong number of arguments (given 3, expected 2)
Rails のセッション管理の仕様変更への対応
APIモードで実行していると Rails 6.x からは以下のエラーが発生するようになり、リクエストが処理させません。
Your application has sessions disabled. To write to the session you must first configure a session store
上記の記事を参考に config/application.rb
内で細工を施してやり過ごしました。
config.api_only = true
# Rails 6 からのセッション周りの仕様変更への対応ハック
config.middleware.use(ActionDispatch::Cookies)
config.middleware.use(ActionDispatch::Session::CookieStore)
config.middleware.use(ActionDispatch::ContentSecurityPolicy::Middleware)