hi!
here’s my solution (first submission ever ;-).
first the cheating one (no extra credit):
ARGV.each { |mod|
puts “require ‘#{mod}’ => #{%x{gem which #{mod}}.split(/\n/).last}”
}
now on to the real one. it uses ruby’s “magic constant” SCRIPT_LINES__
and (as
fallback) rubygem’s ‘which’ command (which does not only find gems,
actually;
see above).
contrary to the quiz “rules”, information is output to STDERR instead of
STDOUT.
the script takes a list of library names as arguments and a few options.
examples:
ww@blackwinter:~/devel/scratch> ./modwhich.rb set date nuggets
require ‘set’ => /usr/lib/ruby/1.8/set.rb
require ‘date’ => /usr/lib/ruby/1.8/date.rb
require ‘nuggets’ =>
/usr/lib/ruby/gems/1.8/gems/ruby-nuggets-0.3.1.277/lib/nuggets.rb
ww@blackwinter:~/devel/scratch> ./modwhich.rb -r set date nuggets
require ‘set’ => /usr/lib/ruby/1.8/set.rb
require ‘date’ => /usr/lib/ruby/1.8/date.rb
require ‘rational’ => /usr/lib/ruby/1.8/rational.rb
require ‘date/format’ => /usr/lib/ruby/1.8/date/format.rb
require ‘nuggets’ =>
/usr/lib/ruby/gems/1.8/gems/ruby-nuggets-0.3.1.277/lib/nuggets.rb
require ‘fileutils’ => /usr/lib/ruby/1.8/fileutils.rb
require ‘etc’ => /usr/lib/ruby/1.8/i486-linux/etc.so
ww@blackwinter:~/devel/scratch> ./modwhich.rb -v set date nuggets
/usr/lib/ruby/1.8/set.rb
/usr/lib/ruby/1.8/rational.rb
/usr/lib/ruby/1.8/date/format.rb
/usr/lib/ruby/1.8/date.rb
/usr/lib/ruby/1.8/i486-linux/etc.so
/usr/lib/ruby/1.8/fileutils.rb
/usr/lib/ruby/gems/1.8/gems/ruby-nuggets-0.3.1.277/lib/nuggets.rb
ww@blackwinter:~/devel/scratch> ruby -rmodwhich test_modwhich.rb
#<Set: {}>
require ‘set’ => /usr/lib/ruby/1.8/set.rb
require ‘date’ => /usr/lib/ruby/1.8/date.rb
require ‘rational’ => /usr/lib/ruby/1.8/rational.rb
require ‘date/format’ => /usr/lib/ruby/1.8/date/format.rb
ww@blackwinter:~/devel/scratch> cat test_modwhich.rb
require ‘set’
require ‘date’
p Set.new
ww@blackwinter:~/devel/scratch> irb -r modwhich.rb
require ‘set’
0.0096 => true
require ‘date’, true
/usr/lib/ruby/1.8/date.rb
0.0084 => false
require ‘hen’, true
/usr/lib/ruby/gems/1.8/gems/hen-0.1.2.273/lib/hen.rb
0.1735 => true
ModWhich.to_h
0.0003 => …snip… (see for yourself
x
require ‘set’ => /usr/lib/ruby/1.8/set.rb
require ‘date’ => /usr/lib/ruby/1.8/date.rb
require ‘hen’ => /usr/lib/ruby/gems/1.8/gems/hen-0.1.2.273/lib/hen.rb
require ‘yaml’ => /usr/lib/ruby/1.8/yaml.rb
require ‘forwardable’ => /usr/lib/ruby/1.8/forwardable.rb
require ‘rubygems’ => /usr/local/lib/site_ruby/1.8/rubygems.rb
require ‘rake’ => /usr/lib/ruby/1.8/rake.rb
require ‘rbconfig’ => /usr/lib/ruby/1.8/i486-linux/rbconfig.rb
require ‘ftools’ => /usr/lib/ruby/1.8/ftools.rb
require ‘getoptlong’ => /usr/lib/ruby/1.8/getoptlong.rb
require ‘fileutils’ => /usr/lib/ruby/1.8/fileutils.rb
require ‘singleton’ => /usr/lib/ruby/1.8/singleton.rb
require ‘thread’ => /usr/lib/ruby/1.8/thread.rb
require ‘ostruct’ => /usr/lib/ruby/1.8/ostruct.rb
[…]
you can also find it on github:
http://github.com/blackwinter/scratch/tree/master/modwhich.rb.
cheers
jens
----[ modwhich.rb ]----
#! /usr/bin/env ruby
#–
###############################################################################
modwhich – Find the location of a library. Solution for Ruby Q.
“Where the Required Things Are” (#175) by Matthew M., 2008/08/29.
modwhich is free software; you can redistribute it and/or modify it
under #
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your
option) #
any later version.
modwhich is distributed in the hope that it will be useful, but
WITHOUT #
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for #
more details.
You should have received a copy of the GNU General Public License
along #
###############################################################################
#++
begin
require ‘rubygems’
require ‘rubygems/commands/which_command’
rescue LoadError
end
class ModWhich
@verbose, @recursive = false, false
class << self
include Enumerable
attr_writer :verbose, :recursive
def init(args = nil, recursive = recursive? || args.nil?)
@args, @recursive = args, recursive
@which, @load_order, @added_paths = {}, [], []
unless Object.const_defined?(:SCRIPT_LINES__)
Object.const_set(:SCRIPT_LINES__, {})
end
unless Object.ancestors.include?(Require)
Object.send(:include, Require)
end
end
def which(mod)
self.require(mod)
@which[mod] if @which
end
def require(mod, verbose = verbose?)
if @which && !required?(mod)
@load_order << mod
current_paths = loaded_paths
ret = _modwhich_original_require(mod)
@added_paths.concat(loaded_paths - current_paths)
@which[mod] = @added_paths.pop || gemwhich(mod)
warn @which[mod] if verbose
ret
end
end
def required?(mod)
@which && @which.has_key?(mod)
end
def verbose?
@verbose
end
def recursive?
@recursive
end
def include?(mod)
!to_a.assoc(mod).nil?
end
def each
if @load_order
if @args
@args.each { |mod| self.require(mod) }
@load_order &= @args unless recursive?
@args = nil
end
@load_order.each { |mod| yield mod, which(mod) }
end
end
def to_h
inject({}) { |h, (mod, path)| h.update(mod => path) }
end
private
# basically equivalent to: <tt>%x{gem which
#{mod}}.split(/\n/).last
def gemwhich(mod)
if defined?(Gem::Commands::WhichCommand)
@gemwhich ||= Gem::Commands::WhichCommand.new
@searcher ||= Gem::GemPathSearcher.new
dirs = $LOAD_PATH
if spec = @searcher.find(mod)
dirs += @gemwhich.gem_paths(spec)
end
# return the last (only?) one
@gemwhich.find_paths(mod, dirs).last
end
end
def loaded_paths
SCRIPT_LINES__.keys - (@which ? @which.values : [])
end
end
module Require
unless respond_to?(:_modwhich_original_require)
alias_method :_modwhich_original_require, :require
end
def require(*args) ModWhich.require(*args) end
end
end
if $0 == FILE
progname = File.basename($0)
usage = <<-EOT.gsub(/^\s+/, ‘’)
#{progname} [-v|–verbose] [-r|–recursive] …
#{progname} [-h|–help]
EOT
help = ARGV.delete(‘-h’) || ARGV.delete(‘–help’)
verbose = ARGV.delete(‘-v’) || ARGV.delete(‘–verbose’)
recursive = ARGV.delete(‘-r’) || ARGV.delete(‘–recursive’)
abort usage if help || ARGV.empty?
ModWhich.verbose = verbose
ModWhich.init(ARGV, recursive)
ARGV.each { |mod| require mod }
else
ModWhich.init
end
at_exit {
ModWhich.each { |mod, path|
warn “require ‘#{mod}’ => #{path || ‘NOT FOUND’}”
} unless ModWhich.verbose?
}