ruby.wasmでオートロードする - @ledsun blog でModule#const_missingをつかってオートローダーを書いてみました。
Module#const_missing
のAPIで、汎用的なオートローダーを書くのは少し難しそうなことがわかりました。
難しそうですが、どれくらい難しいのかよくわかっていません。
そこで、先人の知恵に頼ります。
具体的には、Rails 5.2 から6.0でオートローダーをclassicからzeitwerkに変更した出来事を調べます。
Rails 5.2までのオートローダーはModule#const_missing
を使って実装されています。
Rails 6.0以降のオートローダーzeitwerkはKernel.#autoloadを使って実装されています。
なぜでしょうか?
ClassicローダーからみるModule#const_missing
を使ったオートローダーの問題点
classicローダーのModule#const_missing
を使った実装は、おおむね動いていましたが細かい問題がありました。
わかりやすい例は Understanding Zeitwerk in Rails 6 | by Marcelo Casiraghi | Cedarcode | Medium で、説明されています。
次のような例が挙げられています。
# app/models/user.rb class User < ApplicationRecord end # app/models/admin/user.rb module Admin class User < ApplicationRecord end end # app/models/admin/user_manager.rb module Admin class UserManager def self.all User.all # Want to load all admin users end end end
UserManager内でUserを呼び出したときに、Module#const_missing
では、呼び出された物がUserかAdmin::Userかわかりません。
Module#const_missing
の引数には、どちらの場合であってもシンボル:User
が渡されます。
その他の多くの問題がRails 5.2のRailsガイドで列挙されています。
Module#const_missing
でオートローダーを実装するには、とても多くの問題があるようです。
Zeitwerkのとった解決方法
これらの問題を解決するためにzeitwerkはKernel.#autoload
を使って再実装されました。
Kernel.#autoload
はモジュールを読み込むパスを指定できます。
モジュールが必要になったときに指定したパスをつかってKernel.#autoload
します。
つまり、オートロードです。
正確にはオートロードに使うヒントをRuby VMに渡します。
zeitwerkでは、起動時にsetupメソッドを使ってルートディレクトリからモジュールのファイルパスを収集し、Kernel.#autoload
を呼び出します。
Kernel.#autoload
を使うには、モジュール呼び出しより前に、モジュールの実体ファイルのパスを調べます。
ruby.wasmへの応用を考える
ruby.wasmでは、この方式は厳しいです。 サーバーに対してパス探索をかけたくありません。 自動的にモジュールのパスを集めるのは難しいです。
さらに前の段階でモジュールパスを調査しておいて、import mapsのようにブラウザに渡せばできるでしょう。
ですが、オートロードがうれしいのは設定が要らない点です。設定より規約(Convention over Configuration、CoC)です。
マップを作ってブラウザに渡すのは設定です。
Kernel#require_relative
を各Rubyスクリプトファイルに書くのと同じです。