Zeitwerk::name由于轨道7中的模块名称而导致错误



我最近将我的应用程序从rails 6升级到了rails 7,由于zeitwerk命名约定,我面临一个问题。我有一个下面的文件,我想自动加载:

app/models/dw/hospital.rb
module DW
class Hospital < DataWarehouse
def self.columns
super.reject{|column| column.name == 'tableau_user' }
end
end
end

我尝试通过在application.rb文件中添加以下行来自动加载此文件:

config.autoload_once_paths << 'app/models/dw'

但是当我启动服务器时,我得到了以下错误:

expected file app/models/dw/hospital.rb to define constant Hospital, but didn't (Zeitwerk::NameError)

我不知道为什么会抛出这样的错误,因为常量已经定义好了。我怀疑这是因为我在类之前定义的模块。如果有人想解决这个问题,请告诉我。我在这方面已经陷得太久了。

因为您已经将app/models/dw添加到自动加载路径,所以必须定义Hospital,但您的定义是按名称空间的DW::Hospital。您不需要触摸自动加载配置,app/models已经在autoload_paths:中

>> ActiveSupport::Dependencies.autoload_paths
=> 
...
"/home/alex/code/stackoverflow/app/jobs",
"/home/alex/code/stackoverflow/app/mailers",
"/home/alex/code/stackoverflow/app/models",   # <======
...

这些就是所谓的根目录。这意味着相对于app/models的文件结构必须与模块/类名相对应。

因此,如果在任何根目录中都有dw/hospital.rb,则必须定义已经定义的Dw::Hospital。你也必须注意屈折,它应该是Dw,除非你有首字母缩略词屈折规则或时代周刊屈折:

>> "dw".camelize
=> "Dw"
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "DW" 
end  
>> "dw".camelize
=> "DW"

如果你必须嵌套根目录,你应该有一个很好的理由:

# if you want it to be reloadable,
# use `autoload_paths` instead of `autoload_once_paths`
config.autoload_paths << Rails.root.join("app/models/dw")
# app/models/dw/hospital.rb
class Hospital
end

但正如Xavier在评论中提到的那样,没有必要进行这种配置。使用默认配置,不要使设置变得不必要的复杂。

看起来像是大写的问题

通常,PascalCase(也称为CamelCase(类名必须与snake_case文件名相对应。相同的规则适用于命名空间和文件夹

但是dw文件夹中有DW(而不是Dw(

你可以创建一些类似的初始值设定项

# config/initializers/zeitwerk.rb 
Rails.autoloaders.each do |autoloader|
autoloader.inflector = Zeitwerk::Inflector.new
autoloader.inflector.inflect(
'dw' => 'DW',
# other special cases
)
end

最新更新