mbigras
mbigras

Reputation: 8055

How does File.expand_path work?

I'm not sure I understand the order in which operations are done with File.expand_path. Below is an example pry session:

[1] pry(main)> File.expand_path('.')
=> "/Users/max/Dropbox/work/src/github.com/mbigras/foobie"
[2] pry(main)> File.expand_path('..')
=> "/Users/max/Dropbox/work/src/github.com/mbigras"
[3] pry(main)> File.expand_path('..', "cats")
=> "/Users/max/Dropbox/work/src/github.com/mbigras/foobie"
[4] pry(main)> File.expand_path('..', __FILE__)
=> "/Users/max/Dropbox/work/src/github.com/mbigras/foobie"
[5] pry(main)> File.expand_path('../lib', __FILE__)
=> "/Users/max/Dropbox/work/src/github.com/mbigras/foobie/lib"
[7] pry(main)> File.expand_path('./lib')
=> "/Users/max/Dropbox/work/src/github.com/mbigras/foobie/lib"
[8] pry(main)> File.expand_path('./lib', __FILE__)
=> "/Users/max/Dropbox/work/src/github.com/mbigras/foobie/(pry)/lib"
[9] pry(main)>

[1] makes sense, I'm expanding the path of the current working directory.

[2] makes sense, I'm expanding the path of the parent directory of the cwd

[3] doesn't make sense, I accept from reading another answer that for some reason ruby implicitly takes the File.dirname of the second arg and in the case of File.dirname('cats') it expands to the cwd . because 'cats' isn't nested. But then why doesn't File.expand_path('..', '.') have the same result?

[18] pry(main)> File.expand_path('..', 'cats')
=> "/Users/max/Dropbox/work/src/github.com/mbigras/foobie"
[19] pry(main)> File.dirname('cats')
=> "."
[20] pry(main)> File.expand_path('..', '.')
=> "/Users/max/Dropbox/work/src/github.com/mbigras"

[4] doesn't make sense but for the same reason as [3]. In this case the "random string" is "(pry)" because p __FILE__ #=> "(pry)" while inside a pry session.

[5] doesn't make sense, why would File.expand_path go to seemingly noone's parent directory and then magically come back to the cwd and decide to go into lib

[7] makes sense, but doesn't help me understand [5]

[8] doesn't make sense, why is the "random string" now wedged between the cwd . and lib

From the docs:

File.expand_path("../../lib/mygem.rb", __FILE__)
#=> ".../path/to/project/lib/mygem.rb"

So first it resolves the parent of __FILE__, that is bin/, then go to the parent, the root of the project and appends lib/mygem.rb.

The order of operations doesn't really add up to me.

  1. Take File.dirname(__FILE__)
  2. Go to the parent which is the root
  3. append lib/mygem.rb

Steps 2 and 3 don't help. Why are we going to the parent? Why did we even do Step 1 in the first place? Why is there ../..? Doesn't that mean go two levels up from the current working directory?

Would love some guiding principles to understand these examples.

Edit to add the Gold:

File.expand_path goes to the first parameter from the directory specified by the second parameter (Dir.pwd if not present). - Eric Duminil

Upvotes: 1

Views: 3915

Answers (1)

Eric Duminil
Eric Duminil

Reputation: 54223

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.

Upvotes: 2

Related Questions