更多資訊請參考 rubyonrails.org:

1 簡介

本指南說明 Rails 應用程式中的自動載入、重新載入和急切載入。

在一般的 Ruby 程式中,您會明確載入定義您想要使用的類別和模組的檔案。例如,以下控制器參考 ApplicationControllerPost,您通常會為它們發出 require 呼叫

# DO NOT DO THIS.
require "application_controller"
require "post"
# DO NOT DO THIS.

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

在 Rails 應用程式中並非如此,應用程式的類別和模組在任何地方都可用,而無需 require 呼叫

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

Rails 會在需要時為您自動載入它們。這歸功於 Rails 代表您設定的幾個 Zeitwerk 載入器,它們提供自動載入、重新載入和急切載入。

另一方面,這些載入器不管理任何其他東西。特別是,它們不管理 Ruby 標準函式庫、gem 相依性、Rails 組件本身,甚至(預設情況下)應用程式的 lib 目錄。這些程式碼必須像往常一樣載入。

2 專案結構

在 Rails 應用程式中,檔案名稱必須與它們定義的常數相符,目錄則作為命名空間。

例如,檔案 app/helpers/users_helper.rb 應定義 UsersHelper,而檔案 app/controllers/admin/payments_controller.rb 應定義 Admin::PaymentsController

預設情況下,Rails 設定 Zeitwerk 以使用 String#camelize 來變形檔案名稱。例如,它期望 app/controllers/users_controller.rb 定義常數 UsersController,因為這是 "users_controller".camelize 回傳的結果。

下方的「自訂變形」一節說明覆寫此預設值的方法。

請查閱 Zeitwerk 文件以取得更多詳細資訊。

3 config.autoload_paths

我們將要自動載入和(可選地)重新載入其內容的應用程式目錄列表稱為自動載入路徑。例如,app/models。這些目錄代表根命名空間:Object

自動載入路徑在 Zeitwerk 文件中稱為根目錄,但我們在本指南中將使用「自動載入路徑」。

在自動載入路徑中,檔案名稱必須與它們定義的常數相符,如此處所述。

預設情況下,應用程式的自動載入路徑包含應用程式啟動時存在的所有 app 子目錄(assetsjavascriptviews 除外),以及它可能相依的引擎的自動載入路徑。

例如,如果 UsersHelperapp/helpers/users_helper.rb 中實作,則模組是可自動載入的,您不需要(也不應該編寫)對其進行 require 呼叫

$ bin/rails runner 'p UsersHelper'
UsersHelper

Rails 會自動將 app 下的自訂目錄新增至自動載入路徑。例如,如果您的應用程式有 app/presenters,則您無需設定任何內容即可自動載入 presenter;它開箱即用。

可以透過推送至 config.autoload_paths 來擴展預設自動載入路徑陣列,在 config/application.rbconfig/environments/*.rb 中。例如

module MyApplication
  class Application < Rails::Application
    config.autoload_paths << "#{root}/extras"
  end
end

此外,引擎可以在引擎類別的主體及其自身的 config/environments/*.rb 中推送。

請不要變更 ActiveSupport::Dependencies.autoload_paths;變更自動載入路徑的公用介面是 config.autoload_paths

您無法在應用程式啟動時自動載入自動載入路徑中的程式碼。特別是在 config/initializers/*.rb 中。請查看下方的應用程式啟動時的自動載入,了解執行此操作的有效方法。

自動載入路徑由 Rails.autoloaders.main 自動載入器管理。

4 config.autoload_lib(ignore:)

預設情況下,lib 目錄不屬於應用程式或引擎的自動載入路徑。

設定方法 config.autoload_lib 會將 lib 目錄新增至 config.autoload_pathsconfig.eager_load_paths。它必須從 config/application.rbconfig/environments/*.rb 中呼叫,並且不適用於引擎。

通常,lib 有不應由自動載入器管理的子目錄。請在所需的 ignore 關鍵字引數中傳遞相對於 lib 的名稱。例如

config.autoload_lib(ignore: %w(assets tasks))

為什麼?雖然 assetstasks 與一般 Ruby 程式碼共用 lib 目錄,但它們的內容不應被重新載入或急切載入。

ignore 清單應包含所有不包含 .rb 副檔名的檔案,或不應重新載入或急切載入的 lib 子目錄。例如,

config.autoload_lib(ignore: %w(assets tasks templates generators middleware))

config.autoload_lib 在 7.1 之前不可用,但只要應用程式使用 Zeitwerk,您仍然可以模擬它

# config/application.rb
module MyApp
  class Application < Rails::Application
    lib = root.join("lib")

    config.autoload_paths << lib
    config.eager_load_paths << lib

    Rails.autoloaders.main.ignore(
      lib.join("assets"),
      lib.join("tasks"),
      lib.join("generators")
    )

    # ...
  end
end

5 config.autoload_once_paths

您可能希望能夠自動載入類別和模組,而無需重新載入它們。autoload_once_paths 設定會儲存可以自動載入但不會重新載入的程式碼。

預設情況下,此集合是空的,但您可以透過推送至 config.autoload_once_paths 來擴展它。您可以在 config/application.rbconfig/environments/*.rb 中執行此操作。例如

module MyApplication
  class Application < Rails::Application
    config.autoload_once_paths << "#{root}/app/serializers"
  end
end

此外,引擎可以在引擎類別的主體及其自身的 config/environments/*.rb 中推送。

如果將 app/serializers 推送至 config.autoload_once_paths,則 Rails 不再將其視為自動載入路徑,即使它是 app 下的自訂目錄。此設定會覆寫該規則。

這對於快取在重新載入後仍然存在的類別和模組至關重要,例如 Rails 框架本身。

例如,Active Job 序列化程式儲存在 Active Job 內部

# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer

而且在重新載入時,Active Job 本身不會重新載入,只有自動載入路徑中的應用程式和引擎程式碼會重新載入。

MoneySerializer 可重新載入會令人困惑,因為重新載入編輯過的版本不會對儲存在 Active Job 中的該類別物件產生任何影響。事實上,如果 MoneySerializer 是可重新載入的,則從 Rails 7 開始,這樣的初始化程式將會引發 NameError

另一個使用案例是引擎裝飾框架類別時

initializer "decorate ActionController::Base" do
  ActiveSupport.on_load(:action_controller_base) do
    include MyDecoration
  end
end

在那裡,初始化程式執行時儲存在 MyDecoration 中的模組物件會變成 ActionController::Base 的祖先,而重新載入 MyDecoration 是沒有意義的,它不會影響該祖先鏈。

來自自動載入一次路徑的類別和模組可以在 config/initializers 中自動載入。因此,透過該設定,此操作有效

# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer

從技術上講,您可以在 :bootstrap_hook 之後執行的任何初始化程式中自動載入由 once 自動載入器管理的類別和模組。

自動載入一次路徑由 Rails.autoloaders.once 管理。

6 config.autoload_lib_once(忽略:)

config.autoload_lib_once 方法與 config.autoload_lib 類似,差別在於它會將 lib 加入至 config.autoload_once_paths。它必須從 config/application.rbconfig/environments/*.rb 中呼叫,而且不適用於引擎。

透過呼叫 config.autoload_lib_oncelib 中的類別和模組可以被自動載入,甚至可以從應用程式的初始化程式中載入,但不會重新載入。

config.autoload_lib_once 在 7.1 之前的版本中不可用,但只要應用程式使用 Zeitwerk,您仍然可以模擬它。

# config/application.rb
module MyApp
  class Application < Rails::Application
    lib = root.join("lib")

    config.autoload_once_paths << lib
    config.eager_load_paths << lib

    Rails.autoloaders.once.ignore(
      lib.join("assets"),
      lib.join("tasks"),
      lib.join("generators")
    )

    # ...
  end
end

7 重新載入

如果自動載入路徑中的應用程式檔案發生變更,Rails 會自動重新載入類別和模組。

更精確地說,如果 Web 伺服器正在運行,且應用程式檔案已修改,Rails 會在處理下一個請求之前,卸載由 main 自動載入器管理的所有自動載入常數。這樣一來,在該請求期間使用的應用程式類別或模組將會再次自動載入,從而採用檔案系統中目前的實作。

重新載入可以啟用或停用。控制此行為的設定是 config.enable_reloading,預設在 development 模式下為 true,在 production 模式下為 false。為了向後相容性,Rails 也支援 config.cache_classes,它等同於 !config.enable_reloading

Rails 預設使用事件驅動的檔案監視器來偵測檔案變更。它可以設定為透過走訪自動載入路徑來偵測檔案變更。這由 config.file_watcher 設定控制。

在 Rails 主控台中,無論 config.enable_reloading 的值為何,都沒有啟用的檔案監視器。這是因為,通常在主控台工作階段中重新載入程式碼會造成混淆。與個別請求類似,您通常希望主控台工作階段由一組一致且不變更的應用程式類別和模組提供服務。

但是,您可以在主控台中執行 reload! 來強制重新載入。

irb(main):001:0> User.object_id
=> 70136277390120
irb(main):002:0> reload!
Reloading...
=> true
irb(main):003:0> User.object_id
=> 70136284426020

如您所見,重新載入後,儲存在 User 常數中的類別物件會有所不同。

7.1 重新載入和過時物件

務必了解 Ruby 沒有真正重新載入記憶體中的類別和模組的方法,並讓它們反映到已使用的所有位置。從技術上講,「卸載」User 類別表示透過 Object.send(:remove_const, "User") 移除 User 常數。

例如,請查看此 Rails 主控台工作階段

irb> joe = User.new
irb> reload!
irb> alice = User.new
irb> joe.class == alice.class
=> false

joe 是原始 User 類別的實例。當發生重新載入時,User 常數會評估為不同的、重新載入的類別。alice 是新載入的 User 的實例,但 joe 不是,他的類別已過時。您可以再次定義 joe、啟動 IRB 子工作階段,或只是啟動新的主控台,而不是呼叫 reload!

您可能會發現這個陷阱的另一種情況是在未重新載入的地方建立可重新載入類別的子類別

# lib/vip_user.rb
class VipUser < User
end

如果重新載入 User,由於 VipUser 沒有重新載入,因此 VipUser 的父類別是原始過時的類別物件。

重點:不要快取可重新載入的類別或模組

8 應用程式啟動時自動載入

在啟動時,應用程式可以從自動載入一次的路徑自動載入,這些路徑由 once 自動載入器管理。請檢查上方 config.autoload_once_paths 區段。

但是,您無法從由 main 自動載入器管理的自動載入路徑自動載入。這適用於 config/initializers 中的程式碼以及應用程式或引擎初始化程式。

為什麼?初始化程式只在應用程式啟動時執行一次。它們不會在重新載入時再次執行。如果初始化程式使用可重新載入的類別或模組,對它們的編輯將不會反映在該初始程式碼中,因此會變成過時。因此,不允許在初始化期間參考可重新載入的常數。

讓我們看看該怎麼做。

8.1 使用案例 1:在啟動期間,載入可重新載入的程式碼

8.1.1 在啟動時和每次重新載入時自動載入

假設 ApiGateway 是一個可重新載入的類別,您需要在應用程式啟動時設定其端點

# config/initializers/api_gateway_setup.rb
ApiGateway.endpoint = "https://example.com" # NameError

初始化程式無法參考可重新載入的常數,您需要將其包裝在 to_prepare 區塊中,該區塊會在啟動時和每次重新載入後執行

# config/initializers/api_gateway_setup.rb
Rails.application.config.to_prepare do
  ApiGateway.endpoint = "https://example.com" # CORRECT
end

由於歷史原因,此回呼可能會執行兩次。它執行的程式碼必須是等冪的。

8.1.2 僅在啟動時自動載入

可重新載入的類別和模組也可以在 after_initialize 區塊中自動載入。這些區塊會在啟動時執行,但不會在重新載入時再次執行。在某些特殊情況下,這可能正是您想要的。

預先檢查是此類情況的使用案例

# config/initializers/check_admin_presence.rb
Rails.application.config.after_initialize do
  unless Role.where(name: "admin").exists?
    abort "The admin role is not present, please seed the database."
  end
end

8.2 使用案例 2:在啟動期間,載入保留快取的程式碼

某些設定會採用類別或模組物件,並將其儲存在不會重新載入的位置。重要的是,這些物件不可重新載入,因為編輯不會反映在那些快取的過時物件中。

一個範例是中介軟體

config.middleware.use MyApp::Middleware::Foo

當您重新載入時,中介軟體堆疊不受影響,因此 MyApp::Middleware::Foo 可重新載入會造成混淆。對其實作所做的變更不會產生任何影響。

另一個範例是 Active Job 序列化程式

# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer

在初始化期間 MoneySerializer 評估的任何內容都會被推送至自訂序列化程式,而且該物件會保留在重新載入時。

另一個範例是透過包含模組來裝飾框架類別的 railties 或引擎。例如,turbo-rails 會以這種方式裝飾 ActiveRecord::Base

initializer "turbo.broadcastable" do
  ActiveSupport.on_load(:active_record) do
    include Turbo::Broadcastable
  end
end

這會在 ActiveRecord::Base 的祖先鏈中新增模組物件。如果重新載入 Turbo::Broadcastable,則其變更不會產生任何影響,祖先鏈仍會具有原始的物件。

推論:這些類別或模組不可重新載入

組織這些檔案的慣用方式是將它們放在 lib 目錄中,並在需要時使用 require 載入它們。例如,如果應用程式在 lib/middleware 中有自訂中介軟體,請在設定它之前發出常規的 require 呼叫

require "middleware/my_middleware"
config.middleware.use MyMiddleware

此外,如果 lib 在自動載入路徑中,請設定自動載入器以忽略該子目錄

# config/application.rb
config.autoload_lib(ignore: %w(assets tasks ... middleware))

因為您自己正在載入這些檔案。

如上所述,另一個選項是讓定義它們的目錄位於自動載入一次的路徑中並自動載入。請檢查 關於 config.autoload_once_paths 的章節以取得詳細資訊。

8.3 使用案例 3:為引擎設定應用程式類別

假設引擎使用對使用者建模的可重新載入應用程式類別,並且具有它的設定點

# config/initializers/my_engine.rb
MyEngine.configure do |config|
  config.user_model = User # NameError
end

為了能夠與可重新載入的應用程式程式碼正常運作,引擎需要應用程式設定該類別的名稱

# config/initializers/my_engine.rb
MyEngine.configure do |config|
  config.user_model = "User" # OK
end

然後,在執行階段,config.user_model.constantize 會提供您目前的類別物件。

9 預先載入

在類似於生產環境的環境中,通常最好在應用程式啟動時載入所有應用程式程式碼。預先載入會將所有內容放入記憶體中,準備好立即提供請求,而且它也與 CoW 相容。

預先載入由標記 config.eager_load 控制,預設在除 production 之外的所有環境中停用。當執行 Rake 工作時,config.eager_load 會被 config.rake_eager_load 覆寫,後者預設為 false。因此,預設情況下,在生產環境中,Rake 工作不會預先載入應用程式。

檔案預先載入的順序未定義。

在預先載入期間,Rails 會呼叫 Zeitwerk::Loader.eager_load_all。這可確保也會預先載入 Zeitwerk 管理的所有 gem 相依性。

10 單表繼承

單表繼承與延遲載入不相容:Active Record 必須知道 STI 階層才能正常運作,但在延遲載入時,類別只會在需要時才精確載入!

為了解決這個根本的不匹配,我們需要預先載入 STI。有幾個選項可以完成此操作,並具有不同的權衡。讓我們來看看它們。

10.1 選項 1:啟用預先載入

預先載入 STI 的最簡單方法是透過設定來啟用預先載入

config.eager_load = true

config/environments/development.rbconfig/environments/test.rb 中。

這很簡單,但可能會付出高昂的代價,因為它會在啟動時和每次重新載入時預先載入整個應用程式。不過,對於小型應用程式而言,這種權衡可能是值得的。

10.2 選項 2:預先載入折疊的目錄

將定義階層的檔案儲存在專用目錄中,這在概念上也說得通。該目錄並非旨在表示命名空間,其唯一目的是將 STI 分組

app/models/shapes/shape.rb
app/models/shapes/circle.rb
app/models/shapes/square.rb
app/models/shapes/triangle.rb

在此範例中,我們仍然希望 app/models/shapes/circle.rb 定義 Circle,而不是 Shapes::Circle。這可能是您個人偏好讓事情保持簡單,同時也避免在現有程式碼庫中重構。Zeitwerk 的 折疊 功能允許我們這樣做

# config/initializers/preload_stis.rb

shapes = "#{Rails.root}/app/models/shapes"
Rails.autoloaders.main.collapse(shapes) # Not a namespace.

unless Rails.application.config.eager_load
  Rails.application.config.to_prepare do
    Rails.autoloaders.main.eager_load_dir(shapes)
  end
end

在此選項中,即使未使用 STI,我們也會在啟動時和重新載入時預先載入這幾個檔案。但是,除非您的應用程式有很多 STI,否則這不會產生任何可測量的影響。

Zeitwerk::Loader#eager_load_dir 方法已在 Zeitwerk 2.6.2 中新增。對於舊版本,您仍然可以列出 app/models/shapes 目錄並在其內容上呼叫 require_dependency

如果從 STI 新增、修改或刪除模型,重新載入會如預期般運作。但是,如果應用程式中新增了新的獨立 STI 階層,您需要編輯初始化程式並重新啟動伺服器。

10.3 選項 3:預先載入常規目錄

與前一個類似,但該目錄旨在作為命名空間。也就是說,app/models/shapes/circle.rb 預期定義 Shapes::Circle

對於這個,初始化程式是相同的,只是沒有設定折疊

# config/initializers/preload_stis.rb

unless Rails.application.config.eager_load
  Rails.application.config.to_prepare do
    Rails.autoloaders.main.eager_load_dir("#{Rails.root}/app/models/shapes")
  end
end

相同的權衡。

10.4 選項 4:從資料庫預先載入類型

在此選項中,我們不需要以任何方式組織檔案,但我們會存取資料庫

# config/initializers/preload_stis.rb

unless Rails.application.config.eager_load
  Rails.application.config.to_prepare do
    types = Shape.unscoped.select(:type).distinct.pluck(:type)
    types.compact.each(&:constantize)
  end
end

即使資料表中沒有所有類型,STI 也能正常運作,但類似於 subclassesdescendants 的方法不會傳回遺失的類型。

如果從 STI 新增、修改或刪除模型,重新載入會如預期般運作。但是,如果應用程式中新增了新的獨立 STI 階層,您需要編輯初始化程式並重新啟動伺服器。

11 自訂變形

預設情況下,Rails 會使用 String#camelize 來知道給定的檔案或目錄名稱應定義哪個常數。例如,posts_controller.rb 應定義 PostsController,因為這是 "posts_controller".camelize 傳回的結果。

有些特定的檔案或目錄名稱可能不會按照您想要的方式進行詞形變化。例如,預設情況下,html_parser.rb 應該定義 HtmlParser。如果您偏好類別名稱為 HTMLParser 該怎麼辦?有幾種方法可以自訂此行為。

最簡單的方法是定義縮寫。

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym "HTML"
  inflect.acronym "SSL"
end

這樣做會影響 Active Support 在全域範圍內如何進行詞形變化。這在某些應用程式中可能沒問題,但您也可以透過將一組覆寫傳遞給預設的詞形變化器,自訂如何將個別基本名稱轉換為駝峰式大小寫,使其獨立於 Active Support。

Rails.autoloaders.each do |autoloader|
  autoloader.inflector.inflect(
    "html_parser" => "HTMLParser",
    "ssl_error"   => "SSLError"
  )
end

不過,這種方法仍然依賴 String#camelize,因為這是預設詞形變化器用作回退的方式。如果您不想依賴 Active Support 的詞形變化,並希望完全控制詞形變化,請將詞形變化器設定為 Zeitwerk::Inflector 的實例。

Rails.autoloaders.each do |autoloader|
  autoloader.inflector = Zeitwerk::Inflector.new
  autoloader.inflector.inflect(
    "html_parser" => "HTMLParser",
    "ssl_error"   => "SSLError"
  )
end

沒有全域設定可以影響這些實例;它們是決定性的。

您甚至可以定義自訂的詞形變化器,以獲得完全的彈性。請查看 Zeitwerk 文件以了解更多詳細資訊。

11.1 詞形變化自訂應該放在哪裡?

如果應用程式未使用 once 自動載入器,則上述程式碼片段可以放在 config/initializers 中。例如,Active Support 用例的 config/initializers/inflections.rb,或其他用例的 config/initializers/zeitwerk.rb

使用 once 自動載入器的應用程式必須將此設定移動或從 config/application.rb 中應用程式類別的主體載入,因為 once 自動載入器在啟動過程的早期就使用詞形變化器。

12 自訂命名空間

如我們在上面看到的,自動載入路徑代表頂層命名空間:Object

讓我們以 app/services 為例。這個目錄預設情況下不會產生,但如果它存在,Rails 會自動將其新增到自動載入路徑。

預設情況下,檔案 app/services/users/signup.rb 應該定義 Users::Signup,但如果您希望整個子樹位於 Services 命名空間下,該怎麼辦?嗯,使用預設設定,可以透過建立子目錄來完成:app/services/services

然而,根據您的喜好,這可能感覺不太對勁。您可能更喜歡讓 app/services/users/signup.rb 簡單地定義 Services::Users::Signup

Zeitwerk 支援自訂根命名空間以解決此用例,您可以自訂 main 自動載入器來實現此目的。

# config/initializers/autoloading.rb

# The namespace has to exist.
#
# In this example we define the module on the spot. Could also be created
# elsewhere and its definition loaded here with an ordinary `require`. In
# any case, `push_dir` expects a class or module object.
module Services; end

Rails.autoloaders.main.push_dir("#{Rails.root}/app/services", namespace: Services)

Rails 7.1 之前的版本不支援此功能,但您仍然可以在同一個檔案中新增此額外程式碼並使其正常運作。

# Additional code for applications running on Rails < 7.1.
app_services_dir = "#{Rails.root}/app/services" # has to be a string
ActiveSupport::Dependencies.autoload_paths.delete(app_services_dir)
Rails.application.config.watchable_dirs[app_services_dir] = [:rb]

once 自動載入器也支援自訂命名空間。但是,由於它在啟動過程的早期設定,因此無法在應用程式初始化器中完成設定。相反地,請將其放置在 config/application.rb 中,例如。

13 自動載入與引擎

引擎在父應用程式的上下文中執行,它們的程式碼由父應用程式自動載入、重新載入和預先載入。如果應用程式以 zeitwerk 模式執行,則引擎程式碼會由 zeitwerk 模式載入。如果應用程式以 classic 模式執行,則引擎程式碼會由 classic 模式載入。

當 Rails 啟動時,引擎目錄會新增到自動載入路徑,從自動載入器的角度來看,沒有任何差異。自動載入器的主要輸入是自動載入路徑,它們屬於應用程式原始程式碼樹還是某些引擎原始程式碼樹並不重要。

例如,此應用程式使用 Devise

$ bin/rails runner 'pp ActiveSupport::Dependencies.autoload_paths'
[".../app/controllers",
 ".../app/controllers/concerns",
 ".../app/helpers",
 ".../app/models",
 ".../app/models/concerns",
 ".../gems/devise-4.8.0/app/controllers",
 ".../gems/devise-4.8.0/app/helpers",
 ".../gems/devise-4.8.0/app/mailers"]

如果引擎控制其父應用程式的自動載入模式,則可以像平常一樣編寫引擎。

但是,如果引擎支援 Rails 6 或 Rails 6.1,並且不控制其父應用程式,則它必須準備好在 classiczeitwerk 模式下執行。需要考慮的事項:

  1. 如果 classic 模式需要 require_dependency 呼叫來確保在某個時間點載入某些常數,請編寫它。雖然 zeitwerk 不需要它,但它不會造成傷害,它也會在 zeitwerk 模式下工作。

  2. classic 模式會將常數名稱加上底線 ("User" -> "user.rb"),而 zeitwerk 模式會將檔案名稱轉換為駝峰式大小寫 ("user.rb" -> "User")。它們在大多數情況下一致,但如果存在一系列連續的大寫字母,如 "HTMLParser" 時,則不一致。相容的最簡單方法是避免使用此類名稱。在這種情況下,請選擇 "HtmlParser"。

  3. classic 模式中,允許檔案 app/model/concerns/foo.rb 定義 FooConcerns::Foo。在 zeitwerk 模式中,只有一個選項:它必須定義 Foo。為了相容,請定義 Foo

14 測試

14.1 手動測試

任務 zeitwerk:check 會檢查專案樹是否遵循預期的命名慣例,並且對於手動檢查非常方便。例如,如果您要從 classic 模式遷移到 zeitwerk 模式,或者如果您要修正某些內容。

$ bin/rails zeitwerk:check
Hold on, I am eager loading the application.
All is good!

可能會根據應用程式設定顯示其他輸出,但最後的 "All is good!" 是您要尋找的。

14.2 自動化測試

在測試套件中驗證專案是否正確預先載入是一種好習慣。

這涵蓋了 Zeitwerk 命名合規性和其他可能的錯誤情況。請查看 關於測試預先載入的部分測試 Rails 應用程式指南中。

15 疑難排解

追蹤載入器運作方式的最佳方法是檢查它們的活動。

最簡單的方法是在 config/application.rb 中包含

Rails.autoloaders.log!

在載入框架預設值之後。這會將追蹤列印到標準輸出。

如果您偏好記錄到檔案,請改為設定此項

Rails.autoloaders.logger = Logger.new("#{Rails.root}/log/autoloading.log")

config/application.rb 執行時,Rails 記錄器尚未可用。如果您偏好使用 Rails 記錄器,請在初始化器中設定此設定

# config/initializers/log_autoloaders.rb
Rails.autoloaders.logger = Rails.logger

16 Rails.autoloaders

管理您應用程式的 Zeitwerk 實例位於

Rails.autoloaders.main
Rails.autoloaders.once

述詞

Rails.autoloaders.zeitwerk_enabled?

仍然在 Rails 7 應用程式中可用,並傳回 true



返回頂端