こんにちは。エンジニアの山口です。
私のチームでは、マネーフォワードが運営する全 Ruby on Rails アプリケーションの基板となるエンジンの開発を担当しています。 最近、そのエンジンが利用する Rails のバージョンを 4.1.8 から 4.2 にアップグレードしてみました。
エンジンの規模がまだ小さいということもあり特に大きな問題はありませんでしたが、細かい変更が必要でしたので実施した作業を共有したいと思います。
まずは RailsGuides の A Guide for Upgrading Ruby on Rails を読む
RailsGuides に「A Guide for Upgrading Ruby on Rails」という記事があります。この記事の「3 Upgrading from Rails 4.1 to Rails 4.2」を読んで、必要な作業を行いました。
ちなみに、今回は以下の gem を追加する以外に特に作業することはありませんでした。
'web-console', '~> 2.0'
を Gemfile の development グループのに追加'responders', '~> 2.0'
を gemspec に追加
config.serve_static_assets を config.serve_static_files に変更
アプリケーションの設定ファイルで指定する config.serve_static_assets
が deprecated になっていたため、新しい config.serve_static_files
オプションを使うように変更しました。
ActiveRecord の has_one に渡す order オプションをスコープに置き換え
ActiveRecord
の has_one
アソシエーションに order
オプションを指定しているモデルがあったのですが、今回からそのようなモデルを読み込んだ時点で rspec が落ちてしまうようになったため、scope として渡すようにしました。
- has_one :my_association, order: 'updated_at DESC' + has_one :my_association, -> { order 'updated_at DESC' }
もともと Rails 3 までで廃止となっていたオプションだったのですが、現在では使われていないアソシエーションだったこともあり、特に問題にはなっていませんでした。それが今回のアップデートで古い記法が残っているのがわかった、という箇所でした。
ActiveRecord の scope に lambda を渡す
Rails 4.0 時点で scope に lambda を渡すことは必須になったため、基本的にすべての scope は lambda を渡すようになっていたのですが、定義だけされて実際には使われていなかった scope で lambda になっていない箇所がありました。それが原因で rspec がモデルを読み込んだ時点で落ちるようになったので、lambda を渡すように変更しました。
数値に対して ago を呼ばない
テストコード中に、時刻を取得するために 5.ago
のような呼び出しを行っているところがありました。Rails 4.1.8 時点ですでに数値に対して ActiveSupport::Duration#ago
や #until
を呼び出すことは deprecated だったようなのですが、Rails 4.2 では NoMethodError
となってしまったので、5.seconds.ago
と #seconds
を先に呼ぶようにしました。
Rspec の raise_error(ActionController::RoutingError) を raise_error(ActionController::UrlGenerationError) に変更
controller の spec に以下のようなテストが書いてあったのですが、これが Rails 4.2 になって失敗するようになっていました。
it do expect { post :action, myparam: param }.to raise_error(ActionController::RoutingError) end
調べていくと、実はここで発生していたエラーは、実際は ActionController::UrlGenerationError
ということがわかりました。ではなぜこれまで raise_error(ActionController::RoutingError)
でテストが成功していたかというと、UrlGenerationError
の superclass が RoutingError
だったためです。
Rspec の raise_error
マッチャーを調べていくと、最終的にraise_error に渡したエラーと実際に発生したエラーのインスタンスを === で比較していました。Module
クラスの ===
オペレータは右辺が自身かそのサブクラスのインスタンスであるとき真を返すので、ActionController::RoutingError === ActionController::UrlGenerationError のインスタンス
が真になって、これまではテストが通っていました。
しかし Rails 4.2 では ActionController::UrlGenerationError
の superclass が ActionController::ActionControllerError
に変更になったため、テストが失敗するようになっていました。コミットログを読む限り、404 のエラーとして扱われなくするためのようです。
ActionMailer の #deliver を #deliver_now に変更
ActionMailer
の #deliver
は deprecated となり、Rails 5.0 からは廃止されるというメッセージが表示されたため、このタイミングで #deliver_now
に置き換えました。
作業後の感想とこれから
今回は対象の規模がそれほど大きくなく、また何よりテストがしっかりと書かれていたため、Rails 4.2 へのアップグレードによって大量に修正箇所が出たり、ハマってしまうことはありませんでした。 自信を持ってアップグレードするためにもテストは大事ですね。
さて、これからは運用中のサービスの Rails バージョンをアップグレードする作業が待っています。 同じくこれから Rails 4.2 にアップグレードする皆さん、がんばりましょう!
最後に
マネーフォワードでは、Railsエンジニアを大募集しています!
みなさまのご応募お待ちしております!