1. ホーム
  2. ruby

[解決済み] File.expand_path("../../Gemfile", __FILE__) これはどのように機能するのでしょうか?ファイルはどこにあるのでしょうか?

2023-04-09 13:58:41

質問

ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile", __FILE__)

私はいくつかのディレクトリから.rbファイルにアクセスしようとしているだけで、チュートリアルはこのコードを使うように言っていますが、どのようにgemファイルを見つけるのかがわかりません。

どのように解決するのですか?

File.expand_path('../../Gemfile', __FILE__)

は、現在のファイルからの相対パスがわかっているときに、そのファイルへの絶対パスを取得するための、やや醜いRubyのイディオムです。別の書き方はこうです。

File.expand_path('../Gemfile', File.dirname(__FILE__))

はどちらも醜いですが、最初の variant の方が短いです。しかし、最初のバリエーションは、コツをつかむまでは非常に直感的ではありません。なぜ余分な .. ? (が必要なのか(しかし、2つ目のバリエーションはそれがなぜ必要なのかについての手がかりを与えるかもしれません)。

このように File.expand_path は、第一引数の絶対パスと第二引数(デフォルトは現在の作業ディレクトリ)からの相対パスを返します。 __FILE__ はコードが存在するファイルへのパスです。この場合、第2引数はファイルへのパスであり File.expand_path はディレクトリを想定しているので、余計な .. を追加しなければなりません。これがその方法です。

File.expand_path は基本的にこのように実装されています (以下のコードでは path の値を持つことになります。 ../../Gemfilerelative_to の値を持つことになります。 /path/to/file.rb ):

def File.expand_path(path, relative_to=Dir.getwd)
  # first the two arguments are concatenated, with the second argument first
  absolute_path = File.join(relative_to, path)
  while absolute_path.include?('..')
    # remove the first occurrence of /<something>/..
    absolute_path = absolute_path.sub(%r{/[^/]+/\.\.}, '')
  end
  absolute_path
end

(もう少しだけ、展開される ~ をホームディレクトリに展開するなど -- 上記のコードには他にもいくつか問題があると思われます)

上のコードの呼び出しをステップスルーする absolute_path は最初に値を取得します。 /path/to/file.rb/../../Gemfile を取得し、ループ内の各ラウンドで最初の .. はその前のパスコンポーネントと一緒に削除されます。最初の /file.rb/.. が削除され、次のラウンドで /to/.. が削除され、次のラウンドでは /path/Gemfile .

長い話を短くすると File.expand_path('../../Gemfile', __FILE__) は、現在のファイルからの相対パスがわかっているときに、 ファイルの絶対パスを取得するためのトリックです。このために .. を追加することで、相対パス中のファイル名が __FILE__ .

Ruby 2.0では Kernel という関数があります。 __dir__ として実装されている File.dirname(File.realpath(__FILE__)) .