在 Ruby < 1.9.2和 > = 1.9.2中解决运行问题的最佳实践

如果我想让 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中不能完全工作,因为,例如: < pre > < code > < code > $cat caller.rb Join (FILE.dirname (_ _ FILE _ _) ,‘ path/to/FILE’) $cat path/to/file. rb 写上“一些测试” $Ruby 来电者 一些测试 $pwd /tmp $Ruby/tmp/call 一些测试 $Ruby tmp/来电者 Tmp/caller.rb: 1: in‘ need’: no such file to load —— tmp/path/to/file (LoadError) 来自 tmp/caller.rb: 1: in“ < main >”
  • 甚至更奇怪的结构:
    require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file') 
    似乎可以工作,但它很奇怪,看起来也不是很好。
  • 使用 后港 gem-它有点沉重,它需要 Rubygems 基础设施,并且包含大量其他的变通方法,而我只想使用 require来处理相关文件。

有一个 与 StackOverflow 密切相关的问题给出了一些更多的例子,但它没有给出一个明确的答案-这是一个最佳实践。

有没有什么像样的、被所有人接受的通用解决方案可以让我的应用程序在 Ruby < 1.9.2和 > = 1.9.2上运行?

更新

澄清一下: 我不想要像“你可以做 X”这样的答案——事实上,我已经提到了大多数问题的选择。我想要 合理性,也就是 为什么,它是一个最佳实践,它的优点和缺点是什么,以及为什么它应该被选择在其他。

73847 次浏览

在跳转到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 代码库,所以我认为它至少是一个足够常见的习惯用法。

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

镐头有一个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

这方面的一个变通方案刚刚被添加到‘ aws’gem 中,所以我想分享一下,因为它受到了这篇文章的启发。

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

这使您可以像在 ruby 1.9.2和 ruby 1.8和1.9.1中那样使用 require_relative

如果您正在构建一个 gem,则不希望污染加载路径。

但是,在绿色软体的情况下,像前面两个例子一样,只需要在加载路径中添加工作目录就非常方便了。

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

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

$LOAD_PATH << '.'


$LOAD_PATH << File.dirname(__FILE__)

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

require './path/to/file'

如果 RUBY _ VERION < 1.9.2,这将不起作用

使用奇怪的结构,例如

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

更奇怪的结构:

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

使用背端口宝石-它是一种沉重的,它需要红宝石 基础设施和包括吨其他变通方法,而我只是 需要处理相关文件。

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

检查 RUBY _ VERION 是否 < 1.9.2,然后将 demand _ relsion 定义为 在需要它的任何地方,都要使用 need _ relant

检查是否已经存在 request _ relant,如果存在,请尝试继续 和之前的案子一样

这可能有效,但是有一种更安全、更快捷的方法: 处理 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

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

将它添加到你的 Gemfile:

gem "rbx-require-relative"

然后在你之前的 require 'require_relative'

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

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

这是所有 IMO 软件中最干净的解决方案,而且这个 gem 也不像 backport 那么重。

backports gem 现在允许单独加载后端口。

然后你可以简单地:

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

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

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

ruby -I /path/to/my/project caller.rb

在基于 _ _ FILE _ _ 的解决方案中,我没有看到指出的一个问题是,它们与符号链接有关。比如说我有:

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

主脚本、入口点和应用程序是 foo.rb。该文件链接到 ~/Scripts/foo,该文件位于我的 $PATH 中。当我执行“ foo”时,这个 request 语句被破坏了:

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

因为 _ _ FILE _ _ 是 ~/Scripts/foo,所以上面的 request 语句查找 ~/Scripts/foo/lib/some include. rb,显然它不存在。解决办法很简单。如果 _ _ FILE _ _ 是一个符号链接,那么它需要被取消引用。Pathname # realpath 将帮助我们解决这种情况:

require "pathname"
require File.join(File.dirname(Pathname.new(__FILE__).realpath), "lib/someinclude")

Ruby on Rails 方式:

config_path = File.expand_path("../config.yml", __FILE__)