KomodoDave
KomodoDave

Reputation: 7319

Ruby: How to deduce the "current" gem name?

I'm aware of the following to grab a Gem's specification for interrogation:

spec = Gem::Specification.find_by_name('my_gem')

Is there a way to programmatically identify "this" gem's name such that the above could be rewritten in a reusable manner?

In other words, how can you get the parent gem's name from some executing Ruby code at runtime?

Upvotes: 5

Views: 1116

Answers (2)

Mike Slinn
Mike Slinn

Reputation: 8403

I used the safe navigation operator on the solution provided by @JacobLukas, and wrapped the code into a method.

I also changed the File.fnmatch pattern from * to **, which means to match directories recursively or files expansively. This makes the method return the proper Gem::Specification when pointed at any file in any directory within a gem.

Tested with Ruby version 3.1.0p0.

# @param file must be a fully qualified file name
# @return Gem::Specification of gem that file points into, or nil if not called from a gem
def current_spec(file)
  searcher = if Gem::Specification.respond_to?(:find)
               Gem::Specification
             elsif Gem.respond_to?(:searcher)
               Gem.searcher.init_gemspecs
             end

  searcher&.find do |spec|
    File.fnmatch(File.join(spec.full_gem_path, '**'), file)
  end
end

A simpler, and more familiar, way to accomplish the same file path match is:

file.start_with? spec.full_gem_path

This version assumes that the file exists. You could be explicit about that:

file.exist?

You could rewrite the method using the above like this:

# @param file must be a fully qualified directory name pointing to an installed gem, or within it,
#             or a file name within an installed gem
# @return Gem::Specification of gem that file points into,
# or nil if no gem exists at the given file
def current_spec(file)
  return nil unless File.exist? file

  searcher = if Gem::Specification.respond_to?(:find)
               Gem::Specification
             elsif Gem.respond_to?(:searcher)
               Gem.searcher.init_gemspecs
             end

  searcher&.find do |spec|
    spec.full_gem_path.start_with? file
  end
end

def gem_path(file)
  spec = self.current_spec2(file)
  spec&.full_gem_path
end

See https://www.mslinn.com/ruby/6550-gem-navel.html#self_discovery

Call either version the same to obtain the complete Gem Specification:

current_spec __FILE__
current_spec2 __FILE__

Obtain the absolute path to the installed gem:

gem_path __FILE__

Upvotes: 0

Jacob Lukas
Jacob Lukas

Reputation: 699

To find the gem specification for the current source file (assuming it's a source file in the lib dir):

require 'rubygems'

searcher = if Gem::Specification.respond_to? :find
  # ruby 2.0
  Gem::Specification
elsif Gem.respond_to? :searcher
  # ruby 1.8/1.9
  Gem.searcher.init_gemspecs
end
spec = unless searcher.nil?
  searcher.find do |spec|
    File.fnmatch(File.join(spec.full_gem_path,'*'), __FILE__)
  end
end

You could make this reusable by passing in the __FILE__ from the source file you're actually interested in, or by examining the caller stack (but that's probably a bad idea).

Upvotes: 8

Related Questions