エンジンの Rails を 4.2 にアップグレードした話

こんにちは。エンジニアの山口です。

私のチームでは、マネーフォワードが運営する全 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 オプションをスコープに置き換え

ActiveRecordhas_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エンジニアを大募集しています!
みなさまのご応募お待ちしております!

マネーフォワード採用サイト
https://recruit.moneyforward.com/

Wantedly
https://www.wantedly.com/companies/moneyforward

Pocket