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

如果我想在Ruby中require一个相关文件, 而且我希望它能在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地方使用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>' 
  • 即使更古怪的build筑:
     require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file') 

    似乎工作,但它很奇怪,不太好看。

  • 使用后端gem – 这是一种沉重的,它需要rubygems基础设施,并包括吨其他解决方法,而我只是想require处理相关文件。

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

是否有任何体面的,通过所有人接受的通用解决scheme,使我的应用程序运行在Ruby 1.9.2和> = 1.9.2?

UPDATE

澄清:我不想只是像“你可以做X”那样的答案 – 事实上,我已经提到了大部分的问题。 我想要理由 ,也就是为什么这是一个最佳实践,它的优缺点是什么,为什么应该select其他的。

一个解决方法是刚刚添加到“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.9.2和1.9.1中使用Ruby 1.9.2一样。

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

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

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

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

这是最佳做法吗? 取决于你的意思,但它似乎是遍及Rails代码库,所以我会说这至less是一个普通的习惯用法。

镐有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

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

即使更古怪的build筑:

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

使用后端gem – 这是一种沉重的,它需要rubygems基础设施,并包括吨其他解决方法,而我只是想要求处理相关文件。

你已经回答了为什么这些不是最好的select。

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

检查require_relative是否已经存在,如果是,则尝试按前面的方式继续

这可能工作,但有更安全和更快的方法:处理LoadErrorexception:

 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-require-relative gem( source )的粉丝。 它最初是为Rubinius编写的,但它也支持MRI 1.8.7,在1.9.2中什么都不做。 需要一个gem很简单,我不必把代码片段扔进我的项目。

将它添加到你的Gemfile:

 gem "rbx-require-relative" 

然后require 'require_relative'之前require 'require_relative'

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

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

这是所有这些国际海事组织中最干净的解决scheme,而且这个gem不像后援那么重。

backports gem现在允许单独加载backports。

那么你可以简单地:

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

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

另一种select是告诉解释器search哪条path

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

有一个我没有看到的基于__FILE__的解决scheme的问题是,它们违背了符号链接。 比如说我有:

 ~/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。 解决scheme很简单。 如果__FILE__是一个符号链接,它需要被解除引用。 path名#realpath将帮助我们处理这种情况:

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

如果你正在build造一个gem,你不想污染负载path。

但是,在独立应用程序的情况下,只需将当前目录添加到加载path,就像在前两个示例中一样。

我的选票转到了名单上的第一个选项。

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

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

Ruby on Rails方式:

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