Rails 7: Action CableがZeitwerkで読み込まれる(翻訳)
Railsの読み込みは、Rails 6から組み込まれたZeitwerk gemによる新しく優れたオートロードに委ねられるようになりました。これがzeitwerkモードです。Rails 6まではActive Supportで実装されたclassicモードのオートローダーに切り替え可能でしたが、Rails 7からはclassicモードのオートローダーは含まれなくなりました。
残念ながら、RailsのネイティブモジュールがすべてZeitwerkに移行していたわけではありませんでした。
改修前
classicオートローダーは非常に有用ではありましたが、いくつかの問題を抱えているためにオートロードが若干複雑になって混乱することもありました。上位レベルでは、classicモードのファイル読み込みでModule.nesting
とModule.ancestors
を用いて探索していました。この探索は端から順にスキャンするため、最初にマッチしたものが読み込まれます。ほとんどの場合はこれでうまくいきますが、classicモードではわかりにくいエラーに遭遇することもありました。Zeitwerkは、オートロードされる定数を登録するというまったく別のアプローチを採用したことで、読み込み順への依存が取り除かれます。
簡単な例を見てみましょう。
# 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 # すべてのadminユーザーを読み込みたい
end
end
end
classicモードの場合、Admin::UserManager
が読み込まれてからAdmin::User
が読み込まれると、User.all
呼び出しでAdmin::User
ではなくUser
が読み込まれてしまいます。
しかしZeitwerkモードのRailsではZeitwek#setup
が呼び出されます。このメソッドは、既知のすべてのautoload_paths
に対するautoloaders
のセットアップを引き受けます。上の例は以下のように読み込まれます。
autoload :User, Rails.root.join('app/models/user.rb')
autoload :Admin::User, Rails.root.join('app/models/admin/user.rb')
autoload :Admin::UserManager, Rails.root.join('app/models/admin/user_manager.rb')
Action Cableの話に戻ると、この変更以前は、Action Cableのすべての依存関係を、const_missing
エラーコールバックに遭遇しない形で明示的にオートロードしなければなりませんでした。
# actioncable/lib/action_cable.rb
autoload :Server
autoload :Connection
autoload :Channel
autoload :RemoteConnections
autoload :SubscriptionAdapter
autoload :TestHelper
autoload :TestCase
改修後
#44618のおかげで、多くの形式張ったコードが取り除かれました。
概要
元サイトの許諾を得て翻訳・公開いたします。
注: この改修は、現時点ではmainブランチにマージされていますが、7-0-stableブランチにはまだ含まれていません。