Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 7: Action CableがZeitwerkで読み込まれる(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

注: この改修は、現時点ではmainブランチにマージされていますが、7-0-stableブランチにはまだ含まれていません。

Rails 7: Action CableがZeitwerkで読み込まれる(翻訳)

Railsの読み込みは、Rails 6から組み込まれたZeitwerk gemによる新しく優れたオートロードに委ねられるようになりました。これがzeitwerkモードです。Rails 6まではActive Supportで実装されたclassicモードのオートローダーに切り替え可能でしたが、Rails 7からはclassicモードのオートローダーは含まれなくなりました。

残念ながら、RailsのネイティブモジュールがすべてZeitwerkに移行していたわけではありませんでした。

改修前

classicオートローダーは非常に有用ではありましたが、いくつかの問題を抱えているためにオートロードが若干複雑になって混乱することもありました。上位レベルでは、classicモードのファイル読み込みでModule.nestingModule.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のおかげで、多くの形式張ったコードが取り除かれました。

関連記事

Rails 7: ArelにSQLのFILTER句のサポートが追加(翻訳)

Rails 7: スキーマキャッシュが遅延読み込み可能になった(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。