Class Gem::SpecFetcher
In: lib/rubygems/spec_fetcher.rb
Parent: Object

SpecFetcher handles metadata updates from remote gem repositories.

Methods

Included Modules

Gem::UserInteraction

Public Class methods

[Source]

    # File lib/rubygems/spec_fetcher.rb, line 36
36:   def self.fetcher
37:     @fetcher ||= new
38:   end

[Source]

    # File lib/rubygems/spec_fetcher.rb, line 44
44:   def initialize
45:     @dir = File.join Gem.user_home, '.gem', 'specs'
46:     @update_cache = File.stat(Gem.user_home).uid == Process.uid
47: 
48:     @specs = {}
49:     @latest_specs = {}
50:     @prerelease_specs = {}
51: 
52:     @fetcher = Gem::RemoteFetcher.fetcher
53:   end

Public Instance methods

Retuns the local directory to write uri to.

[Source]

    # File lib/rubygems/spec_fetcher.rb, line 58
58:   def cache_dir(uri)
59:     File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path)
60:   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.

[Source]

    # File lib/rubygems/spec_fetcher.rb, line 68
68:   def fetch(dependency, all = false, matching_platform = true, prerelease = false)
69:     specs_and_sources = find_matching dependency, all, matching_platform, prerelease
70: 
71:     specs_and_sources.map do |spec_tuple, source_uri|
72:       [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
73:     end
74: 
75:   rescue Gem::RemoteFetcher::FetchError => e
76:     raise unless warn_legacy e do
77:       require 'rubygems/source_info_cache'
78: 
79:       return Gem::SourceInfoCache.search_with_source(dependency,
80:                                                      matching_platform, all)
81:     end
82:   end

[Source]

     # File lib/rubygems/spec_fetcher.rb, line 84
 84:   def fetch_spec(spec, source_uri)
 85:     spec = spec - [nil, 'ruby', '']
 86:     spec_file_name = "#{spec.join '-'}.gemspec"
 87: 
 88:     uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
 89: 
 90:     cache_dir = cache_dir uri
 91: 
 92:     local_spec = File.join cache_dir, spec_file_name
 93: 
 94:     if File.exist? local_spec then
 95:       spec = Gem.read_binary local_spec
 96:     else
 97:       uri.path << '.rz'
 98: 
 99:       spec = @fetcher.fetch_path uri
100:       spec = Gem.inflate spec
101: 
102:       if @update_cache then
103:         FileUtils.mkdir_p cache_dir
104: 
105:         open local_spec, 'wb' do |io|
106:           io.write spec
107:         end
108:       end
109:     end
110: 
111:     # TODO: Investigate setting Gem::Specification#loaded_from to a URI
112:     Marshal.load spec
113:   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.

[Source]

     # File lib/rubygems/spec_fetcher.rb, line 120
120:   def find_matching(dependency, all = false, matching_platform = true, prerelease = false)
121:     found = {}
122: 
123:     list(all, prerelease).each do |source_uri, specs|
124:       found[source_uri] = specs.select do |spec_name, version, spec_platform|
125:         dependency =~ Gem::Dependency.new(spec_name, version) and
126:           (not matching_platform or Gem::Platform.match(spec_platform))
127:       end
128:     end
129: 
130:     specs_and_sources = []
131: 
132:     found.each do |source_uri, specs|
133:       uri_str = source_uri.to_s
134:       specs_and_sources.push(*specs.map { |spec| [spec, uri_str] })
135:     end
136: 
137:     specs_and_sources
138:   end

Returns Array of gem repositories that were generated with RubyGems less than 1.2.

[Source]

     # File lib/rubygems/spec_fetcher.rb, line 144
144:   def legacy_repos
145:     Gem.sources.reject do |source_uri|
146:       source_uri = URI.parse source_uri
147:       spec_path = source_uri + "specs.#{Gem.marshal_version}.gz"
148: 
149:       begin
150:         @fetcher.fetch_size spec_path
151:       rescue Gem::RemoteFetcher::FetchError
152:         begin
153:           @fetcher.fetch_size(source_uri + 'yaml') # re-raise if non-repo
154:         rescue Gem::RemoteFetcher::FetchError
155:           alert_error "#{source_uri} does not appear to be a repository"
156:           raise
157:         end
158:         false
159:       end
160:     end
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.

[Source]

     # File lib/rubygems/spec_fetcher.rb, line 168
168:   def list(all = false, prerelease = false)
169:     # TODO: make type the only argument
170:     type = if all
171:              :all
172:            elsif prerelease
173:              :prerelease
174:            else
175:              :latest
176:            end
177: 
178:     list = {}
179: 
180:     file = { :latest => 'latest_specs',
181:       :prerelease => 'prerelease_specs',
182:       :all => 'specs' }[type]
183: 
184:     cache = { :latest => @latest_specs,
185:       :prerelease => @prerelease_specs,
186:       :all => @specs }[type]
187:     
188:     Gem.sources.each do |source_uri|
189:       source_uri = URI.parse source_uri
190: 
191:       unless cache.include? source_uri
192:         cache[source_uri] = load_specs source_uri, file
193:       end
194: 
195:       list[source_uri] = cache[source_uri]
196:     end
197: 
198:     if type == :all
199:       list.values.map do |gems|
200:         gems.reject! { |g| g[1].prerelease? }
201:       end
202:     end
203: 
204:     list
205:   end

Loads specs in file, fetching from source_uri if the on-disk cache is out of date.

[Source]

     # File lib/rubygems/spec_fetcher.rb, line 211
211:   def load_specs(source_uri, file)
212:     file_name  = "#{file}.#{Gem.marshal_version}"
213:     spec_path  = source_uri + "#{file_name}.gz"
214:     cache_dir  = cache_dir spec_path
215:     local_file = File.join(cache_dir, file_name)
216:     loaded     = false
217: 
218:     if File.exist? local_file then
219:       spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file)
220: 
221:       if spec_dump.nil? then
222:         spec_dump = Gem.read_binary local_file
223:       else
224:         loaded = true
225:       end
226:     else
227:       spec_dump = @fetcher.fetch_path spec_path
228:       loaded = true
229:     end
230: 
231:     specs = begin
232:               Marshal.load spec_dump
233:             rescue ArgumentError
234:               spec_dump = @fetcher.fetch_path spec_path
235:               loaded = true
236: 
237:               Marshal.load spec_dump
238:             end
239: 
240:     if loaded and @update_cache then
241:       begin
242:         FileUtils.mkdir_p cache_dir
243: 
244:         open local_file, 'wb' do |io|
245:           Marshal.dump specs, io
246:         end
247:       rescue
248:       end
249:     end
250: 
251:     specs
252:   end

Warn about legacy repositories if exception indicates only legacy repositories are available, and yield to the block. Returns false if the exception indicates some other FetchError.

[Source]

     # File lib/rubygems/spec_fetcher.rb, line 259
259:   def warn_legacy(exception)
260:     uri = exception.uri.to_s
261:     if uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ then
262:       alert_warning "RubyGems 1.2+ index not found for:\n\\t\#{legacy_repos.join \"\\n\\t\"}\n\nRubyGems will revert to legacy indexes degrading performance.\n"
263: 
264:       yield
265: 
266:       return true
267:     end
268: 
269:     false
270:   end

[Validate]