Rails アップグレードガイド

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

目次

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

1 一般的なアドバイス

既存のアプリケーションをアップグレードする前に、アップグレードする理由を明確にしておく必要があります。「新しいバージョンのどの機能か必要か」「既存コードのサポートがどのぐらい困難になるか」「アップグレードに割り当てられる時間と人員スキルはどのぐらいか」など、いくつもの要素を調整しなければなりません。

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

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

1.2 Rubyバージョン

Railsでは、一般にRubyの最新版がリリースされると最新版のRubyに近い状態に合わせます。

  • Rails 7: Ruby 2.7.0以降が必須
  • Rails 6: Ruby 2.5.0以降が必須
  • Rails 5: Ruby 2.2.2以降が必須

RubyのアップグレードとRailsのアップグレードは別々に行うのがよい方法です。最初にRubyを可能な限り最新版にアップグレードし、それからRailsをアップグレードします。

1.3 アップグレード手順

Railsのバージョンを変更する場合、マイナーバージョンを1つずつゆっくりと上げながら、その都度表示される非推奨機能の警告メッセージを上手に利用するのがベストです。言い換えると、アップグレードを急ぐあまりバージョンをスキップするべきではありません。Railsのバージョン番号は「メジャー番号.マイナー番号.パッチ番号」の形式を取ります。メジャーバージョンやマイナーバージョンの変更では、public APIの変更によるエラーがアプリケーションで発生する可能性があります。パッチバージョンはバグ修正のみが含まれ、public API変更は含まれません。

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

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

上の手順を繰り返して、最終的にRailsを目的のバージョンにアップグレードします。

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

1.3.1 Railsバージョン間を移動する

Railsのバージョン間を移動するには以下のようにします。

  1. Gemfileファイル内のRailsバージョン番号を変更し、bundle updateを実行する。
  2. package.jsonファイル内のRails JavaScriptパッケージのバージョンを変更する。Webpackerで動かす場合はyarn installを実行する。
  3. #アップデートタスクを実行する。
  4. テストを実行する。

リリースされたすべてのRails gemリストについてはこちらを参照してください。

1.4 アップデートタスク

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

$ bin/rails app:update
       exist  config
    conflict  config/application.rb
Overwrite /myapp/config/application.rb? (enter "h" for help) [Ynaqdh]
       force  config/application.rb
      create  config/initializers/new_framework_defaults_7_0.rb
...

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

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

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

app:updateタスクでは、アプリケーションを新しいデフォルト設定に1つずつアップグレードできるように、config/initializers/new_framework_defaults_X.Y.rbファイルが作成されます(ファイル名にはRailsのバージョンが含まれます)。このファイル内のコメントを解除して、新しいデフォルト設定を有効にする必要があります。この作業は、数回のデプロイに分けて段階的に実行できます。アプリケーションを新しいデフォルト設定で動かせる準備が整ったら、このファイルを削除してconfig.load_defaultsの値を反転できます。

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

2.1 ActionView::Helpers::UrlHelper#button_toの振る舞いが変更された

Rails 7.0以降のbutton_toは、ボタンURLをビルドするのに使われるActive Recordオブジェクトが永続化されている場合は、patch HTTP verbを用いるformタグをレンダリングします。現在の振る舞いを維持するには、以下のように明示的にmethod:オプションを渡します。

-button_to("Do a POST", [:my_custom_post_action_on_workshop, Workshop.find(1)])
+button_to("Do a POST", [:my_custom_post_action_on_workshop, Workshop.find(1)], method: :post)

または、以下のようにURLをビルドするヘルパーを使います。

-button_to("Do a POST", [:my_custom_post_action_on_workshop, Workshop.find(1)])
+button_to("Do a POST", my_custom_post_action_on_workshop_workshop_path(Workshop.find(1)))

2.2 spring gem

アプリケーションでspring gemを使っている場合は、spring gemのバージョンを3.0.0以上にアップグレードする必要があります。そうしないと以下のエラーが発生します。

undefined method `mechanism=' for ActiveSupport::Dependencies:Module

また、config/environments/test.rbconfig.cache_classes設定を必ずfalseにしてください。

2.3 Sprocketsへの依存がオプショナルになった

rails gemはsprockets-railsに依存しなくなりました。アプリケーションで引き続きSprocketsを使う必要がある場合は、Gemfileにsprockets-railsを追加してください。

gem "sprockets-rails"

2.4 アプリケーションはzeitwerkモードでの実行が必須

classicモードで動作しているアプリケーションは、zeitwerkモードに切り替えなければなりません。詳しくはクラシックオートローダーからZeitwerkへの移行ガイドを参照してください。

2.5 config.autoloader=セッターが削除された

Rails 7では、オートロードのモードを指定するconfig.autoloader=設定そのものがなくなりました。何らかの理由で:zeitwerkに設定していた場合は、その設定行を削除してください。

2.6 ActiveSupport::Dependenciesのprivate APIが削除された

ActiveSupport::Dependenciesのprivate APIが削除されました。hook!unhook!depend_onrequire_or_loadmechanismなど多数のメソッドが削除されています。

注意点をいくつか示します。

  • ActiveSupport::Dependencies.constantizeまたはActiveSupport::Dependencies.safe_constantizeを使っている場合は、String#constantizeまたはString#safe_constantizeに変更してください。
  ActiveSupport::Dependencies.constantize("User") # 今後は利用不可
  "User".constantize # 👍
  • ActiveSupport::Dependencies.mechanismやそのリーダーやライターを使っている場合は、config.cache_classesのアクセスで置き換える必要があります。

  • オートローダーの動作をトレースしたい場合、ActiveSupport::Dependencies.verbose=は利用できなくなりました。config/application.rbRails.autoloaders.log!をスローしてください。

ActiveSupport::Dependencies::ReferenceActiveSupport::Dependencies::Blamableなどの補助的なクラスやモジュールも削除されました。

2.7 初期化中のオートロード

Rails 6.0以降では、アプリケーションの初期化中に、再読み込み可能な定数をto_prepareブロックの外でオートロードすると、それらの定数がアンロードされて以下の警告が出力されます。

DEPRECATION WARNING: Initialization autoloaded the constant ....

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

...

この警告が引き続きログに出力される場合は、アプリケーション起動時の自動読み込みでアプリケーション起動時のオートロードについての記述を参照してください。これに対応しないと、Rails 7でNameErrorが出力されます。

2.8 config.autoload_once_pathsを設定可能になった

config.autoload_once_pathsは、config/application.rbで定義されるApplicationクラスの本体、またはconfig/environments/*の環境向け設定で設定可能です。

エンジンも同様に、エンジンクラスのクラス本体内にあるコレクションや、環境向けの設定内にあるコレクションを設定可能です。

コレクションは以後frozenになり、これらのパスからオートロードできるようになります。特に、これらのパスから初期化中にオートロードできるようになります。これらのパスは、Rails.autoloaders.onceオートローダーで管理されます。このオートローダーはリロードを行わず、オートロードやeager loadingのみを行います。

環境設定が完了した後でこの設定を行ったときにFrozenErrorが発生する場合は、コードの置き場所を移動してください。

2.9 ActionDispatch::Request#content_typeが Content-Typeヘッダーをそのまま返すようになった

従来は、ActionDispatch::Request#content_typeが返す値にcharsetパートが含まれて「いませんでした」。 この振る舞いが変更され、charsetパートを含むContent-Typeヘッダーをそのまま返すようになりました。

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

変更前:

request = ActionDispatch::Request.new("CONTENT_TYPE" => "text/csv; header=present; charset=utf-16", "REQUEST_METHOD" => "GET")
request.content_type #=> "text/csv"

変更後:

request = ActionDispatch::Request.new("Content-Type" => "text/csv; header=present; charset=utf-16", "REQUEST_METHOD" => "GET")
request.content_type #=> "text/csv; header=present; charset=utf-16"
request.media_type   #=> "text/csv"

2.10 キージェネレータのメッセージダイジェストクラスがSHA256に変更

キージェネレータで用いられるデフォルトのダイジェストクラスが、SHA1からSHA256に変更されました。 その結果、Railsで生成されるあらゆる暗号化メッセージがこの影響を受けるようになり、暗号化cookieも同様に影響を受けます。

古いダイジェストクラスを用いてメッセージを読めるようにするには、ローテータの登録が必要です。

以下は、暗号化cookie向けのローテータの設定例です。

Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
  salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
  secret_key_base = Rails.application.secret_key_base

  key_generator = ActiveSupport::KeyGenerator.new(
    secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
  )
  key_len = ActiveSupport::MessageEncryptor.key_len
  secret = key_generator.generate_key(salt, key_len)

  cookies.rotate :encrypted, secret
end

2.11 ActiveSupport::Digestで用いられるメッセージダイジェストクラスがSHA256に変更

ActiveSupport::Digestで用いられるデフォルトのダイジェストクラスがSHA1からSHA256に変更されます。 その結果、Etagなどの変更やキャッシュキーにも影響します。 これらのキーを変更すると、キャッシュのヒット率が低下する可能性があるので、新しいハッシュにアップグレードする際は慎重に進めるようご注意ください。

2.12 ActiveSupport::Cacheの新しいシリアライズフォーマット

より高速かつコンパクトな新しいシリアライズフォーマットが導入されました。

これを有効にするには、以下のようにconfig.active_support.cache_format_version = 7.0を設定する必要があります。

# config/application.rb

config.load_defaults 6.1
config.active_support.cache_format_version = 7.0

または以下のようにシンプルに設定します。

# config/application.rb

config.load_defaults 7.0

ただし、Rails 6.1アプリケーションはこの新しいシリアライズフォーマットを読み取れないので、シームレスにアップグレードするには、まずRails 7.0へのアップグレードをconfig.active_support.cache_format_version = 6.1でデプロイし、Railsプロセスがすべて更新されたことを確かめてからconfig.active_support.cache_format_version = 7.0を設定する必要があります。

Rails 7.0は新旧両方のフォーマットを読み取れるので、アップグレード中にキャッシュが無効になることはありません。

2.13 ActiveStorageの動画プレビュー画像生成

動画のプレビュー画像生成で、FFmpegの場面転換検出機能を用いて従来よりも意味のあるプレビュー画像を生成するようになりました。従来は動画の冒頭フレームが使われたため、黒画面からフェードインして開始される動画で問題が生じました。この変更にはFFmpeg v3.4以降が必要です。

2.14 Active Storageのデフォルトのバリアントプロセッサが :vipsに変更

新規アプリの画像変換では、従来のImageMagickに代えてlibvipsが使われるようになります。これにより、バリアント(サムネイルなどで用いられるサイズ違いの画像)の生成時間が短縮されるとともにCPUやメモリの使用量も削減され、Active Storageで画像を配信するアプリのレスポンスが向上します。

:mini_magickオプションは非推奨化されていませんので、引き続き問題なく利用できます。

既存のアプリをlibvipsに移行するには、以下を設定します。

Rails.application.config.active_storage.variant_processor = :vips

続いて、既存の画像変換コードをimage_processingマクロに変更し、さらにImageMagickのオプションをlibvipsのオプションに置き換える必要があります。

2.14.1 resizeresize_to_limitに置き換える
- variant(resize: "100x")
+ variant(resize_to_limit: [100, nil])

上の置き換えを行わないと、vipsに切り替えたときにno implicit conversion to float from stringエラーが表示されます。

2.14.2 cropで配列を使うよう変更する
- variant(crop: "1920x1080+0+0")
+ variant(crop: [0, 0, 1920, 1080])

上の置き換えを行わないと、vipsに移行したときにunable to call crop: you supplied 2 arguments, but operation needs 5エラーが表示されます。

2.14.3 cropの値を固定する

vipsのcropは、ImageMagickよりも厳密です。

  1. xyが負の値の場合はcropされない。例: [-10, -10, 100, 100]

  2. 位置(xまたはy)とcropのサイズ(width、height)が画像サイズを上回る場合はcropされない。例: 125x125の画像に対して[50, 50, 100, 100]crop`する。

上を守らない場合、vipsに移行したときにextract_area: bad extract areaエラーが表示されます。

2.14.4 resize_and_padの背景色を調整する

vipsのresize_and_padでは、デフォルトバックグラウンド色にImageMagickの白ではなく黒が使われます。これは以下のようにbackground:オプションで修正できます。

- variant(resize_and_pad: [300, 300])
+ variant(resize_and_pad: [300, 300, background: [255]])
2.14.5 EXIFベースの画像回転を止める

vipsでは、バリアントを処理中にEXIF値を用いて画像を自動回転します。ユーザーがアップロードした写真の回転値を保存してImageMagickで回転するのであれば、以下のようにこの機能を止める必要があります。

- variant(format: :jpg, rotate: rotation_value)
+ variant(format: :jpg)
2.14.6 monochromecolourspaceに置き換える。

vipsでは、モノクロ画像を作成するオプションを以下のように変更する必要があります。

- variant(monochrome: true)
+ variant(colourspace: "b-w")
2.14.7 libvipsの画像圧縮オプションに変更する

JPEGの場合。

- variant(strip: true, quality: 80, interlace: "JPEG", sampling_factor: "4:2:0", colorspace: "sRGB")
+ variant(saver: { strip: true, quality: 80, interlace: true })

PNGの場合。

- variant(strip: true, quality: 75)
+ variant(saver: { strip: true, compression: 9 })

WEBPの場合。

- variant(strip: true, quality: 75, define: { webp: { lossless: false, alpha_quality: 85, thread_level: 1 } })
+ variant(saver: { strip: true, quality: 75, lossless: false, alpha_q: 85, reduction_effort: 6, smart_subsample: true })

GIFの場合。

- variant(layers: "Optimize")
+ variant(saver: { optimize_gif_frames: true, optimize_gif_transparency: true })
2.14.8 production環境へのデプロイ

Active Storageは、実行されなければならない変換のリストを画像URLにエンコードします。 アプリケーションがこれらの画像URLをキャッシュしていると、新しいコードをproduction環境にデプロイした後で画像が破損します。 このため、影響を受けるキャッシュキーを手動で無効にしなければなりません。

たとえば、以下のようなビューがあるとします。

<% @products.each do |product| %>
  <% cache product do %>
    <%= image_tag product.cover_photo.variant(resize: "200x") %>
  <% end %>
<% end %>

このキャッシュを無効にするには、productを以下のように変更するか、キャッシュキーを変更します。

<% @products.each do |product| %>
  <% cache ["v2", product] do %>
    <%= image_tag product.cover_photo.variant(resize_to_limit: [200, nil]) %>
  <% end %>
<% end %>

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

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

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

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

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

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

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

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

3.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 }

3.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

3.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が使われなくなったことによる変更です。

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

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

3.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])

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

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

4.1 Webpackerの利用について

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

gem "webpacker"
$ bin/rails webpacker:install

4.2 SSLの強制

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

4.3 セキュリティ向上のためpurposeとexpiryメタデータが署名済みcookieや暗号化済み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を設定してください。

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

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

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

4.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
    

4.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"

4.7 オートローディング

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

# config/application.rb

config.load_defaults "6.0"

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

以前のバージョンのRailsのデフォルトを使っている場合は、以下の方法でzeitwerkを有効にできます。

# config/application.rb

config.autoloader = :zeitwerk
4.7.1 public APIについて

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

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

Rails.autoloaders.main

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

4.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!

4.7.3 require_dependencyについて

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

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

4.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
4.7.5 concernsについて

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

app/models
app/models/concerns

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

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

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

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

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

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

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

app/models/hotel.rb         # Hotelが定義される
app/models/hotel/pricing.rb # Hotel::Pricingが定義される

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

class Hotel
end

上は問題ありませんが、以下はどちらも動きません。

Hotel = Class.new
Hotel = Struct.new

後者はHotel::Pricingなどの子オブジェクトを探索できなくなります。

この制約は、明示的な名前空間にのみ適用されます。名前空間を定義しないクラスやモジュールであれば、後者の方法でも定義できます。

4.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も再読み込みされます。

4.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
4.7.10 Bootsnapについて

Bootsnapのバージョンは1.4.2以上にする必要があります。

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

4.7.11 config.add_autoload_paths_to_load_path

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

config.add_autoload_paths_to_load_path

この設定変更は、ほとんどのアプリケーションで有用です(app/models内などのファイルは決してrequireすべきではなく、Zeitwerkは内部で絶対パスだけを使うからです)。

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

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

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

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

4.7.13 config.autoload_pathsの注意事項

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

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

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

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

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

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

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

4.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に設定することをおすすめします。

4.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 7.0で非推奨化され、Rails 7.1で削除される予定です。

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

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

5.1 Bootsnap

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

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

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

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

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

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

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

6.1 トップレベルのHashWithIndifferentAccessが緩やかに非推奨化された

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

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

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

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

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

変更前:

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

変更後:

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

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

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

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

6.4 非推奨化されたredirect_to :backサポートが削除された

Rails 5.0で非推奨化されたredirect_to :backは、Rails 5.1で完全に削除されました。

今後は代わりにredirect_backをお使いください。redirect_backは、HTTP_REFERERが見つからない場合に使われるfallback_locationオプションも受け取る点にご注意ください。

redirect_back(fallback_location: root_path)

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

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

7.1 Ruby 2.2.2以上が必須

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

7.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を継承するように変更し、動作を確認してください。

7.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を参照してください。

7.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を参照してください。

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

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

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

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

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

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

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

7.6 production環境での起動後はオートロードが無効になる

今後Railsがproduction環境で起動されると、オートロードがデフォルトで無効になります。

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

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

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

7.7 XMLシリアライズのgem化

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7.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/* %>

7.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を参照してください。

7.16 protected_attributes gemのサポートが終了

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

7.17 activerecord-deprecated_finders gemのサポートが終了

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

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

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

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

7.19 ActionController::LiveConcernに変更された

コントローラにincludeされている別のモジュールにActionController::Liveincludeされている場合、ActiveSupport::Concernextendするコードの追加も必要です。または、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

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

7.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
7.20.2 フォーム単位のCSRFトークン

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

config.action_controller.per_form_csrf_tokens = true
7.20.3 OriginチェックによるCSRF保護

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

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

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

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

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

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

schema_search_pathなどのPostgreSQL拡張を使っている場合、スキーマのダンプ方法を指定できます。以下のように:allを指定するとすべてのダンプが生成され、:schema_search_pathを指定するとスキーマ検索パスからダンプが生成されます。

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

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

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

Ruby 2.4を利用している場合、to_timeの呼び出しでレシーバのタイムゾーンを変更しないようにできます。

ActiveSupport.to_time_preserves_timezone = false

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

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

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

8.1 web-console gem

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

8.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を参照してください。

8.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を参照してください。

8.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

8.5 シリアル化属性

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

8.6 Productionログのレベル

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

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

8.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

8.8 RailsのHTMLサニタイザ

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

これにより、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'

8.9 RailsのDOMのテスト

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

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

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

8.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

8.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にすべて含まれていることを確認する

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

9.1 リモート <script> タグのCSRF保護

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

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

つまり、以下を使う機能テストや結合テストではCSRF保護が発動します。

get :index, format: :js

XmlHttpRequestを明示的にテストするには、以下のように書き換えます。

xhr :get, :index, format: :js

自サイトの<script>はクロスオリジンとして扱われるため、同様にブロックされます。JavaScriptを実際に<script>タグから読み込む場合は、そのアクションでCSRF保護を明示的にスキップしなければなりません。

9.2 spring gem

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

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

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

9.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_token.rbイニシャライザにある既存のsecret_key_basesecrets.ymlのproductionセクションにコピーし、'<%= ENV["SECRET_KEY_BASE"] %>'を置き換えることもできます。

  3. secret_token.rbイニシャライザを削除する。

  4. developmentセクションとtestセクションで使う新しい鍵をrake secretで生成する。

  5. サーバーを再起動する。

9.4 テストヘルパーの変更

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

9.5 cookieシリアライザ

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

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

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

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

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には文字列や数字などの単純なデータだけを保存することをおすすめします。cookieに複雑なオブジェクトを保存しなければならない場合は、以後のリクエストでcookieから値を読み出すときに自分で変換する必要があります。

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

9.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メッセージのキーは必ず文字列と比較してください。

9.7 JSONの扱いの変更点

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

9.7.1 MultiJSONの廃止

MultiJSONはその役目を終えてRailsから削除されました(#10576)。

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

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

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

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

9.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
irb> FooBar.new.to_json
=> "{\"foo\":\"bar\"}"
irb> JSON.generate(FooBar.new, quirks_mode: true)
=> "\"#<FooBar:0x007fa80a481610>\""
9.7.3 新しいJSONエンコーダ

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

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

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

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

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

ActiveSupport::JSON::Encoding.time_precision = 0

9.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 など)も含まれます。

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

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

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

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

module FixtureFileHelpers
  def file_sha(path)
    OpenSSL::Digest::SHA256.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
  end
end

ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers

9.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にしないでください。

9.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!

9.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'

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

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

  • 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で送信されるため、セキュリティ上のリスクが生じる可能性があります。

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

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

9.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 }

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

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

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

10.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ブログのこの記事を参照してください。

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

JSON Patchは、PATCH verbの正誤表で指摘されている「PATCHでは異なるメディアタイプを使う必要がある」に該当するものの1つです。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が代表的ですが、最新の仕様変更をすべてサポートしているわけではありません。

10.2 Gemfile

Rails 4.0のGemfileからassetsグループが削除されました。アップグレード時にはこの記述を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)

10.3 vendor/plugins

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

10.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値を渡す("--- \n...\n")のではなく、NULLとしてデータベースに保存されます。

  • 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 }
    
  • Rails 4.0ではActiveRecord::Fixturesが非推奨となりました。今後はActiveRecord::FixtureSetをお使いください。

  • Rails 4.0ではActiveRecord::TestCaseが非推奨となりました。今後はActiveSupport::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間のリレーションも同様に更新する必要があります。

10.5 Active Resource

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

10.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
    

10.7 Action Pack

  • Rails 4.0からActiveSupport::KeyGeneratorが導入され、署名付きcookieの生成や照合などに使われるようになりました。Rails 3.xで生成された既存の署名付きcookieは、既存の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で署名されたcookieにはRails 3.xのcookieとの後方互換性がないためです。他のアップグレードが完全に完了するまでは、既存のsecret_tokenをそのままにしてsecret_key_baseを設定せず、非推奨警告を無視する方法も可能です。

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

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

    セッションcookieを暗号化する方法について詳しくは#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属性をお使いください(例: data: { confirm: 'Are you sure?' })。link_to_iflink_to_unlessなどでも同様の対応が必要です。

  • Rails 4.0ではassert_generatesassert_recognizesassert_routingの動作が変更されました。これらのアサーションはActionController::RoutingErrorの代わりにAssertionをraiseします。

  • 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のような低レベルの例外の代わりにこの例外をrescueできます。

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

  • Rails 4.0ではActionController::Integrationが非推奨となりました。今後はActionDispatch::Integrationをお使いください。

  • Rails 4.0ではActionController::IntegrationTestは非推奨となりました。今後はActionDispatch::IntegrationTestをお使いください。

  • Rails 4.0ではActionController::PerformanceTestが非推奨となりました。今後はActionDispatch::PerformanceTestをお使いください。

  • Rails 4.0ではActionController::AbstractRequestが非推奨となりました。今後はActionDispatch::Requestをお使いください。

  • Rails 4.0ではActionController::Requestが非推奨となりました。今後はActionDispatch::Requestをお使いください。

  • Rails 4.0ではActionController::AbstractResponseが非推奨となりました。今後はActionDispatch::Responseをお使いください。

  • Rails 4.0ではActionController::Responseが非推奨となりました。今後はActionDispatch::Responseをお使いください。

  • Rails 4.0ではActionController::Routingが非推奨となりました。今後はActionDispatch::Routingをお使いください。

10.8 Active Support

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

10.8.1 キャッシュ

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

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

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

10.10 Active Record ObserverとAction Controller Sweeper

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

10.11 sprockets-rails

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

    config.assets.js_compressor = :uglifier
    

10.12 sass-rails

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

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

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

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

11.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

11.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

11.3 config/environments/test.rb

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

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

11.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に書いてください。

11.5 Active Record

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

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

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

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

12.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'

12.2 config/application.rb

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

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

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

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

12.3 config/environments/development.rb

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

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

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

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

12.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とセキュアcookieを使う
# config.force_ssl = true

12.5 config/environments/test.rb

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

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

12.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

12.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

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

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

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。よろしくお願いします。

YassLab 株式会社
https://yasslab.jp/

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。Railsガイドへの支援・協賛にご興味あれば info@yasslab.jp までお問い合わせください。

  1. Star
  2. このエントリーをはてなブックマークに追加