My previous blog post received some good comments. In this blog post I am going to further expand on what others are saying .i.e. using expanded path is a bad idea.
Ability to provide alternative implementation
Let's say I have two files.
#main.rb
require 'lib/util'
#lib/util.rb
puts "Hello World"
If you execute ruby main.rb you will see Hello World. No surprise there.
However let's say that I have another directory called common in my dev folder. In this dirctory create a directory called lib and then store a file named util.rb.
#~/dev/common/lib/util.rb
puts "Hello Common World"
Now let's say that for testing purpose I want main.rb to load util.rb from our common library. Here is how you have to do to ensure that util.rb is picked up from common directory.
ruby -I/Users/nsingh/dev/common main.rb
This time the output I got was
Hello Common World
Now let's change original main.rb to be use expanded_path. New code would look like this
require File.expand_path("../lib/util", __FILE__)
Now let's try to use util.rb from common directory.
ruby -I/Users/nsingh/dev/common main.rb
This time output will be NOT be what we saw last time.
Hello World
This is exactly what Aaraon Patterson is talking about when he says if you use File.expand_path then others can't provide an alternative implementation.
In real life I have never done anything like providing alternative implemenation of an item. However I do understand that in theory it makes sense to not to use expanded path.
spec_helper
If you are testing using rspec then your specs will have require 'spec_helper' at the top of the file. If you want to run tests for an individual file then you can do
ruby spec/models/user_spec.rb
Note that there are better ways to execute a particular rspec file. I am using the given case only to discuss about require using relative path vs require using absolute path.
However that code will fail since spec_helper is not in the path. One way to fix that would be to execute following command
ruby -Ispec spec/models/user_spec.rb
Another way to fix it would be to change the user_spec.rb to require spec_heper using expanded path.
require File.dirname(__FILE__) + '/../spec_helper'
Now you can run ruby spec/models/user_spec.rb and it will run fine. However declaring expanded path comes with a cost. Let's say that tomorrow I move user_spec.rb to spec/models/users directory. Now the expanded path is pointing to a wrong file. If I had used spec_helper.rb then this problem would have not been there.
Since usage of expanded path reduces the flexibility of providing alternative implementation and increases the cost of moving around files, I am changing my vote from using expanded path to not using expanded path.
requiring rubygems
Ryan Tomayko wrote about the need to not to require . Sintarta testing documentation follows that guidelines and does not require rubygems. Here is the issue. If a ruby newbie comes along and stores the test file in test.rb and executes the file with command ruby test.rb , then the user will get following error message
`require': no such file to load 'sinatra'
Most likey user will do gem install sinatra and it will still not work. The user does not know that rubygems is not being loaded. The correct command would be
ruby -rubygems test.rb
I like the approach taken by cucumber. If you look at cucumber/cli/main.rb you will see following code
begin
require 'gherkin'
rescue LoadError
require 'rubygems'
require 'gherkin'
end
The cucumber team is trying to not to require rubygems but in case rubygems is not already loaded then they load it for you.
I know this rubygems issue is not directly related to expanded path discussion but since it came in the discussion in the comments, I wanted to provide my point of view.