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