Загрузите Ruby gem в пользовательское пространство имен - PullRequest
9 голосов
/ 06 января 2012

Учитывая гем, который определяет классы верхнего уровня, которые конфликтуют с некоторым кодом, который я написал, возможно ли потребовать гем таким образом, чтобы все его классы были сгруппированы внутри модуля, который я могу определить?Например, если 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 ]

5 голосов
/ 06 января 2012

Другой, возможно лучший ответ, приходит от этого вопроса.

Воспользуйтесь преимуществом того факта, что классы и модули являются просто объектами, например:

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.

1 голос
/ 06 января 2012

Простой ответ "нет"

Если у нас есть файл «word.rb»;

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

и мы попробуем require, он всегда будет загружаться в глобальном масштабе.

Если вы знали, что драгоценный камень был всего лишь одним файлом, вы могли бы, однако, сделать следующее.

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

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

1 голос
/ 06 января 2012

Я думаю, что вам лучше всего заключить собственный код в модуль.В зависимости от того, сколько кода вы написали, это может быть или не быть огромной болью.Тем не менее, это лучший способ убедиться, что ваш код не будет конфликтовать с чужим.

Таким образом, ваш класс Word становится

module LoismsProject
  class Word
    #some awesome code
  end
end

Таким образом, вы можете безопасно require 'unsafe_gem'.

...