Ruby / Depending on other Ruby files and libraries
From WhyNotWiki
Ruby / Dependencies edit (Category edit)
(deprecated (why??))
[edit] Requiring files
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.
[edit] Caveat: require will include the same file again if you spell the path differently
Workaround: Use File.expand_path
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
[edit] Caveat: the starting file is not counted as already required
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.
[edit] require_local
I'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
[edit] Why using require_local is more reliable than relying on the $LOAD_PATH and the "." directory
Example:
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:
- what's been added to
$LOAD_PATHby this point (not demonstrated, but still true) - what the current working directory (= "
." =Dir.getwd) is
Instead 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.
[edit] Load path/include path
$: and $LOAD_PATH are equivalent
Add a path to it like this: $LOAD_PATH << 'my/path'
[edit] How to require a file if it exists, but otherwise silently continue
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 ?
[edit] How to require all the files in a certain directory
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'
[edit] Caveat: 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'
[edit] [Gems (category)]
[edit] The file 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}"
