Rails运行一段时间后无法找到自定义类问题求助
uninitialized constant A::B的问题 这个问题我之前也碰到过,根源大多是Rails自动加载机制(尤其是默认的Zeitwerk加载器)和手动require语句的冲突,结合你的代码和场景,给你几个排查和解决的思路:
1. 移除手动require_relative,遵循Zeitwerk的命名约定
Rails 6及以后默认使用Zeitwerk作为自动加载器,它依赖文件结构与模块/类命名严格对应的规则,不需要手动写require语句。你的当前结构是:
classes/ ├── a.rb └── b/ └── b.rb
但按照Zeitwerk的规则,A::B类对应的文件应该放在classes/a/b.rb(模块A对应a.rb,它的嵌套类B要放在a目录下的b.rb)。调整目录结构后,直接删掉a.rb里的require_relative './b/b.rb',Zeitwerk会自动在需要的时候加载A::B。
2. 确认自动加载路径配置的规范性
在application.rb里,推荐用更规范的写法配置自动加载路径:
config.autoload_paths << Rails.root.join("classes")
另外,如果你用的是Rails 7+,可以考虑用config.autoload_lib(ignore: %w(assets tasks))这类专门的配置,但autoload_paths对于自定义classes目录依然有效。
3. 排查开发环境的代码重载冲突
开发环境下,Rails会自动重载修改过的文件。当你手动用require_relative加载B类时,第一次启动没问题,但当a.rb被修改重载时,require_relative不会再次执行,导致重新加载的A模块找不到B类。移除手动require后,Zeitwerk会在A::B被引用时自动触发加载,避免重载时的依赖丢失。
4. 用Zeitwerk检查工具验证配置
运行Rails内置的Zeitwerk检查命令,能快速找出命名和文件结构不匹配的问题:
bin/rails zeitwerk:check
如果有不符合规则的地方,这个命令会给出明确的提示,帮你快速定位问题。
5. 生产环境预加载的验证
生产环境下,Rails会预加载所有自动加载路径中的文件。如果文件结构不对,预加载阶段可能没加载到A::B,导致运行时出错。你可以在部署前先本地运行生产环境的预加载检查:
RAILS_ENV=production bin/rails runner "puts A::B"
如果这个命令报错,说明预加载时没加载到B类,需要回到文件结构的调整上。
内容的提问来源于stack exchange,提问作者bo-oz

