Загрузите Ruby 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") уже обернут в собственное пространство имен. Однако я хочу предоставить пользователю возможность включить форму "синтаксического сахара", которая делает некоторые классы непосредственно доступными в пространстве имен верхнего уровня. Это, однако, создает столкновение имен с одним из драгоценных камней, которые я использую, который определяет класс верхнего уровня. Ни одно из предложенных в настоящее время решений не работает, потому что гем фактически полагается на свой глобально определенный класс, находящийся там; так что неопределенный класс ломает драгоценный камень. Конечно, у гема есть более одного файла, и индивидуальное требование его файлов в модуль кажется очень хрупким решением. В настоящее время я нашел единственный способ обойти это:
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 ответа
Другой, возможно, лучший ответ, вытекает из этого вопроса.
Воспользуйтесь тем фактом, что классы и модули являются просто объектами, вот так:
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
это, он всегда будет загружаться в глобальном масштабе.
Если вы знали, что драгоценный камень был всего лишь одним файлом, вы могли бы сделать следующее.
module SafeContainer
module_eval(File.read("word.rb"))
end
но это вряд ли сработает в вашем случае.