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: # Correct for windows paths 74: escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/') 75: File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path) 76: end
# File lib/rubygems/spec_fetcher.rb, line 100 100: def fetch(*args) 101: fetch_with_errors(*args).first 102: end
# File lib/rubygems/spec_fetcher.rb, line 104 104: def fetch_spec(spec, source_uri) 105: source_uri = URI.parse source_uri if String === source_uri 106: spec = spec - [nil, 'ruby', ''] 107: spec_file_name = "#{spec.join '-'}.gemspec" 108: 109: uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}" 110: 111: cache_dir = cache_dir uri 112: 113: local_spec = File.join cache_dir, spec_file_name 114: 115: if File.exist? local_spec then 116: spec = Gem.read_binary local_spec 117: else 118: uri.path << '.rz' 119: 120: spec = @fetcher.fetch_path uri 121: spec = Gem.inflate spec 122: 123: if @update_cache then 124: FileUtils.mkdir_p cache_dir 125: 126: open local_spec, 'wb' do |io| 127: io.write spec 128: end 129: end 130: end 131: 132: # TODO: Investigate setting Gem::Specification#loaded_from to a URI 133: Marshal.load spec 134: 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 84 84: def fetch_with_errors(dependency, 85: all = false, 86: matching_platform = true, 87: prerelease = false) 88: specs_and_sources, errors = find_matching_with_errors(dependency, 89: all, 90: matching_platform, 91: prerelease) 92: 93: ss = specs_and_sources.map do |spec_tuple, source_uri| 94: [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri] 95: end 96: 97: return [ss, errors] 98: end
# File lib/rubygems/spec_fetcher.rb, line 172 172: def find_matching(*args) 173: find_matching_with_errors(*args).first 174: 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 141 141: def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false) 142: found = {} 143: 144: rejected_specs = {} 145: 146: list(all, prerelease).each do |source_uri, specs| 147: found[source_uri] = specs.select do |spec_name, version, spec_platform| 148: if dependency.match?(spec_name, version) 149: if matching_platform and !Gem::Platform.match(spec_platform) 150: pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version)) 151: pm.add_platform spec_platform 152: false 153: else 154: true 155: end 156: end 157: end 158: end 159: 160: errors = rejected_specs.values 161: 162: specs_and_sources = [] 163: 164: found.each do |source_uri, specs| 165: uri_str = source_uri.to_s 166: specs_and_sources.push(*specs.map { |spec| [spec, uri_str] }) 167: end 168: 169: [specs_and_sources, errors] 170: 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 209 209: def list(all = false, prerelease = false) 210: # TODO: make type the only argument 211: type = if all 212: :all 213: elsif prerelease 214: :prerelease 215: else 216: :latest 217: end 218: 219: list = {} 220: file = FILES[type] 221: cache = @caches[type] 222: 223: Gem.sources.each do |source_uri| 224: source_uri = URI.parse source_uri 225: 226: unless cache.include? source_uri 227: cache[source_uri] = load_specs source_uri, file 228: end 229: 230: list[source_uri] = cache[source_uri] 231: end 232: 233: if type == :all 234: list.values.map do |gems| 235: gems.reject! { |g| !g[1] || g[1].prerelease? } 236: end 237: end 238: 239: list 240: 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 246 246: def load_specs(source_uri, file) 247: file_name = "#{file}.#{Gem.marshal_version}" 248: spec_path = source_uri + "#{file_name}.gz" 249: cache_dir = cache_dir spec_path 250: local_file = File.join(cache_dir, file_name) 251: loaded = false 252: 253: if File.exist? local_file then 254: spec_dump = 255: @fetcher.fetch_path(spec_path, File.mtime(local_file)) rescue nil 256: 257: loaded = true if spec_dump 258: 259: spec_dump ||= Gem.read_binary local_file 260: else 261: spec_dump = @fetcher.fetch_path spec_path 262: loaded = true 263: end 264: 265: specs = begin 266: Marshal.load spec_dump 267: rescue ArgumentError 268: spec_dump = @fetcher.fetch_path spec_path 269: loaded = true 270: 271: Marshal.load spec_dump 272: end 273: 274: if loaded and @update_cache then 275: begin 276: FileUtils.mkdir_p cache_dir 277: 278: open local_file, 'wb' do |io| 279: io << spec_dump 280: end 281: rescue 282: end 283: end 284: 285: specs 286: 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 182 182: def suggest_gems_from_name gem_name 183: gem_name = gem_name.downcase 184: max = gem_name.size / 2 185: specs = list.values.flatten 1 186: 187: matches = specs.map { |name, version, platform| 188: next unless Gem::Platform.match platform 189: 190: distance = levenshtein_distance gem_name, name.downcase 191: 192: next if distance >= max 193: 194: return [name] if distance == 0 195: 196: [name, distance] 197: }.compact 198: 199: matches = matches.uniq.sort_by { |name, dist| dist } 200: 201: matches.first(5).map { |name, dist| name } 202: end