Class | Gem::SpecFetcher |
In: |
lib/rubygems/spec_fetcher.rb
|
Parent: | Object |
SpecFetcher handles metadata updates from remote gem repositories.
FILES | = | { :all => 'specs', :latest => 'latest_specs', :prerelease => 'prerelease_specs', } |
# File lib/rubygems/spec_fetcher.rb, line 50 50: def initialize 51: require 'fileutils' 52: 53: @dir = File.join Gem.user_home, '.gem', 'specs' 54: @update_cache = File.stat(Gem.user_home).uid == Process.uid 55: 56: @specs = {} 57: @latest_specs = {} 58: @prerelease_specs = {} 59: 60: @caches = { 61: :latest => @latest_specs, 62: :prerelease => @prerelease_specs, 63: :all => @specs 64: } 65: 66: @fetcher = Gem::RemoteFetcher.fetcher 67: end
Returns the local directory to write uri to.
# File lib/rubygems/spec_fetcher.rb, line 72 72: def cache_dir(uri) 73: File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path) 74: end
# File lib/rubygems/spec_fetcher.rb, line 92 92: def fetch(*args) 93: fetch_with_errors(*args).first 94: end
# File lib/rubygems/spec_fetcher.rb, line 96 96: def fetch_spec(spec, source_uri) 97: spec = spec - [nil, 'ruby', ''] 98: spec_file_name = "#{spec.join '-'}.gemspec" 99: 100: uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}" 101: 102: cache_dir = cache_dir uri 103: 104: local_spec = File.join cache_dir, spec_file_name 105: 106: if File.exist? local_spec then 107: spec = Gem.read_binary local_spec 108: else 109: uri.path << '.rz' 110: 111: spec = @fetcher.fetch_path uri 112: spec = Gem.inflate spec 113: 114: if @update_cache then 115: FileUtils.mkdir_p cache_dir 116: 117: open local_spec, 'wb' do |io| 118: io.write spec 119: end 120: end 121: end 122: 123: # TODO: Investigate setting Gem::Specification#loaded_from to a URI 124: Marshal.load spec 125: end
Fetch specs matching dependency. If all is true, all matching (released) versions are returned. If matching_platform is false, all platforms are returned. If prerelease is true, prerelease versions are included.
# File lib/rubygems/spec_fetcher.rb, line 82 82: def fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false) 83: specs_and_sources, errors = find_matching_with_errors dependency, all, matching_platform, prerelease 84: 85: ss = specs_and_sources.map do |spec_tuple, source_uri| 86: [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri] 87: end 88: 89: return [ss, errors] 90: end
# File lib/rubygems/spec_fetcher.rb, line 163 163: def find_matching(*args) 164: find_matching_with_errors(*args).first 165: end
Find spec names that match dependency. If all is true, all matching released versions are returned. If matching_platform is false, gems for all platforms are returned.
# File lib/rubygems/spec_fetcher.rb, line 132 132: def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false) 133: found = {} 134: 135: rejected_specs = {} 136: 137: list(all, prerelease).each do |source_uri, specs| 138: found[source_uri] = specs.select do |spec_name, version, spec_platform| 139: if dependency.match?(spec_name, version) 140: if matching_platform and !Gem::Platform.match(spec_platform) 141: pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version)) 142: pm.add_platform spec_platform 143: false 144: else 145: true 146: end 147: end 148: end 149: end 150: 151: errors = rejected_specs.values 152: 153: specs_and_sources = [] 154: 155: found.each do |source_uri, specs| 156: uri_str = source_uri.to_s 157: specs_and_sources.push(*specs.map { |spec| [spec, uri_str] }) 158: end 159: 160: [specs_and_sources, errors] 161: end
Returns a list of gems available for each source in Gem::sources. If all is true, all released versions are returned instead of only latest versions. If prerelease is true, include prerelease versions.
# File lib/rubygems/spec_fetcher.rb, line 200 200: def list(all = false, prerelease = false) 201: # TODO: make type the only argument 202: type = if all 203: :all 204: elsif prerelease 205: :prerelease 206: else 207: :latest 208: end 209: 210: list = {} 211: file = FILES[type] 212: cache = @caches[type] 213: 214: Gem.sources.each do |source_uri| 215: source_uri = URI.parse source_uri 216: 217: unless cache.include? source_uri 218: cache[source_uri] = load_specs source_uri, file 219: end 220: 221: list[source_uri] = cache[source_uri] 222: end 223: 224: if type == :all 225: list.values.map do |gems| 226: gems.reject! { |g| !g[1] || g[1].prerelease? } 227: end 228: end 229: 230: list 231: end
Loads specs in file, fetching from source_uri if the on-disk cache is out of date.
# File lib/rubygems/spec_fetcher.rb, line 237 237: def load_specs(source_uri, file) 238: file_name = "#{file}.#{Gem.marshal_version}" 239: spec_path = source_uri + "#{file_name}.gz" 240: cache_dir = cache_dir spec_path 241: local_file = File.join(cache_dir, file_name) 242: loaded = false 243: 244: if File.exist? local_file then 245: spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file) 246: 247: if spec_dump.nil? then 248: spec_dump = Gem.read_binary local_file 249: else 250: loaded = true 251: end 252: else 253: spec_dump = @fetcher.fetch_path spec_path 254: loaded = true 255: end 256: 257: specs = begin 258: Marshal.load spec_dump 259: rescue ArgumentError 260: spec_dump = @fetcher.fetch_path spec_path 261: loaded = true 262: 263: Marshal.load spec_dump 264: end 265: 266: if loaded and @update_cache then 267: begin 268: FileUtils.mkdir_p cache_dir 269: 270: open local_file, 'wb' do |io| 271: io << spec_dump 272: end 273: rescue 274: end 275: end 276: 277: specs 278: end
Suggests a gem based on the supplied gem_name. Returns a string of the gem name if an approximate match can be found or nil otherwise. NOTE: for performance reasons only gems which exactly match the first character of gem_name are considered.
# File lib/rubygems/spec_fetcher.rb, line 173 173: def suggest_gems_from_name gem_name 174: gem_name = gem_name.downcase 175: max = gem_name.size / 2 176: specs = list.values.flatten 1 177: 178: matches = specs.map { |name, version, platform| 179: next unless Gem::Platform.match platform 180: 181: distance = levenshtein_distance gem_name, name.downcase 182: 183: next if distance >= max 184: 185: return [name] if distance == 0 186: 187: [name, distance] 188: }.compact 189: 190: matches = matches.uniq.sort_by { |name, dist| dist } 191: 192: matches.first(5).map { |name, dist| name } 193: end