$" and does not know if we refer to the same file using different path. e.g.
#file : lib/foo.rb
puts "loading #{__FILE__}"
#file : lib/bar.rb
require File.dirname(__FILE__) + '/foo'
puts "loading #{__FILE__}"
#file : app/runme.rb
require File.dirname(__FILE__) +'/../lib/foo'
require File.dirname(__FILE__) +'/../lib/bar'
puts $"
Then run
ruby app/runme.rb loading ./app/../lib/foo.rb loading ./app/../lib/foo.rb loading ./app/../lib/bar.rb ["app/../lib/foo.rb", "./app/../lib/foo.rb", "app/../lib/bar.rb"]There are basically few well known techniques to deal with this problem
1. Using absolute path
2. Modifying $LOAD_PATH
3. Using defined?
USING ABSOLUTE PATH
In this variant we always call Kernel#require with a absolute path, the File::expand_path will be used to remove '..' symbol representing parent directory e.g
#file : lib/bar.rb
require File.expand_path(File.dirname(__FILE__)) + '/foo'
puts "loading #{__FILE__}"
#file : app/runme.rb
require File.expand_path(File.dirname(__FILE__)+'/../lib')+'/foo'
require File.expand_path(File.dirname(__FILE__)+'/../lib')+'/bar'
puts $"
run
ruby app/runme.rb loading D:/huy/rubyapp/require_1/lib/foo.rb loading D:/huy/rubyapp/require_1/lib/bar.rb ["D:/huy/rubyapp/require_1/lib/foo.rb", "D:/huy/rubyapp/require_1/lib/bar.rb"]This method is described in post ruby require idiom
MODIFYING $LOAD_PATH
The second quite popular technique is to modify $LOAD_PATH directly e.g.
#file : lib/bar.rb
libpath=File.expand_path(File.dirname(__FILE__)+'/lib')
$LOAD_PATH.unshift(libpath) unless $LOAD_PATH.first==libpath
require 'foo'
puts "loading #{__FILE__}"
#file : app/runme.rb
libpath=File.expand_path(File.dirname(__FILE__)+'/lib')
$LOAD_PATH.unshift(libpath) unless $LOAD_PATH.first==libpath
require 'foo'
require 'bar'
puts $"
In the previous mentioned techniques, the File#expand_path method is used to get absolute path of either file or directory, the same file or directory is kept only once in a relevant global variable. USING defined?
This technique is very old and frequently used by C programmers to guard header file to include multiple e.g.
#file foo.h #ifdef __FOO_H #define __FOO_H ... #endifIn Ruby, I have seen this technique being applied using Kernel#defined? e.g.
#file : lib/foo.rb
unless defined?(FooDefined)
FooDefined=true
puts "loading #{__FILE__}"
end
#file : lib/bar.rb
unless defined?(BarDefined)
BarDefined=true
require File.dirname(__FILE__) + '/foo'
puts "loading #{__FILE__}"
end
#file : app/runme.rb
require File.dirname(__FILE__)+'/../lib/foo'
require File.dirname(__FILE__)+'/../lib/bar'
puts $"
UPDATE on 26-12-2007 The new Kernel#require in Ruby 1.9 store full path in $" make this article obsolete.
1 comment:
Thanks Le Huy, that load path snippet helped me!
Post a Comment