Railsガイドにきちんと目を通して新しい知識を得る - Rails の自動読み込みと再読み込み編 -

ドキュメントを読み込むのは大事、ということでRailsガイドを頭から読んでいく取り組みをしています。 各章ごとに、(Railsガイドにちゃんと書いてあるのに)知らなかった機能を雑にまとめていきます。

今回は、Rails の自動読み込みと再読み込みの章です。

railsguides.jp

config.autoload_paths

リンクはこちら

app/下のディレクトリは自動読み込みされる。

例えばapp/samples/dummy.rbの中でDummyクラスを定義すると、rails consoleで起動した際などに読み込みができる。

一方で、例えばルートディレクトリ直下にsamples/dummy.rbというファイルを作った場合、samplesディレクトリは自動読み込みの対象外なのでDummyクラスは見つからない。

この場合に、config.autoload_pathsに登録することで自動読み込みの対象にできる。

# application.rb
config.autoload_paths << "#{root}/samples"

一度しか自動読み込みしないconfig.autoload_once_paths

リンクはこちら

アプリケーション起動時の一度だけ読み込まれるファイルを指定します。

config.enable_reloading = trueとの関係はどうなるんだろう?と思って開発環境で試してみました。

# development.rb
config.enable_reloading = true
config.autoload_once_paths << "#{root}/samples"

仮に上記のように設定した場合、samples配下のファイルを修正してリロードしても修正が反映されませんでした。

reload!

リンクはこちら

reloadをするとクラスが別物になります。アプリケーション内でreload!を入れることはあんまりなさそうですが、知っておくとハマりポイントを1つ回避できそうですね。

book_1 = Book.new
reload!
book_2 = Book.new

book_1.class.object_id
#=> 245440
book_2.class.object_id
#=> 462620

初期化時にリロード可能な定数を参照することは禁止

リンクはこちら

あまり使うケースがなく知らなかったのですが、config/initializers内でActiveRecordのクラスを読み込むなどしようとするとNameErrorで失敗します。

# config/initializers/sample.rb
p Book.new
rails s
# NameError

コードの変更(今回の例だとBook)があった場合ごとに読み込みたい場合はconfig.to_prepareを使えます。

# config/initializers/sample.rb
Rails.application.config.to_prepare { p Book.new }

もしくは、初期化時の1度だけ読み込めば良い時はafter_initializeを使えます。

# config/initializers/sample.rb
Rails.application.config.after_initialize { p Book.new }

Zeitwerkのcollapsing(折り畳み)機能

リンクはこちら

STI継承をするときなどに使えるメソッドとしてcollapsingが紹介されています。

これはディレクトリ構造をクラス名の名前空間に反映させたくなり場合などに使えます。

# app/models/samples/dummy.rb
class Dummy; end

上記だと、本来期待されるのはSamples::Dummyなので呼び出そうとしてもNameErrorが発生します。

これを例えばコンソールでcollapsingを試すと以下のようになります。

Dummy
#=> NameError)
Rails.autoloaders.main.collapse("#{Rails.root}/app/models/samples")

reload!

Dummy
#=> Dummy

push_dirでカスタム名前空間を作成する

リンクはこちら

collapseとは逆に、デフォルトの名前空間ディレクトリなしに作成したい時にはpush_dirが使えます。
Railsガイドに記載の通り、サービスクラスのクラスに対して、Services::名前空間を必要にしたい場合、ディレクトリ構成をapp/services/services/xxxとしなくても実現できます。