将Ruby gem加载到用户定义的命名空间中
给定一个定义与我编写的代码冲突的顶级类的gem,是否可以以这样的方式要求gem,使其所有类都分组在我可以定义的模块中? 例如,如果unsafe_gem定义了一个类:
class Word # ... some code end
我需要这样的东西:
class Word # My word class. end module SafeContainer # This obviously doesn't work # (ie 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’类)已经包装在它自己的命名空间中。 但是,我希望能够为用户提供启用“语法糖”forms的选项,这使得某些类可以在顶级命名空间下直接访问。 但是,这会与我正在使用的某个gem创建一个名称冲突,它定义了一个顶级类。 目前提出的解决方案都不起作用,因为gem实际上依赖于其全球定义的类; 因此,取消定义课程打破了gem。 当然,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
我不知道为什么,但这让我的脚趾甲curl。 有人有更好的解决方案吗?
另一个可能更好的答案来自这个问题。
利用类和模块只是对象的事实,如下所示:
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
它,因此您不会意外地将自己的东西设置为nil
。
我认为最好的办法是将自己的代码包装在一个模块中。 根据您编写的代码量,这可能会或可能不会是一个巨大的痛苦。 但是,这是确保您的代码不会与其他人冲突的最佳方式。
所以你的Word
课程就变成了
module LoismsProject class Word #some awesome code end end
这样你就可以安全地require 'unsafe_gem'
。
简单回答是不”
如果我们有一个’word.rb’文件;
class Word def say puts "I'm a word" end end
我们尝试并require
它,它将始终加载到全局范围。
如果您知道gem只是一个文件,那么您可以执行以下操作。
module SafeContainer module_eval(File.read("word.rb")) end
但这不太适用于你的情况。