From: Greg Hurrell Date: 2007-03-08T01:50:05+09:00 Subject: Making "require" just work with some dynamic load path manipulation The problem: If I have a simple file, "a.rb": #!/usr/bin/env ruby require 'b' And it's companion, "b.rb": puts "Hello, world!" Running "a.rb" works fine if I start from the directory containing those files. If I change to another directory, however, I get a LoadError because Ruby doesn't know how to find "b.rb". Workarounds: 1. Set or modify the RUBYLIB environment variable so that Ruby knows which additional locations to search. 2. Manipulate the load path ($:) from within the program itself. These workarounds are fine for me because I understand the quirks of Ruby's load path. But I am looking for a way to make this stuff just work automatically for other users who don't necessarily understand Ruby's load path and will expect "a" to be able to find "b" because they're in the same directory, no matter what the current working directory is at the time. The solution: So I already have a solution for this based on workaround "2" and I wanted to get some feedback on whether this is a good idea or not. The basic idea is to make a "custom_require" method which does the following: 1. Try requiring the file normally. 2. On a LoadError, try adding the directory containing the file to the load path (only if it's not already present). 3. Now try requiring it again. 4. If you get a second LoadError then put the load path back the way it was and re-raise the original error. 5. If you succeed then put the load path back the way it was anyway. So here's the basic code to do this. I try to make it as conservative as possible by making the modifications to the load path temporary, and always adding absolute paths so as to minimize the likelihood of the same file being evaluated twice. The security of dynamically modifying the load path is not really a concern because the users running this scripts already have full write access to them anyway and can therefore already do whatever they want, and they (and nobody else) have full control over the directories in which the scripts are stored. To use the new method you would do: custom_require 'b', __FILE__ The second parameter is necessary (as far as I can tell) because I know of no other clean way for the "custom_require" method (itself in a different file) to know which file was being evaluated when the call to "custom_require" was made. What I'd like to know is, is there a better way? def custom_require(file, fallback) begin require file rescue LoadError => e candidate = Pathname.new(fallback).dirname.realpath if $:.any? { |path| Pathname.new(path).realpath == candidate rescue false } raise e else begin $:.push(candidate) status = require file rescue LoadError raise e ensure $:.pop status end end end end