如何临时更改 Ruby 中的 require 路径 ($:)?

发布于 2024-08-30 07:16:58 字数 536 浏览 4 评论 0原文

我正在为一个复杂的项目使用一堆 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

这显然是一个可怕的黑客行为。有更好的办法吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

宛菡 2024-09-06 07:16:58

由于 $: 是一个数组,因此您必须小心您所做的事情。您需要获取一份副本(通过dup)并稍后替换。不过,简单地删除您添加的内容更简单:

def foo
  $: << special_path

  # Do work ...
  bar()

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

如果没有更多信息,很难知道是否有更好的方法。

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.

要走就滚别墨迹 2024-09-06 07:16:58

require 实际上是一个方法,它是 Kernel#require (它调用 rb_require_safe),所以你至少可以在猴子补丁版本中执行你的黑客攻击。如果你喜欢这样的事情。

  • 给原始 require 起别名
  • 如果传递了绝对路径,则调用原始 require 方法,
  • 否则通过创建绝对路径并调用原始 require 方法来迭代加载路径。

只是为了好玩,我对此进行了快速的抨击,原型如下。这尚未经过充分测试,我还没有检查 rb_require_safe 的语义,您可能还需要查看 #load#includeKernel 模块的猴子补丁。这也许并不完全是可怕的,但它肯定是一种黑客行为。您的电话是否比弄乱全局 $: 变量更好或更坏。

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' ]

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' ]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文