class Gem::SpecFetcher
SpecFetcher
处理来自远程 gem 仓库的元数据更新。
公共类方法
fetcher() 点击切换源代码
默认的获取器实例。使用这个而不是 ::new
可以减少对象分配。
# File rubygems/spec_fetcher.rb, line 42 def self.fetcher @fetcher ||= new end
new(sources = nil) 点击切换源代码
创建一个新的 SpecFetcher
。通常你想要使用来自 Gem::SpecFetcher::fetcher
的默认获取器,它使用 Gem.sources
。
如果你需要从不同的 source
获取规范,你可以将其作为参数发送。
# File rubygems/spec_fetcher.rb, line 57 def initialize(sources = nil) @sources = sources || Gem.sources @update_cache = begin File.stat(Gem.user_home).uid == Process.uid rescue Errno::EACCES, Errno::ENOENT false end @specs = {} @latest_specs = {} @prerelease_specs = {} @caches = { latest: @latest_specs, prerelease: @prerelease_specs, released: @specs, } @fetcher = Gem::RemoteFetcher.fetcher end
公共实例方法
available_specs(type) 点击切换源代码
返回 Gem::sources
中每个源可用的 gem 列表。
type
可以是以下 3 个值之一: :released => 返回所有已发布规范的列表 :complete => 返回所有规范的列表 :latest => 仅返回每个 gem 的最高版本列表 :prerelease => 仅返回所有预发布规范的列表
# File rubygems/spec_fetcher.rb, line 243 def available_specs(type) errors = [] list = {} @sources.each_source do |source| names = case type when :latest tuples_for source, :latest when :released tuples_for source, :released when :complete names = tuples_for(source, :prerelease, true) + tuples_for(source, :released) names.sort when :abs_latest names = tuples_for(source, :prerelease, true) + tuples_for(source, :latest) names.sort when :prerelease tuples_for(source, :prerelease) else raise Gem::Exception, "Unknown type - :#{type}" end rescue Gem::RemoteFetcher::FetchError => e errors << Gem::SourceFetchProblem.new(source, e) else list[source] = names end [list, errors] end
detect(type=:complete) { |tup| ... } 点击切换源代码
返回其名称与 obj
匹配的所有 gem 名称元组
# File rubygems/spec_fetcher.rb, line 133 def detect(type=:complete) tuples = [] list, _ = available_specs(type) list.each do |source, specs| specs.each do |tup| if yield(tup) tuples << [tup, source] end end end tuples end
search_for_dependency(dependency, matching_platform=true) 点击切换源代码
查找并获取与 dependency
匹配的 gem 名称元组。
如果 matching_platform
为 false,则返回所有平台的 gem。
# File rubygems/spec_fetcher.rb, line 86 def search_for_dependency(dependency, matching_platform=true) found = {} rejected_specs = {} list, errors = available_specs(dependency.identity) list.each do |source, specs| if dependency.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] if start_index && end_index end found[source] = specs.select do |tup| if dependency.match?(tup) if matching_platform && !Gem::Platform.match_gem?(tup.platform, tup.name) pm = ( rejected_specs[dependency] ||= \ Gem::PlatformMismatch.new(tup.name, tup.version)) pm.add_platform tup.platform false else true end end end end errors += rejected_specs.values tuples = [] found.each do |source, specs| specs.each do |s| tuples << [s, source] end end tuples = tuples.sort_by {|x| x[0].version } [tuples, errors] end
spec_for_dependency(dependency, matching_platform=true) 点击切换源代码
查找并获取与 dependency
匹配的规范。
如果 matching_platform
为 false,则返回所有平台的 gem。
# File rubygems/spec_fetcher.rb, line 153 def spec_for_dependency(dependency, matching_platform=true) tuples, errors = search_for_dependency(dependency, matching_platform) specs = [] tuples.each do |tup, source| spec = source.fetch_spec(tup) rescue Gem::RemoteFetcher::FetchError => e errors << Gem::SourceFetchProblem.new(source, e) else specs << [spec, source] end [specs, errors] end
suggest_gems_from_name(gem_name, type = :latest, num_results = 5) 点击切换源代码
基于提供的 gem_name
建议 gem。返回一个备选 gem 名称的数组。
# File rubygems/spec_fetcher.rb, line 172 def suggest_gems_from_name(gem_name, type = :latest, num_results = 5) gem_name = gem_name.downcase.tr("_-", "") # All results for 3-character-or-shorter (minus hyphens/underscores) gem # names get rejected, so we just return an empty array immediately instead. return [] if gem_name.length <= 3 max = gem_name.size / 2 names = available_specs(type).first.values.flatten(1) min_length = gem_name.length - max max_length = gem_name.length + max gem_name_with_postfix = "#{gem_name}ruby" gem_name_with_prefix = "ruby#{gem_name}" matches = names.filter_map do |n| len = n.name.length # If the gem doesn't support the current platform, bail early. next unless n.match_platform? # If the length is min_length or shorter, we've done `max` deletions. # This would be rejected later, so we skip it for performance. next if len <= min_length # The candidate name, normalized the same as gem_name. normalized_name = n.name.downcase normalized_name.tr!("_-", "") # If the gem is "{NAME}-ruby" and "ruby-{NAME}", we want to return it. # But we already removed hyphens, so we check "{NAME}ruby" and "ruby{NAME}". next [n.name, 0] if normalized_name == gem_name_with_postfix next [n.name, 0] if normalized_name == gem_name_with_prefix # If the length is max_length or longer, we've done `max` insertions. # This would be rejected later, so we skip it for performance. next if len >= max_length # If we found an exact match (after stripping underscores and hyphens), # that's our most likely candidate. # Return it immediately, and skip the rest of the loop. return [n.name] if normalized_name == gem_name distance = levenshtein_distance gem_name, normalized_name # Skip current candidate, if the edit distance is greater than allowed. next if distance >= max # If all else fails, return the name and the calculated distance. [n.name, distance] end matches = if matches.empty? && type != :prerelease suggest_gems_from_name gem_name, :prerelease else matches.uniq.sort_by {|_name, dist| dist } end matches.map {|name, _dist| name }.uniq.first(num_results) end