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

[Source]

     # File lib/rubygems/spec_fetcher.rb, line 100
100:   def fetch(*args)
101:     fetch_with_errors(*args).first
102:   end

[Source]

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

[Source]

    # 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

[Source]

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

[Source]

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

[Source]

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

[Source]

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

[Source]

     # 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

[Validate]