3
\$\begingroup\$

From the command line, I can easily find the path to rubygems.rb

$ gem which rubygems
/usr/lib/ruby/1.9.1/rubygems.rb

and from a Ruby script I can also do this

require 'rubygems/commands/which_command'
wc = Gem::Commands::WhichCommand.new
puts wc.find_paths 'rubygems', $LOAD_PATH

However is a simpler way available to do this, for example without using
require 'rubygems/commands/which_command', and without a system() call?

Update from posted answer

puts $".grep(/rubygems.rb/).first

Update from posted comment

puts Gem.method(:dir).source_location.first
\$\endgroup\$
  • \$\begingroup\$ Don't you think this question should go on SO? \$\endgroup\$ – Uri Agassi Apr 26 '14 at 8:26
  • \$\begingroup\$ Sounds strange... I agree the answer is excellent, it is just a pity (IMHO) that it is not on SO... I think that people will look for similar things there, not here... \$\endgroup\$ – Uri Agassi Apr 26 '14 at 9:01
  • 1
    \$\begingroup\$ stackoverflow.com/a/660129 \$\endgroup\$ – Nakilon Apr 27 '14 at 1:19
3
\$\begingroup\$

Whether a simpler solution exists depends on what your motivation is.

Let's look inside the implementation of Gem::Commands::WhichCommand#find_paths.

  def find_paths(package_name, dirs)
    result = []

    dirs.each do |dir|
      Gem.suffixes.each do |ext|
        full_path = File.join dir, "#{package_name}#{ext}"
        if File.exist? full_path and not File.directory? full_path then
          result << full_path
          return result unless options[:show_all]
        end
      end
    end

    result
  end

That's the code you need. If there were a simpler way, find_paths would have used it.

However, there is a way to cheat: Kernel#require needs to do a very similar thing when it actually tries to load a module:

require(name) → true or false

Loads the given name, returning true if successful and false if the feature is already loaded.

If the filename does not resolve to an absolute path, it will be searched for in the directories listed in $LOAD_PATH ($:).

If the filename has the extension “.rb”, it is loaded as a source file; if the extension is “.so”, “.o”, or “.dll”, or the default shared library extension on the current platform, Ruby loads the shared library as a Ruby extension. Otherwise, Ruby tries adding “.rb”, “.so”, and so on to the name until found. If the file named cannot be found, a LoadError will be raised.

For Ruby extensions the filename given may use any shared library extension. For example, on Linux the socket extension is “socket.so” and require 'socket.dll' will load the socket extension.

The absolute path of the loaded file is added to $LOADED_FEATURES ($"). A file will not be loaded again if its path already appears in $". For example, require 'a'; require './a' will not load a.rb again.

Or, it might be more accurate to say that find_paths simulates what require would do.

Therefore, if you are willing to let Ruby actually load the file first, then you could ask Ruby after the fact:

require 'rubygems'
$LOADED_FEATURES.grep /\/rubygems\./
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.