Matthew Moss wrote: > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- > > The three rules of Ruby Quiz 2: > > 1. Please do not post any solutions or spoiler discussion for this > quiz until 48 hours have passed from the time on this message. > > 2. Support Ruby Quiz 2 by submitting ideas as often as you can! (A > permanent, new website is in the works for Ruby Quiz 2. Until then, > please visit the temporary website at > > <http://splatbang.com/rubyquiz/>. > > 3. Enjoy! > > Suggestion: A [QUIZ] in the subject of emails about the problem > helps everyone on Ruby Talk follow the discussion. Please reply to > the original quiz message, if you can. > > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- > > ## Where the Required Things Are (#175) > > Occasionally, I've taken a look at the source for some Ruby module, > often because there is no manual or man page, or what documentation is > available is outdated or incomplete. Or sometimes I just want to see > how some Ruby stuff is implemented. > > One such example was from the previous quiz: I want to learn more > about the Sys::Uptime module. I have it installed, and the call to > `require 'sys/uptime'` works, but I don't know how to use it. But, > alas, I also don't know where the installed files are located. The > shell command `which` doesn't help here, since the module is unlikely > to be in the shell's executable path. > > What I would like is a script that works like `which` but for Ruby > modules. Examples: > > > ruby modwhich.rb "sys/uptime" > require 'sys/uptime' => > /opt/local/lib/ruby/vendor_ruby/1.8/i686-darwin8.11.1/sys/uptime.bundle > > > ruby modwhich.rb date > require 'date' => /opt/local/lib/ruby/1.8/date.rb > > For extra credit, preserve this behavior when modwhich.rb is the main > program, but slightly different behavior is modwhich.rb is required by > another script: > > > ruby -r modwhich upsince.rb > > require 'sys/uptime' => > /opt/local/lib/ruby/vendor_ruby/1.8/i686-darwin8.11.1/sys/uptime.bundle > require 'date' => /opt/local/lib/ruby/1.8/date.rb > Last reboot: 2008 Aug 22 at 18:49 > > Note that we allow upsince.rb to run as normal; the output of > modwhich.rb is mixed into stdout. > > Problem: Something like require 'rubygems' sets off a while chains of require statements with circular requires. It ends up printing a lot of the entries more than once. Also, none of the modules are added to $LOADED_FEATURES until after the original require statement succeeds, that can't be used to check for duplicates. Keep track of which modules were printed with $MODWHICH_SEEN, but be sure to store the full path. Else if you require 'some-mod', then change $LOAD_PATH or require 'rubygems' or overload the require method again, 'some-mod' could mean something completely different. Also, if 'foo' and 'foo.rb' are both required, storing the complete path prevents multiples from being printed. I think require tries other extensions other than .rb, it could be extended to support that. I didn't feel like mucking around in eval.c though :P module Kernel alias modwhich_original_require require $MODWHICH_SEEN = [] def require(path) p = path + (path =~ %r{\.[^/]+$} ? '' : '.rb') dir = $LOAD_PATH.find do|d| File.exists? "#{d}/#{p}" end return if $MODWHICH_SEEN.include? "#{dir}/#{p}" $MODWHICH_SEEN << "#{dir}/#{p}" puts "require: #{path} => #{dir}/#{p}" unless dir.nil? modwhich_original_require path end end -- Michael Morin Guide to Ruby http://ruby.about.com/ Become an About.com Guide: beaguide.about.com About.com is part of the New York Times Company