Загрузите 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

но это вряд ли сработает в вашем случае.

Другие вопросы по тегам