Where the Required Things Are (#175)

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Q. 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 Q. 2 by submitting ideas as often as you can! (A
    permanent, new website is in the works for Ruby Q. 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 T. 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.

On 29-08-2008, at 10:02, Matthew M. wrote:

Where the Required Things Are (#175)

shell command which doesn’t help here, since the module is unlikely

ruby modwhich.rb date
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.

Lots of fun with this quiz :smiley:
so far, I came up with something that does like this:

rolando@dev02:~$ ruby quiz175.rb “date”
require ‘date’ => /usr/local/lib/ruby/1.8/date.rb
rolando@dev02:~$ ruby quiz175.rb “rubygems”
require ‘rubygems’ => /usr/local/lib/ruby/site_ruby/1.8/rubygems.rb
rolando@dev02:~$ ruby quiz175.rb “rubygems” “sequel”
require ‘rubygems’ => /usr/local/lib/ruby/site_ruby/1.8/rubygems.rb
require ‘sequel’ => /usr/local/lib/ruby/gems/1.8/gems/sequel-2.4.0/
lib/sequel.rb
rolando@dev02:~$ ruby -r quiz175 test_quiz175.rb
require ‘date’ => /usr/local/lib/ruby/1.8/date.rb
require ‘rubygems’ => /usr/local/lib/ruby/site_ruby/1.8/rubygems.rb
require ‘sequel’ => /usr/local/lib/ruby/gems/1.8/gems/sequel-2.4.0/
lib/sequel.rb
require ‘sequel_model’ => /usr/local/lib/ruby/gems/1.8/gems/
sequel-2.4.0/lib/sequel_model.rb
i’m cool piece of ruby code
rolando@dev02:~$ cat test_quiz175.rb
require ‘date’
require ‘rubygems’
#puts “requiring sequel”
require ‘sequel’
require ‘sequel_model’

puts “i’m cool piece of ruby code”
rolando@dev02:~$ REQUIRE_SHOW_ALL=1 ruby quiz175.rb “date”
require ‘date’ => /usr/local/lib/ruby/1.8/date.rb
require ‘rational’ => /usr/local/lib/ruby/1.8/rational.rb
require ‘date/format’ => /usr/local/lib/ruby/1.8/date/format.rb
rolando@dev02:~$ REQUIRE_SHOW_ALL=1 ruby quiz175.rb “rubygems”
require ‘rubygems’ => /usr/local/lib/ruby/site_ruby/1.8/rubygems.rb
require ‘rubygems/rubygems_version’ => /usr/local/lib/ruby/
site_ruby/1.8/rubygems/rubygems_version.rb
require ‘rubygems/defaults’ => /usr/local/lib/ruby/site_ruby/1.8/
rubygems/defaults.rb
require ‘thread’ => /usr/local/lib/ruby/1.8/thread.rb
require ‘thread.so’ => /usr/local/lib/ruby/1.8/i686-linux/thread.so
require ‘rbconfig’ => /usr/local/lib/ruby/1.8/i686-linux/rbconfig.rb
require ‘rubygems/exceptions’ => /usr/local/lib/ruby/site_ruby/1.8/
rubygems/exceptions.rb
require ‘rubygems/version’ => /usr/local/lib/ruby/site_ruby/1.8/
rubygems/version.rb
require ‘rubygems/requirement’ => /usr/local/lib/ruby/site_ruby/1.8/
rubygems/requirement.rb
require ‘rubygems/dependency’ => /usr/local/lib/ruby/site_ruby/1.8/
rubygems/dependency.rb
require ‘rubygems/gem_path_searcher’ => /usr/local/lib/ruby/
site_ruby/1.8/rubygems/gem_path_searcher.rb
require ‘rubygems/source_index’ => /usr/local/lib/ruby/site_ruby/
1.8/rubygems/source_index.rb
require ‘rubygems/user_interaction’ => /usr/local/lib/ruby/
site_ruby/1.8/rubygems/user_interaction.rb
require ‘rubygems/specification’ => /usr/local/lib/ruby/site_ruby/
1.8/rubygems/specification.rb
require ‘rubygems/platform’ => /usr/local/lib/ruby/site_ruby/1.8/
rubygems/platform.rb
require ‘rubygems/spec_fetcher’ => /usr/local/lib/ruby/site_ruby/
1.8/rubygems/spec_fetcher.rb
require ‘zlib’ => /usr/local/lib/ruby/1.8/i686-linux/zlib.so
require ‘rubygems/remote_fetcher’ => /usr/local/lib/ruby/site_ruby/
1.8/rubygems/remote_fetcher.rb
require ‘net/http’ => /usr/local/lib/ruby/1.8/net/http.rb
require ‘net/protocol’ => /usr/local/lib/ruby/1.8/net/protocol.rb
require ‘socket’ => /usr/local/lib/ruby/1.8/i686-linux/socket.so
require ‘timeout’ => /usr/local/lib/ruby/1.8/timeout.rb
require ‘uri’ => /usr/local/lib/ruby/1.8/uri.rb
require ‘uri/common’ => /usr/local/lib/ruby/1.8/uri/common.rb
require ‘uri/generic’ => /usr/local/lib/ruby/1.8/uri/generic.rb
require ‘uri/ftp’ => /usr/local/lib/ruby/1.8/uri/ftp.rb
require ‘uri/http’ => /usr/local/lib/ruby/1.8/uri/http.rb
require ‘uri/https’ => /usr/local/lib/ruby/1.8/uri/https.rb
require ‘uri/ldap’ => /usr/local/lib/ruby/1.8/uri/ldap.rb
require ‘uri/mailto’ => /usr/local/lib/ruby/1.8/uri/mailto.rb
require ‘stringio’ => /usr/local/lib/ruby/1.8/i686-linux/stringio.so
require ‘rubygems/builder’ => /usr/local/lib/ruby/site_ruby/1.8/
rubygems/builder.rb
require ‘rubygems/custom_require’ => /usr/local/lib/ruby/site_ruby/
1.8/rubygems/custom_require.rb

(Matthew: I sent you my answer to the quiz in private, since I’ll be
away for the next week)


Matthew M. [email protected]

thanks Matthew for the fun :smiley:
regards,

My solution (pastied too: http://pastie.org/263518):

module Kernel
alias :require_orig :require

def require mod
if success = require_orig(mod)
file = $".last

  $:.each do |dir|
    path = "#{dir}/#{file}"
    if File.readable?(path)
      puts "require #{mod} => #{path}"
      break
    end
  end
end

success

end
end

if FILE == $0
ARGV.each { |mod| require mod }
end

Matthew M. wrote:

Where the Required Things Are (#175)

shell command which doesn’t help here, since the module is unlikely
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 :stuck_out_tongue:

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

Matthew M. wrote:

What I would like is a script that works like which but for Ruby
modules. Examples:

Here’s my solution:

[68] erikh@islay ~% ruby quiz.rb date
/usr/lib/ruby/1.8/date.rb
[69] erikh@islay ~% ruby quiz.rb date digest/sha1
/usr/lib/ruby/1.8/date.rb
/usr/lib/ruby/1.8/x86_64-linux/digest/sha1.so
[70] erikh@islay ~% irb -r quiz.rb
irb(main):001:0> require ‘digest/sha1’
/usr/lib/ruby/1.8/x86_64-linux/digest/sha1.so
=> true
irb(main):002:0> require ‘date’
/usr/lib/ruby/1.8/date.rb
/usr/lib/ruby/1.8/rational.rb
/usr/lib/ruby/1.8/date/format.rb
/usr/lib/ruby/1.8/rational.rb
=> true

Code emailed to Matthew. Again, thanks for the fun. :slight_smile:

-Erik

Michael M. wrote:

  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

Small bug fix to account of files that aren’t found.

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

 # Let the original require raise an exception if
 # the file is not found.
 return modwhich_original_require(path) if dir.nil?

 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

On 8/29/08, Matthew M. [email protected] wrote:

> 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

This is what came to mind. Interesting idea. I may just keep this
around.

% ruby which.rb rubygems rake fake/thing date/format
“rubygems” found in /Library/Ruby/Site/1.8/rubygems.rb
“rake” found in /Library/Ruby/Site/1.8/rake.rb
Could not find “fake/thing” anywhere
“date/format” found in
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/date/format.rb

% cat which.rb
alias untracked_require require
$req = Hash.new
def require(path)
old_features = $LOADED_FEATURES.dup
untracked_require(path)
$req[path] ||= ($LOADED_FEATURES - old_features).last
end

ARGV.each do |path|
begin
require(path)
rescue LoadError
$stderr.puts “Could not find #{path.inspect} anywhere”
else
found = $LOAD_PATH.find { |dir| File.exist? “#{dir}/#{$req[path]}” }
$stderr.puts “#{path.inspect} found in #{found}/#{$req[path]}”
end
end

Mine:

def locate(file)
extensions = [".rb",".so",".o",".dll"]
$:.each do |dir|
path = dir + “/” + file
if File.exist? path
puts “require ‘#{file}’ => #{path}”
break
else
extensions.each do |ext|
if File.exist? path + ext
puts “require ‘#{file}’ => #{path + ext}”
break
end
end
end
end
end

module Kernel
alias old_require require
def require(string)
locate string
old_require string
end
end

ARGV.each do |file|
locate file if file
end

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 :wink:
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.

Copyright (C) 2008 Jens W. [email protected]

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 #

with modwhich. If not, see http://www.gnu.org/licenses/.

###############################################################################
#++

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?
}

Hello, here’s my solution for this quiz.

I have never put together a piece of metaprogramming that I would use
with confidence until this quiz :wink:

On 29/08/2008, Matthew M. [email protected] wrote:

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

This should work although the solution is not tested on a Mac.

> ruby modwhich.rb date
require 'date' => /opt/local/lib/ruby/1.8/date.rb

This works.

Last reboot: 2008 Aug 22 at 18:49

This should also work within reason - modules loaded from files
located in rubylibdir are not printed.

Thanks

Michal

Erik H. wrote:

Matthew M. wrote:

What I would like is a script that works like which but for Ruby
modules. Examples:

Here’s my solution:

[68] erikh@islay ~% ruby quiz.rb date
/usr/lib/ruby/1.8/date.rb
[69] erikh@islay ~% ruby quiz.rb date digest/sha1
/usr/lib/ruby/1.8/date.rb
/usr/lib/ruby/1.8/x86_64-linux/digest/sha1.so
[70] erikh@islay ~% irb -r quiz.rb
irb(main):001:0> require ‘digest/sha1’
/usr/lib/ruby/1.8/x86_64-linux/digest/sha1.so
=> true
irb(main):002:0> require ‘date’
/usr/lib/ruby/1.8/date.rb
/usr/lib/ruby/1.8/rational.rb
/usr/lib/ruby/1.8/date/format.rb
/usr/lib/ruby/1.8/rational.rb
=> true

Here’s the code:

#!/usr/bin/env ruby

module Kernel
def require_which(filespec)
$:.each do |path|
spec_parts = filespec.split(///)

        files = Dir[File.join(path, *spec_parts) + ".*"]
        unless files.empty?
            return files
        end
    end

    [ ]
end

end

if FILE == $0
ARGV.each do |arg|
require_which(arg).each { |f| puts f }
end
else
module Kernel
@@old_require = Kernel.method(:require)
def require(*args)
require_which(args[0]).each { |f| puts f }
@@old_require.call(*args)
end
end
end

Benefits:

  1. Should properly work on windows (or anything with a weird pathspec)
    in any situation
  2. catches .so/.bundle/.whatever
  3. terminates early to avoid catching things that could be loaded if the
    load path were different, but aren’t actually (which is actually against
    how ‘which’ generally works, but meh)

Drawbacks:
Suffers from the same require-itis mentioned earlier: change def require
to:

def require(*args)
res = @@old_require.call(*args)
if res
require_which(args[0]).each { |f| puts f }
end
return res
end

Which should solve that problem.

Erik H. wrote:

Drawbacks:

It also doesn’t catch absolute paths, which I completely forgot about.
:slight_smile:

-Erik

just fixed verbosity setting when used in conjunction with recursion:

http://github.com/blackwinter/scratch/commit/60eb300

cheers
jens

I think I’m gonna keep that around, looks useful to me.

You’re not the first person to say that.

It would probably be useful, all around, to squish a couple of these
solutions together into a nice feature set and make a gem or rubyforge
pkg out of it.

Here is Rolando’s solution:

module Kernel
alias kernel_original_require require

FILE_EXTENSIONS = [".so", “.bundle”, “.dll”]

def check_require2(lib, path)
$_req_disp_table ||= {}
if File.file?(path)
if $_req_disp_table[lib].nil?
puts " require ‘#{lib}’ => #{path}"
$_req_disp_table[lib] = 1
end
return true
else
return false
end
end

def check_require(lib, path)
if (ext = File.extname(path)).empty? || ext == “.rb”
# puts “>>> #{path} does not have an extension”
ext = “.rb”
[".rb"].concat FILE_EXTENSIONS
else
# puts “>>> #{path} has extension”
FILE_EXTENSIONS
end.each { |_ext|
res = check_require2(lib, File.join(File.dirname(path),
File.basename(path, ext) + _ext))
return res if res
}
return false
end

def require(lib)
# puts “>>> requiring #{lib} (#{$_req_top})
(#{$_req_table.inspect})”
$_req_top ||= lib
if (ENV[‘REQUIRE_SHOW_ALL’].nil? && lib == $_req_top) ||
ENV[‘REQUIRE_SHOW_ALL’]
got_it = false
$:.each { |d|
fpath = File.join(d, lib)
got_it = check_require(lib, fpath)
break if got_it
}
end
if (res = kernel_original_require(lib)) && got_it
$_req_top = nil
end
res
end
end

if FILE == $0
ARGV.each { |m| require m }
end

Matthew M. wrote:

I think I’m gonna keep that around, looks useful to me.

You’re not the first person to say that.

It would probably be useful, all around, to squish a couple of these
solutions together into a nice feature set and make a gem or rubyforge
pkg out of it.

A require wrapper would be pretty slick:

module Kernel
def wrap_require(method=nil)
if block_given?
@@require_wrapper = block.to_proc
else
@@require_wrapper = method
end
end

@@old_require = method(:require)

def require(*args)
res = @@old_require.call(*args)
@@require_wrapper.call(*args) if (@@require_wrapper and res)
return res
end
end

Dunno if it really warrants a gem though. Maybe a snippet?

-Erik

On 8/29/08, Matthew M. [email protected] wrote:

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The three rules of Ruby Q. 2:

What I would like is a script that works like which but for Ruby
modules. Examples:

Here’s my solution. I used the mod_req_level counter to restrict the
output to the files required at the top level. Without it, requiring
rubygems prints the rubygems location about 9 times.
-Adam

alias :original_require :require

$mod_req_level=0
$mod_req_exts = %w{rb rbw o so dll bundle sl jar}.map{|ex| Regexp.new
“.#{ex}$”}

def require lib
$:.each{|d| files=Dir.glob(d+“/#{lib}*”)
f=files.find{|f|$mod_req_exts.find{|re|re=~f}}
(puts “require #{lib} => #{f}”;break) if f
} if 1==($mod_req_level+=1)
begin
original_require lib
ensure
$mod_req_level-=1
end
end

if FILE==$0
ARGV.each{|f| require f}
end

This is what my solution does:


~$ ruby modwhich.rb date set bigdecimal
require “date” => /usr/lib/ruby/1.8/date.rb
require “set” => /usr/lib/ruby/1.8/set.rb
require “bigdecimal” => /usr/lib/ruby/1.8/i486-linux/bigdecimal.so
~$ ruby modwhich.rb /usr/lib/ruby/1.8/date
require “/usr/lib/ruby/1.8/date” => /usr/lib/ruby/1.8/date.rb
~$ cat modwhich_test.rb
require ‘date’
require ‘set’
require ‘bigdecimal’
~$ ruby -rmodwhich modwhich_test.rb
require “date” => /usr/lib/ruby/1.8/date.rb
require “set” => /usr/lib/ruby/1.8/set.rb
require “bigdecimal” => /usr/lib/ruby/1.8/i486-linux/bigdecimal.so
~$ MODWHICH_VERBOSE=1 ruby -rmodwhich modwhich_test.rb
(modwhich_test.rb:1) require “date” => /usr/lib/ruby/1.8/date.rb
(/usr/lib/ruby/1.8/date.rb:196) require “rational” =>
/usr/lib/ruby/1.8/rational.rb
(/usr/lib/ruby/1.8/date.rb:197) require “date/format” =>
/usr/lib/ruby/1.8/date/format.rb
(/usr/lib/ruby/1.8/date/format.rb:4) require “rational” =>
/usr/lib/ruby/1.8/rational.rb
(modwhich_test.rb:2) require “set” => /usr/lib/ruby/1.8/set.rb
(modwhich_test.rb:3) require “bigdecimal” =>
/usr/lib/ruby/1.8/i486-linux/bigdecimal.so

In code it looks like this:


require ‘pathname’

module ModWhich
SUFFIXES = [‘’] + %w{ .rb .rbw .o .so .bundle .dll .sl .jar }

def self.which(mod)
if Pathname.new(mod).absolute?
paths = File.dirname(mod).to_a
mod = File.basename(mod)
else
paths = $LOAD_PATH
end

file_names = SUFFIXES.map { |suff| mod + suff }

paths.each do |path|
  file_names.each do |file|
    full_path = File.expand_path(File.join(path, file))
    return full_path if File.file?(full_path)
  end
end

nil

end

def self.report(mod)
puts “require %s => %s” % [mod.inspect, which(mod) || “(no such
file)”]
end

def self.verbose?
ENV[‘MODWHICH_VERBOSE’]
end

def self.trap_require
Kernel.module_eval do
alias :modwhich_original_require :require

  def require(arg)
    if ModWhich::verbose? || caller.first.split(':')[0] == $0
      if ModWhich::verbose?
        print "(#{caller.first}) "
      end
      ModWhich::report(arg)
    end
    modwhich_original_require(arg)
  end
end

end
end

if FILE == $0
while mod = ARGV.shift
ModWhich::report(mod)
end
else
ModWhich::trap_require
end

As a pastie: http://pastie.org/264164

I think I’m gonna keep that around, looks useful to me.

Regards,
Matthias

Yes, rubygems are quite common so the solution should probably should
include support for them, at least if I wanted to really use it in the
future.

Here is one that only shows dependencies between gems (and stdlib),
not the requires inside gems.

It generates somewhat excessive output when gems are involved as their
paths tend to be long.

Thanks

Michal

$ cat q175t.rb

require ‘date’
require ‘readline’
require ‘matrix’
require ‘rubygems’
require ‘time’
require ‘hpricot’
require ‘rubyforge’
require ‘hoe’
require ‘curses’

$ ruby -r q175.rb q175t.rb
require: q175t.rb:1: date => /usr/lib/ruby/1.8/date.rb
require: q175t.rb:2: readline =>
/usr/lib/ruby/1.8/x86_64-linux/readline.so
require: q175t.rb:3: matrix => /usr/lib/ruby/1.8/matrix.rb
require: q175t.rb:4: rubygems => /usr/lib/ruby/1.8/rubygems.rb
require: q175t.rb:5: time => /usr/lib/ruby/1.8/time.rb
require:
/var/lib/gems/1.8/gems/hpricot-0.6.161/lib/hpricot/traverse.rb:2:
uri => /usr/lib/ruby/1.8/uri.rb
require:
/var/lib/gems/1.8/gems/hpricot-0.6.161/lib/hpricot/inspect.rb:1:
pp => /usr/lib/ruby/1.8/pp.rb
require:
/var/lib/gems/1.8/gems/hpricot-0.6.161/lib/hpricot/builder.rb:2:
fast_xs =>
/var/lib/gems/1.8/gems/hpricot-0.6.161/lib/universal-darwin9.0/fast_xs.so
require: q175t.rb:6: hpricot =>
/var/lib/gems/1.8/gems/hpricot-0.6.161/lib/hpricot.rb
require: /var/lib/gems/1.8/gems/rubyforge-1.0.0/lib/rubyforge.rb:5:
yaml => /usr/lib/ruby/1.8/yaml.rb
require: /var/lib/gems/1.8/gems/rubyforge-1.0.0/lib/rubyforge.rb:6:
open-uri => /usr/lib/ruby/1.8/open-uri.rb
require:
/var/lib/gems/1.8/gems/rubyforge-1.0.0/lib/rubyforge/client.rb:1:
webrick/cookie => /usr/lib/ruby/1.8/webrick/cookie.rb
require:
/var/lib/gems/1.8/gems/rubyforge-1.0.0/lib/rubyforge/client.rb:2:
net/http => /usr/lib/ruby/1.8/net/http.rb
require:
/var/lib/gems/1.8/gems/rubyforge-1.0.0/lib/rubyforge/client.rb:3:
net/https => /usr/lib/ruby/1.8/net/https.rb
require: q175t.rb:7: rubyforge =>
/var/lib/gems/1.8/gems/rubyforge-1.0.0/lib/rubyforge.rb
require: /var/lib/gems/1.8/gems/rake-0.8.1/lib/rake.rb:35: getoptlong
=> /usr/lib/ruby/1.8/getoptlong.rb
require: /var/lib/gems/1.8/gems/rake-0.8.1/lib/rake.rb:37: singleton
=> /usr/lib/ruby/1.8/singleton.rb
require: /var/lib/gems/1.8/gems/rake-0.8.1/lib/rake.rb:39: ostruct =>
/usr/lib/ruby/1.8/ostruct.rb
require: /var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:4: rake =>
/var/lib/gems/1.8/gems/rake-0.8.1/lib/rake.rb
require: /var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:5:
rake/gempackagetask =>
/var/lib/gems/1.8/gems/rake-0.8.1/lib/rake/gempackagetask.rb
require: /var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:6: rake/rdoctask
=> /var/lib/gems/1.8/gems/rake-0.8.1/lib/rake/rdoctask.rb
require: /var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:7: rake/testtask
=> /var/lib/gems/1.8/gems/rake-0.8.1/lib/rake/testtask.rb
require: q175t.rb:8: hoe => /var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb
require: q175t.rb:9: curses => /usr/lib/ruby/1.8/x86_64-linux/curses.so

On 03/09/2008, Michal S. [email protected] wrote:

require ‘hoe’
require ‘curses’

As pointed out by Matthew the second solution does not work on OS X
(or perhaps pre-1.8.7).
While trying to cope reasonably well with Rubygems I introduced some
problems which make the solution nonportable.

First I stumbled upon start_with? which is a String method only
available in 1.8.7. I recall implementing workarounds with regexps
quite a few times so I was like “Hell, why did I never use that?”, and
the reason is it does not exist. Packaging 1.8.7 as the default Ruby
interpreter is evil :wink:

Second, there is an interesting problem with Rubygems: at one point
the class/module Gem exists but Gem.path does not. I did not see this
situation happen on the 1.8.7 Linux interpreter but it happens on
1.8.6 OS X.

Also there is a small bug with detecting the gem path. I thought
File.split splits to components bu it only splits to dirname and
basename.

So here goes the corrected solution which should again work on
multiple platforms.
It also removes some of the noise from the reported gem dependencies.

$ ruby -r q175.rb q175t.rb
require: q175t.rb:2: date => /sw/lib/ruby/1.8/date.rb
require: q175t.rb:3: readline =>
/sw/lib/ruby/1.8/i686-darwin/readline.bundle
require: q175t.rb:4: matrix => /sw/lib/ruby/1.8/matrix.rb
require: q175t.rb:5: rubygems => /sw/lib/ruby/1.8/rubygems.rb
require: q175t.rb:6: time => /sw/lib/ruby/1.8/time.rb
require: q175t.rb:7: hpricot =>
/sw/var/lib/gems/1.8/gems/hpricot-0.6.161/lib/hpricot.rb
require: q175t.rb:8: rubyforge =>
/sw/var/lib/gems/1.8/gems/rubyforge-1.0.0/lib/rubyforge.rb
require: /sw/var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:4: rake =>
/sw/var/lib/gems/1.8/gems/rake-0.8.1/lib/rake.rb
require: /sw/var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:5:
rake/gempackagetask =>
/sw/var/lib/gems/1.8/gems/rake-0.8.1/lib/rake/gempackagetask.rb
require: /sw/var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:6:
rake/rdoctask =>
/sw/var/lib/gems/1.8/gems/rake-0.8.1/lib/rake/rdoctask.rb
require: /sw/var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb:7:
rake/testtask =>
/sw/var/lib/gems/1.8/gems/rake-0.8.1/lib/rake/testtask.rb
require: q175t.rb:9: hoe =>
/sw/var/lib/gems/1.8/gems/hoe-1.7.0/lib/hoe.rb
require: q175t.rb:10: curses =>
/sw/lib/ruby/1.8/i686-darwin/curses.bundle

Thanks

Michal