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


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已经存在(如果存在),请尝试像以前一样进行
  • 使用怪异的结构,例如-they,它们似乎无法完全在Ruby 1.9中工作,因为,例如:
    require File.join(File.dirname(__FILE__), 'path/to/file')
    $ 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')
  • 使用backport gem-有点沉重,它需要rubygems基础架构,并包含大量其他解决方法,而我只想require使用相关文件。

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

是否有任何体面的,每个人都能接受的通用解决方案使我的应用程序在Ruby <1.9.2和> = 1.9.2上运行?

更新

澄清:我不想要像“您可以做X”之类的答案-实际上,我已经提到了大多数有问题的选择。我想要一个基本原理,即为什么它是一种最佳实践,它的优缺点是什么,为什么要在其他实践中选择它。


3
嗨,我是新手。有人可以从一开始就进行解释吗?require和之间有什么区别require_relative
Panic Panic

3
在较早的Ruby 1.8中,如果您运行文件a.rb并想让解释器读取和解析b.rb当前目录中的文件内容(通常与dir相同a.rb),则只需编写即可require 'b',因为默认搜索路径包括当前目录就可以了。在更现代的Ruby 1.9中,require_relative 'b'在这种情况下,您将不得不编写代码,就像require 'b'只在标准库路径中搜索一样。对于无法正确安装的简单脚本(例如,安装脚本本身)而言,这就是那种向前和向后兼容性的问题。
GreyCat 2012年

您现在可以使用backports只是require_relative,看到我的答案...
马克-安德烈·Lafortune

Answers:


64

刚刚将一种解决方法添加到了“ 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

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


3
您如何要求require_relative.rb文件?您必须需要require_relative.rb,然后需要其余需求。还是我错过了什么?
ethicalhack3r 2012年

7
require_relative函数包含在Ruby核心库的扩展项目中,可以在以下位置找到:rubyforge.org/projects/extensions 您应该能够使用进行安装gem install extensions。然后在你的代码之前添加以下行require_relative:需要“扩展/所有”(来源从Aurril的帖子在这里
thegreendroid

@ ethicalhack3r只需将代码复制并粘贴到ruby脚本的顶部,或者将其粘贴在您的ruby脚本的顶部,或者将其放在top环境.rb或其他地方。
特拉维斯·里德

46

在我跳到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代码库,因此我想至少这是一个足够普遍的习惯用法。


1
我也很常见。这很丑陋,但似乎效果很好。
yfeldblum

12
更加简洁:需要File.expand_path('relative / path',File.dirname(FILE))
Yannick Wurm

1
我认为这不是更干净,只是更长。它们都很笨拙,当在两个错误的选项之间进行选择时,我更喜欢打字少的选项。
西奥(Theo)

6
似乎File.expand_path('../ relpath.x',File.dirname(FILE))是一个更好的习惯用法,尽管它比较冗长。当/如果该功能被修复,则依赖于可以被解释为具有额外不存在的目录的目录路径的文件路径的可能被破坏的功能可能会中断。
jpgeek 2012年

1
也许是折断的,但在UNIX中一直如此。目录和文件在路径和“ ..”的分辨率方面没有什么区别,因此我不会为此感到疲倦。
西奥(Theo)2012年

6

镐有一个用于此的代码段。这里是:

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


如何检查此代码段是否应该被激活?使用$RUBY_VERSION还是通过检查是否require_relative直接存在?
GreyCat 2010年

1
始终为鸭子类型,检查是否require_relative已定义。
Theo

@Theo @GreyCat是的,我会检查是否需要。我只是在向人们展示片段。就我个人而言,无论如何我都会使用格雷格的答案,实际上我只是在发布此内容,因为有人自己没有提到它。
Paul Hoffer 2010年

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

使用backport gem-有点沉重,它需要rubygems基础结构,并包含大量其他解决方法,而我只想要求使用相关文件。

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

检查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

5

我很喜欢使用rbx-require-relative gem()。它最初是为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中最干净的解决方案,而且它的功能不如反向移植那么重。


4

backports现在,gem允许单独加载反向端口。

然后,您可以简单地:

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

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


3

另一个选择是告诉解释器要搜索的路径

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

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__是符号链接,则需要取消引用。Pathname#realpath将在这种情况下帮助我们:

需要“路径名”
需要File.join(File.dirname(Pathname.new(__ FILE __)。realpath),“ lib / someinclude”)

2

如果要构建宝石,则不希望污染加载路径。

但是,对于独立应用程序,就像在前两个示例中所做的那样,仅将当前目录添加到加载路径非常方便。

我的表决权投到了清单上的第一个选项上。

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


1
回复:“我希望看到一些可靠的Ruby最佳实践文献。” 您可以下载Gregory Brown的Ruby最佳实践。您也可以访问Rails最佳实践网站
Michael Stalker 2012年

1

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


0

Ruby on Rails方式:

config_path = File.expand_path("../config.yml", __FILE__)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.