# frozen_string_literal: truerequire'rubygems/remote_fetcher'require'rubygems/user_interaction'require'rubygems/errors'require'rubygems/text'require'rubygems/name_tuple'### SpecFetcher handles metadata updates from remote gem repositories.classGem::SpecFetcherincludeGem::UserInteractionincludeGem::Text### Cache of latest specsattr_reader:latest_specs# :nodoc:### Sources for this SpecFetcherattr_reader:sources# :nodoc:### Cache of all released specsattr_reader:specs# :nodoc:### Cache of prerelease specsattr_reader:prerelease_specs# :nodoc:@fetcher=nil### Default fetcher instance. Use this instead of ::new to reduce object# allocation.defself.fetcher@fetcher||=newenddefself.fetcher=(fetcher)# :nodoc:@fetcher=fetcherend### Creates a new SpecFetcher. Ordinarily you want to use the default fetcher# from Gem::SpecFetcher::fetcher which uses the Gem.sources.## If you need to retrieve specifications from a different +source+, you can# send it as an argument.definitializesources=nil@sources=sources||Gem.sources@update_cache=beginFile.stat(Gem.user_home).uid==Process.uidrescueErrno::EACCES,Errno::ENOENTfalseend@specs={}@latest_specs={}@prerelease_specs={}@caches={:latest=>@latest_specs,:prerelease=>@prerelease_specs,:released=>@specs,}@fetcher=Gem::RemoteFetcher.fetcherend#### Find and fetch gem name tuples that match +dependency+.## If +matching_platform+ is false, gems for all platforms are returned.defsearch_for_dependency(dependency,matching_platform=true)found={}rejected_specs={}ifdependency.prerelease?ifdependency.specific?type=:completeelsetype=:abs_latestendelsifdependency.latest_version?type=:latestelsetype=:releasedendlist,errors=available_specs(type)list.eachdo|source,specs|ifdependency.name.is_a?(String)&&specs.respond_to?(:bsearch)start_index=(0...specs.length).bsearch{|i|specs[i].name>=dependency.name}end_index=(0...specs.length).bsearch{|i|specs[i].name>dependency.name}specs=specs[start_index...end_index]ifstart_index&&end_indexendfound[source]=specs.selectdo|tup|ifdependency.match?(tup)ifmatching_platformand!Gem::Platform.match(tup.platform)pm=(rejected_specs[dependency]||=\Gem::PlatformMismatch.new(tup.name,tup.version))pm.add_platformtup.platformfalseelsetrueendendendenderrors+=rejected_specs.valuestuples=[]found.eachdo|source,specs|specs.eachdo|s|tuples<<[s,source]endendtuples=tuples.sort_by{|x|x[0]}return[tuples,errors]end### Return all gem name tuples who's names match +obj+defdetect(type=:complete)tuples=[]list,_=available_specs(type)list.eachdo|source,specs|specs.eachdo|tup|ifyield(tup)tuples<<[tup,source]endendendtuplesend### Find and fetch specs that match +dependency+.## If +matching_platform+ is false, gems for all platforms are returned.defspec_for_dependency(dependency,matching_platform=true)tuples,errors=search_for_dependency(dependency,matching_platform)specs=[]tuples.eachdo|tup,source|beginspec=source.fetch_spec(tup)rescueGem::RemoteFetcher::FetchError=>eerrors<<Gem::SourceFetchProblem.new(source,e)elsespecs<<[spec,source]endendreturn[specs,errors]end### Suggests gems based on the supplied +gem_name+. Returns an array of# alternative gem names.defsuggest_gems_from_namegem_namegem_name=gem_name.downcase.tr('_-','')max=gem_name.size/2names=available_specs(:latest).first.values.flatten(1)matches=names.map{|n|nextunlessn.match_platform?distance=levenshtein_distancegem_name,n.name.downcase.tr('_-','')nextifdistance>=maxreturn[n.name]ifdistance==0[n.name,distance]}.compactmatches=matches.uniq.sort_by{|name,dist|dist}matches.first(5).map{|name,dist|name}end### Returns a list of gems available for each source in Gem::sources.## +type+ can be one of 3 values:# :released => Return the list of all released specs# :complete => Return the list of all specs# :latest => Return the list of only the highest version of each gem# :prerelease => Return the list of all prerelease only specs#defavailable_specs(type)errors=[]list={}@sources.each_sourcedo|source|beginnames=casetypewhen:latesttuples_forsource,:latestwhen:releasedtuples_forsource,:releasedwhen:completenames=tuples_for(source,:prerelease,true)+tuples_for(source,:released)names.sortwhen:abs_latestnames=tuples_for(source,:prerelease,true)+tuples_for(source,:latest)names.sortwhen:prereleasetuples_for(source,:prerelease)elseraiseGem::Exception,"Unknown type - :#{type}"endrescueGem::RemoteFetcher::FetchError=>eerrors<<Gem::SourceFetchProblem.new(source,e)elselist[source]=namesendend[list,errors]end### Retrieves NameTuples from +source+ of the given +type+ (:prerelease,# etc.). If +gracefully_ignore+ is true, errors are ignored.deftuples_for(source,type,gracefully_ignore=false)# :nodoc:@caches[type][source.uri]||=source.load_specs(type).sort_by{|tup|tup.name}rescueGem::RemoteFetcher::FetchErrorraiseunlessgracefully_ignore[]endend