Geminstaller C0 Coverage Information - RCov

spec/fixture/rubygems_dist/rubygems-trunk/lib/rubygems/spec_fetcher.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
spec/fixture/rubygems_dist/rubygems-trunk/lib/rubygems/spec_fetcher.rb 300 188
74.33%
65.43%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 require 'zlib'
2 require 'fileutils'
3 
4 require 'rubygems/remote_fetcher'
5 require 'rubygems/user_interaction'
6 require 'rubygems/errors'
7 
8 ##
9 # SpecFetcher handles metadata updates from remote gem repositories.
10 
11 class Gem::SpecFetcher
12 
13   include Gem::UserInteraction
14 
15   ##
16   # The SpecFetcher cache dir.
17 
18   attr_reader :dir # :nodoc:
19 
20   ##
21   # Cache of latest specs
22 
23   attr_reader :latest_specs # :nodoc:
24 
25   ##
26   # Cache of all released specs
27 
28   attr_reader :specs # :nodoc:
29 
30   ##
31   # Cache of prerelease specs
32 
33   attr_reader :prerelease_specs # :nodoc:
34 
35   @fetcher = nil
36 
37   def self.fetcher
38     @fetcher ||= new
39   end
40 
41   def self.fetcher=(fetcher) # :nodoc:
42     @fetcher = fetcher
43   end
44 
45   def initialize
46     @dir = File.join Gem.user_home, '.gem', 'specs'
47     @update_cache = File.stat(Gem.user_home).uid == Process.uid
48 
49     @specs = {}
50     @latest_specs = {}
51     @prerelease_specs = {}
52 
53     @fetcher = Gem::RemoteFetcher.fetcher
54   end
55 
56   ##
57   # Returns the local directory to write +uri+ to.
58 
59   def cache_dir(uri)
60     File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path)
61   end
62 
63   ##
64   # Fetch specs matching +dependency+.  If +all+ is true, all matching
65   # (released) versions are returned.  If +matching_platform+ is
66   # false, all platforms are returned. If +prerelease+ is true,
67   # prerelease versions are included.
68 
69   def fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
70     specs_and_sources, errors = find_matching_with_errors dependency, all, matching_platform, prerelease
71 
72     ss = specs_and_sources.map do |spec_tuple, source_uri|
73       [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
74     end
75 
76     return [ss, errors]
77 
78   rescue Gem::RemoteFetcher::FetchError => e
79     raise unless warn_legacy e do
80       require 'rubygems/source_info_cache'
81 
82       return [Gem::SourceInfoCache.search_with_source(dependency,
83                                                      matching_platform, all), nil]
84     end
85   end
86 
87   def fetch(*args)
88     fetch_with_errors(*args).first
89   end
90 
91   def fetch_spec(spec, source_uri)
92     spec = spec - [nil, 'ruby', '']
93     spec_file_name = "#{spec.join '-'}.gemspec"
94 
95     uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
96 
97     cache_dir = cache_dir uri
98 
99     local_spec = File.join cache_dir, spec_file_name
100 
101     if File.exist? local_spec then
102       spec = Gem.read_binary local_spec
103     else
104       uri.path << '.rz'
105 
106       spec = @fetcher.fetch_path uri
107       spec = Gem.inflate spec
108 
109       if @update_cache then
110         FileUtils.mkdir_p cache_dir
111 
112         open local_spec, 'wb' do |io|
113           io.write spec
114         end
115       end
116     end
117 
118     # TODO: Investigate setting Gem::Specification#loaded_from to a URI
119     Marshal.load spec
120   end
121 
122   ##
123   # Find spec names that match +dependency+.  If +all+ is true, all
124   # matching released versions are returned.  If +matching_platform+
125   # is false, gems for all platforms are returned.
126 
127   def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
128     found = {}
129 
130     rejected_specs = {}
131 
132     list(all, prerelease).each do |source_uri, specs|
133       found[source_uri] = specs.select do |spec_name, version, spec_platform|
134         if dependency.match?(spec_name, version)
135           if matching_platform and !Gem::Platform.match(spec_platform)
136             pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version))
137             pm.add_platform spec_platform
138             false
139           else
140             true
141           end
142         end
143       end
144     end
145 
146     errors = rejected_specs.values
147 
148     specs_and_sources = []
149 
150     found.each do |source_uri, specs|
151       uri_str = source_uri.to_s
152       specs_and_sources.push(*specs.map { |spec| [spec, uri_str] })
153     end
154 
155     [specs_and_sources, errors]
156   end
157 
158   def find_matching(*args)
159     find_matching_with_errors(*args).first
160   end
161 
162   ##
163   # Returns Array of gem repositories that were generated with RubyGems less
164   # than 1.2.
165 
166   def legacy_repos
167     Gem.sources.reject do |source_uri|
168       source_uri = URI.parse source_uri
169       spec_path = source_uri + "specs.#{Gem.marshal_version}.gz"
170 
171       begin
172         @fetcher.fetch_size spec_path
173       rescue Gem::RemoteFetcher::FetchError
174         begin
175           @fetcher.fetch_size(source_uri + 'yaml') # re-raise if non-repo
176         rescue Gem::RemoteFetcher::FetchError
177           alert_error "#{source_uri} does not appear to be a repository"
178           raise
179         end
180         false
181       end
182     end
183   end
184 
185   ##
186   # Returns a list of gems available for each source in Gem::sources.  If
187   # +all+ is true, all released versions are returned instead of only latest
188   # versions. If +prerelease+ is true, include prerelease versions.
189 
190   def list(all = false, prerelease = false)
191     # TODO: make type the only argument
192     type = if all
193              :all
194            elsif prerelease
195              :prerelease
196            else
197              :latest
198            end
199 
200     list = {}
201 
202     file = { :latest => 'latest_specs',
203       :prerelease => 'prerelease_specs',
204       :all => 'specs' }[type]
205 
206     cache = { :latest => @latest_specs,
207       :prerelease => @prerelease_specs,
208       :all => @specs }[type]
209 
210     Gem.sources.each do |source_uri|
211       source_uri = URI.parse source_uri
212 
213       unless cache.include? source_uri
214         cache[source_uri] = load_specs source_uri, file
215       end
216 
217       list[source_uri] = cache[source_uri]
218     end
219 
220     if type == :all
221       list.values.map do |gems|
222         gems.reject! { |g| !g[1] || g[1].prerelease? }
223       end
224     end
225 
226     list
227   end
228 
229   ##
230   # Loads specs in +file+, fetching from +source_uri+ if the on-disk cache is
231   # out of date.
232 
233   def load_specs(source_uri, file)
234     file_name  = "#{file}.#{Gem.marshal_version}"
235     spec_path  = source_uri + "#{file_name}.gz"
236     cache_dir  = cache_dir spec_path
237     local_file = File.join(cache_dir, file_name)
238     loaded     = false
239 
240     if File.exist? local_file then
241       spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file)
242 
243       if spec_dump.nil? then
244         spec_dump = Gem.read_binary local_file
245       else
246         loaded = true
247       end
248     else
249       spec_dump = @fetcher.fetch_path spec_path
250       loaded = true
251     end
252 
253     specs = begin
254               Marshal.load spec_dump
255             rescue ArgumentError
256               spec_dump = @fetcher.fetch_path spec_path
257               loaded = true
258 
259               Marshal.load spec_dump
260             end
261 
262     if loaded and @update_cache then
263       begin
264         FileUtils.mkdir_p cache_dir
265 
266         open local_file, 'wb' do |io|
267           io << spec_dump
268         end
269       rescue
270       end
271     end
272 
273     specs
274   end
275 
276   ##
277   # Warn about legacy repositories if +exception+ indicates only legacy
278   # repositories are available, and yield to the block.  Returns false if the
279   # exception indicates some other FetchError.
280 
281   def warn_legacy(exception)
282     uri = exception.uri.to_s
283     if uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ then
284       alert_warning <<-EOF
285 RubyGems 1.2+ index not found for:
286 \t#{legacy_repos.join "\n\t"}
287 
288 RubyGems will revert to legacy indexes degrading performance.
289       EOF
290 
291       yield
292 
293       return true
294     end
295 
296     false
297   end
298 
299 end
300 

Generated on Mon May 10 23:40:28 -0700 2010 with rcov 0.9.8