summaryrefslogtreecommitdiff
path: root/lib/rubygems/resolver/api_set.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/resolver/api_set.rb')
-rw-r--r--lib/rubygems/resolver/api_set.rb139
1 files changed, 139 insertions, 0 deletions
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
new file mode 100644
index 0000000000..3f443519d8
--- /dev/null
+++ b/lib/rubygems/resolver/api_set.rb
@@ -0,0 +1,139 @@
+# frozen_string_literal: true
+
+##
+# The global rubygems pool, available via the Compact Index API.
+# Returns instances of APISpecification.
+
+class Gem::Resolver::APISet < Gem::Resolver::Set
+ autoload :GemParser, File.expand_path("api_set/gem_parser", __dir__)
+
+ ##
+ # The URI for the Compact Index API this APISet uses.
+
+ attr_reader :dep_uri # :nodoc:
+
+ ##
+ # The Gem::Source that gems are fetched from
+
+ attr_reader :source
+
+ ##
+ # The corresponding place to fetch gems.
+
+ attr_reader :uri
+
+ ##
+ # Creates a new APISet that will retrieve gems from +uri+ using the Compact
+ # Index API URL +dep_uri+ which is described at
+ # https://guides.rubygems.org/rubygems-org-compact-index-api
+
+ def initialize(dep_uri = "https://index.rubygems.org/info/")
+ super()
+
+ dep_uri = Gem::URI dep_uri unless Gem::URI === dep_uri
+
+ @dep_uri = dep_uri
+ @uri = dep_uri + ".."
+
+ @data = Hash.new {|h,k| h[k] = [] }
+ @source = Gem::Source.new @uri
+
+ @to_fetch = []
+ end
+
+ ##
+ # Return an array of APISpecification objects matching
+ # DependencyRequest +req+.
+
+ def find_all(req)
+ res = []
+
+ return res unless @remote
+
+ if @to_fetch.include?(req.name)
+ prefetch_now
+ end
+
+ versions(req.name).each do |ver|
+ if req.dependency.match? req.name, ver[:number], @prerelease
+ res << Gem::Resolver::APISpecification.new(self, ver)
+ end
+ end
+
+ res
+ end
+
+ ##
+ # A hint run by the resolver to allow the Set to fetch
+ # data for DependencyRequests +reqs+.
+
+ def prefetch(reqs)
+ return unless @remote
+ names = reqs.map {|r| r.dependency.name }
+ needed = names - @data.keys - @to_fetch
+
+ @to_fetch += needed
+ end
+
+ def prefetch_now # :nodoc:
+ needed = @to_fetch
+ @to_fetch = []
+
+ needed.sort.each do |name|
+ versions(name)
+ end
+ end
+
+ def pretty_print(q) # :nodoc:
+ q.group 2, "[APISet", "]" do
+ q.breakable
+ q.text "URI: #{@dep_uri}"
+
+ q.breakable
+ q.text "gem names:"
+ q.pp @data.keys
+ end
+ end
+
+ ##
+ # Return data for all versions of the gem +name+.
+
+ def versions(name) # :nodoc:
+ if @data.key?(name)
+ return @data[name]
+ end
+
+ uri = @dep_uri + name
+
+ begin
+ str = Gem::RemoteFetcher.fetcher.fetch_path uri
+ rescue Gem::RemoteFetcher::FetchError
+ @data[name] = []
+ else
+ lines(str).each do |ver|
+ number, platform, dependencies, requirements = parse_gem(ver)
+
+ platform ||= "ruby"
+ dependencies = dependencies.map {|dep_name, reqs| [dep_name, reqs.join(", ")] }
+ requirements = requirements.map {|req_name, reqs| [req_name.to_sym, reqs] }.to_h
+
+ @data[name] << { name: name, number: number, platform: platform, dependencies: dependencies, requirements: requirements }
+ end
+ end
+
+ @data[name]
+ end
+
+ private
+
+ def lines(str)
+ lines = str.split("\n")
+ header = lines.index("---")
+ header ? lines[header + 1..-1] : lines
+ end
+
+ def parse_gem(string)
+ @gem_parser ||= GemParser.new
+ @gem_parser.parse(string)
+ end
+end