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__
Foo
contains the "this gem" code thenFoo
elseBar
. There's no reason why this information shouldn't be available, it entirely depends on whether they've included the feature in the Gem API. Since code is loaded from a gem the ruby runtime could easily be aware of the parent gem for an execution context. – TracheoFoo
, if it's in Bar thenBar
etc. As I said before, it simply depends on whether they've coded the runtime to provide this information. – Tracheo