Rails 项目中不断自动加载的问题(偶尔工作)

Posted

技术标签:

【中文标题】Rails 项目中不断自动加载的问题(偶尔工作)【英文标题】:Problem with constant autoloading in a Rails project (works occasionally) 【发布时间】:2019-10-11 23:35:42 【问题描述】:

我正在处理一个 Rails 项目,但不太了解 Rails 自动加载在我的特定情况下是如何工作的。我读了一些关于 Rails 的自动加载及其陷阱的文章,但这些并没有真正帮助我

我正在为任务(练习)构建一个处理器。每个任务在Tasks::<TaskName>::Processor 中都有其自定义处理器类,该类混合在模块Tasks::Processor 中,其中包含任务处理器的共享代码。处理器包含位于 Tasks::<TaskName>::Processor::Get 中的类 Get(用于处理 GET 请求),它混合了包含通用 Get 代码的 Tasks::Processor::Get

我已经稍微简化了代码,以便更容易理解并删除所有业务逻辑,但仍然足以重现问题。

所以问题是:

当我运行 Tasks::TaskOne::Processor.new.get 时它工作正常,但如果我运行 Tasks::TaskTwo::Processor.new.get 之后它会引发错误:NoMethodError: undefined method `new' for 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 不喜欢我有一个同名的类和一个模块并且它会感到困惑的事实,但我认为这应该不是问题,因为它们位于不同的命名空间中。 我本可以将泛型类命名为不同的名称,但我真的很想了解为什么对特定实现和泛型使用相同的类名仅适用于要加载的第一件事,但不适用于下一个。非常感谢您的帮助!

附:我的 Ruby 版本是 2.5.1,Rails 版本是 5.2.1

【问题讨论】:

文件将按其层次结构的顺序被要求。如果您有一个文件依赖于另一个相同深度的文件,您可能需要先显式加载它(可能在 application.rb 中?) 【参考方案1】:

我昨天确实在阅读有关自动加载的信息。您的问题与此处概述的问题相同:

https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#when-constants-aren-t-missed

基本上,任何时候你写Get.new.call,你都需要更具体。它不知道在可能的Gets 的树中使用哪个Get。第一次调用它时,它不必加载多个 Get 类,因此它实际上找到了正确的类。在那次调用之后,你现在已经自动加载了更多的类,现在事情开始变得冒险了。您需要使您的Get 更具体,和/或使用require_dependency 强制加载正确的类。但是考虑到您的情况,我认为require_dependency 只会让它每次都失败,因为您现在将加载所有类。

【讨论】:

我尝试过具体化(在处理器代码中写入“Tasks::::Processor::Get.new.call”),但我仍然遇到相同的错误(未定义的方法`new ' for Tasks::Processor::Get:Module) -- 我什至尝试更改我的代码,以便通用处理器和 Get 是类而不是模块,并尝试继承它们而不是混合 -- 结果相同,错误仍然存​​在。虽然 require_dependency 实际上有所帮助(没有具体说明 Get in Processor)。 我开始讨厌 Rails 自动加载。在这个问题上浪费了一整天,仍然不太明白它是如何工作的。 您使用的是低于 2.5 的 Ruby 版本吗?我知道 2.5 对常量(和类)的查找方式有所改变。因此,如果您还没有,使用 2.5+ 可能会让您的生活更轻松。 啊。哦,您可能能做的一件事是self::Get.new。由于self.get 是一个类方法,所以self 指的是当前处理器类。因此,这可能是您使用继承来获得适当的 Get 类的一种方式,而无需每次在每个类中都写出整个内容。 哦,你是对的。我不知道为什么我认为self.get 是一个类方法。很高兴你的工作正常!

以上是关于Rails 项目中不断自动加载的问题(偶尔工作)的主要内容,如果未能解决你的问题,请参考以下文章

在 Rails 4 中自动加载 lib 文件

Websocket 控制器未在 Rails 开发环境中自动加载

在使用 rails 的动态加载视图中使用 Jquery 插件

在 Rails 中,如何在加载子对象时自动加载父对象?

无法在 Rails 4 中自动加载常量 API 控制器

Rails 6 - 加载自定义 JS 文件的正确方法是啥?