如何防止 Ruby 以 Gem 的类而不是我的类为目标?

How can I prevent Ruby from targeting a gem's class instead of my class?

提问人:David Gay 提问时间:3/15/2021 更新时间:3/17/2021 访问量:45

问:

我在使用官方 SendGrid gem 的 Rails 代码库中遇到了问题。发生相同行为的两个独立实例。

第一个示例

我有一堂课:Engine

module API
  class Engine < ::Rails::Engine
    isolate_namespace API
  end
  ...
end

我曾经在我的路线中这样引用该类:

mount API::Engine, at: :api, as: :api

...但我偶尔会遇到这个错误:.(我的意思是“偶尔”字面意思;它似乎在开发过程中随机发生,刷新可以修复它。所以,我把这一行改成了这样:uninitialized constant SendGrid::API:Engine

mount ::API::Engine, at: :api, as: :api

我认为这应该明确针对我的而不是 SendGrid 的,但是当我处理与 SendGrid 相关的代码时,我仍然在开发过程中偶尔遇到错误。API

第二个例子

这个似乎更奇怪。这个问题是本周才出现的,让我意识到上面的例子并不是一个孤立的事件。

我有一个班级:Response

class Survey::Response < Sequel::Model Sequel[:survey][:responses]
  ...
end

我们的监控刚刚发现了这个错误:

NoMethodError
undefined method `[]' for SendGrid::Response:Class

...在这一行:

results = results.pluck(:response_id).uniq.map { |r| Survey::Response[r] }

这个例子似乎更加极端,因为我们明确地定位了,而且显然没有定位。Survey::ResponseSendGrid::Response

问题

我很想知道是什么原因导致的,但主要是我想知道如何阻止它。我怎样才能 100% 确定一条线针对的是我的班级而不是宝石的班级?sendgrid-ruby

我确实找到了这个问题,它得出了与我第一个问题相同的结论,尽管这个问题的问题得到了解决,而我的问题却没有。

Ruby-on-Rails Ruby 命名空间 Rubygems

评论

1赞 Giuseppe Schembri 3/16/2021
这 SO answe 可能会有所帮助。

答:

0赞 David Gay 3/17/2021 #1

我的问题是,一个模块被包含在全局命名空间中,因此它的常量污染了整个代码库。

例如:

require 'sendgrid-ruby'
include SendGrid

module MyModule
  ...
end

以这种方式包括可能会导致我的问题中描述的问题。以这种方式包含任何宝石都可能导致类似的问题。SendGrid

解决方案是仅包含必须包含的模块,例如:

require 'sendgrid-ruby'

module MyModule
  include SendGrid
  ...
  # I can refer to Email here, and it will target SendGrid::Email
  ...
end

...或者更好的是,根本不包含该模块,而是显式引用该模块的类:

require 'sendgrid-ruby'

module MyModule
  ...
  # Instead of referring to Email, I must explicitly refer to SendGrid::Email
  ...
end

这个问题帮助我认识到了这个问题。但是,由于我在搜索中没有遇到这个问题,因此我将把这个问题和答案留在这里,供未来的旅行者使用。