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

SpecFetcher handles metadata updates from remote gem repositories.

Methods

Included Modules

Gem::UserInteraction Gem::Text

Constants

FILES = { :all => 'specs', :latest => 'latest_specs', :prerelease => 'prerelease_specs', }

Public Class methods

[Source]

    # File lib/rubygems/spec_fetcher.rb, line 42
42:   def self.fetcher
43:     @fetcher ||= new
44:   end

[Source]

    # 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

Public Instance methods

Returns the local directory to write uri to.

[Source]

    # 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

[Source]

    # File lib/rubygems/spec_fetcher.rb, line 92
92:   def fetch(*args)
93:     fetch_with_errors(*args).first
94:   end

[Source]

     # 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.

[Source]

    # 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

[Source]

     # 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.

[Source]

     # 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.

[Source]

     # 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.

[Source]

     # 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.

[Source]

     # 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

[Validate]