Rails 5アップグレードへの道
この記事はQiitaに投稿した内容を一部加筆修正し、移行してきたものです。
Railsのメジャーバージョンアップは毎回大幅なアップデートが含まれているため、アップグレード作業がたいへんになることがあります。
今回はとあるアプリケーションをRails 4.2.xからRails 5.0.0.1へアップグレードしたので、その記録を備忘として残しておきます。
- 前提
- ライブラリのバーションアップ
- Rails 5のgemをインストール
- Railsアプリケーションの設定の更新
- 各ファイルの調整
- xxx::Baseの継承が変更
- xxx_filterの非推奨
- envの直接参照が非推奨
- plain/textのレンダリング方法が変更
- response.headersのオブジェクトの変更
- response.headers= APIの消失
- errors[]の非推奨
- リクエストのJSONパーサの独自定義方法の変更
- ActiveRecord::Migrationにバーション指定が追加
- jsonおよびjsonb型カラムが存在するテーブルのマイグレーション時にデフォルト値の解釈が変更
- alias_method_chainの使用が非推奨
- ActionController::TestCaseが非推奨
- ミドルウェアの差し込みのクラス名の文字列指定が非推奨
- ActionController::ParametersがHash継承ではなくなった
- まとめ
前提
Rubyのバージョンが 2.2.2
以上でなければ、Rails 5は動作しません。
2.2.2
未満を使用している場合は、まずRubyのバージョンをアップデートしてからRailsのアップデートを行うことをオススメします。
ライブラリのバーションアップ
Rails 4系に依存しているライブラリをRails 5系に対応しているものにしたいため、
bundle update
を行います。
この方法はすべてのgemをアップデートしてしまうため、関連するものだけをアップデートしたい場合は個別に指定してください。
Rails 5へのアップグレードの原因切り分けのため、この時点でご自身のRailsアプリケーションのテストがすべて通過していることを確認します。
Rails 5のgemをインストール
Gemfile
の rails
の項目を、以下のように変更します。
# Gemfile
gem 'rails', '5.0.0.1'
変更を保存したら、
bundle update rails
のコマンドを実行し、アップデートを行います。
これでうまくアップデートできない場合、依存しているライブラリがRails 5対応していないと考えられます。
その場合はIssue立てるなりPull Requestを出すなりして対応するのが良いでしょう。
Railsアプリケーションの設定の更新
Railsの設定項目にいくつか追加および変更があります。 Railsにはこの更新をサポートしてくれるコマンドが用意されています。
rails app:update
上記のコマンドを実行すると差分が表示されるので、問題なければ上書きを、問題ありそうであれば、うまく差分を吸収していきましょう。
routes.rb
やapplication.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_filter
やafter_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::Header
はDelegateClass(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::Migration
をActiveRecord::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::Parameters
がHash
クラスの継承でなくなりました。 入れ子になっているパラメータ(オブジェクト)も 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ライフをお送りください。