努力載入中

rails Initialization autoloaded the constant 問題

2023-06-19

文章目錄

問題說明

自我們專案中 rails 採用 zeitwerk 後,每次啟動時總會出現以下錯誤訊息

DEPRECATION WARNING: Initialization autoloaded the constant MyClass

Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed duringinitialization does not run again. So, if you reload TyrSetting, for example,the expected changes won't be reflected in that stale Class object.

This autoloaded constant has been unloaded.

起因於如果我們在 config/initializers 下,呼叫了某個類別(class)例如 User,而 User 位於 app/models/user.rb 是沒有在 rails 啟動(initialize)之前先進行 require 的話,就會顯示這個棄用警告(deprecation warning)。

這樣的措施用意在於:若要在 rails initialize 階段使用的 class,就必須事先 require。在開發環境(development)下,當我們修改程式碼後進行 reload! 時,在 rails initialize 階段跑的程式碼,即 config/initializers 下的所有程式,不會因為 reload! 而重跑一次,因此會導致類別和實例會有 reload! 前後版本不一致的問題,

舉例如下

# config/initializers/foo.rb
User.extend(FooModule)

當修改過 FooModule 內容時,User 並不會因為 reload! 而跟著修改,只能 restart rails app。


解決辦法

rails 提供了 ActiveSupport::Reloader 的載入框架來解決這類需求。

ActiveSupport::Reloader#to_prepare 內的程式碼,會在 reload! 時,也再重新執行一遍。

# config/initializers/foo.rb
ActiveSupport::Reloader#to_prepare do
  User.extend(FooModule)
end

如果是一些 Setting 之類需要在非常早期優先載入的 class

則可以在 config/application.rb 內先行 require

# config/application.rb

module MyApp
  class Application < Rails::Application
    require 'setting

注意事項

to_prepare 裡面的程式碼會是最後才執行,即先把 initializers 下的都執行完畢後才逐一執行 to_preare 內的。

# config/initializers/foo.rb
puts "1..."
ActiveSupport::Reloader#to_prepare do
  puts "2..."
end

# config/initializers/bar.rb
puts "3..."
ActiveSupport::Reloader#to_prepare do
  puts "4..."
end

輸出結果為

1...
3...
2...
4...

因此執行順序若會造成影響,則這裡需要特別注意。


非正規解法

在 initializers 內,如果先 require 的話,就可以讓 deprecation warning 消失。不過這樣就必須把該 class 內所用到的其他 class, module 需要一併 require。

# config/initializers/foo.rb
require 'application_record'
require 'user'
require 'foo_module'

User.extend(FooModule)

這樣做在未來新版本時,仍會導致 reload! 無效或噴錯,因此不太建議用這種方式解,除非你也不打算升級更新了 😏。

相關文章

設定電子郵件 DKIM

設定電子郵件 DKIM

以 AWS SES 和 Gmail 為例,說明如何設定 DKIM,並且提供相關檢測工具。DKIM 除了需要透過 DNS TXT 設定完,需要先申請好簽署用的金鑰。
正念冥想使用心得

正念冥想使用心得

在實行正念冥想後一年半的日子裡,所獲得的體悟,以及對生活和工作上的改變,同時分享正念冥想的一些小技巧。
用 AWS Transfer + S3 架設 FTP

用 AWS Transfer + S3 架設 FTP

本篇文章將一步步教學如何利用 AWS 架設 FTP,使用的服務包含 AWS Transfer 、S3、SNS(Simple Notification Service)。實現檔案傳輸,以及被動獲得檔案上傳的通知。
Sidekiq 介面壞掉

Sidekiq 介面壞掉

很久以前修過的問題,再次出現時,又忘了為何如此,所以這次完整把 Sidekiq web 介面遇到的壞掉問題修復方式完整記錄起來。