为什么Ruby 1.9.2从LOAD_PATH中删除“.”,有什么替代方案?

156
最新的Ruby 1.9.2变更不再将当前目录.包含在你的LOAD_PATH中。我有许多Rakefiles的内容假定.LOAD_PATH的一部分,所以这些文件现在无法正常运行(所有基于项目路径的require语句都会报告“找不到文件”)。这个改动有什么特殊的理由吗?
至于解决方法,到处添加$: << "."是可行的,但似乎有点hacky,而且我不想这样做。有什么更好的方法使我的Rakefiles与1.9.2+兼容?
7个回答

142

被认为是“安全”风险。

您可以通过使用绝对路径来解决此问题。

File.expand_path(__FILE__) et al

或者执行

require './filename' (ironically).

或者通过使用

require_relative 'filename'

或者添加一个“include”目录

ruby -I . ...

或者使用irb执行相同的操作;

$irb -I .

27
我最终使用了 require_relative。谢谢。 - John Feminella
11
这是否类似于大多数Unix不在运行可执行文件的路径中包含当前目录? - Andrew Grimm
5
如果你的脚本所在的工作目录与执行脚本时设置的工作目录不同,那么require './filename'将无法正常工作。这种情况在多目录项目中经常发生。 - mxcl

34

有两个原因:

  • 鲁棒性和
  • 安全性

这两者都基于同样的基本原则:通常情况下,当你的代码运行时,你无法知道当前目录是什么。这意味着,当你需要文件并且依赖于它在当前目录中时,你无法控制该文件是否存在,或者它是否是你实际期望的文件。


5
我认为强制要求两个文件相对于彼此处于同一位置并不一定是一个不好的要求。如果这是真的,那么我们就没有使用目录的必要了。 - John Feminella
4
@John Feminella:这与将文件放在相对路径下有什么关系?该问题是关于相对于“.”即当前工作目录放置文件的。如果用户使用“cd”命令进入了另一个目录,当前工作目录就会改变,你现在需要完全不同的文件来满足脚本调用时用户所在目录的不同。我认为这不是一个好主意。 - Jörg W Mittag
4
个人而言,我不喜欢这样做。(但请不要查看我的旧代码,因为它满是这种东西 :-) )。我只是假装lib目录在$LOAD_PATH上,然后相对于librequire所有文件。换句话说:我让管理员想办法正确设置$LOAD_PATH。如果使用RubyGems,那么很简单,因为RubyGems会自动为您完成这项工作;如果使用Debian软件包,则是软件包维护者的工作。总的来说,这个方法似乎非常有效。 - Jörg W Mittag
8
作为对从 $LOAD_PATH 中移除 . 的某种平衡,Ruby 1.9.2 引入了 require_relative。该方法可以让你相对于当前正在执行的文件位置(即相对于 File.dirname(__FILE__))来引用文件,非常方便。 - Jörg W Mittag
大家好。我之前有些困惑,但现在我认为我已经正确地理解了。Ruby表现出了真正的动态特性;当动态调用require时,会加载一些具有相似名称的其他文件。通常情况下,使用Nix命令时,加载上下文将与脚本或可执行文件相关(使用*require_relative**)。据我所知,只有在Ruby程序动态加载代码时才会出现这个问题,你们怎么看? - will
显示剩余2条评论

16

如其他答案所指出的,这是一种安全风险,因为在您的加载路径中,. 指代当前工作目录 Dir.pwd,而不是正在被加载的当前文件所在的目录。所以,任何执行您脚本的人都可以通过简单地cd到另一个目录来更改路径。这很糟糕!

我已经开始使用从__FILE__构建的完整路径作为替代方法。

require File.expand_path(File.join(File.dirname(__FILE__), 'filename'))

require_relative不同的是,这个方法向后兼容Ruby 1.8.7。


4
也有这种变体(我个人认为更易读):require Pathname.new(__FILE__).dirname + 'filename' - Tyler Rick

8

使用require_relative 'file_to_require'来引入需要的文件。

将以下代码放入你的程序中,可以在1.8.7版本中使用require_relative

unless Kernel.respond_to?(:require_relative)
  module Kernel
    def require_relative(path)
      require File.join(File.dirname(caller.first), path.to_str)
    end
  end
end

6

3
正如Jörg W Mittag所指出的那样,我认为你要使用的是require_relative,这样你需要的文件就相对于require声明的源文件,而不是当前工作目录。
你的依赖关系应该与你的rake构建文件相关。

3

我觉得这是一个令人困惑的变化,直到我意识到了一些事情。

你可以在你的 .profile 文件(Unix)中设置 RUBYLIB,并像以前一样继续生活:

export RUBYLIB="."

但正如上面提到的那样,长期以来这被认为是不安全的。

对于绝大多数情况,你可以通过在 Ruby 脚本前加上 '.' 来避免问题,例如:./scripts/server。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接