Reputation: 7319
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
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
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