Paths in Ruby
You’ve got a file, but where does it live? Which directory are you in? Where am I? What’s my name?
For the following examples, imagine:
- you’re executing a file called
foo.rb
- which lives in the file system at
/Users/me/myDir/foo.rb
- you’re located in
/Users/me/myDir
- you’ve executed the command
ruby foo.rb
from the terminal
Remember:
- absolute path - the full path of the file in the file system: it starts with the root directory and includes all the directories between it and the file in question.
- relative path - a partial representation of the absolute path because it starts from some other directory. An example would be
../myDir/foo.rb
.
WTF is __FILE__
You’ve probably seen this keyword used in Ruby. __FILE__
is a constant that represents the name of the file that is currently executing. In our example it’s foo.rb
.
How do I get the absolute pathname of the currently executing file?
Now we know the name of the currently executing file, we can pass it to the expand_path
method of the File
class.
expand_path
returns the absolute pathname of the file provided as an argument.
File.expand_path(__FILE__)
=> "/Users/my/myDir/foo.rb"
N.B. It can also take relative pathnames.
How do I find the absolute directory name of the currently executing file?
If you aren’t interested in the pathname and want the file’s absolute directory without the filename, combine the previous method with the dirname
method.
File.dirname(File.expand_path(__FILE__))
# Or also ...
File.expand_path(File.dirname(__FILE__))
# Or even ...
File.dirname(File.realpath(__FILE__))
=> "/Users/my/myDir"
But __dir__
?
If you only have to worry about supporting Ruby 2.0.0 and above, you can use the __dir__
constant instead.
What’s the working directory and how do I find it?
This is the folder you are currently in - nothing to do with the file that is executing. Use Dir.pwd
to get it.
OMG I’ve always wanted to know - what’s the best way to make a pathname out of lots of different strings??
Well you might have tried ['a','b','c'].join('/')
, but you’d be wrong!
What you really need is File.join()
: this method concatenates arguments with a single slash, and will ensure that there is only one slash between each argument. You’d have to do this cleanup yourself if you used .join('/')
File.join('a','b') => 'a/b'
File.join('a/','b') => 'a/b'
File.join('a/','/b') => 'a/b'
['a/','b'].join('/') => 'a//b'
['a/','/b'].join('/') => 'a///b'
expand_path
bonus time!
So you already know that File.expand_path
returns the absolute path of the 1st argument. But if you provide a 2nd argument it will find the absolute path of the 1st argument relative to the 2nd argument. Get that?
So File.expand_path('../lib', __FILE__)
gets the path to the lib directory relative to the currently executing file, meaning you don’t have to worry about where you actually are when finding paths.
How do I know that you’re not talking baloney? 💩
Whip up a quick rb file and put the following:
puts "__FILE__ = #{__FILE__}"
puts "Absolute pathname = #{File.expand_path(__FILE__)}"
puts "Absolute directory = #{File.dirname(File.expand_path(__FILE__))}"
puts "Alternative absolute directory = #{File.expand_path(File.dirname(__FILE__))}"
puts "Another absolute directory = #{File.dirname(File.realpath(__FILE__))}"
puts "Working directory = #{Dir.pwd}"
Execute and see you for yourself.