Ruby:require vs require_relative-Ruby <1.9.2と> = 1.9.2の両方で実行する回避策のベストプラクティス


153

私がしたい場合はベストプラクティスであるrequireRubyで相対ファイル私はそれが両方は1.8.xおよび> = 1.9.2で動作するようにしたいですか?

いくつかのオプションが表示されます:

  • ただやれば$LOAD_PATH << '.'、すべてを忘れる
  • 行う $LOAD_PATH << File.dirname(__FILE__)
  • require './path/to/file'
  • RUBY_VERSION1.9.2未満かどうかを確認し、require_relativeとして定義しrequirerequire_relative後で必要な場所すべてで使用する
  • require_relativeすでに存在するかどうかを確認し、存在する場合は、前のケースと同様に続行してください。
  • 次のような奇妙な構造を使用してください-残念ながら、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')
  • バックポートの gemを使用します。これはかなり重いです。rubygemsインフラストラクチャが必要で、他の回避策がたくさん含まれていrequireますが、相対ファイルで作業したいだけです。

ありますStackOverflowのに密接に関連する質問いくつかのより多くの例を示しますが、それは明確な答え与えていない-ベストプラクティスです。

Ruby <1.9.2と> = 1.9.2の両方でアプリケーションを実行できるようにするための、誰でも受け入れられる汎用的な解決策はありますか?

更新

明確化:「Xを実行できます」のような答えだけが欲しいのではありません。実際、問題の選択肢のほとんどについてはすでに述べました。私が欲しいの理論的根拠、つまり、なぜそれが最善の方法である、その長所と短所は何であり、なぜそれが他の人の中から選択する必要がありますが。


3
こんにちは私は新しいです。誰かが最初から説明できますか?requireとの違いは何require_relativeですか?
大佐パニック

3
古いRuby 1.8では、ファイルを実行a.rbし、インタープリターb.rbに現在のディレクトリ(通常はと同じディレクトリa.rb)のファイルの内容を読み取って解析させたい場合は、単に書き込むだけでrequire 'b'、デフォルトの検索パスに現在のディレクトリが含まれているので問題ありません。最新のRuby 1.9では、標準のライブラリパスで検索するrequire_relative 'b'場合と同様に、この場合は記述する必要がありrequire 'b'ます。これは、適切にインストールされない単純なスクリプト(たとえば、インストールスクリプト自体)の前方互換性と後方互換性を損なうものです。
GreyCat

あなたは今使用することができるbackportsだけのためにrequire_relative、私の答えを参照してください...
マルク=アンドレ・Lafortune

回答:


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を必須にし、残りのrequire_relativeをrequire_relativeにする必要があります。それとも何か不足していますか?
ethicalhack3r

7
このrequire_relative関数は、Rubyコアライブラリの拡張プロジェクトに含まれています。次の場所にあり ます:rubyforge.org/projects/extensionsこれらは、でインストールできるはずgem install extensionsです。次に、コードの前に次の行を追加しますrequire_relative:require 'extensions / all'(Aurrilの投稿はこちらから提供)
thegreendroid

@ ethicalhack3rは、そのコードをコピーしてルビスクリプトの上部に貼り付けるか、レールにある場合は、上部のenvironment.rbなどに配置します。
Travis Reeder、

46

1.9.2にジャンプする前に、相対的な要件として次を使用しました。

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

最初に「..」が追加されているように見えるので、初めて見ると少し変です。その理由はexpand_path、2番目の引数に相対的なパスを展開し、2番目の引数がディレクトリであるかのように解釈されるためです。__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のコードベース全体にあるように思われるので、少なくとも一般的な十分なイディオムだと思います。


1
これもよく見ます。醜いですが、うまく機能しているようです。
yfeldblum 2010

12
少しクリーナー:File.expand_path( 'relative / path'、File.dirname(FILE))が必要
Yannick Wurm

1
私はそれがもっときれいだとは思わない、それはちょうどより長い。どちらも地獄のようにぼんやりしていて、2つの悪いオプションから選択するときは、入力の手間が少ないオプションを選びます。
Theo

6
File.expand_path( '../ relpath.x'、File.dirname(FILE))は、より冗長ですが、より良いイディオムのようです。間違いなく壊れているファイルパスの機能が、余分な存在しないディレクトリを含むディレクトリパスとして解釈されることに依存すると、その機能が修正されたときに、壊れる可能性があります。
jpgeek

1
壊れているかもしれませんが、UNIXではずっとそうでした。パスと「..」の解像度に関しては、ディレクトリとファイルの間に違いはありません。そのため、スリープ状態を失うことはありません。
Theo

6

つるはしには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


このスニペットをアクティブにする必要があるかどうかを確認するにはどうすればよいですか?を使用$RUBY_VERSIONするか、require_relative存在するかどうかを直接確認することによって?
GreyCat

1
常にアヒルのタイプrequire_relativeです。定義されているかどうかを確認してください。
Theo

@Theo @GreyCatはい、必要かどうかを確認します。ここにスニペットを置いて、人々に見せていました。個人的には、とにかくグレッグの答えを使用します。誰かが自分で言わずに言ったので、私は本当にこれを投稿していました。
Paul Hoffer

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

バックポートの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では何もしません。宝石を要求するのは簡単で、コードスニペットをプロジェクトに投入する必要はありません。

それをGemfileに追加します。

gem "rbx-require-relative"

その後require 'require_relative'、あなたの前にrequire_relative

たとえば、私のテストファイルの1つは次のようになります。

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

これは、これらのIMOの中で最もクリーンなソリューションであり、gemはバックポートほど重くありません。


4

backports宝石は今、バックポートの個々のロードを可能にします。

その後、単純に次のことができます。

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__に基づくソリューションで指摘したことのない問題の1つは、シンボリックリンクに関して解決できないことです。たとえば、私が持っているとしましょう:

~/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

gemを構築している場合は、ロードパスを汚染することはできません。

ただし、スタンドアロンアプリケーションの場合、最初の2つの例のように、現在のディレクトリをロードパスに追加するだけで非常に便利です。

私の投票はリストの最初のオプションに行きます。

Rubyのベストプラクティスに関する確かな資料を見たいと思います。


1
Re:「Rubyのベストプラクティスに関する確かな資料を見たいです。」Gregory BrownのRuby Best Practicesをダウンロードできます。Railsのベストプラクティスサイトもご覧ください。
マイケルストーカー

1

relative_requireそれが存在しない場合(つまり1.8未満の場合)は自分で定義し、どこでも同じ構文を使用します。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.