File.expand_path( ../../Gemfile , __FILE__) How does this work? Where is the file?
File.expand_path('../../Gemfile', __FILE__)
is a somewhat ugly Ruby idiom for getting the absolute path to a file when you know the path relative to the current file. Another way of writing it is this:
File.expand_path('../Gemfile', File.dirname(__FILE__))
both are ugly, but the first variant is shorter. The first variant is, however, also very non-intuitive until you get the hang of it. Why the extra ..
? (but the second variant may give a clue as to why it is needed).
This is how it works: File.expand_path
returns the absolute path of the first argument, relative to the second argument (which defaults to the current working directory). __FILE__
is the path to the file the code is in. Since the second argument in this case is a path to a file, and File.expand_path
assumes a directory, we have to stick an extra ..
in the path to get the path right. This is how it works:
File.expand_path
is basically implemented like this (in the following code path
will have the value of ../../Gemfile
and relative_to
will have the value of /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
(there's a little bit more to it, it expands ~
to the home directory and so on -- there are probably also some other issues with the code above)
Stepping through a call to the code above absolute_path
will first get the value /path/to/file.rb/../../Gemfile
, then for each round in the loop the first ..
will be removed, along with the path component before it. First /file.rb/..
is removed, then on the next round /to/..
is removed, and we get /path/Gemfile
.
To make a long story short, File.expand_path('../../Gemfile', __FILE__)
is a trick to get the absolute path of a file when you know the path relative to the current file. The extra ..
in the relative path is to eliminate the name of the file in __FILE__
.
In Ruby 2.0 there is a Kernel
function called __dir__
that is implemented as File.dirname(File.realpath(__FILE__))
.
How does File.expand_path work?
Theory
I think you missed a ..
while reading the answer you link to.
No File.dirname
is ever done implicitely by expand_path
.
File.expand_path('../../Gemfile', __FILE__)
# ^^ Note the difference between here and
# vv there
File.expand_path('../Gemfile', File.dirname(__FILE__))
What confused me at first was that the second parameter is always considered to be a directory by File.expand_path
, even if it doesn't exist, even if it looks like a file or even if it is an existing file.
File.expand_path
goes to the first parameter from the directory specified by the second parameter (Dir.pwd
if not present).
Examples
[3]
File.expand_path('..', "cats")
It is executed in the current directory, which is "/Users/max/Dropbox/work/src/github.com/mbigras/foobie"
.
Is cats
an existing file, an existing directory or a non-existent directory?
It doesn't matter to File.expand_path
: "cats"
is considered to be an existing directory, and File.expand_path
starts inside it.
This command is equivalent to launching :
File.expand_path('..')
inside the "/Users/max/Dropbox/work/src/github.com/mbigras/foobie/cats"
directory.
So expand_path
goes back one directory, and lands back to :
"/Users/max/Dropbox/work/src/github.com/mbigras/foobie"
[4]
Same thing. It is equivalent to File.expand_path('..')
from the (probably) non-existing :
"/Users/max/Dropbox/work/src/github.com/mbigras/foobie/(pry)"
So it is :
"/Users/max/Dropbox/work/src/github.com/mbigras/foobie"
[5]
Going from [4], it just goes to the subfolder lib
.
[8]
Starting from "/Users/max/Dropbox/work/src/github.com/mbigras/foobie/(pry)"
, it just goes to the subfolder lib
.
Once again, File.expand_path
never checks if the corresponding folders and subfolders exist.
What does __FILE__ mean in Ruby?
It is a reference to the current file name. In the file foo.rb
, __FILE__
would be interpreted as "foo.rb"
.
Edit: Ruby 1.9.2 and 1.9.3 appear to behave a little differently from what Luke Bayes said in his comment. With these files:
# test.rb
puts __FILE__
require './dir2/test.rb'
# dir2/test.rb
puts __FILE__
Running ruby test.rb
will output
test.rb
/full/path/to/dir2/test.rb
What Does require File.expand_path('../../config/environment', __FILE__) do Exactly?
__FILE__
is the relative path to the file from current directory. File.expand_path
will get you absolute path of file, so above in your question require the environment.rb
file. $:
contains the array of required path, so $:.push
append the your given path into list of required path, so that you can require that file in your app. Rails push various file while booting process.
File paths for parsing in Ruby using Rake
__dir_ method returns directory of the current file you're in, so this should work for you:
File.read(__dir__ + "/../metadata.json")
why File.expand_path(__FILE__)?
__dir__
or (__FILE__
) is not necessarily the same as "."
. The former is the location of the file. The latter is the location from where the main command was called (or wherever it was changed to by commands like Dir.chdir
).
How to read files (.yml) from a Gem
Solution 1
You can store the root path in a constant from your main gem file and retrieve it in other locations of your code. You must ensure that the gem got initialized before the code in your app runs otherwise you'll have an errors because the constant won't be defined.
# lib/my_gem.rb
GEM_ROOT = File.expand_path("../..", __FILE__)
# app/.../some_class.rb
IO.read("#{GEM_ROOT}/config/test.yml")
Solution 2
The most advisable, you can get the gem path programmatically from Bundler
, then use that root path to retrieve the full path of your yml file.
Have a look at this answer that you can easily adapt to your case
Related Topics
How to Install Ruby 1.9.3 in MAC Os X Lion
Guard with Rspec on Rails 4 Giving a Lot of Warnings
How to Run a Ruby Script Using Rbenv with Cron
Size, Length and Count in Rails
Creating a Model That Has a Tree Structure
How to Count the Number of Records That Have a Unique Value in a Particular Field in Ror
How to Select Every Nth Item in an Array
Finding the Element of a Ruby Array with the Maximum Value for a Particular Attribute
Put a Link in a Flash[:Notice]
In Ruby on Rails, After Send_File Method Delete the File from Server
How to Reference a Function in Ruby
Couldn't Require Openssl in Ruby
How to Get the Current Time as 13-Digit Integer in Ruby
Setting Up Private Github Access with Aws Elastic Beanstalk and Ruby Container