rubygems-update 3.5.11 → 3.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/Manifest.txt +1 -0
  4. data/bundler/CHANGELOG.md +28 -0
  5. data/bundler/lib/bundler/build_metadata.rb +2 -2
  6. data/bundler/lib/bundler/cli.rb +5 -8
  7. data/bundler/lib/bundler/compact_index_client/cache.rb +47 -81
  8. data/bundler/lib/bundler/compact_index_client/parser.rb +84 -0
  9. data/bundler/lib/bundler/compact_index_client.rb +51 -80
  10. data/bundler/lib/bundler/definition.rb +13 -3
  11. data/bundler/lib/bundler/endpoint_specification.rb +11 -0
  12. data/bundler/lib/bundler/fetcher/compact_index.rb +15 -24
  13. data/bundler/lib/bundler/installer/gem_installer.rb +0 -1
  14. data/bundler/lib/bundler/man/bundle-config.1 +1 -1
  15. data/bundler/lib/bundler/man/bundle-config.1.ronn +1 -1
  16. data/bundler/lib/bundler/rubygems_gem_installer.rb +4 -1
  17. data/bundler/lib/bundler/rubygems_integration.rb +14 -0
  18. data/bundler/lib/bundler/self_manager.rb +5 -0
  19. data/bundler/lib/bundler/settings.rb +12 -8
  20. data/bundler/lib/bundler/setup.rb +3 -0
  21. data/bundler/lib/bundler/shared_helpers.rb +2 -2
  22. data/bundler/lib/bundler/source/git.rb +14 -0
  23. data/bundler/lib/bundler/source/path.rb +0 -13
  24. data/bundler/lib/bundler/source/rubygems.rb +29 -13
  25. data/bundler/lib/bundler/version.rb +1 -1
  26. data/bundler/lib/bundler/yaml_serializer.rb +1 -8
  27. data/bundler/lib/bundler.rb +6 -1
  28. data/lib/rubygems/basic_specification.rb +27 -0
  29. data/lib/rubygems/commands/pristine_command.rb +3 -3
  30. data/lib/rubygems/specification.rb +11 -2
  31. data/lib/rubygems/specification_record.rb +0 -1
  32. data/lib/rubygems/stub_specification.rb +21 -0
  33. data/lib/rubygems/uninstaller.rb +13 -12
  34. data/lib/rubygems/yaml_serializer.rb +1 -8
  35. data/lib/rubygems.rb +1 -1
  36. data/rubygems-update.gemspec +1 -1
  37. metadata +4 -3
@@ -4,8 +4,6 @@ require_relative "base"
4
4
  require_relative "../worker"
5
5
 
6
6
  module Bundler
7
- autoload :CompactIndexClient, File.expand_path("../compact_index_client", __dir__)
8
-
9
7
  class Fetcher
10
8
  class CompactIndex < Base
11
9
  def self.compact_index_request(method_name)
@@ -36,15 +34,8 @@ module Bundler
36
34
 
37
35
  until remaining_gems.empty?
38
36
  log_specs { "Looking up gems #{remaining_gems.inspect}" }
39
-
40
- deps = begin
41
- parallel_compact_index_client.dependencies(remaining_gems)
42
- rescue TooManyRequestsError
43
- @bundle_worker&.stop
44
- @bundle_worker = nil # reset it. Not sure if necessary
45
- serial_compact_index_client.dependencies(remaining_gems)
46
- end
47
- next_gems = deps.flat_map {|d| d[3].flat_map(&:first) }.uniq
37
+ deps = fetch_gem_infos(remaining_gems).flatten(1)
38
+ next_gems = deps.flat_map {|d| d[CompactIndexClient::INFO_DEPS].flat_map(&:first) }.uniq
48
39
  deps.each {|dep| gem_info << dep }
49
40
  complete_gems.concat(deps.map(&:first)).uniq!
50
41
  remaining_gems = next_gems - complete_gems
@@ -61,7 +52,7 @@ module Bundler
61
52
  return nil
62
53
  end
63
54
  # Read info file checksums out of /versions, so we can know if gems are up to date
64
- compact_index_client.update_and_parse_checksums!
55
+ compact_index_client.available?
65
56
  rescue CompactIndexClient::Updater::MismatchedChecksumError => e
66
57
  Bundler.ui.debug(e.message)
67
58
  nil
@@ -81,20 +72,20 @@ module Bundler
81
72
  end
82
73
  end
83
74
 
84
- def parallel_compact_index_client
85
- compact_index_client.execution_mode = lambda do |inputs, &blk|
86
- func = lambda {|object, _index| blk.call(object) }
87
- worker = bundle_worker(func)
88
- inputs.each {|input| worker.enq(input) }
89
- inputs.map { worker.deq }
90
- end
91
-
92
- compact_index_client
75
+ def fetch_gem_infos(names)
76
+ in_parallel(names) {|name| compact_index_client.info(name) }
77
+ rescue TooManyRequestsError # rubygems.org is rate limiting us, slow down.
78
+ @bundle_worker&.stop
79
+ @bundle_worker = nil # reset it. Not sure if necessary
80
+ compact_index_client.reset!
81
+ names.map {|name| compact_index_client.info(name) }
93
82
  end
94
83
 
95
- def serial_compact_index_client
96
- compact_index_client.sequential_execution_mode!
97
- compact_index_client
84
+ def in_parallel(inputs, &blk)
85
+ func = lambda {|object, _index| blk.call(object) }
86
+ worker = bundle_worker(func)
87
+ inputs.each {|input| worker.enq(input) }
88
+ inputs.map { worker.deq }
98
89
  end
99
90
 
100
91
  def bundle_worker(func = nil)
@@ -54,7 +54,6 @@ module Bundler
54
54
  spec.source.install(
55
55
  spec,
56
56
  force: force,
57
- ensure_builtin_gems_cached: standalone,
58
57
  build_args: Array(spec_settings),
59
58
  previous_spec: previous_spec,
60
59
  )
@@ -307,7 +307,7 @@ Any \fB\.\fR characters in a host name are mapped to a double underscore (\fB__\
307
307
  .P
308
308
  This means that if you have a gem server named \fBmy\.gem\-host\.com\fR, you'll need to use the \fBBUNDLE_MY__GEM___HOST__COM\fR variable to configure credentials for it through ENV\.
309
309
  .SH "CONFIGURE BUNDLER DIRECTORIES"
310
- Bundler's home, config, cache and plugin directories are able to be configured through environment variables\. The default location for Bundler's home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
310
+ Bundler's home, cache and plugin directories and config file can be configured through environment variables\. The default location for Bundler's home directory is \fB~/\.bundle\fR, which all directories inherit from by default\. The following outlines the available environment variables and their default values
311
311
  .IP "" 4
312
312
  .nf
313
313
  BUNDLE_USER_HOME : $HOME/\.bundle
@@ -397,7 +397,7 @@ through ENV.
397
397
 
398
398
  ## CONFIGURE BUNDLER DIRECTORIES
399
399
 
400
- Bundler's home, config, cache and plugin directories are able to be configured
400
+ Bundler's home, cache and plugin directories and config file can be configured
401
401
  through environment variables. The default location for Bundler's home directory is
402
402
  `~/.bundle`, which all directories inherit from by default. The following
403
403
  outlines the available environment variables and their default values
@@ -29,7 +29,10 @@ module Bundler
29
29
  write_build_info_file
30
30
  run_post_build_hooks
31
31
 
32
- generate_bin
32
+ SharedHelpers.filesystem_access(bin_dir, :write) do
33
+ generate_bin
34
+ end
35
+
33
36
  generate_plugins
34
37
 
35
38
  write_spec
@@ -469,11 +469,25 @@ module Bundler
469
469
  end
470
470
 
471
471
  def all_specs
472
+ SharedHelpers.major_deprecation 2, "Bundler.rubygems.all_specs has been removed in favor of Bundler.rubygems.installed_specs"
473
+
472
474
  Gem::Specification.stubs.map do |stub|
473
475
  StubSpecification.from_stub(stub)
474
476
  end
475
477
  end
476
478
 
479
+ def installed_specs
480
+ Gem::Specification.stubs.reject(&:default_gem?).map do |stub|
481
+ StubSpecification.from_stub(stub)
482
+ end
483
+ end
484
+
485
+ def default_specs
486
+ Gem::Specification.default_stubs.map do |stub|
487
+ StubSpecification.from_stub(stub)
488
+ end
489
+ end
490
+
477
491
  def find_bundler(version)
478
492
  find_name("bundler").find {|s| s.version.to_s == version }
479
493
  end
@@ -92,6 +92,7 @@ module Bundler
92
92
  def autoswitching_applies?
93
93
  ENV["BUNDLER_VERSION"].nil? &&
94
94
  Bundler.rubygems.supports_bundler_trampolining? &&
95
+ ruby_can_restart_with_same_arguments? &&
95
96
  SharedHelpers.in_bundle? &&
96
97
  lockfile_version
97
98
  end
@@ -151,6 +152,10 @@ module Bundler
151
152
  !version.to_s.end_with?(".dev")
152
153
  end
153
154
 
155
+ def ruby_can_restart_with_same_arguments?
156
+ $PROGRAM_NAME != "-e"
157
+ end
158
+
154
159
  def updating?
155
160
  "update".start_with?(ARGV.first || " ") && ARGV[1..-1].any? {|a| a.start_with?("--bundler") }
156
161
  end
@@ -103,6 +103,7 @@ module Bundler
103
103
  def initialize(root = nil)
104
104
  @root = root
105
105
  @local_config = load_config(local_config_file)
106
+ @local_root = root || Pathname.new(".bundle").expand_path
106
107
 
107
108
  @env_config = ENV.to_h
108
109
  @env_config.select! {|key, _value| key.start_with?("BUNDLE_") }
@@ -142,7 +143,7 @@ module Bundler
142
143
  end
143
144
 
144
145
  def set_local(key, value)
145
- local_config_file || raise(GemfileNotFound, "Could not locate Gemfile")
146
+ local_config_file = @local_root.join("config")
146
147
 
147
148
  set_key(key, value, @local_config, local_config_file)
148
149
  end
@@ -491,6 +492,10 @@ module Bundler
491
492
  valid_file = file.exist? && !file.size.zero?
492
493
  return {} unless valid_file
493
494
  serializer_class.load(file.read).inject({}) do |config, (k, v)|
495
+ k = k.dup
496
+ k << "/" if /https?:/i.match?(k) && !k.end_with?("/", "__#{FALLBACK_TIMEOUT_URI_OPTION.upcase}")
497
+ k.gsub!(".", "__")
498
+
494
499
  unless k.start_with?("#")
495
500
  if k.include?("-")
496
501
  Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
@@ -518,26 +523,25 @@ module Bundler
518
523
  YAMLSerializer
519
524
  end
520
525
 
521
- PER_URI_OPTIONS = %w[
522
- fallback_timeout
523
- ].freeze
526
+ FALLBACK_TIMEOUT_URI_OPTION = "fallback_timeout"
524
527
 
525
528
  NORMALIZE_URI_OPTIONS_PATTERN =
526
529
  /
527
530
  \A
528
531
  (\w+\.)? # optional prefix key
529
532
  (https?.*?) # URI
530
- (\.#{Regexp.union(PER_URI_OPTIONS)})? # optional suffix key
533
+ (\.#{FALLBACK_TIMEOUT_URI_OPTION})? # optional suffix key
531
534
  \z
532
535
  /ix
533
536
 
534
537
  def self.key_for(key)
535
- key = normalize_uri(key).to_s if key.is_a?(String) && key.start_with?("http", "mirror.http")
536
- key = key_to_s(key).gsub(".", "__")
538
+ key = key_to_s(key)
539
+ key = normalize_uri(key) if key.start_with?("http", "mirror.http")
540
+ key = key.gsub(".", "__")
537
541
  key.gsub!("-", "___")
538
542
  key.upcase!
539
543
 
540
- key.prepend("BUNDLE_")
544
+ key.gsub(/\A([ #]*)/, '\1BUNDLE_')
541
545
  end
542
546
 
543
547
  # TODO: duplicates Rubygems#normalize_uri
@@ -5,6 +5,9 @@ require_relative "shared_helpers"
5
5
  if Bundler::SharedHelpers.in_bundle?
6
6
  require_relative "../bundler"
7
7
 
8
+ # autoswitch to locked Bundler version if available
9
+ Bundler.auto_switch
10
+
8
11
  # try to auto_install first before we get to the `Bundler.ui.silence`, so user knows what is happening
9
12
  Bundler.auto_install
10
13
 
@@ -4,14 +4,14 @@ require_relative "version"
4
4
  require_relative "rubygems_integration"
5
5
  require_relative "current_ruby"
6
6
 
7
+ autoload :Pathname, "pathname"
8
+
7
9
  module Bundler
8
10
  autoload :WINDOWS, File.expand_path("constants", __dir__)
9
11
  autoload :FREEBSD, File.expand_path("constants", __dir__)
10
12
  autoload :NULL, File.expand_path("constants", __dir__)
11
13
 
12
14
  module SharedHelpers
13
- autoload :Pathname, "pathname"
14
-
15
15
  def root
16
16
  gemfile = find_gemfile
17
17
  raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
@@ -32,6 +32,20 @@ module Bundler
32
32
  @local = false
33
33
  end
34
34
 
35
+ def remote!
36
+ return if @allow_remote
37
+
38
+ @local_specs = nil
39
+ @allow_remote = true
40
+ end
41
+
42
+ def cached!
43
+ return if @allow_cached
44
+
45
+ @local_specs = nil
46
+ @allow_cached = true
47
+ end
48
+
35
49
  def self.from_lock(options)
36
50
  new(options.merge("uri" => options.delete("remote")))
37
51
  end
@@ -18,9 +18,6 @@ module Bundler
18
18
  @options = options.dup
19
19
  @glob = options["glob"] || DEFAULT_GLOB
20
20
 
21
- @allow_cached = false
22
- @allow_remote = false
23
-
24
21
  @root_path = options["root_path"] || root
25
22
 
26
23
  if options["path"]
@@ -41,16 +38,6 @@ module Bundler
41
38
  @original_path = @path
42
39
  end
43
40
 
44
- def remote!
45
- @local_specs = nil
46
- @allow_remote = true
47
- end
48
-
49
- def cached!
50
- @local_specs = nil
51
- @allow_cached = true
52
- end
53
-
54
41
  def self.from_lock(options)
55
42
  new(options.merge("path" => options.delete("remote")))
56
43
  end
@@ -10,7 +10,7 @@ module Bundler
10
10
  # Ask for X gems per API request
11
11
  API_REQUEST_SIZE = 50
12
12
 
13
- attr_accessor :remotes
13
+ attr_reader :remotes
14
14
 
15
15
  def initialize(options = {})
16
16
  @options = options
@@ -20,6 +20,7 @@ module Bundler
20
20
  @allow_cached = false
21
21
  @allow_local = options["allow_local"] || false
22
22
  @checksum_store = Checksum::Store.new
23
+ @original_remotes = nil
23
24
 
24
25
  Array(options["remotes"]).reverse_each {|r| add_remote(r) }
25
26
  end
@@ -94,10 +95,15 @@ module Bundler
94
95
  new(options)
95
96
  end
96
97
 
98
+ def remotes=(new_remotes)
99
+ @original_remotes = @remotes
100
+ @remotes = new_remotes
101
+ end
102
+
97
103
  def to_lock
98
104
  out = String.new("GEM\n")
99
- remotes.reverse_each do |remote|
100
- out << " remote: #{remove_auth remote}\n"
105
+ lockfile_remotes.reverse_each do |remote|
106
+ out << " remote: #{remote}\n"
101
107
  end
102
108
  out << " specs:\n"
103
109
  end
@@ -136,20 +142,17 @@ module Bundler
136
142
  index = @allow_remote ? remote_specs.dup : Index.new
137
143
  index.merge!(cached_specs) if @allow_cached
138
144
  index.merge!(installed_specs) if @allow_local
145
+
146
+ # complete with default specs, only if not already available in the
147
+ # index through remote, cached, or installed specs
148
+ index.use(default_specs) if @allow_local
149
+
139
150
  index
140
151
  end
141
152
  end
142
153
 
143
154
  def install(spec, options = {})
144
- force = options[:force]
145
- ensure_builtin_gems_cached = options[:ensure_builtin_gems_cached]
146
-
147
- if ensure_builtin_gems_cached && spec.default_gem? && !cached_path(spec)
148
- cached_built_in_gem(spec) unless spec.remote
149
- force = true
150
- end
151
-
152
- if installed?(spec) && !force
155
+ if (spec.default_gem? && !cached_built_in_gem(spec)) || (installed?(spec) && !options[:force])
153
156
  print_using_message "Using #{version_message(spec, options[:previous_spec])}"
154
157
  return nil # no post-install message
155
158
  end
@@ -362,7 +365,7 @@ module Bundler
362
365
 
363
366
  def installed_specs
364
367
  @installed_specs ||= Index.build do |idx|
365
- Bundler.rubygems.all_specs.reverse_each do |spec|
368
+ Bundler.rubygems.installed_specs.reverse_each do |spec|
366
369
  spec.source = self
367
370
  if Bundler.rubygems.spec_missing_extensions?(spec, false)
368
371
  Bundler.ui.debug "Source #{self} is ignoring #{spec} because it is missing extensions"
@@ -373,6 +376,15 @@ module Bundler
373
376
  end
374
377
  end
375
378
 
379
+ def default_specs
380
+ @default_specs ||= Index.build do |idx|
381
+ Bundler.rubygems.default_specs.each do |spec|
382
+ spec.source = self
383
+ idx << spec
384
+ end
385
+ end
386
+ end
387
+
376
388
  def cached_specs
377
389
  @cached_specs ||= begin
378
390
  idx = Index.new
@@ -457,6 +469,10 @@ module Bundler
457
469
 
458
470
  private
459
471
 
472
+ def lockfile_remotes
473
+ @original_remotes || credless_remotes
474
+ end
475
+
460
476
  # Checks if the requested spec exists in the global cache. If it does,
461
477
  # we copy it to the download path, and if it does not, we download it.
462
478
  #
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  module Bundler
4
- VERSION = "2.5.11".freeze
4
+ VERSION = "2.5.13".freeze
5
5
 
6
6
  def self.bundler_major_version
7
7
  @bundler_major_version ||= VERSION.split(".").first.to_i
@@ -60,7 +60,6 @@ module Bundler
60
60
  indent, key, quote, val = match.captures
61
61
  val = strip_comment(val)
62
62
 
63
- convert_to_backward_compatible_key!(key)
64
63
  depth = indent.size / 2
65
64
  if quote.empty? && val.empty?
66
65
  new_hash = {}
@@ -92,14 +91,8 @@ module Bundler
92
91
  end
93
92
  end
94
93
 
95
- # for settings' keys
96
- def convert_to_backward_compatible_key!(key)
97
- key << "/" if /https?:/i.match?(key) && !%r{/\Z}.match?(key)
98
- key.gsub!(".", "__")
99
- end
100
-
101
94
  class << self
102
- private :dump_hash, :convert_to_backward_compatible_key!
95
+ private :dump_hash
103
96
  end
104
97
  end
105
98
  end
@@ -42,6 +42,7 @@ module Bundler
42
42
  autoload :Checksum, File.expand_path("bundler/checksum", __dir__)
43
43
  autoload :CLI, File.expand_path("bundler/cli", __dir__)
44
44
  autoload :CIDetector, File.expand_path("bundler/ci_detector", __dir__)
45
+ autoload :CompactIndexClient, File.expand_path("bundler/compact_index_client", __dir__)
45
46
  autoload :Definition, File.expand_path("bundler/definition", __dir__)
46
47
  autoload :Dependency, File.expand_path("bundler/dependency", __dir__)
47
48
  autoload :Deprecate, File.expand_path("bundler/deprecate", __dir__)
@@ -166,6 +167,10 @@ module Bundler
166
167
  end
167
168
  end
168
169
 
170
+ def auto_switch
171
+ self_manager.restart_with_locked_bundler_if_needed
172
+ end
173
+
169
174
  # Automatically install dependencies if Bundler.settings[:auto_install] exists.
170
175
  # This is set through config cmd `bundle config set --global auto_install 1`.
171
176
  #
@@ -356,7 +361,7 @@ module Bundler
356
361
  def settings
357
362
  @settings ||= Settings.new(app_config_path)
358
363
  rescue GemfileNotFound
359
- @settings = Settings.new(Pathname.new(".bundle").expand_path)
364
+ @settings = Settings.new
360
365
  end
361
366
 
362
367
  # @return [Hash] Environment present before Bundler was activated
@@ -98,6 +98,20 @@ class Gem::BasicSpecification
98
98
  File.dirname(loaded_from) == Gem.default_specifications_dir
99
99
  end
100
100
 
101
+ ##
102
+ # Regular gems take precedence over default gems
103
+
104
+ def default_gem_priority
105
+ default_gem? ? 1 : -1
106
+ end
107
+
108
+ ##
109
+ # Gems higher up in +gem_path+ take precedence
110
+
111
+ def base_dir_priority(gem_path)
112
+ gem_path.index(base_dir) || gem_path.size
113
+ end
114
+
101
115
  ##
102
116
  # Returns full path to the directory where gem's extensions are installed.
103
117
 
@@ -143,6 +157,19 @@ class Gem::BasicSpecification
143
157
  end
144
158
  end
145
159
 
160
+ ##
161
+ # Returns the full name of this Gem (see `Gem::BasicSpecification#full_name`).
162
+ # Information about where the gem is installed is also included if not
163
+ # installed in the default GEM_HOME.
164
+
165
+ def full_name_with_location
166
+ if base_dir != Gem.dir
167
+ "#{full_name} in #{base_dir}"
168
+ else
169
+ full_name
170
+ end
171
+ end
172
+
146
173
  ##
147
174
  # Full paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
148
175
  # activated.
@@ -148,7 +148,7 @@ extensions will be restored.
148
148
  end
149
149
 
150
150
  unless spec.extensions.empty? || options[:extensions] || options[:only_executables] || options[:only_plugins]
151
- say "Skipped #{spec.full_name}, it needs to compile an extension"
151
+ say "Skipped #{spec.full_name_with_location}, it needs to compile an extension"
152
152
  next
153
153
  end
154
154
 
@@ -157,7 +157,7 @@ extensions will be restored.
157
157
  unless File.exist?(gem) || options[:only_executables] || options[:only_plugins]
158
158
  require_relative "../remote_fetcher"
159
159
 
160
- say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
160
+ say "Cached gem for #{spec.full_name_with_location} not found, attempting to fetch..."
161
161
 
162
162
  dep = Gem::Dependency.new spec.name, spec.version
163
163
  found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
@@ -201,7 +201,7 @@ extensions will be restored.
201
201
  installer.install
202
202
  end
203
203
 
204
- say "Restored #{spec.full_name}"
204
+ say "Restored #{spec.full_name_with_location}"
205
205
  end
206
206
  end
207
207
  end
@@ -771,6 +771,11 @@ class Gem::Specification < Gem::BasicSpecification
771
771
  end
772
772
  private_class_method :clear_load_cache
773
773
 
774
+ def self.gem_path # :nodoc:
775
+ Gem.path
776
+ end
777
+ private_class_method :gem_path
778
+
774
779
  def self.each_gemspec(dirs) # :nodoc:
775
780
  dirs.each do |dir|
776
781
  Gem::Util.glob_files_in_dir("*.gemspec", dir).each do |path|
@@ -830,7 +835,11 @@ class Gem::Specification < Gem::BasicSpecification
830
835
  next names if names.nonzero?
831
836
  versions = b.version <=> a.version
832
837
  next versions if versions.nonzero?
833
- Gem::Platform.sort_priority(b.platform)
838
+ platforms = Gem::Platform.sort_priority(b.platform) <=> Gem::Platform.sort_priority(a.platform)
839
+ next platforms if platforms.nonzero?
840
+ default_gem = a.default_gem_priority <=> b.default_gem_priority
841
+ next default_gem if default_gem.nonzero?
842
+ a.base_dir_priority(gem_path) <=> b.base_dir_priority(gem_path)
834
843
  end
835
844
  end
836
845
 
@@ -906,7 +915,7 @@ class Gem::Specification < Gem::BasicSpecification
906
915
  # Return the directories that Specification uses to find specs.
907
916
 
908
917
  def self.dirs
909
- @@dirs ||= Gem::SpecificationRecord.dirs_from(Gem.path)
918
+ @@dirs ||= Gem::SpecificationRecord.dirs_from(gem_path)
910
919
  end
911
920
 
912
921
  ##
@@ -68,7 +68,6 @@ module Gem
68
68
  installed_stubs = installed_stubs(pattern)
69
69
  installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform
70
70
  stubs = installed_stubs + Gem::Specification.default_stubs(pattern)
71
- stubs = stubs.uniq(&:full_name)
72
71
  Gem::Specification._resort!(stubs)
73
72
  stubs
74
73
  end
@@ -210,4 +210,25 @@ class Gem::StubSpecification < Gem::BasicSpecification
210
210
  def stubbed?
211
211
  data.is_a? StubLine
212
212
  end
213
+
214
+ def ==(other) # :nodoc:
215
+ self.class === other &&
216
+ name == other.name &&
217
+ version == other.version &&
218
+ platform == other.platform
219
+ end
220
+
221
+ alias_method :eql?, :== # :nodoc:
222
+
223
+ def hash # :nodoc:
224
+ name.hash ^ version.hash ^ platform.hash
225
+ end
226
+
227
+ def <=>(other) # :nodoc:
228
+ sort_obj <=> other.sort_obj
229
+ end
230
+
231
+ def sort_obj # :nodoc:
232
+ [name, version, Gem::Platform.sort_priority(platform)]
233
+ end
213
234
  end
@@ -86,11 +86,7 @@ class Gem::Uninstaller
86
86
 
87
87
  list = []
88
88
 
89
- dirs =
90
- Gem::Specification.dirs +
91
- [Gem.default_specifications_dir]
92
-
93
- Gem::Specification.each_spec dirs do |spec|
89
+ specification_record.stubs.each do |spec|
94
90
  next unless dependency.matches_spec? spec
95
91
 
96
92
  list << spec
@@ -102,7 +98,7 @@ class Gem::Uninstaller
102
98
 
103
99
  default_specs, list = list.partition(&:default_gem?)
104
100
  warn_cannot_uninstall_default_gems(default_specs - list)
105
- @default_specs_matching_uninstall_params = default_specs
101
+ @default_specs_matching_uninstall_params = default_specs.map(&:to_spec)
106
102
 
107
103
  list, other_repo_specs = list.partition do |spec|
108
104
  @gem_home == spec.base_dir ||
@@ -126,7 +122,7 @@ class Gem::Uninstaller
126
122
  remove_all list
127
123
 
128
124
  elsif list.size > 1
129
- gem_names = list.map(&:full_name)
125
+ gem_names = list.map(&:full_name_with_location)
130
126
  gem_names << "All versions"
131
127
 
132
128
  say
@@ -147,7 +143,9 @@ class Gem::Uninstaller
147
143
  ##
148
144
  # Uninstalls gem +spec+
149
145
 
150
- def uninstall_gem(spec)
146
+ def uninstall_gem(stub)
147
+ spec = stub.to_spec
148
+
151
149
  @spec = spec
152
150
 
153
151
  unless dependencies_ok? spec
@@ -165,6 +163,8 @@ class Gem::Uninstaller
165
163
  remove_plugins @spec
166
164
  remove @spec
167
165
 
166
+ specification_record.remove_spec(stub)
167
+
168
168
  regenerate_plugins
169
169
 
170
170
  Gem.post_uninstall_hooks.each do |hook|
@@ -178,7 +178,7 @@ class Gem::Uninstaller
178
178
  # Removes installed executables and batch files (windows only) for +spec+.
179
179
 
180
180
  def remove_executables(spec)
181
- return if spec.executables.empty?
181
+ return if spec.executables.empty? || default_spec_matches?(spec)
182
182
 
183
183
  executables = spec.executables.clone
184
184
 
@@ -275,8 +275,6 @@ class Gem::Uninstaller
275
275
 
276
276
  safe_delete { FileUtils.rm_r gemspec }
277
277
  announce_deletion_of(spec)
278
-
279
- Gem::Specification.reset
280
278
  end
281
279
 
282
280
  ##
@@ -292,7 +290,6 @@ class Gem::Uninstaller
292
290
  # Regenerates plugin wrappers after removal.
293
291
 
294
292
  def regenerate_plugins
295
- specification_record = @install_dir ? Gem::SpecificationRecord.from_path(@install_dir) : Gem::Specification.specification_record
296
293
  latest = specification_record.latest_spec_for(@spec.name)
297
294
  return if latest.nil?
298
295
 
@@ -381,6 +378,10 @@ class Gem::Uninstaller
381
378
 
382
379
  private
383
380
 
381
+ def specification_record
382
+ @specification_record ||= @install_dir ? Gem::SpecificationRecord.from_path(@install_dir) : Gem::Specification.specification_record
383
+ end
384
+
384
385
  def announce_deletion_of(spec)
385
386
  name = spec.full_name
386
387
  say "Successfully uninstalled #{name}"