将Ruby gem加载到用户定义的命名空间中

时间:2012-01-06 18:22:30

标签: ruby namespaces gem

鉴于一个gem定义了与我编写的一些代码冲突的顶级类,是否可以以这样的方式要求gem,使其所有类都分组在我可以定义的模块中?例如,如果unsafe_gem定义了一个类:

class Word
  # ... some code
end

我需要类似的东西:

class Word
  # My word class.
end

module SafeContainer
  # This obviously doesn't work
  # (i.e. the gem still defines ::Word).
  require 'unsafe_gem'
end

这样我就可以区分:

Word.new # => The class I defined.
SafeContainer::Word.new # => The class defined by the gem.

更多细节:我的代码(例如“Word”类)已经包含在自己的命名空间中。但是,我希望能够为用户提供启用“语法糖”形式的选项,这使得某些类可以在顶级命名空间下直接访问。但是,这会与我正在使用的一个宝石创建一个名称冲突,它定义了一个顶级类。目前提出的解决方案都不起作用,因为宝石实际上依赖于其全球定义的类;因此,取消定义课程打破了宝石。当然,gem有多个文件,单独要求将其文件放入模块似乎是一个非常脆弱的解决方案。目前,我发现的唯一解决方法是:

begin
  # Require the faulty gem.
  require 'rbtagger'
rescue 
  # If syntactic sugar is enabled...
  if NAT.edulcorated?
    # Temporarily remove the sugar for the clashing class.
    Object.const_unset(:Word); retry
  else; raise; end
ensure
  # Restore syntactic sugar for the clashing class.
  if NAT.edulcorated?
    Object.const_set(:Word, NAT::Entities::Word)
  end
end

我不知道为什么,但这让我的脚趾甲卷曲。有人有更好的解决方案吗?

3 个答案:

答案 0 :(得分:5)

另一个可能更好的答案来自this问题。

利用类和模块只是对象的事实,如下所示:

require 'unsafe_gem'
namespaced_word = Word
Word = nil


# now we can use namespaced_word to refer to the Word class from 'unsafe_gem'

#now your own code
class Word
  #awesome code
end

您必须确保unsafe_gem只定义了一个类,并且在定义自己的类和模块之前require,这样您就不会意外地将自己的内容设置为{ {1}}。

答案 1 :(得分:1)

我认为最好的办法是将自己的代码包装在一个模块中。根据您编写的代码量,这可能会或可能不会是一个巨大的痛苦。但是,这是确保您的代码不会与其他人冲突的最佳方式。

所以你的Word班级变成了

module LoismsProject
  class Word
    #some awesome code
  end
end

这样你就可以放心require 'unsafe_gem'

答案 2 :(得分:0)

简单的答案是“不”

如果我们有一个'word.rb'文件;

class Word
  def say
    puts "I'm a word"
  end
end

我们尝试require它,它将始终加载到全局范围。

如果您知道gem只是一个文件,那么您可以执行以下操作。

module SafeContainer
  module_eval(File.read("word.rb"))
end

但这不太适用于你的情况。