Ruby / Dependencies edit (Category edit)
(deprecated (why??))
The require statement is executed at runtime (there is no compile-time!) and uses the global variable $: to decide where to look for the file. You can modify or inspect $: if you discover that it can't find the file you're trying to include.
Workaround: Use File.expand_path
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
So if you did something like this, you could end up stuck in an infinite loop:
#starting_file.rb require "a.rb" #a.rb require "starting_file.rb" Then run -- don't require, just run it from the command line -- starting_file.rb > ruby starting_file.rb
Granted, you probably shouldn't have circular dependencies like that, but if you do (by accident or design), I think it should be smarter than to get stuck in an infinite loop and overflow the stack! I would consider this a bug.
require_localI'm sure glad the folks at http://facets.rubyforge.org/ wrote this little extension, because I get sick of always doing requires like this:
require File.dirname(__FILE__) + "/something_in_the_same_directory"
How verbose and ugly. Here's the better way that you've been looking for:
require "rubygems" require 'facets/core/kernel/require_local' require_local "something_in_the_same_directory"
http://extensions.rubyforge.org/ has a similar method: require_relative
require_local is more reliable than relying on the $LOAD_PATH and the "." directoryExample:
Even something as innocent-looking as this line at the top of a test can spell trouble:
require 'test/test_helper'
It causes a problem when you try to execute the test script from a directory other than the one the developer anticipated.
active_record_test.rb:2:in `require': no such file to load -- test/test_helper (LoadError)
That kind of brittleness, I hope you will agree, ought to be avoided.
Case in point: http://activescaffold.googlecode.com/svn/tags/current/test/extensions/active_record_test.rb
p $LOAD_PATH # Added by Tyler require 'test/test_helper'
If we run it from the root of the plugin directory, the test works just fine!
~/code/plugins/activescaffold > ruby test/extensions/active_record_test.rb ["/usr/lib/ruby/site_ruby/1.8", "/usr/lib/ruby/site_ruby/1.8/i386-linux", "/usr/lib/ruby/site_ruby", "/usr/lib/site_ruby/1.8", "/usr/lib/site_ruby/1.8/i386-linux", "/usr/lib/site_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/i386-linux", "."] Loaded suite test/extensions/active_record_test ...
You can see that it had no trouble finding the file 'test/test_helper', because the $LOAD_PATH contains the path ".", which in my case was ~/code/plugins/activescaffold.
But I for one am used to running tests directly from the directory that contains the test. What happens if I try to run the test from within the test/extensions directory?
~/code/plugins/activescaffold/test/extensions > ruby active_record_test.rb ["/usr/lib/ruby/site_ruby/1.8", "/usr/lib/ruby/site_ruby/1.8/i386-linux", "/usr/lib/ruby/site_ruby", "/usr/lib/site_ruby/1.8", "/usr/lib/site_ruby/1.8/i386-linux", "/usr/lib/site_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/i386-linux", "."] active_record_test.rb:2:in `require': no such file to load -- test/test_helper (LoadError) ...
Uh oh. You will notice that even though the value of the $LOAD_PATH variable is identical, it wasn't able to find 'test/test_helper' due to the fact that "." no longer refers to ~/code/plugins/activescaffold but now refers to ~/code/plugins/activescaffold/test/extensions! So even though it tried looking for './test/test_helper', that ended up causing it to search for ~/code/plugins/activescaffold/test/extensions/test/test_helper, which is obviously not a valid path.
The moral of the story is that we shouldn't make assumptions about:
$LOAD_PATH by this point (not demonstrated, but still true)." = Dir.getwd) isInstead of doing this:
require 'test/test_helper'
, consider doing this:
require 'facets/core/kernel/require_local' require_local '../test_helper'
Or, if for some reason you really want to use $LOAD_PATH, then you should rely on an entry in $LOAD_PATH whose meaning is fixed, not one which changes depending on which directory you are in at the moment:
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', '..') require 'test/test_helper'
(This will add a path such as "/home/tyler/code/plugins/activescaffold/test/extensions/../.." onto the $LOAD_PATH.)
Then you can be 100% confident that the require 'test/test_helper' will succeed no matter which directory you run the script from.
$: and $LOAD_PATH are equivalent
Add a path to it like this: $LOAD_PATH << 'my/path'
path=File.expand_path(File.dirname(__FILE__) + "/../../../../vendor/plugins/shared/lib/test_helper.rb"); require path if FileTest.exists?(path)
Or could we do that more cleanly with a begin/rescue block ?
The quick, no-dependency-way:
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].each {|f| load f}
The slightly prettier way:
(using qualitysmith_extensions)
require_all File.dirname(__FILE__) + '/tasks/*.rake'
require only works with files ending in .rb!I knew it worked to omit the ".rb" from the require command...
Either of these work:
require 'my_file' # finds my_file.rb require 'my_file.rb' # finds my_file.rb
What I didn't know was that you can't use require to load files that don't end in .rb! For example, if you have a file bin/my_command (not my_command.rb), then this will fail:
require 'my_command'
This, however, works (but inherits any problems that load comes with, such as possibly doubly loading a file):
load 'my_command'
gem_b/the_name.rb, if it exists, may be loaded instead of the the_name/the_name.rb file (from the the_name gem)In other words, if you simply do a require 'gem_name' in your code and expect it to load that file (gem_name.rb) from the gem_name gem, you may be in for a surprise if there is another gem out there that happens to have a file by the same name and happens to be in a more preferred position in the load path.
Here's a real-life example that happened to me, when I was trying to do require 'ruby2ruby'...
Initial symptoms:
> sudo gem install ruby2ruby > irb irb -> require 'ruby2ruby' => true irb -> def a(&block); block.to_ruby; end => nil irb -> a { foo() } NoMethodError: undefined method `to_ruby' for #<Proc:0xb7c80bd8@(irb):3> from (irb):2:in `a' from (irb):3What?? The documentation for ruby2ruby suggests that the method `to_ruby' is defined for objects of class
Proc!
Further investigation:
> gemwhich ruby2ruby /usr/lib/ruby/gems/1.8/gems/ZenHacks-1.0.1/lib/ruby2ruby.rbOh.
After putting appropriate debug output code in both of:
- /usr/lib/ruby/gems/1.8/gems/ZenHacks-1.0.1/lib/ruby2ruby.rb
- /usr/lib/ruby/gems/1.8/gems/ruby2ruby-1.1.6/lib/ruby2ruby.rb
> irb irb -> require 'ruby2ruby' Loaded ZenHacks gem version => true
There are 2 solutions to this problem:
The least-destructive / easiest way to get around this is to simply activate the gem you want to include the file from before you do the require...
> irb irb -> gem 'ruby2ruby' => true irb -> require 'ruby2ruby' Loaded ruby2ruby gem version => trueThe other solution is to simply move the file you don't want to be loaded out of the way (or simply remove it).
> sudo mv /usr/lib/ruby/gems/1.8/gems/ZenHacks-1.0.1/lib/ruby2ruby.rb /usr/lib/ruby/gems/1.8/gems/ZenHacks-1.0.1/lib/ruby2ruby.rb2 > gemwhich ruby2ruby /usr/lib/ruby/gems/1.8/gems/ruby2ruby-1.1.6/lib/ruby2ruby.rb > irb irb -> require 'ruby2ruby' Loaded ruby2ruby gem version => trueThis method:
- requires you to have write permissions to whatever directory the gems are stored in.
- will cause any code that uses ZenHacks and tries to use the version of ruby2ruby.rb found there, to fail (unless the ruby2ruby gem's ruby2ruby.rb is backwards compatible...).
That's what I ended up doing, because I think the version from the ruby2ruby gem is the official, most up-to-date version of that code; and more importantly, it's the only version that works the way I expect it to.
When I follow an example I find on the web that says you can just
require 'ruby2ruby'and then do such-and-such, I want it to hold true for me too! The ZenHacks gem, as cool as it may be, has no business interfering with that behavior (the behavior that would have existed forrequire 'ruby2ruby'had the ZenHacks gem not been installed).
Now I can do this and it behaves as expected:
> irb
irb -> require 'ruby2ruby'
=> true
irb -> def a(&block); block.to_ruby; end
=> nil
irb -> a { foo() }
=> "proc {\n foo()\n}"