Rails アップグレードガイド

本章では、アプリケーションで使われているRuby on Railsのバージョンを、新しいバージョンにアップグレードする際の手順について示します。アップグレードの手順は、Railsのバージョンごとに記載されています。

目次

  1. 一般的なアドバイス
  2. Rails 6.0からRails 6.1へのアップグレード
  3. Rails 5.2からRails 6.0へのアップグレード
  4. Rails 5.1からRails 5.2へのアップグレード
  5. Rails 5.0からRails 5.1へのアップグレード
  6. Rails 4.2からRails 5.0へのアップグレード
  7. Rails 4.1からRails 4.2へのアップグレード
  8. Rails 4.0からRails 4.1へのアップグレード
  9. Rails 3.2からRails 4.0へのアップグレード
  10. Rails 3.1からRails 3.2へのアップグレード
  11. Rails 3.0からRails 3.1へのアップグレード

1 一般的なアドバイス

言うまでもないことですが、既存のアプリケーションをアップグレードする際には、アップグレードの目的を明確にする必要があります。新しいバージョンのうちどの機能が必要になるのか、既存のコードのサポートがどのぐらい困難になるのか、アップグレードに必要な時間とスキルはどれほど必要かなど、いくつもの要素を調整しなければなりません。

1.1 テスティングのカバレッジ

アップグレード後にアプリケーションが正常に動作していることを確認する方法としては、良いテストカバレッジをアップグレード前に準備しておくのが最善です。アプリケーションを一気に検査する自動テストがないと、変更点をすべて手動で確認しなければならず膨大な時間がかかってしまいます。Railsのようなアプリケーションの場合、これはアプリケーションのあらゆる機能を一つ残らず確認しなければならないということです。アップグレードの実施は、テストカバレッジをきちんと準備してから行なうよう、くれぐれもお願いします。

1.2 アップグレード手順

Railsのバージョンを変更する場合、マイナーバージョンを1つずつゆっくりと変更して、非推奨機能の警告をすべて確認・利用するのが最善の方法であると言えます。言い換えると、アップグレードを急ぐあまりバージョンをスキップするべきではありません。Railsのバージョン番号は「メジャー番号.マイナー番号.パッチ番号」の形式を取ります。メジャーバージョンやマイナーバージョンが変更される場合、公開APIの変更によるエラーがアプリケーションで発生する可能性があります。パッチバージョンはバグ修正のみが含まれ、公開API変更は含まれません。

アップグレードは以下の手順で行います。

  1. テストを書き、テストがパスすることを確認する。
  2. 現時点のバージョンのパッチバージョンを最新のパッチに移行する。
  3. テストを修正し、非推奨の機能を修正する。
  4. 次のマイナーバージョンの最新パッチに移行する。

上の手順を繰り返して、最終的にRailsを目的のバージョンにアップグレードします。バージョンを移行するたびに、Gemfile内のRailsバージョン番号を変更(これに伴い、他のgemのバージョン変更が必要になることもあります)し、bundle update を実行する必要があります。続いて、以下のアップデートタスクを実行して設定ファイルをアップデートし、テストを実行します。

リリース済みのRailsバージョンのリストはここで確認できます。

1.3 Rubyのバージョン

Railsは、そのバージョンがリリースされた時点で最新のバージョンのRubyに依存しています。

  • Rails 6: Ruby 2.5.0以降が必須です。
  • Rails 5: Ruby 2.2.2以降が必須です。
  • Rails 4: Ruby 2.0が推奨されます。Ruby 1.9.3以上が必須です。
  • Rails 3.2.x: Ruby 1.8.7の最終ブランチです。
  • Rails 3以降: Ruby 1.8.7以降が必須です。これより古いRubyのサポートは公式に停止しています。できるだけ早くアップグレードをお願いします。

Ruby 1.8.7 p248およびp249にはRailsをクラッシュさせるマーシャリングバグがあります。Ruby Enterprise Editionでは1.8.7-2010.02以降このバグは修正されています。Ruby 1.9系を使う場合、Ruby 1.9.1はあからさまなセグメンテーション違反が発生するため利用できません。1.9.3をお使いください。

1.4 アップデートタスク

Rails ではapp:updateというコマンドが提供されています (Rails 4.2以前では rails:update という名前でした)。Gemfileに記載されているRailsのバージョンを更新後、このコマンドを実行することで、新しいバージョンでのファイル作成や既存ファイルの変更を対話形式で行うことができます。

$ rails app:update
   identical  config/boot.rb
       exist  config
    conflict  config/routes.rb
Overwrite /myapp/config/routes.rb? (enter "h" for help) [Ynaqdh]
       force  config/routes.rb
    conflict  config/application.rb
Overwrite /myapp/config/application.rb? (enter "h" for help) [Ynaqdh]
       force  config/application.rb
    conflict  config/environment.rb
...

予期しなかった変更が発生した場合は、必ず差分を十分にチェックしてください。

1.5 フレームワークのデフォルトを設定する

新しいバージョンのRailsでは前のバージョンとデフォルトの設定が異なることがあります。しかし上述の手順に従うことで、アプリケーションを引き続き従来バージョンのRailsのデフォルト設定で動かせることもあります(config/application.rbconfig.load_defaultsの値が変更されていないため)。

updateタスクでは、アプリケーションを新しいデフォルト設定に1つずつアップグレードできるように、config/initializers/new_framework_defaults.rbファイルが作成されます。アプリケーションを新しいデフォルト設定で動かせる準備が整ったら、このファイルを削除してconfig.load_defaultsの値を反転できます。

2 Rails 6.0からRails 6.1へのアップグレード

Rails 6.0の変更点について詳しくはリリースノートを参照してください。

2.1 Rails.application.config_forの戻り値をStringキーでアクセスするサポートが終了した

以下のような設定ファイルがあるとします。

# config/example.yml
development:
  options:
    key: value
Rails.application.config_for(:example).options

従来は、Stringキーで値にアクセス可能なハッシュをひとつ返しました。この機能は6.0で非推奨化され、6.1で削除されました。

従来どおりStringキーを用いて値にアクセスしたい場合は、config_forの戻り値でwith_indifferent_accessを呼び出せます。

Rails.application.config_for(:example).with_indifferent_access.dig('options', 'key')

2.2 respond_to#anyを使う場合のレスポンスのContent-Typeヘッダーについて

レスポンスで返されるContent-Typeヘッダーは、Rails 6.0で返されるものと異なる可能性があります。特にアプリケーションでrespond_to { |format| format.any }を使っている場合、Content-Typeヘッダーはリクエストのフォーマットではなく、渡されたブロックを元にするようになりました。

以下の例をご覧ください。

  def my_action
    respond_to do |format|
      format.any { render(json: { foo: 'bar' }) }
    end
  end

  get('my_action.csv')

従来の振る舞いではレスポンスのContent-Typeでtext/csvを返していましたが、実際にはJSONレスポンスをレンダリングしているので正しくありません。現在の振る舞いではレスポンスのContent-Typeでapplication/jsonを正しく返すようになりました。

アプリケーションが従来の正しくない振る舞いに依存している場合は、以下のようにアクションで受け取るフォーマットを明示的に指定するようにしてください。

  format.any(:xml, :json) { render request.format.to_sym => @people }

2.3 ActiveSupport::Callbacks#halted_callback_hookに第2引数を渡せるようになった

Active Supportは、コールバックのチェーンがhalt(停止)したときのhalted_callback_hookをオーバーライドできます。このメソッドに、halt中のコールバック名を第2引数として渡せるようになりました。このメソッドをオーバーライドするクラスがある場合は、引数を2つ受け取れるようにしてください。なおパフォーマンス上の理由のため、この破壊的変更は非推奨化を経ていません。

以下の例をご覧ください。

  class Book < ApplicationRecord
    before_save { throw(:abort) }
    before_create { throw(:abort) }

    def halted_callback_hook(filter, callback_name) # => このメソッドが1個ではなく2個の引数を取れるようになった
      Rails.logger.info("Book couldn't be #{callback_name}d")
    end
  end

2.4 コントローラ内でhelperのクラスメソッドがString#constantizeを使うようになった

概念について説明します。

helper "foo/bar"

Rails 6.1より前は、上のコードから以下の結果が得られました。

require_dependency "foo/bar_helper"
module_name = "foo/bar_helper".camelize
module_name.constantize

Rail 6.1では以下のような結果になります。

prefix = "foo/bar".camelize
"#{prefix}Helper".constantize

この変更は、多くのアプリケーションで後方互換性を維持しており、これに該当する場合は対応不要です。

ただし技術的には、autoloadパス上にない$LOAD_PATH内のディレクトリを指すようコントローラがhelpers_pathを設定することも可能でしたが、今後このようなユースケースはすぐ使える形ではサポートされません。ヘルパーモジュールがオートロード可能でない場合は、helperを呼び出す前にアプリケーションが明示的に読み込んでおく責任があります。

(訳注) 詳しくはRemove `require_dependency` usage in `helper` [Closes #37632] · rails/rails@5b28a0eもどうぞ。helperでの読み込みにrequire_dependencyが使われなくなったことによる変更です。

2.5 a HTTPからHTTPSへのリダイレクトでHTTP 308ステータスコードが使われるようになった

ActionDispatch::SSLでGETやHEAD以外のリクエストをHTTPからHTTPSにリダイレクトする場合のデフォルトHTTPステータスコードが、RFC7538の定義に従って308に変更されました。

2.6 Active Storageでimage_processing gemが必須になった

Active Storageでvariantを処理する場合、従来のようにmini_magickを直接利用するのではなく、image_processing gemのバンドルが必須になりました。image_processingの背後ではデフォルトでmini_magickが使われるので、不要になった明示的なcombine_optionsを必ず削除してください。

できれば、image_processingresizeマクロの直接呼び出しも変更しておくことをおすすめします(変更することでリサイズ後のサムネイルもシャープになります)。

video.preview(resize: "100x100")
video.preview(resize: "100x100>")
video.preview(resize: "100x100^")

たとえば、上のコードはそれぞれ以下のように変更できます。

video.preview(resize_to_fit: [100, 100])
video.preview(resize_to_limit: [100, 100])
video.preview(resize_to_fill: [100, 100])

3 Rails 5.2からRails 6.0へのアップグレード

Rails 6.0の変更点について詳しくはリリースノートを参照してください。

3.1 Webpackerの利用について

WebpackerはRails 6におけるデフォルトのJavaScriptコンパイラですが、アプリケーションを以前のバージョンからアップグレードした場合は自動的には有効になりません。 Webpackerを使いたい場合は、以下をGemfileに追記し、rails webpacker:installコマンドを実行してインストールしてください。

gem "webpacker"
$ bin/rails webpacker:install

3.2 Force SSL

コントローラのforce_sslメソッドは非推奨化され、Rails 6.1で削除される予定です。config.force_sslを有効にしてアプリ全体でHTTPS接続を強制することをおすすめします。特定のエンドポイントのみをリダイレクトしないようにする必要がある場合は、config.ssl_optionsで振る舞いを変更できます。

3.3 セキュリティ向上のためpurposeとexpiryメタデータが署名済みおよび暗号化済みcookieに埋め込まれるようになった

これにより、Railsはcookieの署名済み/暗号化済みの値をコピーして別のcookieで流用することを阻止できるようになります。

新たに埋め込まれるこのpurpose情報によって、Rails 6.0のcookieはそれより前のバージョンのcookieとの互換性が失われます。

cookieを引き続きRails 5.2以前でも読み取れるようにする必要がある場合や、6.0のデプロイを検証中で前のバージョンに戻せるようにしたい場合は、Rails.application.config.action_dispatch.use_cookies_with_metadatafalseを設定してください。

3.4 npmの全パッケージが@railsスコープに移動

これまで「actioncable」「activestorage」「rails-ujs」パッケージのいずれかをnpmまたはyarn経由で読み込んでいた場合は、これらを6.0.0にアップグレードする前にそれらの依存関係の名前を以下のように更新しなければなりません。

actioncable   → @rails/actioncable
activestorage → @rails/activestorage
rails-ujs     → @rails/ujs

3.5 Action Cable JavaScript APIの変更

Action Cable JavaScriptパッケージがCoffeeScriptからES2015に置き換えられ、ソースコードをnpmディストリビューションでパブリッシュできるようになりました。

今回のリリースでは、Action Cable JavaScript APIの選択可能な部分に若干のbreaking changesが生じます。

  • WebSocketアダプタやロガーアダプタの設定が、ActionCableのプロパティからActionCable.adaptersのプロパティに移動しました。これらのアダプタを設定している場合は、以下の変更が必要です。
  -    ActionCable.WebSocket = MyWebSocket
  +    ActionCable.adapters.WebSocket = MyWebSocket
  -    ActionCable.logger = myLogger
  +    ActionCable.adapters.logger = myLogger
  • ActionCable.startDebugging()メソッドとActionCable.stopDebugging()メソッドが削除され、ActionCable.logger.enabledに置き換えられました。これらのメソッドを使っている場合は、以下の変更が必要です。
  -    ActionCable.startDebugging()
  +    ActionCable.logger.enabled = true
  -    ActionCable.stopDebugging()
  +    ActionCable.logger.enabled = false

3.6 ActionDispatch::Response#content_typeがContent-Typeヘッダーを変更せずに返すようになった

従来は、ActionDispatch::Response#content_typeの戻り値にcharsetパートが含まれていませんでした。 この振る舞いは変更され、従来省略されていたcharsetパートも含まれるようになりました。

MIMEタイプだけが欲しい場合は、代わりにActionDispatch::Response#media_typeをお使いください。

変更前:

resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
resp.content_type #=> "text/csv; header=present"

変更後:

resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
resp.content_type #=> "text/csv; header=present; charset=utf-16"
resp.media_type   #=> "text/csv"

3.7 オートローディング

Rails 6のデフォルト設定では、CRubyでzeitwerkのオートローディングモードが有効になります。

# config/application.rb

config.load_defaults "6.0"

オートローディングモードでは、オートロード、再読み込み、eager loadingをZeitwerkで管理します。

3.7.1 Public APIについて

一般に、アプリケーションでZeitwerk APIの利用が直接必要になることはありません。Railsは、config.autoload_pathsconfig.cache_classesといった既存の約束事に沿ってセットアップを行います。

アプリケーションはこのインターフェイスを遵守すべきですが、実際のZeitwerkローダーオブジェクトに以下のようにアクセスできます。

Rails.autoloaders.main

上は、たとえばSTI(単一テーブル継承)をプリロードする必要がある場合や、カスタムのinflectorを設定する必要が生じた場合には役立つことがあるでしょう。

3.7.2 プロジェクトの構成

アップグレードしたアプリケーションのオートロードが正しく動いていれば、プロジェクトの構成はほとんど互換が取れているはずです。

ただしclassicモードは、見つからない定数名からファイル名を推測しますが(underscore)、zeitwerkモードはファイル名から定数名を推測します(camelize)。特に略語がからむ場合、これらのヘルパーの動作が互いにきれいに逆になるとは限りません。たとえば、"FOO".underscore"foo"になりますが、 "foo".camelize"FOO"ではなく"Foo"になります。

互換性については、以下のようにzeitwerk:checkタスクでチェックできます。

$ bin/rails zeitwerk:check
Hold on, I am eager loading the application.
All is good!

3.7.3 require_dependencyについて

require_dependencyの既知のユースケースはすべて排除されました。自分のプロジェクトをgrepしてrequire_dependencyを削除してください。

アプリケーションでSTI(単一テーブル継承)が使われている場合は、定数の自動読み込みと再読み込み(Zeitwerkモード)ガイドの該当セクションをご覧ください。

3.7.4 クラス定義やモジュール定義の完全修飾名

クラス定義やモジュール定義で、定数パスを安定して使えるようになりました。

# このクラスの本文のオートロードがRubyのセマンティクスと一致するようになった
class Admin::UsersController < ApplicationController
  # ...
end

ここで知っておいていただきたいのは、classicモードのオートローダーでは、実行順序によっては以下のコードのFoo::Wadusをオートロードできてしまう場合があるということです。

class Foo::Bar
  Wadus
end

上のFooはネストしていないのでRubyのセマンティクスと一致せず、zeitwerkではまったく動かなくなります。こうしたエッジケースが見つかったら、以下のように完全修飾名のFoo::Wadusを使えます。

class Foo::Bar
  Foo::Wadus
end

または、以下のようにFooでネストすることもできます

module Foo
  class Bar
    Wadus
  end
end
3.7.5 concernsについて

以下のような標準的な構造は、オートロードもeager loadも可能です。

app/models
app/models/concerns

上は、(オートロードパスに属するので)app/models/concernsがルートディレクトリであると仮定され、名前空間としては無視されます。したがって、app/models/concerns/foo.rbConcerns::FooではなくFooと定義すべきです。

Concerns::名前空間は、classicモードのオートローダーでは実装の副作用によって動作していましたが、これは意図した動作ではありませんでした。Concerns::を使っているアプリケーションがzeitwerkモードで動くようにするには、こうしたクラスやモジュールをリネームする必要があります。

3.7.6 オートロードパス内にappがある場合

プロジェクトによっては、API::Baseを定義するためにapp/api/base.rbのようなものが欲しい場合があります。classicモードではこれを行うためにオートロードパスにappを追加します。Railsはappの全サブディレクトリをオートロードに自動的に追加するので、ネストしたルートディレクトリがある状況がもうひとつ存在することになり、セットアップが機能しなくなります。上述したのと似た原則がconcernsにも当てはまります。

そうした構造を維持したい場合は、イニシャライザで以下のようにそのサブディレクトリをオートロードパスから削除する必要が生じます。

ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")
3.7.7 定数のオートロードと明示的な名前空間

あるファイルの中で名前空間が1つ定義されているとします(ここではHotel)。

app/models/hotel.rb         # Defines Hotel.
app/models/hotel/pricing.rb # Defines Hotel::Pricing.

このHotelという定数の定義には、必ずclassキーワードまたはmoduleキーワードを使わなければなりません。次の例をご覧ください。

class Hotel
end

上は問題ありません。

しかし以下はどちらも動きません。

Hotel = Class.new
Hotel = Struct.new

どちらも、Hotel::Pricingなどの子オブジェクトを探索できなくなります。

この制約は、明示的な名前空間にのみ適用されます。名前空間を定義しないクラスやモジュールであれば、そうしたイディオムで定義することもできます。

3.7.8 「1つのファイルには1つの定数だけ」(同じトップレベルで)

classicモードでは、同じトップレベルに複数の定数を定義して、それらをすべて再読み込みすることが技術的には可能でした。以下の例をご覧ください。

# app/models/foo.rb

class Foo
end

class Bar
end

上でFooをオートロードすると、Barをオートロードできなかった場合にもBarをオートロード済みとマーキングすることがありました。このようなコードはzeitwerkでは対象外です。Barはそれ専用のbar.rbというファイルに移すべきです。「1つのファイルには1つの定数だけ」となります。

この影響を受けるのは、上の例のように「同じトップレベルにある」複数の定数だけです。ネストの内側にあるクラスやモジュールは影響を受けません。以下の例をご覧ください。

# app/models/foo.rb

class Foo
  class InnerClass
  end
end

アプリケーションでFooを再読み込みすれば、Foo::InnerClassも再読み込みされます。

3.7.9 spring gemとtest環境について

spring gemは、アプリケーションのコードが変更されると再読み込みします。test環境では、そのために再読み込みを有効にしておく必要があります。

# config/environments/test.rb

config.cache_classes = false

有効にしておかないと、以下のエラーが表示されます。

reloading is disabled because config.cache_classes is true
3.7.10 Bootsnapについて

Bootsnapのバージョンは1.4.2以上にするべきです。

また、Ruby 2.5を実行中は、インタプリタのバグの関係で、iseqキャッシュを無効にする必要があります。その場合はBootsnap 1.4.4以上に依存させるようにしてください。

3.7.11 config.add_autoload_paths_to_load_path

以下の新しい設定は、後方互換性のためデフォルトでtrueになっていますが、これを使って$LOAD_PATHに追加されるオートロードパスを減らせます。

config.add_autoload_paths_to_load_path

これは、ほとんどのアプリケーションにとって合理的です(app/models内のファイルをrequireするような行為は決してすべきではないので)。しかも、Zeitwerkは内部で絶対パスだけを使います。

この新しい設定を無効にすれば、$LOAD_PATHの探索を最適化して(つまりチェックするディレクトリを減らして)、Bootsnapの動作を軽くしてメモリ消費量を削減できます。Bootsnapがそうしたディレクトリのインデックスをビルドする必要がなくなるからです。

3.7.12 スレッド安全性について

classicモードの定数オートロードはスレッド安全ではありません。Railsには、オートロードが有効な状態でWebのリクエストをスレッド安全にする(これはdevelopment環境でよくあることです)などのためのインプレースのロックがあるにもかかわらずです。

zeitwerkモードの定数オートロードは、スレッド安全です。たとえば、runnerコマンドで実行されるマルチスレッドでもオートロードが可能です。

3.7.13 config.autoload_pathsの汚れに注意

以下のような設定は要注意です。

config.autoload_paths += Dir["#{config.root}/lib/**/"]

config.autoload_pathsのあらゆる要素は、トップレベルの名前空間(Object)を表すべきなので、ネストできなくなります(前述のconcernsディレクトリは例外)。

この修正は、ワイルドカードを削除するだけでできます。

config.autoload_paths << "#{config.root}/lib"
3.7.14 eager loadingとオートロードが一貫するようになる

classicの場合、たとえばapp/models/foo.rbBarを定義すると、そのファイルをオートロードできなくなりますが、eager loading(一括読み込み)は盲目的にファイルを再帰読み込みするため、可能です。この挙動のため、テストでeager loadingを最初に行うとその後の実行でオートロードが失敗し、エラーの原因となる可能性があります。

zeitwerkモードの場合、どちらの読み込みモードも一貫するので、失敗やエラーは同一のファイルで発生するようになります。

3.7.15 Rails 6でclassicモードのオートローダーを使う方法

アプリケーションはRails 6のデフォルトを読み込みますが、以下のようにconfig.autoloaderを設定することでclassicモードのオートローダを使うこともできます。

# config/application.rb

config.load_defaults 6.0
config.autoloader = :classic

Rails 6アプリケーションでclassicオートローダーを使う場合は、スレッド安全性上の懸念があるため、development環境ではWebサーバーやバックグラウンド処理のconcurrency levelを1に設定することをおすすめします。

3.8 Active Storageの代入の振る舞いの変更

Rails 5.2のデフォルト設定に関して、has_many_attachedで宣言された添付ファイル(attachment)のコレクションへの代入は、新しいファイルの追加(append)操作になります。

class User < ApplicationRecord
  has_many_attached :highlights
end

user.highlights.attach(filename: "funky.jpg", ...)
user.highlights.count # => 1

blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
user.update!(highlights: [ blob ])

user.highlights.count # => 2
user.highlights.first.filename # => "funky.jpg"
user.highlights.second.filename # => "town.jpg"

Rails 6.0のデフォルト設定では、添付ファイルのコレクションへの代入は、追加ではなく既存ファイルの置き換え操作になります。これにより、Active Recordでコレクションの関連付けに代入するときの振る舞いと一貫するようになります。

user.highlights.attach(filename: "funky.jpg", ...)
user.highlights.count # => 1
blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
user.update!(highlights: [ blob ])
user.highlights.count # => 1
user.highlights.first.filename # => "town.jpg"

既存のものを削除せずに添付ファイルを新たに追加するには、#attachが利用できます。

blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
user.highlights.attach(blob)
user.highlights.count # => 2
user.highlights.first.filename # => "funky.jpg"
user.highlights.second.filename # => "town.jpg"

この新しい振る舞いは、設定でconfig.active_storage.replace_on_assign_to_manytrueにすることで選択できます。従来の振る舞いはRails 6.1で非推奨化され、その後のリリースで削除される予定です。

4 Rails 5.1からRails 5.2へのアップグレード

Rails 5.2 の変更点について詳しくはリリースノートを参照してください。

4.1 Bootsnap

Rails 5.2 では新規作成したアプリケーションのGemfileに bootsnap gem が追加されました。boot.rbapp:updateコマンドを実行するとセットアップが行われます。使いたい場合は、Gemfileにbootsnap gemを追加してください。boot.rbを変更し、bootsnapを使わないようにすることもできます。

4.2 暗号化または署名付きcookieに有効期限情報が付与されました

セキュリティ向上のため、Railsでは暗号化または署名付きcookieに有効期限情報を埋め込むようになりました。

有効期限情報が付与されたcookieは、Rails 5.1 以前のバージョンとの互換性はありません。

Rails 5.1 以前で新しいcookieを読み込みたい場合、もしくは Rails 5.2 でうまくデプロイできるか確認したい場合は (必要に応じてロールバックできるようにしたい場合は) Rails.application.configaction_dispatch.use_authenticated_cookie_encryptionfalse に設定してください。

5 Rails 5.0からRails 5.1へのアップグレード

Rails 5.1 の変更点について詳しくはリリースノートを参照してください。

5.1 トップレベルのHashWithIndifferentAccessが弱く非推奨化された

アプリケーションでトップレベルのHashWithIndifferentAccessクラスを使っている場合、すぐでなくてもよいのでActiveSupport::HashWithIndifferentAccessに置き換えてください。

これは「弱い非推奨化」であり、しばらくは正常に動作し、非推奨警告も表示されません。ただし、この定数は将来削除されます。

また、こうしたオブジェクトのダンプを含むかなり古いYAMLドキュメントがある場合は、YAMLを再度読み込み/ダンプして、正しい定数が参照されるようにしておく必要があるかもしれません。また、読み込みについては今後も利用できます。

5.2 application.secretsですべてのキーをシンボルとして読み込むようになった

config/secrets.ymlに保存されているアプリケーションの設定がネストしている場合、すべてのキーがシンボルとして読み込まれます。このため、文字列による設定へのアクセス方法を以下のように変更する必要があります。

変更前:

Rails.application.secrets[:smtp_settings]["address"]

変更後:

Rails.application.secrets[:smtp_settings][:address]

5.3 非推奨化されたrender :textrender :nothingサポートが削除された

ビューのrender :textは今後利用できません。MIME typeを「text/plain」にしてテキストをレンダリングする新しい方法はrender :plainを使うことです。

render :nothingも同様に削除されますので、今後ヘッダーのみのレスポンスを返すにはheadメソッドをお使いください。 例: head :okは、bodyをレンダリングせずにresponse 200を返します。

6 Rails 4.2からRails 5.0へのアップグレード

Rails 5.0 の変更点について詳しくはリリースノートを参照してください。

6.1 Ruby 2.2.2以上が必須

Ruby on Rails 5.0以降は、バージョン2.2.2以降の Ruby だけをサポートします。 Rubyのバージョンが2.2.2以降であることを確認してから手順を進めてください。

6.2 Active Record モデルは今後デフォルトで ApplicationRecord を継承する

Rails 4.2のActive RecordモデルはActiveRecord::Baseを継承していました。Rails 5.0では、すべてのモデルがApplicationRecordを継承するようになりました。

アプリケーションのコントローラーがActionController::Baseに代わってApplicationControllerを継承するように、アプリケーションのすべてのモデルがApplicationRecordをスーパークラスとして使うようになりました。この変更により、アプリケーション全体のモデルの動作を1か所で変更できるようになりました。

Rails 4.2をRails 5.0にアップグレードする場合、app/models/ディレクトリにapplication_record.rbファイルを追加し、このファイルに以下の設定を追加する必要があります。

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

最後に、すべてのモデルがApplicationRecordを継承するように変更し、動作を確認してください。

6.3 throw(:abort)でコールバックチェーンを停止する

Rails 4.2では、Active RecordやActive Modelで「before」系コールバックがfalseを返すと、すべてのコールバックチェーンが停止する仕様でした。この場合、以後「before」系コールバックは実行されず、コールバック内にラップされているアクションも実行されません。

Rails 5.0ではこの副作用が修正され、Active RecordやActive Modelのコールバックでfalseが返ってもコールバックチェーンが停止しなくなりました。その代わり、今後コールバックチェーンはthrow(:abort)で明示的に停止する必要があります。

Rails 4.2をRails5.0にアップグレードした場合、こうしたコールバックでfalseが返ったときに従来同様コールバックチェーンは停止しますが、この変更にともなう非推奨警告が表示されます。

この変更内容とその影響を十分理解しているのであれば、config/application.rbに以下の記述を追加して非推奨警告をオフにできます。

ActiveSupport.halt_callback_chains_on_return_false = false

Active Supportのコールバックはこのオプションの影響を受けないことにご注意ください。Active Supportのチェーンはどのような値が返っても停止しません。

詳しくは#17227を参照してください。

6.4 ActiveJob は今後デフォルトで ApplicationJob を継承する

Rails 4.2のActive JobはActiveJob::Baseを継承しますが、Rails 5.0ではデフォルトでApplicationJobを継承するよう変更されました。

Rails 4.2をRails 5.0にアップグレードする場合、app/jobs/ディレクトリにapplication_job.rbファイルを追加し、このファイルに以下の設定を追加する必要があります。

class ApplicationJob < ActiveJob::Base
end

これにより、すべてのjobクラスがActiveJob::Baseを継承するようになります。

詳しくは#19034を参照してください。

6.5 Rails コントローラのテスト

6.5.1 ヘルパーメソッドの一部をrails-controller-testingに移転

assignsメソッドとassert_templateメソッドはrails-controller-testing gemに移転しました。これらのメソッドを引き続きコントローラのテストで使いたい場合は、Gemfilegem 'rails-controller-testing'を追加してください。

テストでRSpecを使っている場合は、このgemのドキュメントで必須となっている追加の設定方法もご確認ください。

6.5.2 ファイルアップロード時の新しい振る舞い

ファイルアップロードのテストでActionDispatch::Http::UploadedFileクラスを使っている場合、Rack::Test::UploadedFileクラスに変更する必要があります。

詳しくは#26404を参照してください。

6.6 production環境での起動後は自動読み込みが無効になる

今後Railsがproduction環境で起動されると、自動読み込みがデフォルトで無効になります。

アプリケーションの一括読み込み(eager loading)は起動プロセスに含まれています。このため、トップレベルの定数についてはファイルをrequireしなくても問題なく利用でき、従来と同様に自動読み込みされます。

トップレベルより下で、実行時にのみ有効にする定数(通常のメソッド本体など)を定義した場合も、起動時に一括読み込みされるので問題なく利用できます。

ほとんどのアプリケーションでは、この変更に関して特別な対応は不要です。めったにないと思われますが、production環境のアプリケーションで自動読み込みが必要な場合は、Rails.application.config.enable_dependency_loadingをtrueに設定してください。

6.7 XMLシリアライズのgem化

RailsのActiveModel::Serializers::Xmlactivemodel-serializers-xml gemに移転しました。アプリケーションで今後もXMLシリアライズを使うには、Gemfilegem 'activemodel-serializers-xml'を追加してください。

6.8 古いmysqlデータベースアダプタのサポートを終了

Rails 5で古いmysqlデータベース アダプタのサポートが終了しました。原則としてmysql2をお使いください。今後古いアダプタのメンテナンス担当者が決まった場合、アダプタは別のgemに切り出されます。

6.9 デバッガのサポートを終了

Rails 5が必要とするRuby 2.2では、debuggerはサポートされていません。代わりに、今後はbyebugをお使いください。

6.10 タスクやテストの実行にはbin/railsを使うこと

Rails 5では、rakeに代わってbin/railsでタスクやテストを実行できるようになりました。原則として、多くのタスクやテストはrakeでも引き続き実行できますが、一部のタスクやテストは完全にbin/railsに移行しました。

今後テストの実行にはbin/rails testをお使いください。

rake dev:cachebin/rails dev:cacheに変更されました。

アプリケーションのルートディレクトリの下でbin/railsを実行すると、利用可能なコマンドリストを表示できます。

6.11 ActionController::Parametersは今後HashWithIndifferentAccessを継承しない

アプリケーションでparamsを呼び出すと、今後はハッシュではなくオブジェクトが返ります。現在使っているパラメーターがRailsで既に利用できている場合、変更は不要です。permitted?の状態にかかわらずハッシュを読み取れることが前提のメソッド(sliceメソッドなど)にコードが依存している場合、まずアプリケーションをアップグレードしてpermitを指定し、それからハッシュに変換する必要があります。

params.permit([:proceed_to, :return_to]).to_h

6.12 protect_from_forgeryは今後デフォルトでprepend: falseに設定される

protect_from_forgeryは今後デフォルトでprepend: falseに設定されます。これにより、protect_from_forgeryはアプリケーションで呼び出される時点でコールバックチェーンに挿入されます。protect_from_forgeryを常に最初に実行したい場合は、アプリケーションの設定でprotect_from_forgery prepend: trueを指定する必要があります。

6.13 デフォルトのテンプレート ハンドラは今後RAWになる

拡張子がテンプレートハンドラになっていないファイルは、今後rawハンドラで出力されるようになります。従来のRailsでは、このような場合にはERBテンプレートハンドラで出力されました。

ファイルをrawハンドラで出力したくない場合は、ファイルに明示的に拡張子を与え、適切なテンプレート ハンドラで処理されるようにしてください。

6.14 テンプレート依存関係の指定でワイルドカードマッチングが追加された

テンプレート依存関係をワイルドカードマッチングで指定できるようになりました。以下のテンプレートを例に説明します。

<% # Template Dependency: recordings/threads/events/subscribers_changed %>
<% # Template Dependency: recordings/threads/events/completed %>
<% # Template Dependency: recordings/threads/events/uncompleted %>

上のようなテンプレートは、以下のようにワイルドカードを使えば1行で設定できます。

<% # Template Dependency: recordings/threads/events/* %>

6.15 ActionView::Helpers::RecordTagHelperは外部のgemに移動(record_tag_helper

content_tag_fordiv_forが削除され、content_tagのみの利用が推奨されます。これらの古いメソッドを使い続けたい場合、record_tag_helper gemをGemfileに追加してください。

gem 'record_tag_helper', '~> 1.0'

詳しくは#18411を参照してください。

6.16 protected_attributes gemのサポートを終了

protected_attributes gemのサポートはRails 5で終了しました。

6.17 activerecord-deprecated_finders gemのサポートを終了

activerecord-deprecated_finders gemのサポートはRails 5で終了しました。

6.18 ActiveSupport::TestCaseでのテストは今後デフォルトでランダムに実行される

アプリケーションのテストのデフォルトの実行順序は、従来の:sortedから:randomに変更されました。:sortedに戻すには以下のオプションを指定します。

# config/environments/test.rb
Rails.application.configure do
  config.active_support.test_order = :sorted
end

6.19 ActionController::LiveConcernに変更された

コントローラにincludeされている別のモジュールにActionController::Liveがincludeされている場合、ActiveSupport::Concernをextendするコードの追加も必要です。または、StreamingSupportがincludeされてから、self.includedフックを使ってActionController::Liveをコントローラに直接includeすることもできます。

理由: アプリケーションで独自のストリーミングモジュールを使っている場合、以下のコードはproduction環境で正常に動作しなくなる可能性があります。

# Warden/Devise で認証するストリーミングコントローラでの回避方法を示すコード
# https://github.com/plataformatec/devise/issues/2332 を参照
# 上のissueではルーター内での認証で解決する方法もアドバイスされている
class StreamingSupport
  include ActionController::Live # Rails 5 の production モードではこの行は動作しない
  # extend ActiveSupport::Concern # この行をコメント解除することで上の行が動作するようになる

  def process(name)
    super(name)
  rescue ArgumentError => e
    if e.message == 'uncaught throw :warden'
      throw :warden
    else
      raise e
    end
  end
end

6.20 フレームワークの新しいデフォルト設定

6.20.1 Active Recordのbelongs_toはデフォルトオプションで必須

関連付けが存在しない場合、belongs_toでバリデーションエラーが発生するようになりました。

なお、この機能は関連付けごとにoptional: trueを指定してオフにできます。

新しいアプリケーションでは、このデフォルト設定が自動で有効になります。この設定を既存のアプリケーションに追加するには、イニシャライザでこの機能をオンにする必要があります

config.active_record.belongs_to_required_by_default = true

これはデフォルトですべてのモデルに対してグローバルに設定されますが、モデルごとに設定をオーバーライドすることもできます。これは、モデルを移行してデフォルトで必要な関連付けをすべてのモデルに持たせるのに便利です。

class Book < ApplicationRecord
  # モデルがデフォルトで必要な関連付けを持つ準備ができていない場合の設定

  self.belongs_to_required_by_default = false
  belongs_to(:author)
end

class Car < ApplicationRecord
  # モデルがデフォルトで必要な関連付けを持つ準備ができた後の設定

  self.belongs_to_required_by_default = true
  belongs_to(:pilot)
end
6.20.2 フォームごとのCSRFトークン

Rails 5 では、JavaScriptで作成されたフォームによるコードインジェクション攻撃に対応するため、フォーム単位でのCSRFトークンをサポートします。このオプションがオンの場合、アクションやメソッド固有のCSRFトークンがアプリケーションのフォームごとに個別に生成されるようになります。

config.action_controller.per_form_csrf_tokens = true
6.20.3 OriginチェックによるCSRF対策

アプリケーションで、CSRF防御の一環としてHTTP Originヘッダによるサイトの出自チェックを設定できるようになりました。以下の設定をtrueにすることで有効になります。

config.action_controller.forgery_protection_origin_check = true
6.20.4 Action Mailerのキュー名がカスタマイズ可能に

デフォルトのメイラー キュー名はmailersです。新しい設定オプションを使うと、キュー名をグローバルに変更できます。以下の方法で設定します。

config.action_mailer.deliver_later_queue_name = :new_queue_name
6.20.5 Action Mailerのビューでフラグメントキャッシュをサポート

設定ファイルのconfig.action_mailer.perform_cachingで、Action Mailerのビューでキャッシュをサポートするかどうかを指定できます。

config.action_mailer.perform_caching = true
6.20.6 db:structure:dumpの出力形式のカスタマイズ

schema_search_pathや、その他のPostgreSQLエクステンションを使っている場合、スキーマのダンプ方法を指定できます。以下のように:allを指定するとすべてのダンプが生成され、:schema_search_pathを指定するとスキーマ検索パスからダンプが生成されます。

config.active_record.dump_schemas = :all
6.20.7 サブドメインでのHSTSを有効にするSSLオプション

サブドメインで HSTS(HTTP Strict Transport Security)を有効にするには、以下の設定を使います。

config.ssl_options = { hsts: { subdomains: true } }
6.20.8 レシーバのタイムゾーンを保存する

Ruby 2.4を利用している場合、to_timeの呼び出しでレシーバのタイムゾーンを保存できます。

ActiveSupport.to_time_preserves_timezone = false

6.21 JSON/JSONBのシリアライズに関する変更点

Rails 5.0では、JSON属性やJSONB属性がシリアライズ/デシリアライズされる方法が変更されました。これにより、たとえばActive RecordでStringに等しいカラムを設定しても、その文字列をHashに変換せず、その文字列のみを返すようになります。この変更はモデル同士がやりとりするコードに限定されず、db/schema.rbで設定される:defaultカラムにも影響します。Stringに等しいカラムを設定せず、Hashを渡すようにしてください。これにより、JSON文字列への変換や逆変換が自動で行われるようになります。

7 Rails 4.1からRails 4.2へのアップグレード

7.1 Web Console gem

最初に、Gemfiledevelopmentグループにgem 'web-console', '~> 2.0'を追加し、bundle installを実行してください (このgemはRailsを過去のバージョンからアップグレードした場合には含まれないので、手動で追加する必要があります)。gemのインストール完了後、<%= console %>などのコンソールヘルパーへの参照をビューに追加するだけで、どのビューでもコンソールを利用できるようになります。このコンソールは、development環境のビューで表示されるすべてのエラーページにも表示されます。

7.2 Responders gem

respond_withおよびクラスレベルのrespond_toメソッドは、responders gemに移転しました。これらのメソッドを使いたい場合は、Gemfilegem 'responders', '~> 2.0'と記述するだけで利用できます。今後、respond_with呼び出し、およびクラスレベルのrespond_to呼び出しは、responders gemなしでは動作しません。

# app/controllers/users_controller.rb

class UsersController < ApplicationController
  respond_to :html, :json

  def show
    @user = User.find(params[:id])
    respond_with @user
  end
end

インスタンスレベルのrespond_toは今回のアップグレードの影響を受けませんので、gemを追加する必要はありません。

# app/controllers/users_controller.rb

class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    respond_to do |format|
      format.html
      format.json { render json: @user }
    end
  end
end

詳しくは#16526を参照してください。

7.3 トランザクションコールバックのエラー処理

現在のActive Recordでは、after_rollbackafter_commitコールバックでの例外を抑制しており、例外時にはログ出力のみが行われます。次のバージョンからは、これらのエラーは抑制されなくなりますのでご注意ください。今後は他のActive Recordコールバックと同様のエラー処理を行います。

after_rollbackコールバックやafter_commitコールバックを定義すると、この変更にともなう非推奨警告が表示されるようになりました。この変更内容を十分理解し、受け入れる準備ができているのであれば、config/application.rbに以下の記述を行なうことで非推奨警告が表示されないようにすることができます。

config.active_record.raise_in_transactional_callbacks = true

詳しくは、#14488および#16537を参照してください。

7.4 テストケースの実行順序

Rails 5.0のテストケースは、デフォルトでランダムに実行されるようになる予定です。この変更に備えて、テスト実行順を明示的に指定するactive_support.test_orderという新しい設定オプションがRails 4.2に導入されました。このオプションを使うと、たとえばテスト実行順を現行の仕様のままにしておきたい場合は:sortedを指定したり、ランダム実行を今のうちに導入したい場合は:randomを指定したりすることができます。

このオプションに値が指定されていないと、非推奨警告が表示されます。非推奨警告が表示されないようにするには、test環境に以下の記述を追加します。

# config/environments/test.rb
Rails.application.configure do
  config.active_support.test_order = :sorted # `:random`にしてもよい
end

7.5 シリアル化属性

serialize :metadata, JSONなどのカスタムコーダーを使っている場合に、シリアル化属性 (serialized attribute) にnilを割り当てると、コーダー内でnil値を渡すのではなく、データベースにNULLとして保存されるようになりました (JSONコーダーを使っている場合の"null"など)。

7.6 Productionログのレベル

Rails 5のproduction環境では、デフォルトのログレベルが:infoから:debugに変更される予定です。現在のログレベルを変更したくない場合はproduction.rbに以下の行を追加してください。

# `:info`を指定すると現在のデフォルト設定が使われ、
# `:debug`を指定すると今後のデフォルト設定が使われる
config.log_level = :info

7.7 Railsテンプレートのafter_bundle

Railsテンプレートを利用し、かつすべてのファイルを (Gitなどで) バージョン管理している場合、生成されたbinstubをバージョン管理システムに追加できません。これは、binstubの生成がBundlerの実行前に行われるためです。

# template.rb
generate(:scaffold, "person name:string")
route "root to: 'people#index'"
$ rake db:migrate

git :init
git add: "."
git commit: %Q{ -m 'Initial commit' }

この問題を回避するために、git呼び出しをafter_bundleブロック内に置くことができるようになりました。こうすることで、binstubの生成が終わってからBundlerが実行されます。

# template.rb
generate(:scaffold, "person name:string")
route "root to: 'people#index'"
rake("db:migrate")

after_bundle do
  git :init
  git add: "."
  git commit: %Q{ -m 'Initial commit' }
end

7.8 RailsのHTMLサニタイザ

アプリケーションでHTMLの断片をサニタイズする方法に新しい選択肢が1つ増えました。従来の伝統的なHTMLスキャンによるサニタイズは公式に非推奨化されました。現在推奨される方法はRails HTMLサニタイザです。

これにより、sanitizesanitize_cssstrip_tags、およびstrip_linksメソッドは新しい実装に基いて動作するようになります。

新しいサニタイザは、内部でLoofahを使っています。そしてLoofahはNokogiriを使っています。Nokogiriで使われているXMLパーサーはCとJavaの両方で記述されているので、利用するRubyのバージョンにかかわらずサニタイズが高速化されるようになりました。

新しいRailsではsanitizeメソッドが更新され、Loofah::Scrubberで強力なスクラブを行なうことができます。スクラブの利用例はここを参照

PermitScrubberおよびTargetScrubberという2つのスクラバーが新たに追加されました。詳しくは、gemのReadmeを参照してください。

PermitScrubberおよびTargetScrubberのドキュメントには、どの要素をどのタイミングで除去すべきかを完全に制御する方法が記載されています。

従来のままのサニタイザの実装が必要な場合は、アプリケーションのGemfilerails-deprecated_sanitizerを追加してください。

gem 'rails-deprecated_sanitizer'

7.9 RailsのDOMのテスト

assert_tagなどを含むTagAssertionsモジュール非推奨になりました。今後推奨されるのは、ActionViewからrails-dom-testing gemに移行したSelectorAssertionsモジュールのassert_selectメソッドです。

7.10 マスク済み真正性トークン

SSL攻撃を緩和するために、form_authenticity_tokenがマスクされるようになりました。これにより、このトークンはリクエストごとに変更されます。トークンの検証はマスク解除 (unmasking)とそれに続く復号化 (decrypting) によって行われます。この変更が行われたことにより、railsアプリケーション以外のフォームから送信される、静的なセッションCSRFトークンに依存するリクエストを検証する際には、このマスク済み真正性トークンのことを常に考慮する必要がありますのでご注意ください。

7.11 Action Mailer

従来は、メイラークラスでメイラーメソッドを呼び出すと、該当するインスタンスメソッドが直接実行されました。Active Jobと#deliver_laterメソッドの導入に伴い、この動作が変更されました。Rails 4.2では、これらのインスタンスメソッド呼び出しはdeliver_nowdeliver_laterが呼び出されるまで実行延期されます。以下に例を示します。

class Notifier < ActionMailer::Base
  def notify(user, ...)
    puts "Called"
    mail(to: user.email, ...)
  end
end

mail = Notifier.notify(user, ...) # Notifier#notifyはこの時点では呼び出されない
mail = mail.deliver_now           # "Called"を出力する

この変更によって実行結果が大きく異なるアプリケーションはそれほどないと思われます。ただし、メイラー以外のメソッドを同期的に実行したい場合、かつ従来の同期的プロキシ動作に依存している場合は、これらのメソッドをメイラークラスにクラスメソッドとして直接定義する必要があります。

class Notifier < ActionMailer::Base
  def self.broadcast_notifications(users, ...)
    users.each { |user| Notifier.notify(user, ...) }
  end
end

7.12 外部キーのサポート

マイグレーションDSLが拡張され、外部キー定義をサポートするようになりました。Foreigner gemを使っていた場合は、この機会に削除するとよいでしょう。Railsの外部キーサポートは、Foreignerの全機能ではなく、一部のみである点にご注意ください。このため、Foreignerの定義を必ずしもRailsのマイグレーションDSLに置き換えられないことがあります。

移行手順は次のとおりです。

  1. Gemfilegem "foreigner"を削除します。
  2. bundle installを実行します。
  3. bin/rake db:schema:dumpを実行します。
  4. 外部キー定義と必要なオプションがdb/schema.rbにすべて含まれていることを確認します。

8 Rails 4.0からRails 4.1へのアップグレード

8.1 リモート <script> タグにCSRF保護を実施

これを行わないと、「なぜかテストがとおらない...orz」「<script>ウィジェットがおかしい!」などという結果になりかねません。

JavaScriptレスポンスを伴うGETリクエストもクロスサイトリクエストフォージェリ (CSRF) 保護の対象となりました。これは、サイトの<script>タグのJavaScriptが第三者のサイトから参照されて重要なデータが奪取されないよう保護するためのものです。

つまり、以下を使う機能テストと結合テストは

get :index, format: :js

CSRF保護をトリガーするようになります。以下のように書き換え、

xhr :get, :index, format: :js

XmlHttpRequestを明示的にテストしてください。

自サイトの<script>はクロス参照の出発点として扱われるため、同様にブロックされます。JavaScriptを実際に<script>タグから読み込む場合は、そのアクションでCSRF保護を明示的にスキップしてください。

8.2 Spring gem

アプリケーションのプリローダーとしてSpringを使う場合は、以下を行う必要があります。

  1. gem 'spring', group: :developmentGemfileに追加する
  2. bundle installを実行してSpringをインストールする
  3. bundle exec spring binstubを実行してbinstubをSpring化する

ユーザーが定義したRakeタスクはデフォルトでdevelopment環境で動作するようになります。これらのRakeタスクを他の環境でも実行したい場合はSpring READMEを参考にしてください。

8.3 config/secrets.yml

新しいsecrets.ymlに秘密鍵を保存したい場合は以下の手順を実行します。

  1. secrets.ymlファイルをconfigフォルダ内に作成し、以下の内容を追加します。

    development:
      secret_key_base:
    
    test:
      secret_key_base:
    
    production:
      secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
    
  2. secret_token.rbイニシャライザに記載されている既存の secret_key_baseの秘密キーを取り出してSECRET_KEY_BASE環境変数に設定し、Railsアプリケーションをproductionで実行するすべてのユーザーが秘密キーの恩恵を受けられるようにします。あるいは、既存のsecret_key_basesecret_token.rbイニシャライザからsecrets.ymlのproductionセクションにコピーし、'<%= ENV["SECRET_KEY_BASE"] %>'を置き換えることもできます。

  3. secret_token.rbイニシャライザを削除します

  4. rake secretを実行し、developmentセクションtestセクションに新しい鍵を生成します。

  5. サーバーを再起動します。

8.4 テストヘルパーの変更

テストヘルパーに含まれているActiveRecord::Migration.check_pending!呼び出しは削除できます。このチェックはrequire "rails/test_help"の際に自動的に行われるようになりました。この呼び出しを削除しなくても悪影響が生じることはありません。

8.5 Cookiesシリアライザ

Rails 4.1より前に作成されたアプリケーションでは、Marshalを使ってcookie値を署名済みまたは暗号化したcookies jarにシリアライズしていました。アプリケーションで新しいJSONベースのフォーマットを使いたい場合、以下のような内容を持つイニシャライザファイルを追加できます。

Rails.application.config.action_dispatch.cookies_serializer = :hybrid

これにより、Marshalでシリアライズされた既存のcookiesを、新しいJSONベースのフォーマットに透過的に移行できます。

:jsonまたは:hybridシリアライザを使う場合、一部のRubyオブジェクトがJSONとしてシリアライズされない可能性があることにご注意ください。たとえば、DateオブジェクトやTimeオブジェクトはstringsとしてシリアライズされ、Hashのキーはstringに変換されます。

class CookiesController < ApplicationController
  def set_cookie
    cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
    redirect_to action: 'read_cookie'
  end

  def read_cookie
    cookies.encrypted[:expiration_date] # => "2014-03-20"
  end
end

cookieには文字列や数字などの単純なデータだけを保存することをお勧めします。cookiesに複雑なオブジェクトを保存しなければならない場合は、後続のリクエストでcookiesから値を読み出す場合の変換については自分で面倒を見る必要があります。

cookieセッションストアを使う場合、sessionflashハッシュについてもこのことは該当します。

8.6 Flash構造の変更

Flashメッセージのキーが文字列に正規化 されました。シンボルまたは文字列のどちらでもアクセスできます。Flashのキーを取り出すと常に文字列になります。

flash["string"] = "a string"
flash[:symbol] = "a symbol"

# Rails < 4.1
flash.keys # => ["string", :symbol]

# Rails >= 4.1
flash.keys # => ["string", "symbol"]

Flashメッセージのキーは必ず文字列と比較してください。

8.7 JSONの扱いの変更点

Rails 4.1ではJSONの扱いが大きく変更された点が4つあります。

8.7.1 MultiJSONの廃止

MultiJSONはその役目を終えて end-of-life Railsから削除されました。

アプリケーションがMultiJSONに直接依存している場合、以下のような対応方法があります。

  1. 'multi_json'をGemfileに追加する。ただしこのGemは将来使えなくなるかもしれません。

  2. obj.to_jsonJSON.parse(str)を用いてMultiJSONから乗り換える。

MultiJson.dumpMultiJson.loadをそれぞれJSON.dumpJSON.loadに単純に置き換えては「いけません」。これらのJSON gem APIは任意のRubyオブジェクトをシリアライズおよびデシリアライズするためのものであり、一般に安全ではありません

8.7.2 JSON gemの互換性

これまでのRailsでは、JSON gemとの互換性に何らかの問題が生じていました。Railsアプリケーション内のJSON.generateJSON.dumpではときたまエラーが生じることがありました。

Rails 4.1では、Rails自身のエンコーダをJSON gemから切り離すことでこれらの問題が修正されました。JSON gem APIは今後正常に動作しますが、その代わりJSON gem APIからRails特有の機能にアクセスすることはできなくなります。以下に例を示します。

class FooBar
  def as_json(options = nil)
    { foo: 'bar' }
  end
end

FooBar.new.to_json # => "{\"foo\":\"bar\"}"
JSON.generate(FooBar.new, quirks_mode: true) # => "\"#<FooBar:0x007fa80a481610>\""
8.7.3 新しいJSONエンコーダ

Rails 4.1のJSONエンコーダは、JSON gemを使うように書き直されました。この変更によるアプリケーションへの影響はほとんどありません。ただし、エンコーダが書き直された際に以下の機能がエンコーダから削除されました。

  1. データ構造の循環検出
  2. encode_jsonフックのサポート
  3. BigDecimalオブジェクトを文字ではなく数字としてエンコードするオプション

アプリケーションがこれらの機能に依存している場合は、activesupport-json_encoder gemをGemfileに追加することで以前の状態に戻すことができます。

8.7.4 TimeオブジェクトのJSON形式表現

日時に関連するコンポーネント(TimeDateTimeActiveSupport::TimeWithZone)を持つオブジェクトに対して#as_jsonを実行すると、デフォルトでミリ秒単位の精度で値が返されるようになりました。ミリ秒より精度の低い従来方式にしておきたい場合は、イニシャライザに以下を設定してください。

ActiveSupport::JSON::Encoding.time_precision = 0

8.8 インラインコールバックブロックでreturnを利用できなくなる

以前のRailsでは、インラインコールバックブロックで以下のようにreturnを使うことが許容されていました。

class ReadOnlyModel < ActiveRecord::Base
  before_save { return false } # 良くない
end

この動作は決して意図されたものではありません。ActiveSupport::Callbacksが書き直され、上のような動作はRails 4.1では許容されなくなりました。インラインコールバックブロックでreturn文を書くと、コールバック実行時にLocalJumpErrorが発生するようになりました。

インラインコールバックブロックでreturnを使っている場合、以下のようにリファクタリングすることで、返された値として評価されるようになります。

class ReadOnlyModel < ActiveRecord::Base
  before_save { false } # 良い
end

returnを使いたいのであれば、明示的にメソッドを定義することが推奨されます。

class ReadOnlyModel < ActiveRecord::Base
  before_save :before_save_callback # 良い

  private
    def before_save_callback
      return false
    end
end

この変更は、Railsでコールバックを使っている多くの箇所に適用されます。これにはActive RecordとActive ModelのコールバックやAction Controllerのフィルタ(before_action など)も含まれます。

詳しくはこのpull requestを参照してください。

8.9 Active Recordフィクスチャで定義されたメソッド

Rails 4.1では、各フィクスチャのERBは独立したコンテキストで評価されます。このため、あるフィクスチャで定義されたヘルパーメソッドは他のフィクスチャでは利用できません。

ヘルパーメソッドを複数のフィクスチャで利用するには、4.1で新しく導入されたActiveRecord::FixtureSet.context_class (test_helper.rb) に含まれるモジュールで定義する必要があります。

module FixtureFileHelpers
  def file_sha(path)
    Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
  end
end
ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers

8.10 I18nオプションでavailable_localesリストの利用が強制される

Rails 4.1からI18nオプションenforce_available_localesがデフォルトでtrueになりました。この設定にすると、I18nに渡されるすべてのロケールは、available_localesリストで宣言されていなければ使えません。

この機能をオフにしてI18nですべての種類のロケールオプションを使えるようにするには、以下のように変更します。

config.i18n.enforce_available_locales = false

enforce_available_localesはセキュリティのために行われていることにご注意ください。つまり、アプリケーションが把握していないロケールを持つユーザー入力が、ロケール情報として使われることのないようにするためのものです。従って、やむを得ない理由がない限りこのオプションはfalseにしないでください。

8.11 リレーションに対する破壊的メソッド呼び出し

Relationには#map!#delete_ifなどの破壊的メソッド (mutator method) が含まれなくなりました。これらのメソッドを使いたい場合は#to_aを呼び出してArrayに変更してからにしてください。

この変更は、Relationに対して破壊的メソッドを直接呼び出すことによる奇妙なバグや混乱を防止するために行われました。

# 以前の破壊的な呼び出し方法
Author.where(name: 'Hank Moody').compact!

# 今後の破壊的な呼び出し方法
authors = Author.where(name: 'Hank Moody').to_a
authors.compact!

8.12 デフォルトスコープの変更

デフォルトのスコープは、条件をチェインした場合にオーバーライドされなくなりました。

以前のバージョンでは、モデルでdefault_scopeを定義すると、同じフィールドで連鎖した条件によってオーバーライドされました。現在は、他のスコープと同様、マージされるようになりました。

変更前:

class User < ActiveRecord::Base
  default_scope { where state: 'pending' }
  scope :active, -> { where state: 'active' }
  scope :inactive, -> { where state: 'inactive' }
end

User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'

User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'

User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive'

変更後:

class User < ActiveRecord::Base
  default_scope { where state: 'pending' }
  scope :active, -> { where state: 'active' }
  scope :inactive, -> { where state: 'inactive' }
end

User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'

User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active'

User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive'

以前と同じ動作に戻したい場合は、unscopedunscoperewhereexceptを用いてdefault_scopeの条件を明示的に除外する必要があります。

class User < ActiveRecord::Base
  default_scope { where state: 'pending' }
  scope :active, -> { unscope(where: :state).where(state: 'active') }
  scope :inactive, -> { rewhere state: 'inactive' }
end

User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'

User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'

User.inactive
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive'

8.13 文字列コンテンツのレンダリング

Rails 4.1のrender:plain:html:bodyオプションが導入されました。以下のようにコンテンツタイプを指定できるため、文字列ベースのコンテンツ表示にはこれらのオプションの利用が推奨されます。

  • render :plainを実行するとcontent typeはtext/plainに設定される
  • render :htmlを実行するとcontent typeはtext/htmlに設定される
  • render :bodyを実行した場合、content typeヘッダーは「設定されない」

セキュリティ上の観点から、レスポンスのbodyにマークアップを含めない場合にはrender :plainを使うすべきです。これによって多くのブラウザが安全でないコンテンツをエスケープできるからです。

今後のバージョンでは、render :textは非推奨にされる予定です。今のうちに、正しい:plain:html:bodyオプションに切り替えてください。render :textを使うとtext/htmlで送信されるため、セキュリティ上のリスクが生じる可能性があります。

8.14 PostgreSQLのデータ型'json'と'hstore'について

Rails 4.1では、PostgreSQLのjsonカラムとhstoreカラムを、文字列をキーとするRubyのHashに対応付けるようになりました。なお、以前のバージョンではHashWithIndifferentAccessが使われていました。この変更は、Rails 4.1以降ではシンボルでこれらのデータ型にアクセスできなくなるということを意味します。store_accessorsメソッドはjsonカラムやhstoreカラムに依存しているので、同様にシンボルでのアクセスが行えなくなります。今後は常に文字列をキーにするようにしてください。

8.15 ActiveSupport::Callbacksでは明示的にブロックを利用すること

Rails 4.1からはActiveSupport::Callbacks.set_callbackの呼び出しの際に明示的にブロックを渡すことが期待されます。これは、ActiveSupport::CallbacksがRails 4.1リリースにあたって大幅に書き換えられたことによるものです。

# Rails 4.0の場合
set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }

# Rails 4.1の場合
set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }

9 Rails 3.2からRails 4.0へのアップグレード

Railsアプリケーションのバージョンが3.2より前の場合、まず3.2へのアップグレードを完了してからRails 4.0へのアップグレードを開始してください。

以下の変更は、アプリケーションをRails 4.0にアップグレードするためのものです。

9.1 HTTP PATCH

Rails 4では、config/routes.rbでRESTfulなリソースが宣言されたときに、更新用の主要なHTTP verbとしてPATCHが使われるようになりました。updateアクションは従来どおり利用でき、PUTリクエストは今後もupdateアクションにルーティングされます。標準的なRESTfulのみを使っている場合、これに関する変更は不要です。

resources :users
<%= form_for @user do |f| %>
class UsersController < ApplicationController
  def update
    # 変更不要:PATCHが望ましいがPUTも使える
  end
end

ただし、form_forを用いてリソースを更新しており、PUT HTTPメソッドを使うカスタムルーティングと連動しているのであれば、変更が必要です。

resources :users do
  put :update_name, on: :member
end
<%= form_for [ :update_name, @user ] do |f| %>
class UsersController < ApplicationController
  def update_name
    # 変更が必要: form_forは、存在しないPATCHルートを探そうとする
  end
end

このアクションがパブリックなAPIで使われておらず、HTTPメソッドを自由に変更できるのであれば、ルーティングを更新してpatchputの代りに利用できます。

resources :users do
  patch :update_name, on: :member
end

Rails 4でPUTリクエストを/users/:idに送信すると、従来と同様updateにルーティングされます。このため、実際のPUTリクエストを受け取るAPIは今後も利用できます。この場合、PATCHリクエストも/users/:id経由でupdateアクションにルーティングされます。

このアクションがパブリックなAPIで使われており、HTTPメソッドを自由に変更できないのであれば、フォームを更新してPUTを代りに使えます。

<%= form_for [ :update_name, @user ], method: :put do |f| %>

PATCHおよびこの変更が行われた理由についてはRailsブログの この記事 を参照してください。

9.1.1 メディアタイプに関するメモ

PATCH verbに関する追加情報 PATCHでは異なるメディアタイプを使う必要があるJSON Patch などが該当します。RailsはJSON Patchをネイティブではサポートしませんが、サポートは簡単に追加できます。

# コントローラに以下を書く
def update
  respond_to do |format|
    format.json do
      # 部分的な変更を行なう
      @article.update params[:article]
    end

    format.json_patch do
      # 何か気の利いた変更を行なう
    end
  end
end

# config/initializers/json_patch.rb に以下を書く
Mime::Type.register 'application/json-patch+json', :json_patch

JSON Patchは最近RFC化されたばかりなのでRubyライブラリはそれほどありません。Aaron Pattersonの hana gemが代表的ですが、最新の仕様変更をすべてサポートしているわけではありません。

9.2 Gemfile

Rails 4.0ではassetsグループがGemfileから削除されました。アップグレード時にはこの記述をGemfileから削除する必要があります。アプリケーションのconfig/application.rbファイルも以下のように更新する必要があります。

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

9.3 vendor/plugins

Rails 4.0 では vendor/plugins 読み込みのサポートは完全に終了しました。利用するプラグインはすべてgemに展開してGemfileに追加しなければなりません。 理由があってプラグインをgemにしないのであれば、プラグインをlib/my_plugin/*に移動し、適切な初期化の記述をconfig/initializers/my_plugin.rbに書いてください。

9.4 Active Record

  • 関連付けに関する若干の不整合のため、Rails 4.0ではActive Recordからidentity mapが削除されました。この機能をアプリケーションで手動で有効にしたい場合は、今や無効になったconfig.active_record.identity_mapを削除する必要があるでしょう。

コレクション関連付けのdeleteメソッドは、integerString引数をレコードの他にレコードIDとしても受け付けるようになりました。これによりdestroyメソッドの動作にかなり近くなりました。以前はこのような引数を使うとActiveRecord::AssociationTypeMismatch例外が発生しました。Rails 4.0からは、deleteメソッドを使うと、与えられたIDにマッチするレコードを自動的に探すようになりました。

  • Rails 4.0では、カラムやテーブルの名前を変更すると、関連するインデックスも自動的にリネームされるようになりました。インデックス名を変更するためだけのマイグレーションは今後不要になりました。

  • Rails 4.0のserialized_attributesメソッドとattr_readonlyメソッドは、クラスメソッドとしてのみ使う形に変更されました。これらのメソッドをインスタンスメソッドとして利用することは非推奨となったため、行わないでください。たとえばself.serialized_attributesself.class.serialized_attributesのようにクラスメソッドとしてお使いください。

  • デフォルトのコーダーを使う場合、シリアル化属性にnilを渡すと、YAML全体にわたって (nil値を渡す代わりに) NULLとしてデータベースに保存されます ("--- \n...\n")。

  • Rails 4.0ではStrong Parametersの導入に伴い、attr_accessibleattr_protectedが廃止されました。これらを引き続き使いたい場合は、Protected Attributes gem を導入することでスムーズにアップグレードすることができます。

  • Protected Attributesを使っていない場合は、whitelist_attributesmass_assignment_sanitizerオプションなど、このgemに関連するすべてのオプションを削除できます。

  • Rails 4.0のスコープでは、Procやlambdaなどの呼び出し可能なオブジェクトの利用が必須となりました。

  scope :active, where(active: true)

  # 上のコードは以下のように変更する必要がある
  scope :active, -> { where active: true }
  • ActiveRecord::FixtureSetの導入に伴い、Rails 4.0ではActiveRecord::Fixturesが非推奨となりました。

  • ActiveSupport::TestCaseの導入に伴い、Rails 4.0ではActiveRecord::TestCaseが非推奨となりました。

  • Rails 4.0では、ハッシュを用いる旧来のfinder APIが非推奨となりました。これまでこうしたfinderオプションを受け付けていたメソッドは、これらのオプションを今後受け付けなくなりますのでご注意ください。たとえば、Book.find(:all, conditions: { name: '1984' })は非推奨です。今後はBook.where(name: '1984')をご利用ください。

  • 動的なメソッドは、find_by_...find_by_...!を除いて非推奨になりました。以下のように変更してください。

    • find_all_by_...: 今後はwhere(...)を使う
    • find_last_by_...: 今後はwhere(...).lastを使う
    • scoped_by_...: 今後はwhere(...)を使う
    • find_or_initialize_by_...: 今後はfind_or_initialize_by(...)を使う
    • find_or_create_by_...: 今後はfind_or_create_by(...)を使う
  • 旧来のfinderが配列を返していたのに対し、where(...)はリレーションを返します。Arrayが必要な場合は, where(...).to_aをお使いください。

  • これらの同等なメソッドが実行するSQLは、従来の実装でのSQLと同じとは限りません。

  • 旧来のfinderを再度有効にしたい場合は、activerecord-deprecated_finders gem を利用できます。

  • Rails 4.0 では、has_and_belongs_to_manyリレーションで2番目のテーブル名の共通プレフィックスを除去する際に、デフォルトでjoin tableを使うよう変更されました。共通プレフィックスがあるモデル同士のhas_and_belongs_to_manyリレーションでは、必ずjoin_tableオプションを指定する必要があります。以下に例を示します。

CatalogCategory < ActiveRecord::Base
  has_and_belongs_to_many :catalog_products, join_table: 'catalog_categories_catalog_products'
end

CatalogProduct < ActiveRecord::Base
  has_and_belongs_to_many :catalog_categories, join_table: 'catalog_categories_catalog_products'
end
  • プレフィックスではスコープも同様に考慮されるので、Catalog::CategoryCatalog::Product間のリレーションや、Catalog::CategoryCatalogProduct間のリレーションも同様に更新する必要があります。

9.5 Active Resource

Rails 4.0ではActive Resourceがgem化されました。この機能が必要な場合はActive Resource gemGemfileに追加できます。

9.6 Active Model

  • Rails 4.0ではActiveModel::Validations::ConfirmationValidatorにエラーがアタッチされる方法が変更されました。確認のバリデーションが失敗したときに、attributeではなく:#{attribute}_confirmationにアタッチされるようになりました。

  • Rails 4.0のActiveModel::Serializers::JSON.include_root_in_jsonのデフォルト値がfalseに変更されました。これにより、Active Model SerializersとActive Recordオブジェクトのデフォルトの動作が同じになりました。これにより、config/initializers/wrap_parameters.rbファイルの以下のオプションをコメントアウトしたり削除したりできるようになりました。

# Disable root element in JSON by default.
# ActiveSupport.on_load(:active_record) do
#   self.include_root_in_json = false
# end

9.7 Action Pack

  • Rails 4.0からActiveSupport::KeyGeneratorが導入され、署名付きcookiesの生成と照合などに使われるようになりました。Rails 3.xで生成された既存の署名付きcookiesは、既存のsecret_tokenはそのままにしてsecret_key_baseを新しく追加することで透過的にアップグレードされます。
  # config/initializers/secret_token.rb
  Myapp::Application.config.secret_token = 'existing secret token'
  Myapp::Application.config.secret_key_base = 'new secret key base'

注意:secret_key_baseを設定するのは、Rails 4.xへのユーザーベースの移行が100%完了し、Rails 3.xにロールバックする必要が完全になくなってからにしてください。これは、Rails 4.xの新しいsecret_key_baseで署名されたcookiesにはRails 3.xのcookiesとの後方互換性がないためです。他のアップグレードが完全に完了するまでは、既存のsecret_tokenをそのままにしてsecret_key_baseを設定せず、非推奨警告を無視するという選択肢もあります。

外部アプリケーションやJavaScriptからRailsアプリケーションの署名付きセッションcookies (または一般の署名付きcookies) を読み出せる必要がある場合は、これらの問題を切り離すまではsecret_key_baseを設定しないでください。

  • Rails 4.0では、secret_key_baseが設定されているとcookieベースのセッションの内容が暗号化されます。Rails 3.xではcookieベースのセッションへの署名は行われますが暗号化は行われません。署名付きcookiesは、そのRailsアプリケーションで生成されたことが確認でき、不正が防止されるという意味では安全です。しかしセッションの内容はエンドユーザーから見えてしまいます。内容を暗号化することで懸念を取り除くことができ、パフォーマンスの低下もそれほどありません。

セッションcookiesを暗号化する方法の詳しくはPull Request #9978 を参照してください。

  • Rails 4.0ではActionController::Base.asset_pathオプションが廃止されました。代りにアセットパイプライン機能をご利用ください。

  • Rails 4.0ではActionController::Base.page_cache_extensionオプションが非推奨になりました。代りにActionController::Base.default_static_extensionをご利用ください。

  • Rails 4.0のAction PackからActionとPageのキャッシュ機能が取り除かれました。コントローラでcaches_actionを使いたい場合はactionpack-action_caching gemを、caches_pageを使いたい場合はactionpack-page_caching gemをそれぞれGemfileに追加する必要があります。

  • Rails 4.0からXMLパラメータパーサーが取り除かれました。この機能が必要な場合はactionpack-xml_parser gemを追加する必要があります。

Rails 4.0では、シンボルやprocがnilを返す場合の、デフォルトのlayoutルックアップ設定が変更されました。動作を「no layout」にするには、nilではなくfalseを返すようにします。

  • Rails 4.0のデフォルトのmemcachedクライアントがmemcache-clientからdalliに変更されました。アップグレードするには、単にgem 'dalli'Gemfileに追加します。

  • Rails 4.0ではコントローラでのdom_idおよびdom_classメソッドの利用が非推奨になりました (ビューでの利用は問題ありません)。この機能が必要なコントローラではActionView::RecordIdentifierモジュールをインクルードする必要があります。

  • Rails 4.0ではlink_toヘルパーでの:confirmオプションが非推奨になりました。代わりにデータ属性をお使いください (例: data: { confirm: 'Are you sure?' })。link_to_iflink_to_unlessなどでも同様の対応が必要です。

  • Rails 4.0ではassert_generatesassert_recognizesassert_routingの動作が変更されました。これらのアサーションからはActionController::RoutingErrorの代りにAssertionが発生するようになりました。

  • Rails 4.0では、名前付きルートの定義が重複している場合にArgumentErrorが発生するようになりました。このエラーは、明示的に定義された名前付きルートやresourcesメソッドによってトリガされます。名前付きルートexample_pathが衝突している例を2つ示します。

  get 'one' => 'test#example', as: :example
  get 'two' => 'test#example', as: :example
  resources :examples
  get 'clashing/:id' => 'test#example', as: :example

最初の例では、複数のルーティングで同じ名前を使わないようにすれば回避できます。次の例では、onlyまたはexceptオプションをresourcesメソッド内で使うことで、作成されるルーティングを制限できます。詳細はRailsのルーティングを参照。

  • Rails 4.0ではunicode文字のルーティングのレンダリング方法が変更されました。unicode文字を用いるルーティングを直接レンダリングできるようになりました。既にこのようなルーティングを使っている場合は、以下の変更が必要です。
get Rack::Utils.escape('こんにちは'), controller: 'welcome', action: 'index'

上のコードは以下のように変更する必要があります。

get 'こんにちは', controller: 'welcome', action: 'index'
  • Rails 4.0でルーティングにmatchを使う場合は、リクエストメソッドの指定が必須となりました。以下に例を示します。
  # Rails 3.x
  match '/' => 'root#index'

  # 上のコードは以下のように変更する必要があります。
  match '/' => 'root#index', via: :get

  # または
  get '/' => 'root#index'
  • Rails 4.0からActionDispatch::BestStandardsSupportミドルウェアが削除されました。<!DOCTYPE html>は既に https://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx の標準モードをトリガするようになり、ChromeFrameヘッダはconfig.action_dispatch.default_headersに移動されました。

アプリケーションコード内にあるこのミドルウェアへの参照はすべて削除する必要がありますのでご注意ください。例:

# 例外発生
config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport)

環境設定も確認し、config.action_dispatch.best_standards_supportがある場合は削除します。

  • Rails 4.0では、config.action_dispatch.default_headersを設定することでHTTPヘッダーを設定できるようになりました。デフォルト設定は以下のとおりです。
  config.action_dispatch.default_headers = {
    'X-Frame-Options' => 'SAMEORIGIN',
    'X-XSS-Protection' => '1; mode=block'
  }

ただし、アプリケーションが特定のページで<frame><iframe>を読み込むことに依存している場合、X-Frame-Optionsを明示的にALLOW-FROM ...またはALLOWALLに設定する必要が生じる可能性があることにご注意ください。

  • Rails 4.0のアセットのプリコンパイルでは、vendor/assetsおよびlib/assetsにある非JS/CSSアセットを自動的にはコピーしなくなりました。Railsアプリケーションとエンジンの開発者は、これらのアセットを手動でapp/assetsに置き、config.assets.precompileを設定してください。

  • Rails 4.0では、リクエストされたフォーマットがアクションで扱えなかった場合にActionController::UnknownFormatが発生するようになりました。デフォルトでは、この例外は406 Not Acceptable応答として扱われますが、この動作をオーバーライドすることができます。Rails 3では常に406 Not Acceptableが返されます。オーバーライドはできません。

  • Rails 4.0では、ParamsParserがリクエストパラメータをパースできなかった場合に一般的なActionDispatch::ParamsParser::ParseError例外が発生するようになりました。MultiJson::DecodeErrorのような低レベルの例外の代りにこの例外をレスキューすることができます。

  • Rails 4.0では、URLプレフィックスで指定されたアプリケーションにエンジンがマウントされている場合にSCRIPT_NAMEが正しく入れ子になるようになりました。今後はURLプレフィックスの上書きを回避するためにdefault_url_options[:script_name]を設定する必要はありません。

  • Rails 4.0ではActionDispatch::Integrationの導入に伴いActionController::Integrationが非推奨となりました。

  • Rails 4.0ではActionDispatch::IntegrationTestの導入に伴いActionController::IntegrationTestは非推奨となりました。

  • Rails 4.0ではActionDispatch::PerformanceTestの導入に伴いActionController::PerformanceTestが非推奨となりました。

  • Rails 4.0ではActionDispatch::Requestの導入に伴いActionController::AbstractRequestが非推奨となりました。

  • Rails 4.0ではActionDispatch::Requestの導入に伴いActionController::Requestが非推奨となりました。

  • Rails 4.0ではActionDispatch::Responseの導入に伴いActionController::AbstractResponseが非推奨となりました。

  • Rails 4.0ではActionDispatch::Responseの導入に伴いActionController::Responseが非推奨となりました。

  • Rails 4.0ではActionDispatch::Routingの導入に伴いActionController::Routingが非推奨となりました。

9.8 Active Support

Rails 4.0ではERB::Util#json_escapeのエイリアスjが廃止されました。このエイリアスjは既にActionView::Helpers::JavaScriptHelper#escape_javascriptで使われているためです。

9.8.1 キャッシュ

Rails 3.xからRails 4.0への移行に伴い、キャッシュ用のメソッドが変更されましたキャッシュの名前空間を変更し、コールドキャッシュ (cold cache) を使って更新してください。

9.9 ヘルパーの読み込み順序

Rails 4.0では複数のディレクトリからのヘルパーの読み込み順が変更されました。以前はすべてのヘルパーをいったん集めてからアルファベット順にソートしていました。Rails 4.0にアップグレードすると、ヘルパーは読み込まれたディレクトリの順序を保持し、ソートは各ディレクトリ内でのみ行われます。helpers_pathパラメータを明示的に利用している場合を除いて、この変更はエンジンからヘルパーを読み込む方法にしか影響しません。ヘルパー読み込みの順序に依存している場合は、アップグレード後に正しいメソッドが使われているかどうかを確認する必要があります。エンジンが読み込まれる順序を変更したい場合は、config.railties_order= メソッドを利用できます。

9.10 Active Record ObserverとAction Controller Sweeper

Active Record ObserverAction Controller Sweeperrails-observers gemに切り出されました。これらの機能が必要な場合はrails-observers gemを追加してください。

9.11 sprockets-rails

  • assets:precompile:primaryおよびassets:precompile:allは削除されました。assets:precompileを代りにお使いください。
  • config.assets.compressオプションは、たとえば以下のようにconfig.assets.js_compressor に変更する必要があります。
config.assets.js_compressor = :uglifier

9.12 sass-rails

  • 引数を2つ使うasset-urlは非推奨となりました。たとえば、asset-url("rails.png", image)asset-url("rails.png")とする必要があります。

10 Rails 3.1からRails 3.2へのアップグレード

Railsアプリケーションのバージョンが3.1よりも古い場合、まず3.1へのアップグレードを完了してからRails 3.2へのアップグレードを開始してください。

以下の変更は、Rails 3.2.xにアップグレードするためのものです。

10.1 Gemfile

Gemfileを以下のように変更します。

gem 'rails', '3.2.21'

group :assets do
  gem 'sass-rails',   '~> 3.2.6'
  gem 'coffee-rails', '~> 3.2.2'
  gem 'uglifier',     '>= 1.0.3'
end

10.2 config/environments/development.rb

development環境にいくつかの新しい設定を追加する必要があります。

# Active Recordのモデルをマスアサインメントから保護するために例外を発生する
config.active_record.mass_assignment_sanitizer = :strict

# クエリの実行計画 (クエリプラン) を現在より多く出力する
# (SQLite、MySQL、PostgreSQLで動作)
config.active_record.auto_explain_threshold_in_seconds = 0.5

10.3 config/environments/test.rb

mass_assignment_sanitizer設定をconfig/environments/test.rbにも追加する必要があります。

# Active Recordのモデルをマスアサインメントから保護するために例外を発生する
config.active_record.mass_assignment_sanitizer = :strict

10.4 vendor/plugins

vendor/plugins はRails 3.2で非推奨となり、Rails 4.0では完全に削除されました。Rails 3.2へのアップグレードでは必須ではありませんが、今のうちにプラグインをgemにエクスポートしてGemfileに追加するのがよいでしょう。理由があってプラグインをgemにしないのであれば、プラグインをlib/my_plugin/*に移動し、適切な初期化の記述をconfig/initializers/my_plugin.rbに書いてください。

10.5 Active Record

:dependent => :restrictオプションはbelongs_toから削除されました。関連付けられたオブジェクトがある場合にこのオブジェクトを削除したくない場合は、:dependent => :destroyを設定し、関連付けられたオブジェクトのdestroyコールバックとの関連付けがあるかどうかを確認してからfalseを返すようにします。

11 Rails 3.0からRails 3.1へのアップグレード

Railsアプリケーションのバージョンが3.0より前の場合、まず3.0へのアップグレードを完了してからRails 3.1へのアップグレードにとりかかってください。

以下の変更は、Rails 3.1.xの最新版であるRails 3.1.12にアップグレードするためのものです。

11.1 Gemfile

Gemfileを以下のように変更します。

gem 'rails', '3.1.12'
gem 'mysql2'

# 新しいアセットパイプラインで必要
group :assets do
  gem 'sass-rails',   '~> 3.1.7'
  gem 'coffee-rails', '~> 3.1.1'
  gem 'uglifier',     '>= 1.0.3'
end

# Rails 3.1からjQueryがデフォルトのJavaScriptライブラリになる
gem 'jquery-rails'

11.2 config/application.rb

アセットパイプラインを利用するには、以下の変更が必要です。

config.assets.enabled = true
config.assets.version = '1.0'

Railsアプリケーションでリソースのルーティングに/assetsルートを使っている場合、コンフリクトを避けるために以下の変更を加えます。

# '/assets'のデフォルト
config.assets.prefix = '/asset-files'

11.3 config/environments/development.rb

RJSの設定config.action_view.debug_rjs = trueを削除してください。

アセットパイプラインを有効にしている場合は以下の設定を追加します。

# 開発環境ではアセットを圧縮しない
config.assets.compress = false

# アセットで読み込んだ行を展開する
config.assets.debug = true

11.4 config/environments/production.rb

以下の変更はほとんどがアセットパイプライン用です。詳しくは アセットパイプライン ガイドを参照してください。

# JavaScriptとCSSを圧縮する
config.assets.compress = true

# プリコンパイル済みのアセットが見当たらない場合にアセットパイプラインにフォールバックしない
config.assets.compile = false

# アセットURLのダイジェストを生成する
config.assets.digest = true

# Rails.root.join("public/assets")へのデフォルト
# config.assets.manifest = 該当するパス

# 追加のアセット (application.js、application.cssおよびすべての非JS/CSSが追加済み) をプリコンパイルする
# config.assets.precompile += %w( admin.js admin.css )

# アプリケーションへのすべてのアクセスを強制的にSSLにし、Strict-Transport-Securityとセキュアクッキーを使う
# config.force_ssl = true

11.5 config/environments/test.rb

テスト環境に以下を追加することでテストのパフォーマンスが向上します。

# Cache-Controlを使うテストで静的アセットサーバーを構成し、パフォーマンスを向上させる
config.public_file_server.enabled = true
config.public_file_server.headers = {
  'Cache-Control' => 'public, max-age=3600'
}

11.6 config/initializers/wrap_parameters.rb

ネストしたハッシュにパラメータを含めたい場合は、このファイルに以下のコンテンツを含めて追加します。新しいアプリケーションではこれがデフォルトになります。

# このファイルを変更後サーバーを必ず再起動してください。
# このファイルにはActionController::ParamsWrapper用の設定が含まれており
# デフォルトでオンになっています。

# JSON用にパラメータをラップします。:formatに空配列を設定することで無効にできます。
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json]
end

# JSONのルート要素をデフォルトで無効にする
ActiveSupport.on_load(:active_record) do
  self.include_root_in_json = false
end

11.7 config/initializers/session_store.rb

何らかの新しいセッションキーを設定するか、すべてのセッションを削除するかのどちらかにする必要があります。

# config/initializers/session_store.rbに以下を設定する
AppName::Application.config.session_store :cookie_store, key: 'SOMETHINGNEW'

または

$ bin/rake db:sessions:clear

11.8 ビューのアセットヘルパー参照から:cacheオプションと:concatオプションを削除する

  • Asset Pipelineの:cacheオプションと:concatは廃止されました。ビューからこれらのオプションを削除してください。

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。