diff --git a/lib/interscript.rb b/lib/interscript.rb index d5483bf8..b7d4e757 100644 --- a/lib/interscript.rb +++ b/lib/interscript.rb @@ -82,8 +82,28 @@ def detect(source, destination, **kwargs) detector.call(source, destination) end + # Locate the interscript-maps registry yaml(s) across installed gems. + # + # rubygems 4 removed StubSpecification#files, which + # Gem.find_latest_files relies on, so the fast path can raise there; + # the fallback globs the interscript-maps gems directly (there are + # only ever one or two of them, so this stays cheap). + def locate_map_gems_yaml + found = begin + Gem.find_latest_files("interscript-maps.yaml") + rescue + nil + end + return found if found.is_a?(Array) && found.any? + + Gem::Specification.latest_specs + .select { |spec| spec.name == "interscript-maps" } + .flat_map { |spec| Dir.glob(File.join(spec.full_gem_path, "**", "interscript-maps.yaml")) } + .uniq + end + def map_gems - @map_gems ||= Gem.find_latest_files("interscript-maps.yaml").map do |i| + @map_gems ||= locate_map_gems_yaml.map do |i| [i, YAML.load_file(i)] end.to_h end diff --git a/spec/interscript/map_gems_spec.rb b/spec/interscript/map_gems_spec.rb new file mode 100644 index 00000000..0afd3f2f --- /dev/null +++ b/spec/interscript/map_gems_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require "interscript" +require "tmpdir" +require "fileutils" +require "yaml" + +RSpec.describe Interscript do + describe ".locate_map_gems_yaml" do + it "finds the registry yaml via Gem.find_latest_files when available" do + Dir.mktmpdir do |dir| + gem_dir = File.join(dir, "gems", "interscript-maps-9.9.9") + FileUtils.mkdir_p(gem_dir) + yaml_path = File.join(gem_dir, "interscript-maps.yaml") + File.write(yaml_path, {"paths" => ["maps"]}.to_yaml) + spec = Struct.new(:name, :full_gem_path).new("interscript-maps", gem_dir) + allow(Gem::Specification).to receive(:latest_specs).and_return([spec]) + allow(Gem).to receive(:find_latest_files).with("interscript-maps.yaml").and_return([yaml_path]) + expect(Interscript.locate_map_gems_yaml).to include(yaml_path) + end + end + + it "falls back to a direct gem-dir glob when Gem.find_latest_files is unavailable (rubygems 4)" do + Dir.mktmpdir do |dir| + gem_dir = File.join(dir, "gems", "interscript-maps-9.9.9") + FileUtils.mkdir_p(File.join(gem_dir, "maps")) + yaml_path = File.join(gem_dir, "interscript-maps.yaml") + File.write(yaml_path, {"paths" => ["maps"]}.to_yaml) + spec = Struct.new(:name, :full_gem_path).new("interscript-maps", gem_dir) + allow(Gem::Specification).to receive(:latest_specs).and_return([spec]) + allow(Gem).to receive(:find_latest_files).with("interscript-maps.yaml") + .and_raise(RuntimeError, "undefined method 'files' for Gem::StubSpecification") + expect(Interscript.locate_map_gems_yaml).to include(yaml_path) + end + end + end +end