Как временно изменить требуемый путь в Ruby ($ :)? - PullRequest
4 голосов
/ 24 апреля 2010

Я делаю небольшую хитрость с кучей задач Rake для сложного проекта, постепенно убирая часть сложности за раз. Это выявило причудливую сеть зависимостей, оставленных предыдущим сопровождающим проекта.

То, что я хотел бы сделать, это добавить определенный путь в проекте в список require путей для поиска, или $:. Однако я хочу, чтобы этот путь был найден только в контексте одного конкретного метода . Прямо сейчас я делаю что-то вроде этого:

def foo()
  # Look up old paths, add new special path.
  paths = $:
  $: << special_path

  # Do work ...
  bar()
  baz()
  quux()

  # Reset.
  $:.clear
  $: << paths
end

def bar()
  require '...' # If called from within foo(), will also search special_path.
  ...
end

Это явно чудовищный хак. Есть ли лучший способ?

Ответы [ 2 ]

3 голосов
/ 25 апреля 2010

Поскольку $: является массивом, вы должны быть осторожны с тем, что вы делаете. Вам нужно взять копию (через dup) и replace позже. Проще просто удалить то, что вы добавили:

def foo
  $: << special_path

  # Do work ...
  bar()

ensure
  # Reset.
  $:.delete(special_path)
end

Без дополнительной информации трудно понять, есть ли лучший способ.

0 голосов
/ 25 апреля 2010

require на самом деле является методом, это Kernel#require (который вызывает rb_require_safe), так что вы можете по крайней мере выполнить свою хакерскую работу в залатанной обезьяной версии. Если тебе нравятся такие вещи.

  • Псевдоним orignal требуется с дороги
  • Если передан абсолютный путь, вызвать оригинальный метод require
  • Иначе переберите путь загрузки, создав абсолютный путь и вызвав оригинальный метод require.

Просто ради забавы, я немного постарался, прототип ниже. Это не полностью протестировано, я не проверил семантику rb_require_safe, и вам, вероятно, также нужно взглянуть на #load и #include для полноты - и это остается обезьяньим патчем Kernel модуль. Возможно, это не совсем чудовищно, но это, безусловно, взломать. Ваш звонок, если это лучше или хуже, чем возиться с глобальной переменной $:.

module Kernel

  alias original_require require

  # Just like standard require but takes an
  # optional second argument (a string or an
  # array of strings) for additional directories
  # to search.
  def require(file, more_dirs=[])
    if file =~ /^\// # absolute path
      original_require(file)
    else
      ($: + [ more_dirs ].flatten).each do |dir|
        path = File.join(dir, file)
        begin
          return original_require(path)
        rescue LoadError
        end
      end
      raise LoadError,
        "no such file to load -- #{file}"
    end
  end

end

Примеры:

require 'mymod'
require 'mymod', '/home/me/lib'
require 'mymod', [ '/home/me/lib', '/home/you/lib' ]
...