153

如果我想require在 Ruby 中创建一个相对文件并且希望它在 1.8.x 和 >=1.9.2 中都可以工作,那么最佳实践是什么?

我看到几个选项:

  • 做就$LOAD_PATH << '.'忘记一切
  • $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • 检查是否RUBY_VERSION< 1.9.2,然后定义require_relative为,然后在需要的任何地方require使用require_relative
  • 检查是否require_relative已经存在,如果存在,请尝试像以前的情况一样继续
  • 使用奇怪的结构,例如
    require File.join(File.dirname(__FILE__), 'path/to/file')
    - 唉,它们似乎并不能完全在 Ruby 1.9 中工作,因为例如:
    $ cat caller.rb
    require File.join(File.dirname(__FILE__), 'path/to/file')
    $ cat path/to/file.rb
    puts 'Some testing'
    $ ruby caller
    Some testing
    $ pwd
    /tmp
    $ ruby /tmp/caller
    Some testing
    $ ruby tmp/caller
    tmp/caller.rb:1:in 'require': no such file to load -- tmp/path/to/file (LoadError)
        from tmp/caller.rb:1:in '<main>'
  • 更奇怪的结构:
    require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')
    似乎有效,但它很奇怪而且看起来不太好看。
  • 使用backports gem - 它有点重,它需要 ruby​​gems 基础架构并包含大量其他解决方法,而我只想require使用相关文件。

StackOverflow 上有一个密切相关的问题,它提供了更多示例,但没有给出明确的答案——这是最佳实践。

是否有任何体面的、被所有人接受的通用解决方案来让我的应用程序同时在 Ruby <1.9.2 和 >=1.9.2 上运行?

更新

澄清:我不想要像“你可以做 X”这样的答案——事实上,我已经提到了大多数有问题的选择。我想要基本原理,即为什么它是最佳实践,它的优点和缺点是什么以及为什么应该在其他方法中选择它。

4

11 回答 11

64

一个解决方法刚刚添加到“aws”宝石中,所以我想我会分享它,因为它是受这篇文章的启发。

https://github.com/appoxy/aws/blob/master/lib/awsbase/require_relative.rb

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

这允许您require_relative在 ruby​​ 1.8 和 1.9.1 中像在 ruby​​ 1.9.2 中一样使用。

于 2011-01-17T22:04:13.557 回答
46

在我跳转到 1.9.2 之前,我使用了以下相对要求:

require File.expand_path('../relative/path', __FILE__)

你第一次看到它时有点奇怪,因为它看起来好像在开头有一个额外的'..'。原因是expand_path它将扩展相对于第二个参数的路径,并且第二个参数将被解释为好像它是一个目录。__FILE__显然不是目录,但这并不重要,因为expand_path它不关心文件是否存在,它只会应用一些规则来扩展诸如..,.~. 如果你能克服最初的“等待一分钟,那里没有额外的..吗?” 我认为上面的行很好用。

假设__FILE__/absolute/path/to/file.rb,会发生什么expand_path将构造字符串/absolute/path/to/file.rb/../relative/path,然后应用一条规则说..应该删除它之前的路径组件(file.rb在这种情况下),返回/absolute/path/to/relative/path.

这是最佳实践吗?取决于你的意思,但它似乎遍布 Rails 代码库,所以我会说它至少是一个足够常见的习语。

于 2010-12-02T09:44:14.310 回答
6
$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

这不是一个好的安全习惯:为什么要公开整个目录?

require './path/to/file'

如果 RUBY_VERSION < 1.9.2 这不起作用

使用奇怪的结构,例如

require File.join(File.dirname(__FILE__), 'path/to/file')

更奇怪的结构:

require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file')

使用 backports gem - 它有点重,它需要 ruby​​gems 基础设施并包含大量其他解决方法,而我只想使用相关文件。

您已经回答了为什么这些不是最佳选择。

检查 RUBY_VERSION < 1.9.2,然后将 require_relative 定义为 require,然后在需要的地方使用 require_relative

检查 require_relative 是否已经存在,如果存在,请尝试像以前的情况一样继续

这可能有效,但有更安全、更快捷的方法:处理 LoadError 异常:

begin
  # require statements for 1.9.2 and above, such as:
  require "./path/to/file"
  # or
  require_local "path/to/file"
rescue LoadError
  # require statements other versions:
  require "path/to/file"
end
于 2011-10-25T14:15:10.090 回答
6

Pickaxe 有一个 1.8 的片段。这里是:

def require_relative(relative_feature)
  c = caller.first
  fail "Can't parse #{c}" unless c.rindex(/:\d+(:in `.*')?$/)
  file = $`
  if /\A\((.*)\)/ =~ file # eval, etc.
    raise LoadError, "require_relative is called in #{$1}"
  end
  absolute = File.expand_path(relative_feature, File.dirname(file))
  require absolute
end

它基本上只是使用 Theo 回答的内容,但您仍然可以使用require_relative.

于 2010-12-02T18:04:27.980 回答
5

我喜欢使用 rbx-require-relative gem ( source )。它最初是为 Rubinius 编写的,但它也支持 MRI 1.8.7,并且在 1.9.2 中什么也不做。需要 gem 很简单,我不必将代码片段扔到我的项目中。

将其添加到您的 Gemfile:

gem "rbx-require-relative"

那么require 'require_relative'在你面前require_relative

例如,我的一个测试文件如下所示:

require 'rubygems'
require 'bundler/setup'
require 'minitest/autorun'
require 'require_relative'
require_relative '../lib/foo'

这是所有这些 IMO 中最干净的解决方案,并且 gem 没有 backports 重。

于 2012-10-04T18:30:15.040 回答
4

backportsgem 现在允许单独加载反向端口。

然后你可以简单地:

require 'backports/1.9.1/kernel/require_relative'
# => Now require_relative works for all versions of Ruby

require不会影响较新的版本,也不会更新任何其他内置方法。

于 2013-03-08T19:25:29.147 回答
3

另一种选择是告诉解释器要搜索哪些路径

ruby -I /path/to/my/project caller.rb
于 2013-03-30T02:59:09.890 回答
3

我没有看到基于 __FILE__ 的解决方案指出的一个问题是它们与符号链接有关。例如说我有:

~/Projects/MyProject/foo.rb
~/Projects/MyProject/lib/someinclude.rb

主脚本,入口点,应用程序是 foo.rb。该文件链接到我的 $PATH 中的 ~/Scripts/foo。当我执行 'foo' 时,此要求语句被破坏:

require File.join(File.dirname(__FILE__), "lib/someinclude")

因为 __FILE__ 是 ~/Scripts/foo 所以上面的 require 语句寻找 ~/Scripts/foo/lib/someinclude.rb 这显然不存在。解决方案很简单。如果 __FILE__ 是符号链接,则需要取消引用。Pathname#realpath 将帮助我们解决这种情况:

需要“路径名”
需要 File.join(File.dirname(Pathname.new(__FILE__).realpath), "lib/someinclude")
于 2013-08-15T18:05:30.023 回答
2

如果你正在构建一个 gem,你不想污染加载路径。

但是,在独立应用程序的情况下,只需将当前目录添加到加载路径就非常方便,就像在前 2 个示例中所做的那样。

我投票给名单上的第一个选项。

我很想看到一些可靠的 Ruby 最佳实践文献。

于 2011-07-01T14:57:22.847 回答
1

relative_require如果它不存在(即低于 1.8),我会定义我自己的,然后在任何地方使用相同的语法。

于 2010-12-02T14:56:23.007 回答
0

Ruby on Rails 方式:

config_path = File.expand_path("../config.yml", __FILE__)
于 2017-02-23T17:24:59.673 回答