我正在与Rails项目一起工作,并且不太了解Rails自动加载在我的特定情况下如何工作。我读了一些有关Rails自动加载及其陷阱的文章,但这些并没有真正帮助我
我正在为任务(练习)构建处理器。每个任务在Tasks::<TaskName>::Processor
中都有其自定义处理器类,该类混合在模块Tasks::Processor
中,其中包含任务处理器的共享代码。处理器包含位于Get
中的类Tasks::<TaskName>::Processor::Get
(用于处理GET请求),该类混入包含通用Tasks::Processor::Get
的代码的Get
中。
我对代码进行了一些简化,以便于理解和删除所有业务逻辑,但仍然足以重现问题。
所以问题是:
当我运行Tasks::TaskOne::Processor.new.get
时,它运行良好,但是如果之后运行Tasks::TaskTwo::Processor.new.get
,则会抛出错误: NoMethodError:Tasks :: Processor :: Get的未定义方法'new':模块。它也以相反的方式起作用:如果我先运行TaskTwo的处理器的代码,则可以正常工作,但TaskOne的处理器将抛出错误。它只是找不到Get的特定实现,而是找到通用模块并尝试实例化它,这显然是不可能的。
这是代码以及结构。
共享代码:
app / models / tasks / processor.rb :
module Tasks
# generic Processor (mixed in by custom processors)
module Processor
# ...
end
end
app / models / tasks / processor / get.rb :
module Tasks
module Processor
# generic Get
module Get
# ...
end
end
end
TaskOne的代码:
app / models / tasks / task_one / processor.rb :
module Tasks
module TaskOne
# processor for task_one
class Processor
include Tasks::Processor # mix in generic task processor
def get
Get.new.call
end
end
end
end
app / models / tasks / task_one / processor / get.rb :
module Tasks
module TaskOne
class Processor
# task_one's processor's custom Get
class Get
include Tasks::Processor::Get # mix in generic Get
def call
puts "in task_one's Processor's Get"
end
end
end
end
end
与TaskTwo的代码几乎相同:
app / models / tasks / task_two / processor.rb :
module Tasks
module TaskTwo
# processor for task_two
class Processor
include Tasks::Processor # mix in generic task processor
def get
Get.new.call
end
end
end
end
app / models / tasks / task_two / processor / get.rb :
module Tasks
module TaskTwo
class Processor
# task_two's processor's custom Get
class Get
include Tasks::Processor::Get # mix in generic Get
def call
puts "in task_two's Processor's Get"
end
end
end
end
end
它很可能与Rails的自动加载有关,因为当我使用纯红宝石并手动要求所有文件并尝试运行代码时,不会发生此问题。 请您解释一下为什么会这样,请告诉我避免这种问题的最佳方法是什么?似乎Rails不喜欢这样的事实,即我有一个类和一个具有相同名称的模块,并且感到困惑,但是我认为这应该不是问题,因为它们位于不同的命名空间中。 我可以将通用类命名为不同的名称,但是我真的很想理解为什么对于特定的实现和通用名称都使用相同的类名,只对第一件事有用,而对第二件事却不起作用。非常感谢您的帮助!
P.S。我的Ruby版本是2.5.1,Rails版本是5.2.1
答案 0 :(得分:1)
我昨天确实在阅读有关自动加载的信息。您的问题与此处概述的问题相同:
https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#when-constants-aren-t-missed
基本上,每次您写Get.new.call
时,都需要更加具体。它不知道在可能的Get
树中使用哪个Get
。第一次调用它时,它不必加载多个Get
类,因此它实际上找到了正确的类。通话结束后,您现在已经自动加载了更多类,现在事情开始变得混乱起来。您需要使您的Get
更加具体,和/或使用require_dependency
来强制装入正确的类。但是考虑到您的情况,我认为require_dependency
只会使每次都会失败,因为您现在已经加载了所有类。