Rails5の自動読み込みの仕様
production環境でのAutoloadの廃止
下記のように自前でautoload_pathsに追加したパスはproduction環境では自動読み込みされません。
# ./config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
例えば、上記の設定を記述した上でlib以下のファイルに定義されている定数を呼んでみると、production環境ではエラーが起きてしまいます。↓
# ./lib/test.rb
class Test
end
development環境
$ RAILS_ENV=development bundle exec rails c
Test
=> Test
(development環境ではlib以下が自動的に読み込まれる)
production環境
$ RAILS_ENV=production bundle exec rails c
Test
=> NameError: uninitialized constant Test
(production環境ではTestが読み込まれてない!!)
理由はシンプルで、Rails5からproduction環境でのみAutoloadが廃止になったからです。
development環境では自動読み込みされるため、知らないと事故になります。
参考) A Guide for Upgrading Ruby on Rails
app以下のファイルは今まで通り
app以下のファイルはproduction環境でも今まで通り自動読み込みされます。production環境で行なわれている自動読み込みは厳密に言うとAutoloadではなく、EagerLoadだからです。
app以下のようにデフォルトで自動読み込み設定してあるパスはRailsのソースコード上でちゃっかりEagerLoadの設定が記述されているため、Autoloadの仕組みが廃止されても未定義エラーになりません。
試しにapp以下のファイルに定義されているコードのそれぞれの環境での自動読み込みの違いを確認します。
# ./app/models/hoge.rb
class Hoge
end
test環境
$ RAILS_ENV=development bundle exec rails c
defined?(Hoge)
=> nil
Hoge
=> Hoge
production環境
$ RAILS_ENV=production bundle exec rails c
defined?(Hoge)
=> "constant"
Hoge
=> Hoge
development環境では定数が実際に呼ばれるまで存在しないのに対し、production環境でははじめから定数が存在しており、EagerLoadされているのがわかります。
解決策
解決策1: EagerLoadの設定をする
# ./config/application.rb
config.paths.add 'lib', eager_load: true
Railsのソースコードに書かれている自動読み込みの設定と同じ設定の記述法です。
この記述だと、development環境ではAutoloadされ、production環境ではEagerLoadされます。
解決策2: Autoloadをデフォルトでonにする
# ./config/application.rb
config.enable_dependency_loading = true
Rails5のアップグレードガイドにはこちらのやり方が書かれていました。
development環境でもproduction環境でもAutoloadされます。
解決策3: Autoloadを使わない
RailsのアップグレードガイドのAutoload廃止の項目には下記のような記述があり、Autoloadするファイルを自分で設定することは稀であるという認識のようです。
For the vast majority of applications this change needs no action. But in the very rare event that your application needs autoloading while running in production mode, set Rails.application.config.enable_dependency_loading to true.
上記の文章からはEagerLoadを推奨しているのかAutoloadを使わないことを推奨しているのかわかりませんが、Matzさんが数年前にAutoloadは使うなという議論をしているので使わないという選択肢を選ぶのもいいかもしれないです。
参考) https://bugs.ruby-lang.org/issues/5653
まとめ
Autoloadの是非はさておき、知らないでやると事故るので気をつけて下さい。
特に、development環境では気付くことができないのでとても厄介です。
参考資料
http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#autoloading-is-disabled-after-booting-in-the-production-environment
https://bugs.ruby-lang.org/issues/5653