web-dev-qa-db-ja.com

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

require Rubyandの相対ファイルにしたい場合のベストプラクティスは何ですか? 1.8.xと> = 1.9.2の両方ですか?

いくつかのオプションがあります:

  • $LOAD_PATH << '.'を実行して、すべてを忘れます
  • $LOAD_PATH << File.dirname(__FILE__)を行う
  • require './path/to/file'
  • Ruby_VERSION <1.9.2かどうかを確認し、require_relativerequireとして定義し、後で必要な場所でrequire_relativeを使用します
  • require_relativeが既に存在するかどうかを確認し、存在する場合は、前の場合と同様に続行してください
  • などの奇妙な構造を使用する
    require File.join(File.dirname(__FILE__), 'path/to/file') </ code>
    -残念ながら、彼らは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>' </ code>
  • さらに奇妙な構造:
    require File.join(File.expand_path(File.dirname(__FILE__)), 'path/to/file') </ code>
    動作しているように見えますが、奇妙で見栄えがよくありません。
  • backports gemを使用します。これは一種の重いもので、rubygemsインフラストラクチャが必要であり、他の多くの回避策が含まれていますが、requireで相対ファイルを操作したいだけです。

StackOverflowで密接に関連した質問 がありますが、これはさらに例を示しますが、明確な答えを与えません-これはベストプラクティスです。

Ruby <1.9.2および> = 1.9.2の両方でアプリケーションを実行するための、誰でも受け入れられるまともなユニバーサルソリューションはありますか?

更新

明確化:「Xを実行できます」のような回答だけが必要なわけではありません。実際、問題の選択肢のほとんどについては既に述べました。私は理論的根拠、すなわちwhyがベストプラクティスですその長所と短所、および他の中で選択する理由。

153
GreyCat

この問題の回避策が「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

これにより、Ruby 1.8および1.9.1のRuby 1.9.2と同様に、require_relativeを使用できます。

64
Travis Reeder

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コードベース全体にあるように見えるため、少なくとも一般的な十分なイディオムだと思います。

46
Theo

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を引き続き使用できます。

6
Paul Hoffer
$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')

Backports 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
6

私はrbx-require-relative gem( source )の使用が好きです。もともとはRubinius用に書かれていましたが、MRI 1.8.7もサポートしており、1.9.2では何もしません。 gemを必要とするのは簡単で、プロジェクトにコードスニペットを挿入する必要はありません。

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はバックポートほど重くありません。

5
Edward Anderson

backports gemにより、バックポートを個別にロードできるようになりました。

次に、単純にできます:

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

このrequireは新しいバージョンには影響せず、他の組み込みメソッドも更新しません。

別のオプションは、インタープリターに検索するパスを伝えることです

Ruby -I /path/to/my/project caller.rb
3
eradman

__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は、この状況に役立ちます。

 require "pathname" 
 require File.join(File.dirname(Pathname.new(__ FILE __)。realpath)、 "lib/someinclude")
3
jptros

Gemを構築している場合、ロードパスを汚染したくないでしょう。

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

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

いくつかの堅実なRubyベストプラクティスの文献を参照してください。

2
Casey Watson

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

1
Phrogz

RailsウェイのRuby:

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