この記事はQiitaに投稿した内容を一部加筆修正し、移行してきたものです。

Railsのメジャーバージョンアップは毎回大幅なアップデートが含まれているため、アップグレード作業がたいへんになることがあります。

今回はとあるアプリケーションをRails 4.2.xからRails 5.0.0.1へアップグレードしたので、その記録を備忘として残しておきます。

前提

Rubyのバージョンが 2.2.2 以上でなければ、Rails 5は動作しません。

2.2.2 未満を使用している場合は、まずRubyのバージョンをアップデートしてからRailsのアップデートを行うことをオススメします。

ライブラリのバーションアップ

Rails 4系に依存しているライブラリをRails 5系に対応しているものにしたいため、

bundle update

を行います。

この方法はすべてのgemをアップデートしてしまうため、関連するものだけをアップデートしたい場合は個別に指定してください。

Rails 5へのアップグレードの原因切り分けのため、この時点でご自身のRailsアプリケーションのテストがすべて通過していることを確認します。

Rails 5のgemをインストール

Gemfilerails の項目を、以下のように変更します。

# Gemfile
gem 'rails', '5.0.0.1'

変更を保存したら、

bundle update rails

のコマンドを実行し、アップデートを行います。

これでうまくアップデートできない場合、依存しているライブラリがRails 5対応していないと考えられます。

その場合はIssue立てるなりPull Requestを出すなりして対応するのが良いでしょう。

Railsアプリケーションの設定の更新

Railsの設定項目にいくつか追加および変更があります。 Railsにはこの更新をサポートしてくれるコマンドが用意されています。

rails app:update

上記のコマンドを実行すると差分が表示されるので、問題なければ上書きを、問題ありそうであれば、うまく差分を吸収していきましょう。

routes.rbapplication.rbなどにも変更が入るため、特に注意しましょう。

各ファイルの調整

上記の変更したら一度テストを実行しましょう。 テストが通れば問題ないのですが、いくつかのAPIが変更されているため、そうは問屋が卸さない感じでした。

アプリケーションのファイルにも少し手を加える必要が出てきます。 また、多数のDEPRECATION WARNINGが出てくると思われますので、地道に1つずつ潰していきましょう。

xxx::Baseの継承が変更

  • ActiveRecord::Base -> ApplicationRecord
  • ActiveJob::Base -> ApplicationJob
  • ActionMailer::Base -> ApplicationMailer

上記の3つが直接継承からもう一層挟むようになりました。 これでBaseクラスにモンキーパッチ当てるような処理をせずに済むようになります。

ただ、このままだとApplicationRecordがテーブルとして認識されてしまいます。

xxx::Baseの継承が変更 - 対応方法

# application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

上記のようにself.abstract_class = trueを記載することでテーブルとして認識されなくなります。

xxx_filterの非推奨

Controllerで使用されるbefore_filterafter_filterなどが非推奨になりました。

xxx_filterの非推奨 - 対応方法

  • before_filter -> before_action
  • after_filter -> after_action

新たに上記のようにxxx_actionが用意されているので、こちらに変更しましょう。

envの直接参照が非推奨

ActionControllerを継承したクラス内でのenvを使用して、rack のenvへのアクセスが非推奨となりました。

envの直接参照が非推奨 - 対応方法

request.envから取得できるようになっているので、こちらに変更しましょう。

plain/textのレンダリング方法が変更

plain/textでレンダリングする場合、render text: "This is a text."での指定が非推奨となりました。

plain/textのレンダリング方法が変更 - 対応方法

render plain: "This is a text."に変更しましょう。

response.headersのオブジェクトの変更

response.headersはRails 4まではHashオブジェクトでしたが、Rails 5からはActionDispatch::Response::Headerオブジェクトに変更になりました。 ActionDispatch::Response::HeaderDelegateClass(Hash)を継承しているので、Hashオブジェクトの子クラスではありません。 そのため、たとえば

expect(response.headers).to include { 'Authorization' => 'Bearer sample_token' }

のようにHashオブジェクトとして扱っていた場合などに影響が出てきます。

response.headersのオブジェクトの変更 - 対応方法

Hashオブジェクトとして扱いたい場合は、response.headers.to_hのように.to_hメソッドを呼び出してHashオブジェクトに変換しましょう。

response.headers= APIの消失

response.headers=のAPIがなくなりました。 そのため、レスポンスヘッダをまとめて直接変更できなくなってしまいました。

response.headers= APIの消失 - 対応方法

response.headers['HEADER NAME'] = 'HEADER VALUE'
# or
response.set_header('HEADER NAME', 'HEADER VALUE')

response.headers[]またはresponse.set_headerを使用して1つずつ変更していきましょう。

errors[]の非推奨

errors[:base] = "This is invalid!"

上記のようにモデルのerrors[]を使用してのエラーの追加が非推奨となりました。

errors[]の非推奨 - 対応方法

errors.add(:base, "This is invalid!")

errors.addを使用しましょう。

リクエストのJSONパーサの独自定義方法の変更

今までリクエストのJSONパーサを独自に定義する場合、

Rails.application.config.middleware.swap(
  ::ActionDispatch::ParamsParser, ::ActionDispatch::ParamsParser,
  ::Mime::JSON => proc do |raw_post|
    ::ActiveSupport::JSON.decode(raw_post).with_indifferent_access
  end
)

のようにミドルウェアにかます方法が利用できましたが、この方法が使用できなくなりました。

リクエストのJSONパーサの独自定義方法の変更 - 対応方法

ActionDispatch::Request.parameter_parsersにパーサの定義が入るようになったため、

ActionDispatch::Request.parameter_parsers[:json] = -> (raw_post) do
  ::ActiveSupport::JSON.decode(raw_post).with_indifferent_access
end

のようにJSONパーサを定義(代入)するようにしましょう。

ActiveRecord::Migrationにバーション指定が追加

ActiveRecord::Migrationにバージョンの指定が必要になりました。

production環境では過去のマイグレーションファイルにバーションが指定されていなくても問題ないのですが、CIでのテストなど、データベースがリセットされており、都度マイグレーションを走らせると警告が出てしまいます。

ActiveRecord::Migrationにバーション指定が追加 - 対応方法

すでに存在するマイグレーションファイルのActiveRecord::MigrationActiveRecord::Migration[4.2]に変更しましょう。

jsonおよびjsonb型カラムが存在するテーブルのマイグレーション時にデフォルト値の解釈が変更

今まではjsonおよびjsonb型カラムのデフォルト値にオブジェクトを入れる際は、

create_table :posts do |t|
  t.jsonb :settings, default: '{}'
end

のように文字列で指定していましたが、このオブジェクトの指定がシリアライズされずに設定されるようになりました。

production環境では、過去のマイグレーションファイルに対してこの対応しなくても影響はないですが、開発時に

rails db:migrate:reset

などを実行する場合 scheme.rb が更新されてしまいます。

jsonおよびjsonb型カラムが存在するテーブルのマイグレーション時にデフォルト値の解釈が変更 - 対応方法

create_table :posts do |t|
  t.jsonb :settings, default: {}
end

Rubyの空ハッシュオブジェクトを、デフォルト値として設定するようにしましょう。

alias_method_chainの使用が非推奨

モンキーパッチなどを行う際、モンキーパッチする前のメソッドを参照できるようにalias_method_chainというメソッドが用意されてしましたが、非推奨になりました。

alias_method_chainの使用が非推奨 - 対応方法

Object.prependを使用するようにしましょう。 具体的な例についてはTechscoreさんのRuby2.0のModule#prependは如何にしてalias_method_chainを撲滅するのか!?に詳しく書かれています。

ActionController::TestCaseが非推奨

クラス名を言われても何を指しているのかピンと来ないかもしれませんが、ControllerやHTTP Requestのテストの際に、

class ControllerTest < ActionController::TestCase
  def test_post
    post '/post', { title: 'sample' }, { 'Authorization' => 'Bearer sample_token' }
    assert_response :success
  end
end

上記のようにテストを実現していました。 このAPIが変更になりました。

ActionController::TestCaseが非推奨 - 対応方法

ActionDispatch::IntegrationTest を使用しましょう。

具体的には、

class ControllerTest < ActionDispatch::IntegrationTest
  def test_post
    post '/post', params: { title: 'sample' }, headers: { 'Authorization' => 'Bearer sample_token' }
    assert_response :success
  end
end

としましょう。また、HTTPリクエストメソッドは、パス以外はすべてキーワード引数を受け取るようになりました。

ミドルウェアの差し込みのクラス名の文字列指定が非推奨

# application.rb
config.middleware.insert_before 0, 'Rack::Cors' do
end

今まで読み込み順の関係もあり、クラス名を指定する場合は文字列で指定している方が多かったかと思いますが、この方法が非推奨になりました。

ミドルウェアの差し込みのクラス名の文字列指定が非推奨 - 対応方法

# application.rb
config.middleware.insert_before 0, Rack::Cors do
end

クラス名をオブジェクトの状態で渡しましょう。

ActionController::ParametersがHash継承ではなくなった

ActionController::ParametersHashクラスの継承でなくなりました。 入れ子になっているパラメータ(オブジェクト)も ActionController::Parametersオブジェクトになっています。

params.mergeは必ず引数にHashオブジェクトを取るようになったため気を付ける必要があります。

また、to_hメソッドはHashオブジェクトに変換するわけではなく、

params[:title]
# => 'hoge'
params[:sub_title]
# => 'fuga'
params.permit(:title).to_h
# => { 'title' => 'hoge' }

上記のようにpermittedなパラメータのみがHashオブジェクトになります。

今回の変更で一番の鬼門はこの変更であると個人的に思っています。

ActionController::ParametersがHash継承ではなくなった - 対応方法

ActionController::Parameters同士をマージする際はHashオブジェクトに変換してください。

キーが不定なパラメータを受け取る場合は、to_unsafe_hというメソッドが用意されているのでこちらを使用します。

「unsafe」とあるように、安全でない可能性があるため、用法用量を守り正しくお使いください。

まとめ

Rails 5にすることで、さまざまな新機能が使用できます。 個人的には、

  • ORクエリの発行
    • 合わせてポリモーフィックなモデルのクエリの改善
  • ActionCable
  • rails-api
  • マイグレーションのMySQLの大幅サポート

あたりが、直近恩恵を受けています。

一部ベンチマークによると、Active Record周りなど少し遅くなったりしているようですが、マイナーおよびパッチバージョンアップで改善されると思われます。

みなさんもRails 5にアップグレードをして、ステキなRails 5ライフをお送りください。