web-dev-qa-db-ja.com

ディレクトリを$ LOAD_PATHに追加する(Ruby)

現在実行されているファイルのディレクトリを$ LOAD_PATH(または$ :)に追加するためによく使用される2つの手法を見てきました。 gemを使用していない場合にこれを行うことの利点がわかります。明らかに、一方はもう一方よりも冗長に見えますが、一方をもう一方に重ねる理由はありますか?

最初の冗長な方法(過剰すぎる可能性があります):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

より簡単で迅速かつ汚い:

$:.unshift File.dirname(__FILE__)

どちらか一方を使用する理由は何ですか?

88
Mark W

単に$:.unshift File.dirname(__FILE__)を他のコードよりも上に行くと言います。これは、コード内で$LOAD_PATHを使用するよりもはるかに多く使用されているためです。

49
Ryan Bigg

Rubyロードパスは一般的に$:と書かれていますが、短いからといって、改善されません。かゆくて、他の人がいるからといってそれをする必要はありません。

$LOAD_PATH

...そして別れを告げる...

# I don't quite understand what this is doing...
$:
146
user176788

私は「迅速で汚い」方法があまり好きではありません。 Rubyを初めて知った人は、どのような$:.です。

これはもっと明白だと思います。

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

または、フルパスを使用したい場合...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

[〜#〜] update [〜#〜] 2009/09/10

最近、私は次のことをしています:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

GitHubを閲覧しているときに、さまざまなRubyプロジェクトの多くで見ました。

コンベンションのようですか?

22
Luke Antins

Railsプロジェクトにscript/consoleと入力して$:と入力すると、Rubyのロードに必要なすべてのディレクトリを含む配列が取得されます。この小さな演習では、$:が配列であるため、unshiftメソッドまたは<<演算子を使用して他のディレクトリの先頭に追加するなどの機能を実行できます。ステートメント$:$LOAD_PATHは同じです。

あなたが言及したようにそれを素早く汚い方法で行うことの欠点はこれです:ブートパスにすでにディレクトリがある場合、それはそれ自体を繰り返します。

例:

私が作成したtodoというプラグインがあります。私のディレクトリは次のように構成されています:

/--- vendor 
 | 
 | ---/plugins 
 | 
 | ---/todo 
 | 
 | ---/lib 
 | 
 | ---/app 
 | 
 | ---/models 
 | ---/controllers 
 | 
 | ---/Rails 
 | 
 | --- init.rb 

Init.rbファイルに次のコードを入力しました。

## In vendor/plugins/todo/Rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

「models」、「controllers」、および「models」という文字列に対してブロック内のアクションを実行するようにコードブロックに指示する方法に注意してください。ここで「models」を繰り返します。 (FYI、%w{ ... }は、Ruby=文字列の配列を保持する)を伝える別の方法です。)script/consoleを実行するとき、次のように入力します。

>> puts $:

そして、文字列の内容を読みやすくするためにこれを入力します。私が得る出力は次のとおりです。

 ... 
 ... 
 ./ Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models 
 ./ Users /Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

ご覧のとおり、これは私が現在取り組んでいるプロジェクトを使用しているときに作成できる簡単な例ですが、気を付けないと、迅速で汚れた方法はパスの繰り返しにつながります。より長い方法では、繰り返されるパスをチェックし、それらが発生しないことを確認します。

あなたが経験豊富なRailsプログラマーなら、あなたはおそらくあなたが何をしているかについて非常に良い考えを持っているでしょう、そしておそらくパスを繰り返すことの間違いを犯さないでしょう。自分が何をしているかを本当に理解するまで、より長い道のりを歩んでください。

8
Dyba

Rspecを使用する場合、相対パスでディレクトリを追加するのが一番良かったです。私はそれが十分に冗長であると思いますが、それでもナイスワンライナーです。

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
Dave Robertson

より良い、よりクリーンなコードでロードパスを設定できるgemがあります。これをチェックしてください: https://github.com/nayyara-samuel/load-path

また、優れたドキュメントがあります

1
Rubyist

この質問が最初に聞かれてから長い時間が経ちましたが、共有したい追加の回答があります。

数年にわたって別のプログラマーによって開発されたRubyアプリケーションがいくつかあり、それらは同じデータベースにアクセスするかもしれませんが、異なるアプリケーションで同じクラスを再利用します。これはDRYルールに違反するため、すべてのRubyアプリケーションで共有されるクラスライブラリを作成することにしました。それをメインのRubyライブラリに入れることもできますが、そうしないと、一般的なコードベースにカスタムコードが隠れてしまいます。

定義済みの名前「profile.rb」と使用しているクラスとの間に名前の競合があるという問題がありました。共通のコードライブラリを作成しようとするまで、この競合は問題ではありませんでした。通常、Rubyは最初にアプリケーションの場所を検索し、次に$ LOAD_PATHの場所に移動します。

Application_controller.rbは作成したクラスを見つけることができず、元の定義がクラスではないため、元の定義にエラーをスローしました。アプリケーションのapp/modelsセクションからクラス定義を削除したため、Rubyはそこに見つからず、Rubyパスで探しました。

そこで、$ LOAD_PATH変数を変更して、使用していたライブラリディレクトリへのパスを含めました。これは、初期化時にenvironment.rbファイルで実行できます。

新しいディレクトリが検索パスに追加された場合でも、Rubyは優先的にシステム定義ファイルを優先的に使用していたため、エラーをスローしていました。 $ LOAD_PATH変数の検索パスは、優先的にRubyパスを優先的に検索します。

そのため、組み込みライブラリを検索する前にRubyが共通ライブラリでクラスを見つけられるように、検索順序を変更する必要がありました。

このコードは、environment.rbファイルでそれを行いました。

Rails::Initializer.run do |config|

* * * * *

path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)

* * * * *

end

このレベルでは、以前に指定した高度なコーディング構造は使用できないと思いますが、アプリの初期化時に何かを設定したい場合はうまく機能します。元の$ LOAD_PATH変数を新しい変数に追加するときに元の順序を維持する必要があります。そうしないと、メインのRubyクラスの一部が失われます。

Application_controller.rbファイルでは、単純に

require 'profile'
require 'etc' #etc

これにより、アプリケーション全体のカスタムライブラリファイルが読み込まれます。つまり、すべてのコントローラーでrequireコマンドを使用する必要はありません。

私にとって、これは私が探していた解決策であり、情報を伝えるためにこの答えにそれを追加すると思いました。

0
Timothy Dooling