Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

参考文献

jnchito
SIer、社内SEを経て、ソニックガーデンに合流したプログラマ。 「プロを目指す人のためのRuby入門」の著者。 http://gihyo.jp/book/2017/978-4-7741-9397-7 および「Everyday Rails - RSpecによるRailsテスト入門」の翻訳者。 https://leanpub.com/everydayrailsrspec-jp
https://blog.jnito.com/
sonicgarden
「お客様に無駄遣いをさせない受託開発」と「習慣を変えるソフトウェアのサービス」に取り組んでいるソフトウェア企業
http://www.sonicgarden.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした