Ruby files have to require other files. There’s no avoiding it. Techniques to manage require statements are numerous and varied. Having tried most of them, I’ve found a system that works well for me. What follows are the guidelines I use to manage Ruby require statements.
Establish a Convenient Search Path
Although it’s possible to use absolute paths or complex relative paths such as below:
1 2 require "/Users/micahmartin/Projects/ttt/lib/game" 3 require File.dirname(__FILE__) + "../../../lib/ai/winner" 4
…this should be avoided as much as possible. Otherwise you’ll be
fixing dozens of require statements every time you move a file. It’s no
fun. You’ll want your
requires to look like this:
1 require "game" 2 require "ai/winner" 3
To achieve this, add your lib directory to the ruby search path:
1 $: << File.expand_path(File.dirname(__FILE__) + “/../lib”) 2
This will have to go in one of the first files that gets loaded. If
you’ve got a standalone Ruby app, you could add this to the startup
script. In a Rails app, it can go in
you’re using RSpec, you may want to add it to
Independent Requiring over Require Farms
In some projects, you’ll find files that contain nothing but
require statements. It’s common in gem projects. The
advantage is that users of the library need only require one file which
in turn requires everything else you’ll need.
Convenient huh? Sure is. But there are 2 major consequences of this:
Every time you add, delete, or rename a file you have to remember to update the require farm file. It’s easy to forget. And the order of requires can get very hard to manage especially if you end up with cyclic dependencies.
Drowning in Dependancies
Require Farms have a tendency to require more than you want. If there’s only a portion of the library you’d like to use, with the Require Farm you get the whole thing. The extra dependancies will consume more memory at run time and they may add undesired behavior to your system. In general, this is a violation of the Dependancy Inversion Principle.
Rather than build Require Farms, allow each file to be responsible for it’s own dependencies. Let each file require the files it needs to work. This approach can be a bit annoying as you’ll find your self using altogether more require statements, but it’ll pay off in the long run.
With each file independently managing it’s dependancies, the system will be easier to maintain and it’s components more reusable.
Building Absolute Paths
Try as you may, you can’t always avoid using absolute file paths in your require statements. The following is reliable way to refer to other files.
1 File.join(File.expand_path(File.dirname(__FILE__)), "..", "spec_helper") 2
Let me break it down. First:
1 File.dirname(__FILE__) 2
This gives you the directory of the current file that is being executed. However, you never know what you’re gonna get since the form of the path is based on how the program was executed.
This might give you an absolute path or it might give you a relative path from anywhere on the system. In some cases, Ruby won’t do what you expect with relative paths so it’s best to expand this into an absolute path.
1 File.expand_path(File.dirname(__FILE__) 2
Now you’ve got an absolute file path and you just have to add a relative path to the desired file. Normally I’d do this:
1 File.expand_path(File.dirname(__FILE__) + "../spec_helper") 2
However, this is not quite portable since I’m using forward slashes. To
get this to work on any system independent of file separator, use
File.join as shown above. Use this technique to require
files when you don’t have your search path configured.