はじめに
通常のRubyでは、親要素を継承した子要素を定義する(依存関係のある)場合はファイルをrequireして、明示的に読み込む必要があります。しかし、Railsでは、親要素のファイルをrequireせずに継承しているものがあることに気づいたので、そのことについて調べてみました。
#rubyでは、本来以下のような記述が必要だが、rails では不要。
#require ‘/application_controller.rb’
class UsersCotroller < ApplicationController
・・・
end
結論
Rails 6.0以降は、Zeitwerkモードでの自動読み込み(オートロード)および再読み込みの仕組みにより、requireせずとも、ファイルが自動読み込みされている。
Zeitwerkとは
Ruby用に作成されたコードローダーの仕組み
プロジェクトで定義されたクラスやモジュールを必要な時(autoloding)もしくは、事前に一括で(eager loading)読み込みます。
ファイルの認識方法
クラスやモジュール名を名前空間に持ったファイル名を定数として表すことで、ファイル構造を認識します。
lib/my_gem.rb -> MyGem
lib/my_gem/foo.rb -> MyGem::Foo
lib/my_gem/bar_baz.rb -> MyGem::BarBaz
lib/my_gem/woo/zoo.rb -> MyGem::Woo::Zoo
参考:fxn/zeitwerk
Rails Zeitwerkモードについて
Rails 6.0以降のバージョンでは、コードローダーとして、Zeitwerkが導入されました。
config.autoload_paths
Rails では、中身が自動読み込みの対象となるアプリケーションディレクトリ(app/modelsなど)の配列を自動読み込みパス(autoload path)として内部で定義しています。
デフォルトでは、あるアプリケーションの自動読み込みパスは次のもので構成されています。
- アプリケーションの起動時にappの下にあるすべてのサブディレクトリ(assets、javascripts、viewsは除外)
- アプリケーションが依存する可能性のあるエンジンの自動読み込みパス。
また、自動読み込みパスは、appの下のあらゆるカスタムディレクトリを自動的に扱います。たとえば、アプリケーションにapp/presentersがあると、自動読み込みの設定を変更しなくてもapp/presentersの下にあるものをすぐ利用できます。
自動読み込みパス(配列)の拡張方法
config/application.rbまたはconfig/environments/*.rb内で
config.autoload_pathsに追加することで拡張可能です。
module MyApplication
class Application < Rails::Application
config.autoload_paths << "#{root}/extras"
end
end
ファイル名の定数化(String#camelize)
自動読み込みパス(autoload path)のディレクトリはルート名前空間Objectを表します。
自動読み込みパス(ルート名前空間Object)の下にあるファイル名は、Zeitwerkのドキュメントに記載されているとおりに定義された定数と一致しなければなりません。そのため
Rails では、ファイル名を定数に変換するString#camelizeメソッドが用意されています。
例えば、app/controllers/users_controller.rbは以下のように、UsersControllerという定数を定義します。
"users_controller".camelize # => UsersController
自動読み込みのパフォーマンスを上げる設定
自動読み込みパスはデフォルトで、$LOAD_PATHという環境変数に追加されます。しかし、Zeitwerkを使用する場合は、別に絶対ファイル名で定義された自動読み込みパス(autoload path)のみを使用するので、この環境変数は不要となります。
config.add_autoload_paths_to_load_path = false
こうすることで探索量が削減されて、正しいrequire呼び出しがわずかに高速化される可能性があります。また、アプリケーションでBootsnapを使っている場合も、ライブラリの不要なインデックス構築や、必要なメモリ量が節約されます。
再読み込み
Railsアプリケーションのファイルが変更されると、クラスやモジュールを自動的に再読み込みします。
正確に言うと、Webサーバーが実行中の状態でアプリケーションのファイルが変更されると、Railsは次のリクエストが処理される直前に、mainオートローダが管理しているすべての定数をアンロードします。これによって、アプリケーションでリクエスト継続中に使われるクラスやモジュールが自動読み込みされるようになり、続いてファイルシステム上の現在の実装が反映されます。
再読み込みの制御
再読み込みは有効にも無効にもできます。この振る舞いを制御するのはconfig.cache_classes設定です。これはdevelopmentモードではデフォルトでfalse(再読み込みが有効)、productionモードではtrue(再読み込みが無効)になります。
Zeitwerkの自動読み込みパスに属さないもの
libディレクトリにあるものや、Ruby標準ライブラリ、Ruby gemなど
これらは、デフォルトではZeitwerkの自動読み込みパスに属さないため、requireが必要です。
libディレクトリは、Zeitwerkの自動読み込みパスに属しませんが、$LOAD_PATHには属しているため、requireするだけで読み込めます。再読み込みができないクラスやモジュールを起動時に読み込みたいなどの場合は、このディレクトリを使用しましょう。
参考:定数の自動読み込みと再読み込み (Zeitwerk) - Railsガイド (railsguides.jp)
まとめ
- Rails6.0以降では、Zeitwerkを導入しているため、依存関係のあるファイルをrequireで明示的に読み込む必要がない。
- Zeitwerkとは、Ruby用の自動読み込み(オートロード)および再読み込みの仕組み。
- Zeitwerkは、クラスやモジュール名を名前空間に持ったファイル名を定数として表すことで、ファイル構造を認識する。
- Railsでは、ファイル構造を定数化するメソッドが用意されている。(String#camelize)
- Railsでは、appディレクトリ配下のファイルが自動読み込み対象ファイルとなる。ただし、(assets、javascripts、views)は除外される。
- 自動読み込みパス(autoload path)は、配列として定義されている。
- libディレクトリにあるものや、Ruby標準ライブラリ、Ruby gemなどZeitwerkの自動読み込みパスに属さないものがある。
参考
fxn/zeitwerk
定数の自動読み込みと再読み込み (Zeitwerk)
【Rails】Zeitwerkとの歩き方