开发者

How do I temporarily change the require path in Ruby ($:)?

I'm doing some trickery with a bunch of Rake tasks for a complex project, gradually refactoring away some of the complexity in chunks at a time. This has exposed the bizarre web of dependencies left behind by the previous project maintainer.

What I'd like to be able to do is to add a specific path in the project to require's list of paths to be searched, aka $:. However, I only want that path to be searched 开发者_运维问答in the context of one particular method. Right now I'm doing something like this:

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

This is clearly a monstrous hack. Is there a better way?


Since $: is an Array, you have to be careful about what you are doing. You need to take a copy (via dup) and replace it later. It' simpler to simply remove what you have added, though:

def foo
  $: << special_path

  # Do work ...
  bar()

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

Without more info, it's difficult to know if there is a better way.


require is actually a method, it's Kernel#require (which calls rb_require_safe) so you could at least perform your hackery in a monkey-patched version. If you like that kind of thing.

  • Alias the orignal require out of the way
  • If passed an absolute path, call the original require method
  • Else iterate over load path by creating an absolute path and calling the original require method.

Just for fun I had a quick bash at that, prototype is below. This isn't fully tested, I haven't checked the semantics of rb_require_safe, and you probably would also need to look at #load and #include for completeness -- and this remains a monkey-patch of the Kernel module. It's perhaps not entirely monstrous, but it's certainly a hack. Your call if it's better or worse than messing with the global $: variable.

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

Examples:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜