Rails 项目中持续自动加载的问题(偶尔工作)
Problem with constant autoloading in a Rails project (works occasionally)
我正在处理一个 Rails 项目,不太了解 Rails 自动加载在我的特定情况下是如何工作的。我读了一些关于 Rails' 自动加载及其陷阱的文章,但这些并没有真正帮助我
我正在为任务(练习)构建一个处理器。每个任务在 Tasks::<TaskName>::Processor
中都有其自定义处理器 class,混合在包含任务处理器共享代码的模块 Tasks::Processor
中。处理器包含 class Get
(用于处理 GET 请求),位于 Tasks::<TaskName>::Processor::Get
中,混合在 Tasks::Processor::Get
中,包含通用 Get
的代码。
我稍微简化了代码以便更容易理解并删除了所有业务逻辑,但它仍然足以重现问题。
所以问题是:
当我 运行 Tasks::TaskOne::Processor.new.get
它工作正常,但如果我 运行 Tasks::TaskTwo::Processor.new.get
之后它抛出一个错误: NoMethodError: undefined method Tasks::Processor::Get:Module 的“新”。反之亦然:如果我先 运行 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' 自动加载有关,因为当我使用纯 ruby 并手动要求所有文件并尝试 运行 代码时,问题就没有了发生。
你能解释一下为什么它会这样工作并告诉我避免这个问题的最好方法是什么吗?似乎 Rails 不喜欢我有一个 class 和一个具有相同名称的模块并且它会混淆的事实,但我认为这应该不是问题,因为它们位于不同的名称空间中。
我本可以将通用 class 命名为不同的东西,但我真的很想了解为什么对特定实现和通用实现使用相同的 class 名称仅适用于第一个要加载的东西而不是对于下一个。非常感谢您的帮助!
P.S。我的Ruby版本是2.5.1,Rails版本是5.2.1
我昨天确实在阅读有关自动加载的内容。您的问题与此处概述的问题相同:
https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#when-constants-aren-t-missed
基本上,任何时候你写Get.new.call
,你都需要更具体。它不知道在可能的 Get
树中使用哪个 Get
。第一次调用它时,它不必加载超过一个 Get
class,所以它实际上找到了正确的。调用之后,您现在已经自动加载了 MORE classes,现在事情开始变得危险了。您需要使 Get
更具体,and/or 使用 require_dependency
强制加载正确的 classes。但是考虑到您的情况,我认为 require_dependency
只会让它每次都失败,因为你现在已经加载了所有 classes。
我正在处理一个 Rails 项目,不太了解 Rails 自动加载在我的特定情况下是如何工作的。我读了一些关于 Rails' 自动加载及其陷阱的文章,但这些并没有真正帮助我
我正在为任务(练习)构建一个处理器。每个任务在 Tasks::<TaskName>::Processor
中都有其自定义处理器 class,混合在包含任务处理器共享代码的模块 Tasks::Processor
中。处理器包含 class Get
(用于处理 GET 请求),位于 Tasks::<TaskName>::Processor::Get
中,混合在 Tasks::Processor::Get
中,包含通用 Get
的代码。
我稍微简化了代码以便更容易理解并删除了所有业务逻辑,但它仍然足以重现问题。
所以问题是:
当我 运行 Tasks::TaskOne::Processor.new.get
它工作正常,但如果我 运行 Tasks::TaskTwo::Processor.new.get
之后它抛出一个错误: NoMethodError: undefined method Tasks::Processor::Get:Module 的“新”。反之亦然:如果我先 运行 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' 自动加载有关,因为当我使用纯 ruby 并手动要求所有文件并尝试 运行 代码时,问题就没有了发生。 你能解释一下为什么它会这样工作并告诉我避免这个问题的最好方法是什么吗?似乎 Rails 不喜欢我有一个 class 和一个具有相同名称的模块并且它会混淆的事实,但我认为这应该不是问题,因为它们位于不同的名称空间中。 我本可以将通用 class 命名为不同的东西,但我真的很想了解为什么对特定实现和通用实现使用相同的 class 名称仅适用于第一个要加载的东西而不是对于下一个。非常感谢您的帮助!
P.S。我的Ruby版本是2.5.1,Rails版本是5.2.1
我昨天确实在阅读有关自动加载的内容。您的问题与此处概述的问题相同:
https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#when-constants-aren-t-missed
基本上,任何时候你写Get.new.call
,你都需要更具体。它不知道在可能的 Get
树中使用哪个 Get
。第一次调用它时,它不必加载超过一个 Get
class,所以它实际上找到了正确的。调用之后,您现在已经自动加载了 MORE classes,现在事情开始变得危险了。您需要使 Get
更具体,and/or 使用 require_dependency
强制加载正确的 classes。但是考虑到您的情况,我认为 require_dependency
只会让它每次都失败,因为你现在已经加载了所有 classes。