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