Class: Gem::SpecFetcher
- Inherits:
-
Object
- Object
- Gem::SpecFetcher
- Includes:
- Text, UserInteraction
- Defined in:
- lib/rubygems/spec_fetcher.rb
Overview
SpecFetcher handles metadata updates from remote gem repositories.
Constant Summary collapse
- FILES =
{ :all => 'specs', :latest => 'latest_specs', :prerelease => 'prerelease_specs', }
Instance Attribute Summary collapse
-
#dir ⇒ Object
readonly
The SpecFetcher cache dir.
-
#latest_specs ⇒ Object
readonly
Cache of latest specs.
-
#prerelease_specs ⇒ Object
readonly
Cache of prerelease specs.
-
#specs ⇒ Object
readonly
Cache of all released specs.
Class Method Summary collapse
Instance Method Summary collapse
-
#cache_dir(uri) ⇒ Object
Returns the local directory to write
uri
to. - #fetch(*args) ⇒ Object
- #fetch_spec(spec, source_uri) ⇒ Object
-
#fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false) ⇒ Object
Fetch specs matching
dependency
. - #find_matching(*args) ⇒ Object
-
#find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false) ⇒ Object
Find spec names that match
dependency
. -
#initialize ⇒ SpecFetcher
constructor
A new instance of SpecFetcher.
-
#list(all = false, prerelease = false) ⇒ Object
Returns a list of gems available for each source in Gem::sources.
-
#load_specs(source_uri, file) ⇒ Object
Loads specs in
file
, fetching fromsource_uri
if the on-disk cache is out of date. -
#suggest_gems_from_name(gem_name) ⇒ Object
Suggests a gem based on the supplied
gem_name
.
Methods included from Text
#format_text, #levenshtein_distance
Methods included from UserInteraction
#alert, #alert_error, #alert_warning, #ask, #ask_for_password, #ask_yes_no, #choose_from_list, #say, #terminate_interaction
Methods included from DefaultUserInteraction
ui, #ui, ui=, #ui=, use_ui, #use_ui
Constructor Details
#initialize ⇒ SpecFetcher
Returns a new instance of SpecFetcher.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/rubygems/spec_fetcher.rb', line 50 def initialize require 'fileutils' @dir = File.join Gem.user_home, '.gem', 'specs' @update_cache = File.stat(Gem.user_home).uid == Process.uid @specs = {} @latest_specs = {} @prerelease_specs = {} @caches = { :latest => @latest_specs, :prerelease => @prerelease_specs, :all => @specs } @fetcher = Gem::RemoteFetcher.fetcher end |
Instance Attribute Details
#dir ⇒ Object (readonly)
The SpecFetcher cache dir.
23 24 25 |
# File 'lib/rubygems/spec_fetcher.rb', line 23 def dir @dir end |
#latest_specs ⇒ Object (readonly)
Cache of latest specs
28 29 30 |
# File 'lib/rubygems/spec_fetcher.rb', line 28 def latest_specs @latest_specs end |
#prerelease_specs ⇒ Object (readonly)
Cache of prerelease specs
38 39 40 |
# File 'lib/rubygems/spec_fetcher.rb', line 38 def prerelease_specs @prerelease_specs end |
#specs ⇒ Object (readonly)
Cache of all released specs
33 34 35 |
# File 'lib/rubygems/spec_fetcher.rb', line 33 def specs @specs end |
Class Method Details
.fetcher ⇒ Object
42 43 44 |
# File 'lib/rubygems/spec_fetcher.rb', line 42 def self.fetcher @fetcher ||= new end |
.fetcher=(fetcher) ⇒ Object
:nodoc:
46 47 48 |
# File 'lib/rubygems/spec_fetcher.rb', line 46 def self.fetcher=(fetcher) # :nodoc: @fetcher = fetcher end |
Instance Method Details
#cache_dir(uri) ⇒ Object
Returns the local directory to write uri
to.
72 73 74 75 76 |
# File 'lib/rubygems/spec_fetcher.rb', line 72 def cache_dir(uri) # Correct for windows paths escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/') File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path) end |
#fetch(*args) ⇒ Object
101 102 103 |
# File 'lib/rubygems/spec_fetcher.rb', line 101 def fetch(*args) fetch_with_errors(*args).first end |
#fetch_spec(spec, source_uri) ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/rubygems/spec_fetcher.rb', line 105 def fetch_spec(spec, source_uri) source_uri = URI.parse source_uri if String === source_uri spec = spec - [nil, 'ruby', ''] spec_file_name = "#{spec.join '-'}.gemspec" uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}" cache_dir = cache_dir uri local_spec = File.join cache_dir, spec_file_name if File.exist? local_spec then spec = Gem.read_binary local_spec else uri.path << '.rz' spec = @fetcher.fetch_path uri spec = Gem.inflate spec if @update_cache then FileUtils.mkdir_p cache_dir open local_spec, 'wb' do |io| io.write spec end end end # TODO: Investigate setting Gem::Specification#loaded_from to a URI Marshal.load spec end |
#fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false) ⇒ Object
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.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/rubygems/spec_fetcher.rb', line 84 def fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false) specs_and_sources, errors = find_matching_with_errors(dependency, all, matching_platform, prerelease) ss = specs_and_sources.map do |spec_tuple, source_uri| [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri] end return [ss, errors] end |
#find_matching(*args) ⇒ Object
176 177 178 |
# File 'lib/rubygems/spec_fetcher.rb', line 176 def find_matching(*args) find_matching_with_errors(*args).first end |
#find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false) ⇒ Object
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.
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/rubygems/spec_fetcher.rb', line 142 def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false) found = {} rejected_specs = {} list(all, prerelease).each do |source_uri, specs| found[source_uri] = specs.select do |spec_name, version, spec_platform| if dependency.match?(spec_name, version) if matching_platform and !Gem::Platform.match(spec_platform) pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version)) pm.add_platform spec_platform false else true end end end end errors = rejected_specs.values specs_and_sources = [] found.each do |source_uri, specs| uri_str = source_uri.to_s specs_and_sources.concat(specs.map { |spec| [spec, uri_str] }) end [specs_and_sources, errors] end |
#list(all = false, prerelease = false) ⇒ Object
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.
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/rubygems/spec_fetcher.rb', line 213 def list(all = false, prerelease = false) # TODO: make type the only argument type = if all :all elsif prerelease :prerelease else :latest end list = {} file = FILES[type] cache = @caches[type] Gem.sources.each do |source_uri| source_uri = URI.parse source_uri unless cache.include? source_uri cache[source_uri] = load_specs source_uri, file end list[source_uri] = cache[source_uri] end if type == :all list.values.map do |gems| gems.reject! { |g| !g[1] || g[1].prerelease? } end end list end |
#load_specs(source_uri, file) ⇒ Object
Loads specs in file
, fetching from source_uri
if the on-disk cache is out of date.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/rubygems/spec_fetcher.rb', line 250 def load_specs(source_uri, file) file_name = "#{file}.#{Gem.marshal_version}" spec_path = source_uri + "#{file_name}.gz" cache_dir = cache_dir spec_path local_file = File.join(cache_dir, file_name) loaded = false if File.exist? local_file then begin spec_dump = @fetcher.fetch_path(spec_path, File.mtime(local_file)) rescue Gem::RemoteFetcher::FetchError => e alert_warning "Error fetching data: #{e.message}" end loaded = true if spec_dump spec_dump ||= Gem.read_binary local_file else spec_dump = @fetcher.fetch_path spec_path loaded = true end specs = begin Marshal.load spec_dump rescue ArgumentError spec_dump = @fetcher.fetch_path spec_path loaded = true Marshal.load spec_dump end if loaded and @update_cache then begin FileUtils.mkdir_p cache_dir open local_file, 'wb' do |io| io << spec_dump end rescue end end specs end |
#suggest_gems_from_name(gem_name) ⇒ Object
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.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/rubygems/spec_fetcher.rb', line 186 def suggest_gems_from_name gem_name gem_name = gem_name.downcase max = gem_name.size / 2 specs = list.values.flatten 1 matches = specs.map { |name, version, platform| next unless Gem::Platform.match platform distance = levenshtein_distance gem_name, name.downcase next if distance >= max return [name] if distance == 0 [name, distance] }.compact matches = matches.uniq.sort_by { |name, dist| dist } matches.first(5).map { |name, dist| name } end |