Rails Rails 6 做 test 时报错:DEPRECATION WARNING: Initialization autoloaded the constants

tomanderson · 2020年09月27日 · 最后由 piecehealth 回复于 2020年09月28日 · 1870 次阅读

我在 config/initializers 中加了一个启动时执行的方法,它会调用 controllers/concerns 中某个 module 里的一些方法。这样做的目的是,项目中要用到一个全局变量,而生成这个全局变量需要几秒钟,为了避免反复生成它,就放到 initializers 中,让 rails 启动时生成一次,后面直接使用即可。实测完全正常。

但是在做 test 时出现了警告:

DEPRECATION WARNING: Initialization autoloaded the constants Const, Util, and Public.

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 during
initialization does not run again. So, if you reload Const, for example,
the expected changes won't be reflected in that stale Module object.

These autoloaded constants have been unloaded.

Please, check the "Autoloading and Reloading Constants" guide for solutions.
 (called from <main> at /home/thomas/rails/project/config/environment.rb:8)
Run options: --seed 18699

# Running:

.

Finished in 0.084160s, 11.8821 runs/s, 47.5284 assertions/s.
1 runs, 4 assertions, 0 failures, 0 errors, 0 skips

如果把 initializers 中那个启动时执行的方法去掉,一切正常。然而,如果不用 initializers,我想不出还有什么更好的办法来加载这个全局变量……

当然,出警告目前并不影响使用。但还是想知道有解决的办法吗?

前天刚遇到,在config/environment/test.rb中,把 stderr 改成 log config.active_support.deprecation = :log

liuminhan 回复

这样做是眼不见为净了,但是下一个版本可能就真变成报错了啊……有彻底的办法吗?

Rails6 官方指南说明:

https://guides.rubyonrails.org/autoloading_and_reloading_constants_classic_mode.html#autoloading-and-require

https://guides.rubyonrails.org/autoloading_and_reloading_constants.html

Rails5 官方指南说明(中译):

https://ruby-china.github.io/rails-guides/autoloading_and_reloading_constants.html

感觉你的全局变量需要计算得出,跟系统参数相关么?如果管理全局变量或环境变量,这 2 个 gem 不知道对你是否有帮助呢?dotenv-rails 和 config。

DaisyWuDi 回复

系统参数已经用 gem config 处理了。这个全局变量实质上是把一个本地文件读取到内存,在 rails 启动时一次性加载。因为后续要不断使用这个文件中的数据,如果每次读一下本地文件太浪费资源了。

tomanderson 回复

按你的描述,你这么实现,感觉没毛病~ 这个 reload 机制不影响你系统功能就行。猜测这个本地文件是不怎么更新的?不知道文件内容很大还是计算比较复杂,生成这个全局变量需要几秒时间。一点拙见:1、是否可以考虑将文件内容结构化存储到数据库中,第一次读库,并用 redis 缓存,之后读 redis,并建立 reload 机制;2、如果不用数据库,单独写一个脚本预读文件写到 redis 里,每当文件有变动,重新运行这个脚本,刷新缓存(可以在启动项目前执行这个脚本)。

DaisyWuDi 回复

感谢回复。不用 redis 或数据库的主要考虑是,这个全局变量很大,而且调用频繁,如果放在外部内存可能性能不如本地内存,而且多了一项环境依赖。如果不能消除那个警告的话,好像只能先这样用着了。

读文件代码不放 concerns 里,只放 initializer 里不就好了。

# initializers/xxx.rb
MY_CONST = File.read(file_name)

# controllers/concerns/xxx.rb
module Xxx
  MY_CONST2 = MY_CONST
end
piecehealth 回复

读文件以后还有一些处理,处理的代码有几百行,这几百行代码又调用了几个 module,每个 module 上千行……

tomanderson 回复

你要不就把不需要 autoreolad 的代码择出来放 initializer 里,要不就在 concern module 里 lazy load (@xxx ||= begin ... end 可能需要加线程锁)

需要 登录 后方可回复, 如果你还没有账号请 注册新账号