Edited at

config.load_defaultsとnew_framework_defaults_x_x.rbの関係を詳しく調べてみた


config.load_defaultsとnew_framework_defaults_x_x.rbの関係を詳しく調べてみた


TL; DR(はじめに結論)

以下はRails 5.1から5.2にアップグレードしたケースを想定しています。

必要に応じてバージョン番号を読み替えてください。


  • Rails 5.2アップグレード後にconfig.load_defaults 5.2と書くと、Rails 5.2のデフォルトの挙動になる

  • 一部にRails 5.1の挙動に戻したい項目があれば、new_framework_defaults_5_2.rbの該当項目のコメントを外し、そのうえで設定値を論理反転させる(truefalseへ、falsetrueへ修正する)

  • Rails 5.1の挙動に戻す必要がなければ、new_framework_defaults_5_2.rbは削除しても構わない


はじめに

既存のRailsアプリを新しいバージョンにアップデートするために rails app:update コマンドを使うと、 config/initializers 以下に new_framework_defaults_5_2.rb のようなファイルが作成されます。

その一方で、config/application.rb を見ると、config.load_defaults 5.1のような設定も存在します。

Railsをアップデートしたときは、これら2つの設定ファイルをどのように使うのが良いのでしょうか?

僕自身、あまりしっかり理解しないまま使ってきてたので、詳しく調べてみました。


この記事で想定するRailsアプリケーション

この記事では以下のようなRailsアプリケーションを想定します。


  • Rails 5.1の時代にrails newした

  • その後、Rails 5.2にアップグレードした

  • アップグレード時には rails app:update コマンドを使った(つまり、 config/initializers/new_framework_defaults_5_2.rb が作成された)


  • config/application.rbconfig.load_defaults5.1 のまま


Gemfile

# ...

gem 'rails', '5.2.3'
# ...


config/initializers/new_framework_defaults_5_2.rb

# Be sure to restart your server when you modify this file.

#
# This file contains migration options to ease your Rails 5.2 upgrade.
#
# Once upgraded flip defaults one by one to migrate to the new default.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.

# Make Active Record use stable #cache_key alongside new #cache_version method.
# This is needed for recyclable cache keys.
# Rails.application.config.active_record.cache_versioning = true

# Use AES-256-GCM authenticated encryption for encrypted cookies.
# Also, embed cookie expiry in signed or encrypted cookies for increased security.
#
# This option is not backwards compatible with earlier Rails versions.
# It's best enabled when your entire app is migrated and stable on 5.2.
#
# Existing cookies will be converted on read then written with the new scheme.
# Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true

# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages
# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true.
# Rails.application.config.active_support.use_authenticated_message_encryption = true

# Add default protection from forgery to ActionController::Base instead of in
# ApplicationController.
# Rails.application.config.action_controller.default_protect_from_forgery = true

# Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and
# 'f' after migrating old data.
# Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true

# Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header.
# Rails.application.config.active_support.use_sha1_digests = true

# Make `form_with` generate id attributes for any generated HTML tags.
# Rails.application.config.action_view.form_with_generates_ids = true



config/application.rb

module SampleApp

class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.1
end
end


調査対象の設定値

この記事では Rails.application.config.action_view.form_with_generates_ids に着目します。

この設定値は form_with メソッドでフォームを作成した際に、各入力項目に id 属性を自動的に付与するかどうか決める設定値です。


app/views/users/_form.html.erb

<%= form_with(model: user, local: true) do |form| %>

<!-- ... -->
<%= form.text_field :email %>
<!-- ... -->
<% end %>

<form action="/users" ...>

<!-- ... -->
<input type="text" name="user[email]">
<!-- または -->
<input id="user_mail" type="text" name="user[email]">
<!-- ... -->
</form>


調査結果

config.load_defaultsnew_framework_defaults_5_2.rbform_with_generates_ids の設定値の組み合わせによって、HTMLの出力内容がどうなるか調査したところ、次のような結果になりました。

load_defaults
5.1
5.1
5.1
5.2
5.2
5.2

form_with_generates_ids
コメントアウト
true
false
コメントアウト
true
false

id属性の有無
付かない
付く
付かない
付く
付く
付かない


わかったこと

上の結果からわかったことは以下のとおりです。



  • load_defaultsが5.1で、form_with_generates_idsがコメントアウトされていると、Rails 5.1相当の挙動になる


  • load_defaultsが5.2で、form_with_generates_idsがコメントアウトされていると、Rails 5.2で導入された新しいデフォルトの挙動になる


  • form_with_generates_idstrueまたはfalseにすると、load_defaultsの挙動を上書きすることができる

また、rails app:updateコマンドを実行して生成されるnew_framework_defaults_5_2.rbは、「Rails 5.2のデフォルトの挙動にするための設定」がコメントアウトされています。


config/initializers/new_framework_defaults_5_2.rb

# trueにするとRails 5.2のデフォルトの挙動に一致する

# Rails.application.config.action_view.form_with_generates_ids = true


実際にはどう活用するのがよいか

では実際のアップグレード作業ではどのように活用するのがよいでしょうか?

理想的には次のような手順になると思います。


  1. Rails 5.2にアップグレードし、rails app:updateコマンドを実行する


  2. load_defaults5.1のままで、new_framework_defaults_5_2.rbのコメントを1つずつ外し(つまり、Rails 5.2のデフォルトの挙動に変えて)、動作確認する


  3. new_framework_defaults_5_2.rbのコメントを全部外しても問題が起きなければ、load_defaults5.2に変更し、new_framework_defaults_5_2.rbを削除する


config/application.rb

module SampleApp

class Application < Rails::Application
# Rails 5.2のデフォルトの挙動で問題ないので5.1から5.2に変更
config.load_defaults 5.2
end
end


config/initializers/new_framework_defaults_5_2.rb

# 不要なので削除



新しいデフォルトの挙動では一部都合が悪い場合

一方、Rails 5.2のデフォルトの挙動では問題が起きる場合(たとえば、どうしてもid属性を自動的に付けたくない場合)は、次のような方法を取ることになるはずです。



  1. load_defaults5.2に変更する


  2. form_with_generates_idsの設定値をtrueではなくfalseにする(Rails 5.1相当の挙動にする)

  3. その他の項目(Rails 5.2のデフォルトの挙動に変えても問題ない項目)については、new_framework_defaults_5_2.rbの設定をコメントアウト(または削除)する


config/application.rb

module SampleApp

class Application < Rails::Application
# いったんすべてのデフォルトの挙動をRails 5.2相当にする
config.load_defaults 5.2
end
end


config/initializers/new_framework_defaults_5_2.rb

# 5.2のデフォルトの挙動では問題がある項目のみ、設定を上書きする

# (コメントを外すだけでなく、trueからfalse、またはfalseからtrueへ、というように、コメントを外す前の設定値を論理反転させる)
Rails.application.config.action_view.form_with_generates_ids = false

# 他の項目はRails 5.2のデフォルトでいいので削除



load_defaultsについてもう少し詳しく


過去のバージョンのデフォルト設定も同時に読み込まれる

load_defaultsで読み込んだデフォルト設定は、過去のRailsのバージョンのデフォルト設定も同時に読み込みます。

つまり、config.load_defaults 5.2と書いた場合は、以下のようにRails 5.2だけでなく、5.1と5.0のデフォルト設定値も反映されます。

Rails 5.0のデフォルトの設定値を反映

 ↓

Rails 5.1のデフォルトの設定値を反映

 ↓

Rails 5.2のデフォルトの設定値を反映

なお、上で示したように、この仕組みが使われるのはRails 5.0までです。

load_defaultsメソッドの実装はそこまで複雑ではないので、詳細な仕様を理解したい場合はソースコードを直接読むのが良いと思います。

https://github.com/rails/rails/blob/v5.2.3/railties/lib/rails/application/configuration.rb#L65-L122


load_defaultsもnew_framework_defaults_x_xも指定しない場合はどうなるのか

config.load_defaultsnew_framework_defaults_x_xもどちらも何も指定しない場合は、どの設定値が使われるのか予想が付きません。

たとえば、form_with_generates_idsはRails 5.1相当の挙動になりましたが、default_protect_from_forgeryはRails 5.2相当の挙動になりました。

このように、挙動の予想が付かなくなるので、load_defaultsには何らかの設定を入れておく方が良いでしょう。


config/application.rb

module SampleApp

class Application < Rails::Application
# こういうことをしてはいけない!!(load_defaultsを指定しない場合は、挙動の予想が付かない)
# config.load_defaults 5.2
end
end


参考文献