File.expand_path (“../../ Gemfile”, __FILE__) Nó hoạt động như thế nào? Tệp ở đâu?


84

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

Tôi chỉ đang cố gắng truy cập tệp .rb từ một số thư mục và một hướng dẫn cho tôi biết sử dụng mã này nhưng tôi không hiểu cách nó tìm thấy tệp gem.


1
Theo

Câu trả lời:


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

là một thành ngữ Ruby hơi xấu xí để lấy đường dẫn tuyệt đối đến một tệp khi bạn biết đường dẫn liên quan đến tệp hiện tại. Một cách viết khác là:

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

cả hai đều xấu, nhưng biến thể đầu tiên ngắn hơn. Tuy nhiên, biến thể đầu tiên cũng không trực quan cho đến khi bạn hiểu rõ về nó. Tại sao thêm ..? (nhưng biến thể thứ hai có thể cung cấp manh mối về lý do tại sao nó lại cần thiết).

Đây là cách hoạt động: File.expand_pathtrả về đường dẫn tuyệt đối của đối số đầu tiên, liên quan đến đối số thứ hai (mặc định là thư mục làm việc hiện tại). __FILE__là đường dẫn đến tệp có mã. Vì đối số thứ hai trong trường hợp này là đường dẫn đến tệp và File.expand_pathgiả sử là một thư mục, chúng ta phải thêm một phần bổ sung ..vào đường dẫn để đường dẫn đúng. Đây là cách nó hoạt động:

File.expand_pathvề cơ bản được thực hiện như thế này (trong đoạn mã sau pathsẽ có giá trị là ../../Gemfilerelative_tosẽ có giá trị là /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

(có một chút nữa về nó, nó mở rộng ~đến thư mục chính, v.v. - có lẽ cũng có một số vấn đề khác với đoạn mã ở trên)

Bước qua một lệnh gọi đến mã trên absolute_pathtrước tiên sẽ nhận được giá trị /path/to/file.rb/../../Gemfile, sau đó đối với mỗi vòng trong vòng lặp, phần đầu tiên ..sẽ bị xóa, cùng với thành phần đường dẫn trước nó. Đầu tiên /file.rb/..bị loại bỏ, sau đó vào vòng tiếp theo /to/..bị loại bỏ, và chúng tôi nhận được /path/Gemfile.

Để làm cho một câu chuyện dài ngắn, File.expand_path('../../Gemfile', __FILE__)là một mẹo để có được đường dẫn tuyệt đối của một tệp khi bạn biết đường dẫn liên quan đến tệp hiện tại. Phần bổ sung ..trong đường dẫn tương đối là loại bỏ tên của tệp trong đó __FILE__.

Trong Ruby 2.0, có một Kernelhàm được gọi __dir__là được thực hiện dưới dạng File.dirname(File.realpath(__FILE__)).


2
Có lý do gì mà bạn không nên chỉ sử dụng 'request_relative' ngoài việc không tương thích với phiên bản trước Ruby 1.9.2 không?
Danny Andrews

9
Kể từ Ruby 2.0, bạn có thể sử dụngFile.expand_path('../Gemfile',__dir__)
Phrogz

Dòng này của Theo cuối cùng đã có nó để nhấp cho tôi File.expand_path assumes a directory, mặc dù __FILE__không phải là một danh bạ. Để mọi thứ có ý nghĩa, hãy sử dụng __dir__nó thực sự là một thư mục.
mbigras

9

Hai tài liệu tham khảo:

  1. Tài liệu về phương thức File :: expand_path
  2. Cách __FILE__hoạt động trong Ruby

Tôi tình cờ thấy điều này hôm nay:

boot.rb cam kết trong Rails Github

Nếu bạn đi lên hai thư mục từ boot.rb trong cây thư mục:

/ railties / lib / rails / máy phát điện / đường ray / ứng dụng / mẫu

bạn thấy Gemfile, khiến tôi tin rằng File.expand_path("../../Gemfile", __FILE__)tham chiếu đến tệp sau:/path/to/this/file/../../Gemfile


Cám ơn bài viết, nhưng điều này đến từ các hướng dẫn gembundler vì vậy tôi đã cố gắng để hiểu nó một cách chính xác làm thế nào họ đã có nó :)
thenengah
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.