Ruby:require vs require_relative - 在Ruby< 1.9.2和> = 1.9.2中运行的解决方法的最佳实践

时间:2010-12-02 09:08:06

标签: ruby ruby-1.9 ruby-1.8

如果我想在{<1>}中使用Ruby 中的相对文件,我希望它能在1.8.x和&gt; = 1.9.2中工作,那么最佳做法是什么?

我看到了几个选项:

  • 只做require而忘记一切
  • 执行$LOAD_PATH << '.'
  • $LOAD_PATH << File.dirname(__FILE__)
  • 检查是否require './path/to/file'&lt; 1.9.2,然后将RUBY_VERSION定义为require_relative,在之后需要的地方使用require
  • 检查require_relative是否已存在,如果已存在,请尝试按上一个案例继续进行
  • 使用奇怪的结构,例如require_relative - 唉它们似乎在Ruby 1.9中完全不起作用,因为,例如:
    require File.join(File.dirname(__FILE__), 'path/to/file')
  • 甚至更奇怪的构造:require File.join(File.dirname(__FILE__), 'path/to/file')似乎有用,但它很奇怪而且不太好看。
  • 使用backports gem - 它有点沉重,需要rubygems基础架构并包含大量其他解决方法,而我只是希望$ 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>'使用相关文件。

有一个closely related question at StackOverflow可以提供更多示例,但它没有给出明确答案 - 这是最佳做法。

是否有任何体面的,全民接受的通用解决方案使我的应用程序在Ruby&lt; 1.9.2和&gt; = 1.9.2上运行?

更新

澄清:我不想只是“你可以做X”这样的答案 - 事实上,我已经提到过大多数有关的选择。我想理由,即为什么这是一种最佳实践,它的优点和缺点是什么以及为什么应该在其他选择中选择它。

11 个答案:

答案 0 :(得分:63)

这个问题的解决方法刚刚添加到'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

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

答案 1 :(得分:45)

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

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

第一次看到它时有点奇怪,因为它看起来在开始时有一个额外的'..'。原因是expand_path将扩展相对于第二个参数的路径,第二个参数将被解释为它是一个目录。 __FILE__显然不是目录,但这并不重要,因为expand_path并不关心文件是否存在,它只会应用一些规则来扩展..之类的内容,.~。如果你可以克服最初的“waitaminute不存在额外的..吗?”我认为上面这一行很有效。

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

这是最佳做法吗?取决于你的意思,但它似乎已经遍布Rails代码库,所以我认为它至少是一个常见的习惯用语。

答案 2 :(得分: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

答案 3 :(得分:6)

$LOAD_PATH << '.'

$LOAD_PATH << File.dirname(__FILE__)

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

require './path/to/file'

如果RUBY_VERSION&lt;这不起作用。 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 - 它有点沉重,它需要rubygems   基础设施,包括大量其他解决方法,而我只是   希望需要使用相关文件。

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

  

检查RUBY_VERSION&lt; 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

答案 4 :(得分: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中最干净的解决方案,并且宝石不像后退那么重。

答案 5 :(得分:4)

backports宝石现在允许单独加载backports。

然后你可以简单地说:

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

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

答案 6 :(得分:3)

另一种选择是告诉口译员要搜索哪些路径

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

答案 7 :(得分:3)

我没有看到基于__FILE__的解决方案指出的一个问题是它们在符号链接方面有所突破。比如说我有:

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

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

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

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

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

答案 8 :(得分:2)

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

但是,对于独立应用程序,只需将当前目录添加到加载路径就像在前两个示例中一样。

我的投票将转到列表中的第一个选项。

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

答案 9 :(得分:1)

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

答案 10 :(得分:0)

Ruby on Rails方式:

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