summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-09-14 03:30:02 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-09-14 03:30:02 +0000
commit4de117a61517e839f2c45eaf45d56fc243d6d5b2 (patch)
tree7cb5af7a7eb513e5dddf5e343746b1611e628387
parente548c09d429a5136285ea81aed418685359ed124 (diff)
* lib/rubygems: Update to RubyGems 2.4.1 master(713ab65)
Complete history at: https://github.com/rubygems/rubygems/blob/master/History.txt#L3-L216 * test/rubygems: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47582 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog8
-rw-r--r--lib/rubygems.rb48
-rw-r--r--lib/rubygems/available_set.rb2
-rw-r--r--lib/rubygems/basic_specification.rb30
-rw-r--r--lib/rubygems/command.rb5
-rw-r--r--lib/rubygems/command_manager.rb1
-rw-r--r--lib/rubygems/commands/cert_command.rb24
-rw-r--r--lib/rubygems/commands/cleanup_command.rb4
-rw-r--r--lib/rubygems/commands/contents_command.rb25
-rw-r--r--lib/rubygems/commands/dependency_command.rb4
-rw-r--r--lib/rubygems/commands/environment_command.rb7
-rw-r--r--lib/rubygems/commands/help_command.rb219
-rw-r--r--lib/rubygems/commands/install_command.rb58
-rw-r--r--lib/rubygems/commands/list_command.rb4
-rw-r--r--lib/rubygems/commands/open_command.rb74
-rw-r--r--lib/rubygems/commands/owner_command.rb4
-rw-r--r--lib/rubygems/commands/search_command.rb10
-rw-r--r--lib/rubygems/commands/setup_command.rb2
-rw-r--r--lib/rubygems/commands/uninstall_command.rb14
-rw-r--r--lib/rubygems/commands/update_command.rb23
-rw-r--r--lib/rubygems/commands/yank_command.rb21
-rw-r--r--lib/rubygems/config_file.rb15
-rw-r--r--lib/rubygems/core_ext/kernel_gem.rb9
-rwxr-xr-xlib/rubygems/core_ext/kernel_require.rb34
-rw-r--r--lib/rubygems/defaults.rb24
-rw-r--r--lib/rubygems/dependency.rb57
-rw-r--r--lib/rubygems/dependency_installer.rb59
-rw-r--r--lib/rubygems/doctor.rb2
-rw-r--r--lib/rubygems/errors.rb30
-rw-r--r--lib/rubygems/exceptions.rb24
-rw-r--r--lib/rubygems/ext/builder.rb2
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb22
-rw-r--r--lib/rubygems/gemcutter_utilities.rb2
-rw-r--r--lib/rubygems/install_update_options.rb17
-rw-r--r--lib/rubygems/installer.rb75
-rw-r--r--lib/rubygems/installer_test_case.rb2
-rw-r--r--lib/rubygems/local_remote_options.rb4
-rw-r--r--lib/rubygems/name_tuple.rb6
-rw-r--r--lib/rubygems/package.rb41
-rw-r--r--lib/rubygems/package/file_source.rb33
-rw-r--r--lib/rubygems/package/io_source.rb45
-rw-r--r--lib/rubygems/package/old.rb8
-rw-r--r--lib/rubygems/package/source.rb3
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb2
-rw-r--r--lib/rubygems/platform.rb3
-rw-r--r--lib/rubygems/rdoc.rb3
-rw-r--r--lib/rubygems/remote_fetcher.rb71
-rw-r--r--lib/rubygems/request.rb176
-rw-r--r--lib/rubygems/request/connection_pools.rb79
-rw-r--r--lib/rubygems/request/http_pool.rb38
-rw-r--r--lib/rubygems/request/https_pool.rb10
-rw-r--r--lib/rubygems/request_set.rb114
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb319
-rw-r--r--lib/rubygems/request_set/lockfile.rb101
-rw-r--r--lib/rubygems/requirement.rb3
-rw-r--r--lib/rubygems/resolver.rb65
-rw-r--r--lib/rubygems/resolver/activation_request.rb7
-rw-r--r--lib/rubygems/resolver/api_set.rb14
-rw-r--r--lib/rubygems/resolver/api_specification.rb6
-rw-r--r--lib/rubygems/resolver/best_set.rb28
-rw-r--r--lib/rubygems/resolver/composed_set.rb16
-rw-r--r--lib/rubygems/resolver/conflict.rb52
-rw-r--r--lib/rubygems/resolver/dependency_request.rb21
-rw-r--r--lib/rubygems/resolver/git_set.rb2
-rw-r--r--lib/rubygems/resolver/git_specification.rb26
-rw-r--r--lib/rubygems/resolver/index_set.rb6
-rw-r--r--lib/rubygems/resolver/installed_specification.rb20
-rw-r--r--lib/rubygems/resolver/installer_set.rb94
-rw-r--r--lib/rubygems/resolver/local_specification.rb25
-rw-r--r--lib/rubygems/resolver/lock_set.rb22
-rw-r--r--lib/rubygems/resolver/lock_specification.rb28
-rw-r--r--lib/rubygems/resolver/set.rb14
-rw-r--r--lib/rubygems/resolver/spec_specification.rb2
-rw-r--r--lib/rubygems/resolver/specification.rb25
-rw-r--r--lib/rubygems/resolver/vendor_set.rb4
-rw-r--r--lib/rubygems/resolver/vendor_specification.rb2
-rw-r--r--lib/rubygems/security/policy.rb1
-rw-r--r--lib/rubygems/server.rb37
-rw-r--r--lib/rubygems/source.rb12
-rw-r--r--lib/rubygems/source/git.rb6
-rw-r--r--lib/rubygems/source/installed.rb7
-rw-r--r--lib/rubygems/source/specific_file.rb7
-rw-r--r--lib/rubygems/spec_fetcher.rb19
-rw-r--r--lib/rubygems/specification.rb188
-rw-r--r--lib/rubygems/stub_specification.rb14
-rw-r--r--lib/rubygems/test_case.rb18
-rw-r--r--lib/rubygems/test_utilities.rb2
-rw-r--r--lib/rubygems/text.rb20
-rw-r--r--lib/rubygems/uninstaller.rb5
-rw-r--r--lib/rubygems/user_interaction.rb8
-rw-r--r--lib/rubygems/version.rb6
-rw-r--r--test/rubygems/test_gem.rb182
-rw-r--r--test/rubygems/test_gem_available_set.rb19
-rw-r--r--test/rubygems/test_gem_command.rb55
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb49
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb7
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb107
-rw-r--r--test/rubygems/test_gem_commands_open_command.rb46
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb9
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb36
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb63
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb4
-rw-r--r--test/rubygems/test_gem_config_file.rb26
-rw-r--r--test/rubygems/test_gem_dependency.rb138
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb238
-rw-r--r--test/rubygems/test_gem_ext_builder.rb13
-rw-r--r--test/rubygems/test_gem_ext_ext_conf_builder.rb4
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb9
-rw-r--r--test/rubygems/test_gem_impossible_dependencies_error.rb32
-rw-r--r--test/rubygems/test_gem_install_update_options.rb34
-rw-r--r--test/rubygems/test_gem_installer.rb83
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb13
-rw-r--r--test/rubygems/test_gem_name_tuple.rb7
-rw-r--r--test/rubygems/test_gem_package.rb19
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb66
-rw-r--r--test/rubygems/test_gem_request.rb136
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb120
-rw-r--r--test/rubygems/test_gem_request_set.rb246
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb182
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb383
-rw-r--r--test/rubygems/test_gem_requirement.rb13
-rw-r--r--test/rubygems/test_gem_resolver.rb132
-rw-r--r--test/rubygems/test_gem_resolver_activation_request.rb10
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb4
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb40
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb57
-rw-r--r--test/rubygems/test_gem_resolver_composed_set.rb27
-rw-r--r--test/rubygems/test_gem_resolver_conflict.rb32
-rw-r--r--test/rubygems/test_gem_resolver_dependency_request.rb64
-rw-r--r--test/rubygems/test_gem_resolver_git_set.rb26
-rw-r--r--test/rubygems/test_gem_resolver_git_specification.rb12
-rw-r--r--test/rubygems/test_gem_resolver_index_set.rb30
-rw-r--r--test/rubygems/test_gem_resolver_installer_set.rb155
-rw-r--r--test/rubygems/test_gem_resolver_lock_set.rb18
-rw-r--r--test/rubygems/test_gem_resolver_lock_specification.rb13
-rw-r--r--test/rubygems/test_gem_resolver_specification.rb32
-rw-r--r--test/rubygems/test_gem_resolver_vendor_set.rb18
-rw-r--r--test/rubygems/test_gem_security_policy.rb4
-rw-r--r--test/rubygems/test_gem_server.rb73
-rw-r--r--test/rubygems/test_gem_source.rb18
-rw-r--r--test/rubygems/test_gem_source_git.rb27
-rw-r--r--test/rubygems/test_gem_source_installed.rb8
-rw-r--r--test/rubygems/test_gem_source_lock.rb4
-rw-r--r--test/rubygems/test_gem_source_specific_file.rb4
-rw-r--r--test/rubygems/test_gem_source_vendor.rb4
-rw-r--r--test/rubygems/test_gem_specification.rb152
-rw-r--r--test/rubygems/test_gem_stub_specification.rb94
-rw-r--r--test/rubygems/test_gem_uninstaller.rb23
-rw-r--r--test/rubygems/test_gem_unsatisfiable_dependency_error.rb32
-rw-r--r--test/rubygems/test_kernel.rb6
-rw-r--r--test/rubygems/test_require.rb60
153 files changed, 5394 insertions, 975 deletions
diff --git a/ChangeLog b/ChangeLog
index 583e864b5d..f1a8fbf052 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Sun Sep 14 12:29:02 2014 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
+
+ * lib/rubygems: Update to RubyGems 2.4.1 master(713ab65)
+ Complete history at:
+ https://github.com/rubygems/rubygems/blob/master/History.txt#L3-L216
+
+ * test/rubygems: ditto.
+
Sun Sep 14 11:03:24 2014 Aaron Patterson <aaron@tenderlovemaking.com>
* ext/psych/lib/psych.rb: update version
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 9861362b97..453520665a 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -6,9 +6,10 @@
#++
require 'rbconfig'
+require 'thread'
module Gem
- VERSION = '2.2.2'
+ VERSION = '2.4.1'
end
# Must be first since it unloads the prelude from 1.9.2
@@ -56,8 +57,8 @@ require 'rubygems/errors'
# RubyGems defaults are stored in rubygems/defaults.rb. If you're packaging
# RubyGems or implementing Ruby you can change RubyGems' defaults.
#
-# For RubyGems packagers, provide lib/rubygems/operating_system.rb and
-# override any defaults from lib/rubygems/defaults.rb.
+# For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb
+# and override any defaults from lib/rubygems/defaults.rb.
#
# For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and
# override any defaults from lib/rubygems/defaults.rb.
@@ -83,7 +84,7 @@ require 'rubygems/errors'
# * Chad Fowler -- chad(at)chadfowler.com
# * David Black -- dblack(at)wobblini.net
# * Paul Brannan -- paul(at)atdesk.com
-# * Jim Weirich -- jim(at)weirichhouse.org
+# * Jim Weirich -- jim(at)weirichhouse.org
#
# Contributors:
#
@@ -156,6 +157,7 @@ module Gem
@configuration = nil
@loaded_specs = {}
+ LOADED_SPECS_MUTEX = Mutex.new
@path_to_default_spec_map = {}
@platforms = []
@ruby = nil
@@ -298,7 +300,7 @@ module Gem
end
##
- # The path the the data directory specified by the gem name. If the
+ # The path to the data directory specified by the gem name. If the
# package is not available as a gem, return nil.
def self.datadir(gem_name)
@@ -544,9 +546,9 @@ module Gem
# Fetching: minitest-3.0.1.gem (100%)
# => [#<Gem::Specification:0x1013b4528 @name="minitest", ...>]
- def self.install name, version = Gem::Requirement.default, **options
+ def self.install name, version = Gem::Requirement.default, *options
require "rubygems/dependency_installer"
- inst = Gem::DependencyInstaller.new(**options)
+ inst = Gem::DependencyInstaller.new(*options)
inst.install name, version
inst.installed_gems
end
@@ -995,19 +997,31 @@ module Gem
end
##
- # Looks for gem dependency files (gem.deps.rb, Gemfile, Isolate) from the
- # current directory up and activates the gems in the first file found.
+ # Looks for a gem dependency file at +path+ and activates the gems in the
+ # file if found. If the file is not found an ArgumentError is raised.
+ #
+ # If +path+ is not given the RUBYGEMS_GEMDEPS environment variable is used,
+ # but if no file is found no exception is raised.
+ #
+ # If '-' is given for +path+ RubyGems searches up from the current working
+ # directory for gem dependency files (gem.deps.rb, Gemfile, Isolate) and
+ # activates the gems in the first one found.
#
# You can run this automatically when rubygems starts. To enable, set
# the <code>RUBYGEMS_GEMDEPS</code> environment variable to either the path
- # of your Gemfile or "-" to auto-discover in parent directories.
+ # of your gem dependencies file or "-" to auto-discover in parent
+ # directories.
#
# NOTE: Enabling automatic discovery on multiuser systems can lead to
# execution of arbitrary code when used from directories outside your
# control.
- def self.use_gemdeps
- return unless path = ENV['RUBYGEMS_GEMDEPS']
+ def self.use_gemdeps path = nil
+ raise_exception = path
+
+ path ||= ENV['RUBYGEMS_GEMDEPS']
+ return unless path
+
path = path.dup
if path == "-" then
@@ -1025,7 +1039,11 @@ module Gem
path.untaint
- return unless File.file? path
+ unless File.file? path then
+ return unless raise_exception
+
+ raise ArgumentError, "Unable to find gem dependencies file at #{path}"
+ end
rs = Gem::RequestSet.new
rs.load_gemdeps path
@@ -1035,6 +1053,10 @@ module Gem
sp.activate
sp
end
+ rescue Gem::LoadError, Gem::UnsatisfiableDependencyError => e
+ warn e.message
+ warn "You may need to `gem install -g` to install missing gems"
+ warn ""
end
class << self
diff --git a/lib/rubygems/available_set.rb b/lib/rubygems/available_set.rb
index fabdd6e79d..dae254b385 100644
--- a/lib/rubygems/available_set.rb
+++ b/lib/rubygems/available_set.rb
@@ -126,7 +126,7 @@ class Gem::AvailableSet
dep = req.dependency
match = @set.find_all do |t|
- dep.matches_spec? t.spec
+ dep.match? t.spec
end
match.map do |t|
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index 470a6ebc8b..f9eb193fb4 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -15,6 +15,11 @@ class Gem::BasicSpecification
attr_writer :extension_dir # :nodoc:
##
+ # Is this specification ignored for activation purposes?
+
+ attr_writer :ignored # :nodoc:
+
+ ##
# The path this gemspec was loaded from. This attribute is not persisted.
attr_reader :loaded_from
@@ -53,7 +58,16 @@ class Gem::BasicSpecification
# Return true if this spec can require +file+.
def contains_requirable_file? file
- build_extensions
+ if instance_variable_defined?(:@ignored) or
+ instance_variable_defined?('@ignored') then
+ return false
+ elsif missing_extensions? then
+ @ignored = true
+
+ warn "Ignoring #{full_name} because its extensions are not built. " +
+ "Try: gem pristine #{full_name}"
+ return false
+ end
suffixes = Gem.suffixes
@@ -120,11 +134,11 @@ class Gem::BasicSpecification
# activated.
def full_require_paths
- full_paths = @require_paths.map do |path|
+ full_paths = raw_require_paths.map do |path|
File.join full_gem_path, path
end
- full_paths.unshift extension_dir unless @extensions.empty?
+ full_paths.unshift extension_dir unless @extensions.nil? || @extensions.empty?
full_paths
end
@@ -176,7 +190,7 @@ class Gem::BasicSpecification
end
def raw_require_paths # :nodoc:
- @require_paths
+ Array(@require_paths)
end
##
@@ -197,13 +211,9 @@ class Gem::BasicSpecification
# spec.require_path = '.'
def require_paths
- return @require_paths if @extensions.empty?
-
- relative_extension_dir =
- File.join '..', '..', 'extensions', Gem::Platform.local.to_s,
- Gem.extension_api_version, full_name
+ return raw_require_paths if @extensions.nil? || @extensions.empty?
- [relative_extension_dir].concat @require_paths
+ [extension_dir].concat raw_require_paths
end
##
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index cba79b9196..0c6abec56c 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -148,6 +148,8 @@ class Gem::Command
##
# Display to the user that a gem couldn't be found and reasons why
+ #--
+ # TODO: replace +domain+ with a parameter to suppress suggestions
def show_lookup_failure(gem_name, version, errors, domain)
if errors and !errors.empty?
@@ -557,7 +559,8 @@ basic help message containing pointers to more information.
Further help:
gem help commands list all 'gem' commands
gem help examples show some examples of usage
- gem help platforms show information about platforms
+ gem help gem_dependencies gem dependencies file guide
+ gem help platforms gem platforms guide
gem help <COMMAND> show help on COMMAND
(e.g. 'gem help install')
gem server present a web page at
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 54636bf996..53d18c29cc 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -48,6 +48,7 @@ class Gem::CommandManager
:list,
:lock,
:mirror,
+ :open,
:outdated,
:owner,
:pristine,
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index e417193bca..a920e7fcc3 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -129,23 +129,21 @@ class Gem::Commands::CertCommand < Gem::Command
end
def build_key # :nodoc:
- if options[:key] then
- options[:key]
- else
- passphrase = ask_for_password 'Passphrase for your Private Key:'
- say "\n"
+ return options[:key] if options[:key]
- passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:'
- say "\n"
+ passphrase = ask_for_password 'Passphrase for your Private Key:'
+ say "\n"
- raise Gem::CommandLineError,
- "Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation
+ passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:'
+ say "\n"
- key = Gem::Security.create_key
- key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase
+ raise Gem::CommandLineError,
+ "Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation
- return key, key_path
- end
+ key = Gem::Security.create_key
+ key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase
+
+ return key, key_path
end
def certificates_matching filter
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index c8f0082bfb..69975640fe 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -67,10 +67,10 @@ If no gems are named all gems in GEM_HOME are cleaned.
say "Clean Up Complete"
- if Gem.configuration.really_verbose then
+ verbose do
skipped = @default_gems.map { |spec| spec.full_name }
- say "Skipped default gems: #{skipped.join ', '}"
+ "Skipped default gems: #{skipped.join ', '}"
end
end
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index 603f1d072a..15657f31a2 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -8,7 +8,8 @@ class Gem::Commands::ContentsCommand < Gem::Command
def initialize
super 'contents', 'Display the contents of the installed gems',
- :specdirs => [], :lib_only => false, :prefix => true
+ :specdirs => [], :lib_only => false, :prefix => true,
+ :show_install_dir => false
add_version_option
@@ -32,6 +33,11 @@ class Gem::Commands::ContentsCommand < Gem::Command
options[:prefix] = prefix
end
+ add_option( '--[no-]show-install-dir',
+ 'Show only the gem install dir') do |show, options|
+ options[:show_install_dir] = show
+ end
+
@path_kind = nil
@spec_dirs = nil
@version = nil
@@ -65,7 +71,12 @@ prefix or only the files that are requireable.
names = gem_names
names.each do |name|
- found = gem_contents name
+ found =
+ if options[:show_install_dir] then
+ gem_install_dir name
+ else
+ gem_contents name
+ end
terminate_interaction 1 unless found or names.length > 1
end
@@ -115,6 +126,16 @@ prefix or only the files that are requireable.
true
end
+ def gem_install_dir name
+ spec = spec_for name
+
+ return false unless spec
+
+ say spec.gem_dir
+
+ true
+ end
+
def gem_names # :nodoc:
if options[:all] then
Gem::Specification.map(&:name)
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index c5d6dd7d70..4a54a3e385 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -31,7 +31,7 @@ class Gem::Commands::DependencyCommand < Gem::Command
end
def arguments # :nodoc:
- "GEMNAME name of gem to show dependencies for"
+ "REGEXP show dependencies for gems whose names start with REGEXP"
end
def defaults_str # :nodoc:
@@ -50,7 +50,7 @@ use with other commands.
end
def usage # :nodoc:
- "#{program_name} GEMNAME"
+ "#{program_name} REGEXP"
end
def fetch_remote_specs dependency # :nodoc:
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index d32d12b757..067d0b1607 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -28,8 +28,9 @@ The RubyGems environment can be controlled through command line arguments,
gemrc files, environment variables and built-in defaults.
Command line argument defaults and some RubyGems defaults can be set in a
-~/.gemrc file for individual users and a /etc/gemrc for all users. These
-files are YAML files with the following YAML keys:
+~/.gemrc file for individual users and a gemrc in the SYSTEM CONFIGURATION
+DIRECTORY for all users. These files are YAML files with the following YAML
+keys:
:sources: A YAML array of remote gem repositories to install gems from
:verbose: Verbosity of the gem command. false, true, and :really are the
@@ -120,6 +121,8 @@ lib/rubygems/defaults/operating_system.rb
out << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n"
+ out << " - SYSTEM CONFIGURATION DIRECTORY: #{Gem::ConfigFile::SYSTEM_CONFIG_PATH}\n"
+
out << " - RUBYGEMS PLATFORMS:\n"
Gem.platforms.each do |platform|
out << " - #{platform}\n"
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index ed7be903ac..ed81ad6e25 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -52,6 +52,183 @@ Some examples of 'gem' usage.
gem update --system
EOF
+ GEM_DEPENDENCIES = <<-EOF
+A gem dependencies file allows installation of a consistent set of gems across
+multiple environments. The RubyGems implementation is designed to be
+compatible with Bundler's Gemfile format. You can see additional
+documentation on the format at:
+
+ http://bundler.io
+
+RubyGems automatically looks for these gem dependencies files:
+
+* gem.deps.rb
+* Gemfile
+* Isolate
+
+These files are looked up automatically using `gem install -g`, or you can
+specify a custom file.
+
+When the RUBYGEMS_GEMDEPS environment variable is set to a gem dependencies
+file the gems from that file will be activated at startup time. Set it to a
+specific filename or to "-" to have RubyGems automatically discover the gem
+dependencies file by walking up from the current directory.
+
+You can also activate gem dependencies at program startup using
+Gem.use_gemdeps.
+
+NOTE: Enabling automatic discovery on multiuser systems can lead to execution
+of arbitrary code when used from directories outside your control.
+
+Gem Dependencies
+================
+
+Use #gem to declare which gems you directly depend upon:
+
+ gem 'rake'
+
+To depend on a specific set of versions:
+
+ gem 'rake', '~> 10.3', '>= 10.3.2'
+
+RubyGems will require the gem name when activating the gem using
+the RUBYGEMS_GEMDEPS environment variable or Gem::use_gemdeps. Use the
+require: option to override this behavior if the gem does not have a file of
+that name or you don't want to require those files:
+
+ gem 'my_gem', require: 'other_file'
+
+To prevent RubyGems from requiring any files use:
+
+ gem 'my_gem', require: false
+
+To load dependencies from a .gemspec file:
+
+ gemspec
+
+RubyGems looks for the first .gemspec file in the current directory. To
+override this use the name: option:
+
+ gemspec name: 'specific_gem'
+
+To look in a different directory use the path: option:
+
+ gemspec name: 'specific_gem', path: 'gemspecs'
+
+To depend on a gem unpacked into a local directory:
+
+ gem 'modified_gem', path: 'vendor/modified_gem'
+
+To depend on a gem from git:
+
+ gem 'private_gem', git: 'git@my.company.example:private_gem.git'
+
+To depend on a gem from github:
+
+ gem 'private_gem', github: 'my_company/private_gem'
+
+To depend on a gem from a github gist:
+
+ gem 'bang', gist: '1232884'
+
+Git, github and gist support the ref:, branch: and tag: options to specify a
+commit reference or hash, branch or tag respectively to use for the gem.
+
+Setting the submodules: option to true for git, github and gist dependencies
+causes fetching of submodules when fetching the repository.
+
+You can depend on multiple gems from a single repository with the git method:
+
+ git 'https://github.com/rails/rails.git' do
+ gem 'activesupport'
+ gem 'activerecord'
+ end
+
+Gem Sources
+===========
+
+RubyGems uses the default sources for regular `gem install` for gem
+dependencies files. Unlike bundler, you do need to specify a source.
+
+You can override the sources used for downloading gems with:
+
+ source 'https://gem_server.example'
+
+You may specify multiple sources. Unlike bundler the prepend: option is not
+supported. Sources are used in-order, to prepend a source place it at the
+front of the list.
+
+Gem Platform
+============
+
+You can restrict gem dependencies to specific platforms with the #platform
+and #platforms methods:
+
+ platform :ruby_21 do
+ gem 'debugger'
+ end
+
+See the bundler Gemfile manual page for a list of platforms supported in a gem
+dependencies file.:
+
+ http://bundler.io/v1.6/man/gemfile.5.html
+
+Ruby Version and Engine Dependency
+==================================
+
+You can specifiy the version, engine and engine version of ruby to use with
+your gem dependencies file. If you are not running the specified version
+RubyGems will raise an exception.
+
+To depend on a specific version of ruby:
+
+ ruby '2.1.2'
+
+To depend on a specific ruby engine:
+
+ ruby '1.9.3', engine: 'jruby'
+
+To depend on a specific ruby engine version:
+
+ ruby '1.9.3', engine: 'jruby', engine_version: '1.7.11'
+
+Grouping Dependencies
+=====================
+
+Gem dependencies may be placed in groups that can be excluded from install.
+Dependencies required for development or testing of your code may be excluded
+when installed in a production environment.
+
+A #gem dependency may be placed in a group using the group: option:
+
+ gem 'minitest', group: :test
+
+To install dependencies from a gemfile without specific groups use the
+`--without` option for `gem install -g`:
+
+ $ gem install -g --without test
+
+The group: option also accepts multiple groups if the gem fits in multiple
+categories.
+
+Multiple groups may be excluded during install by comma-separating the groups for `--without` or by specifying `--without` multiple times.
+
+The #group method can also be used to place gems in groups:
+
+ group :test do
+ gem 'minitest'
+ gem 'minitest-emoji'
+ end
+
+The #group method allows multiple groups.
+
+The #gemspec development dependencies are placed in the :development group by
+default. This may be overriden with the :development_group option:
+
+ gemspec development_group: :other
+
+ EOF
+
PLATFORMS = <<-'EOF'
RubyGems platforms are composed of three parts, a CPU, an OS, and a
version. These values are taken from values in rbconfig.rb. You can view
@@ -90,6 +267,16 @@ When building platform gems, set the platform in the gem specification to
Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's
platform.
EOF
+
+ # NOTE when updating also update Gem::Command::HELP
+
+ SUBCOMMANDS = [
+ ["commands", :show_commands],
+ ["options", Gem::Command::HELP],
+ ["examples", EXAMPLES],
+ ["gem_dependencies", GEM_DEPENDENCIES],
+ ["platforms", PLATFORMS],
+ ]
# :startdoc:
def initialize
@@ -98,15 +285,6 @@ platform.
@command_manager = Gem::CommandManager.instance
end
- def arguments # :nodoc:
- args = <<-EOF
- commands List all 'gem' commands
- examples Show examples of 'gem' usage
- <command> Show specific help for <command>
- EOF
- return args.gsub(/^\s+/, '')
- end
-
def usage # :nodoc:
"#{program_name} ARGUMENT"
end
@@ -114,19 +292,20 @@ platform.
def execute
arg = options[:args][0]
- if begins? "commands", arg then
- show_commands
-
- elsif begins? "options", arg then
- say Gem::Command::HELP
-
- elsif begins? "examples", arg then
- say EXAMPLES
+ _, help = SUBCOMMANDS.find do |command,|
+ begins? command, arg
+ end
- elsif begins? "platforms", arg then
- say PLATFORMS
+ if help then
+ if Symbol === help then
+ send help
+ else
+ say help
+ end
+ return
+ end
- elsif options[:help] then
+ if options[:help] then
show_help
elsif arg then
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 8219eef6ea..1bf5928ebb 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -21,6 +21,8 @@ class Gem::Commands::InstallCommand < Gem::Command
def initialize
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:format_executable => false,
+ :lock => true,
+ :suggest_alternate => true,
:version => Gem::Requirement.default,
:without_groups => [],
})
@@ -69,6 +71,16 @@ class Gem::Commands::InstallCommand < Gem::Command
o[:explain] = v
end
+ add_option(:"Install/Update", '--[no-]lock',
+ 'Create a lock file (when used with -g/--file)') do |v,o|
+ o[:lock] = v
+ end
+
+ add_option(:"Install/Update", '--[no-]suggestions',
+ 'Suggest alternates when gems are not found') do |v,o|
+ o[:suggest_alternate] = v
+ end
+
@installed_specs = []
end
@@ -78,7 +90,7 @@ class Gem::Commands::InstallCommand < Gem::Command
def defaults_str # :nodoc:
"--both --version '#{Gem::Requirement.default}' --document --no-force\n" +
- "--install-dir #{Gem.dir}"
+ "--install-dir #{Gem.dir} --lock"
end
def description # :nodoc:
@@ -92,6 +104,25 @@ The wrapper allows you to choose among alternate gem versions using _version_.
For example `rake _0.7.3_ --version` will run rake version 0.7.3 if a newer
version is also installed.
+Gem Dependency Files
+====================
+
+RubyGems can install a consistent set of gems across multiple environments
+using `gem install -g` when a gem dependencies file (gem.deps.rb, Gemfile or
+Isolate) is present. If no explicit file is given RubyGems attempts to find
+one in the current directory.
+
+When the RUBYGEMS_GEMDEPS environment variable is set to a gem dependencies
+file the gems from that file will be activated at startup time. Set it to a
+specific filename or to "-" to have RubyGems automatically discover the gem
+dependencies file by walking up from the current directory.
+
+NOTE: Enabling automatic discovery on multiuser systems can lead to
+execution of arbitrary code when used from directories outside your control.
+
+Extension Install Failures
+==========================
+
If an extension fails to compile during gem installation the gem
specification is not written out, but the gem remains unpacked in the
repository. You may need to specify the path to the library's headers and
@@ -204,23 +235,20 @@ to write the specification by hand. For example:
install_gem_without_dependencies name, req
else
inst = Gem::DependencyInstaller.new options
+ request_set = inst.resolve_dependencies name, req
if options[:explain]
- request_set = inst.resolve_dependencies name, req
-
puts "Gems to install:"
- request_set.specs.map { |s| s.full_name }.sort.each do |s|
- puts " #{s}"
+ request_set.sorted_requests.each do |s|
+ puts " #{s.full_name}"
end
return
else
- inst.install name, req
+ @installed_specs.concat request_set.install options
end
- @installed_specs.push(*inst.installed_gems)
-
show_install_errors inst.errors
end
end
@@ -250,6 +278,14 @@ to write the specification by hand. For example:
inst = Gem::Installer.new gem, options
inst.install
+ require 'rubygems/dependency_installer'
+ dinst = Gem::DependencyInstaller.new options
+ dinst.installed_gems.replace [inst.spec]
+
+ Gem.done_installing_hooks.each do |hook|
+ hook.call dinst, [inst.spec]
+ end unless Gem.done_installing_hooks.empty?
+
@installed_specs.push(inst.spec)
end
@@ -264,8 +300,10 @@ to write the specification by hand. For example:
rescue Gem::InstallError => e
alert_error "Error installing #{gem_name}:\n\t#{e.message}"
exit_code |= 1
- rescue Gem::GemNotFoundException => e
- show_lookup_failure e.name, e.version, e.errors, options[:domain]
+ rescue Gem::GemNotFoundException, Gem::UnsatisfiableDependencyError => e
+ domain = options[:domain]
+ domain = :local unless options[:suggest_alternate]
+ show_lookup_failure e.name, e.version, e.errors, domain
exit_code |= 2
end
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index 4edeabef02..c6ff237311 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -8,13 +8,13 @@ require 'rubygems/commands/query_command'
class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
def initialize
- super 'list', 'Display local gems whose name starts with STRING'
+ super 'list', 'Display local gems whose name matches REGEXP'
remove_option('--name-matches')
end
def arguments # :nodoc:
- "STRING start of gem name to look for"
+ "REGEXP regexp to look for in gem name"
end
def defaults_str # :nodoc:
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
new file mode 100644
index 0000000000..91963bba73
--- /dev/null
+++ b/lib/rubygems/commands/open_command.rb
@@ -0,0 +1,74 @@
+require 'English'
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/util'
+
+class Gem::Commands::OpenCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'open', 'Open gem sources in editor'
+
+ add_option('-e', '--editor EDITOR', String,
+ "Opens gem sources in EDITOR") do |editor, options|
+ options[:editor] = editor || get_env_editor
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to open in editor"
+ end
+
+ def defaults_str # :nodoc:
+ "-e #{get_env_editor}"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+ The open command opens gem in editor and changes current path
+ to gem's source directory. Editor can be specified with -e option,
+ otherwise rubygems will look for editor in $EDITOR, $VISUAL and
+ $GEM_EDITOR variables.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [-e EDITOR]"
+ end
+
+ def get_env_editor
+ ENV['GEM_EDITOR'] ||
+ ENV['VISUAL'] ||
+ ENV['EDITOR'] ||
+ 'vi'
+ end
+
+ def execute
+ @version = options[:version] || Gem::Requirement.default
+ @editor = options[:editor] || get_env_editor
+
+ found = open_gem(get_one_gem_name)
+
+ terminate_interaction 1 unless found
+ end
+
+ def open_gem name
+ spec = spec_for name
+ return false unless spec
+
+ open_editor(spec.full_gem_path)
+ end
+
+ def open_editor path
+ system(*@editor.split(/\s+/) + [path])
+ end
+
+ def spec_for name
+ spec = Gem::Specification.find_all_by_name(name, @version).last
+
+ return spec if spec
+
+ say "Unable to find gem '#{name}'"
+ end
+end
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index 13b8793021..322bf6590a 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -86,7 +86,9 @@ permission to.
request.add_field "Authorization", api_key
end
- with_response response, "Removing #{owner}"
+ action = method == :delete ? "Removing" : "Adding"
+
+ with_response response, "#{action} #{owner}"
rescue
# ignore
end
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
index 5809690735..a1e2c1a00e 100644
--- a/lib/rubygems/commands/search_command.rb
+++ b/lib/rubygems/commands/search_command.rb
@@ -4,7 +4,7 @@ require 'rubygems/commands/query_command'
class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
def initialize
- super 'search', 'Display remote gems whose name contains STRING'
+ super 'search', 'Display remote gems whose name matches REGEXP'
remove_option '--name-matches'
@@ -12,7 +12,7 @@ class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
end
def arguments # :nodoc:
- "STRING fragment of gem name to search for"
+ "REGEXP regexp to search for in gem name"
end
def defaults_str # :nodoc:
@@ -21,8 +21,8 @@ class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
def description # :nodoc:
<<-EOF
-The search command displays remote gems whose name contains the given
-string.
+The search command displays remote gems whose name matches the given
+regexp.
The --details option displays additional details from the gem but will
take a little longer to complete as it must download the information
@@ -33,7 +33,7 @@ To list local gems use the list command.
end
def usage # :nodoc:
- "#{program_name} [STRING]"
+ "#{program_name} [REGEXP]"
end
end
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index 681db0dc1d..6617396780 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -446,7 +446,7 @@ abort "#{deprecation_message}"
history.force_encoding Encoding::UTF_8 if
Object.const_defined? :Encoding
- history = history.sub(/^# coding:.*?^=/m, '')
+ history = history.sub(/^# coding:.*?(?=^=)/m, '')
text = history.split(HISTORY_HEADER)
text.shift # correct an off-by-one generated by split
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index e62095a336..71ffdc89fc 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -15,7 +15,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
def initialize
super 'uninstall', 'Uninstall gems from the local repository',
:version => Gem::Requirement.default, :user_install => true,
- :check_dev => false
+ :check_dev => false, :vendor => false
add_option('-a', '--[no-]all',
'Uninstall all matching versions'
@@ -76,6 +76,18 @@ class Gem::Commands::UninstallCommand < Gem::Command
add_version_option
add_platform_option
+
+ add_option('--vendor',
+ 'Uninstall gem from the vendor directory.',
+ 'Only for use by gem repackagers.') do |value, options|
+ unless Gem.vendor_dir then
+ raise OptionParser::InvalidOption.new 'your platform is not supported'
+ end
+
+ alert_warning 'Use your OS package manager to uninstall vendor gems'
+ options[:vendor] = true
+ options[:install_dir] = Gem.vendor_dir
+ end
end
def arguments # :nodoc:
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index b4ee59b3bb..9e9bd088f2 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -16,6 +16,8 @@ class Gem::Commands::UpdateCommand < Gem::Command
attr_reader :installer # :nodoc:
+ attr_reader :updated # :nodoc:
+
def initialize
super 'update', 'Update installed gems to the latest version',
:document => %w[rdoc ri],
@@ -45,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
end
def arguments # :nodoc:
- "GEMNAME name of gem to update"
+ "REGEXP regexp to search for in gem name"
end
def defaults_str # :nodoc:
@@ -56,13 +58,13 @@ class Gem::Commands::UpdateCommand < Gem::Command
<<-EOF
The update command will update your gems to the latest version.
-The update comamnd does not remove the previous version. Use the cleanup
+The update command does not remove the previous version. Use the cleanup
command to remove old versions.
EOF
end
def usage # :nodoc:
- "#{program_name} GEMNAME [GEMNAME ...]"
+ "#{program_name} REGEXP [REGEXP ...]"
end
def check_latest_rubygems version # :nodoc:
@@ -97,10 +99,14 @@ command to remove old versions.
updated = update_gems gems_to_update
+ updated_names = updated.map { |spec| spec.name }
+ not_updated_names = options[:args].uniq - updated_names
+
if updated.empty? then
say "Nothing to update"
else
- say "Gems updated: #{updated.map { |spec| spec.name }.join ' '}"
+ say "Gems updated: #{updated_names.join(' ')}"
+ say "Gems already up-to-date: #{not_updated_names.join(' ')}" unless not_updated_names.empty?
end
end
@@ -199,15 +205,11 @@ command to remove old versions.
@installer ||= Gem::DependencyInstaller.new options
- success = false
-
say "Updating #{name}"
begin
@installer.install name, Gem::Requirement.new(version)
- success = true
- rescue Gem::InstallError => e
+ rescue Gem::InstallError, Gem::DependencyError => e
alert_error "Error installing #{name}:\n\t#{e.message}"
- success = false
end
@installer.installed_gems.each do |spec|
@@ -259,7 +261,7 @@ command to remove old versions.
highest_installed_gems.each do |l_name, l_spec|
next if not gem_names.empty? and
- gem_names.all? { |name| /#{name}/ !~ l_spec.name }
+ gem_names.none? { |name| name == l_spec.name }
highest_remote_ver = highest_remote_version l_spec
@@ -272,4 +274,3 @@ command to remove old versions.
end
end
-
diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb
index 2285bb4017..3c7859e763 100644
--- a/lib/rubygems/commands/yank_command.rb
+++ b/lib/rubygems/commands/yank_command.rb
@@ -44,10 +44,7 @@ as the reason for the removal request.
options[:undo] = true
end
- add_option('-k', '--key KEY_NAME',
- 'Use API key from your gem credentials file') do |value, options|
- options[:key] = value
- end
+ add_key_option
end
def execute
@@ -55,14 +52,12 @@ as the reason for the removal request.
version = get_version_from_requirements(options[:version])
platform = get_platform_from_requirements(options)
- api_key = Gem.configuration.rubygems_api_key
- api_key = Gem.configuration.api_keys[options[:key].to_sym] if options[:key]
if version then
if options[:undo] then
- unyank_gem(version, platform, api_key)
+ unyank_gem(version, platform)
else
- yank_gem(version, platform, api_key)
+ yank_gem(version, platform)
end
else
say "A version argument is required: #{usage}"
@@ -70,19 +65,19 @@ as the reason for the removal request.
end
end
- def yank_gem(version, platform, api_key)
+ def yank_gem(version, platform)
say "Yanking gem from #{self.host}..."
- yank_api_request(:delete, version, platform, "api/v1/gems/yank", api_key)
+ yank_api_request(:delete, version, platform, "api/v1/gems/yank")
end
- def unyank_gem(version, platform, api_key)
+ def unyank_gem(version, platform)
say "Unyanking gem from #{host}..."
- yank_api_request(:put, version, platform, "api/v1/gems/unyank", api_key)
+ yank_api_request(:put, version, platform, "api/v1/gems/unyank")
end
private
- def yank_api_request(method, version, platform, api, api_key)
+ def yank_api_request(method, version, platform, api)
name = get_one_gem_name
response = rubygems_api_request(method, api) do |request|
request.add_field("Authorization", api_key)
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 1f4afba0f4..1bdc79ae06 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -57,7 +57,7 @@ class Gem::ConfigFile
# :stopdoc:
- system_config_path =
+ SYSTEM_CONFIG_PATH =
begin
require "etc"
Etc.sysconfdir
@@ -86,7 +86,7 @@ class Gem::ConfigFile
# :startdoc:
- SYSTEM_WIDE_CONFIG_FILE = File.join system_config_path, 'gemrc'
+ SYSTEM_WIDE_CONFIG_FILE = File.join SYSTEM_CONFIG_PATH, 'gemrc'
##
# List of arguments supplied to the config file object.
@@ -383,6 +383,8 @@ if you believe they were disclosed to a third party.
@backtrace = true
when /^--debug$/ then
$DEBUG = true
+
+ warn 'NOTE: Debugging mode prints all exceptions even when rescued'
else
@args << arg
end
@@ -428,6 +430,15 @@ if you believe they were disclosed to a third party.
DEFAULT_VERBOSITY
end
+ yaml_hash[:ssl_verify_mode] =
+ @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
+
+ yaml_hash[:ssl_ca_cert] =
+ @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
+
+ yaml_hash[:ssl_client_cert] =
+ @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
+
keys = yaml_hash.keys.map { |key| key.to_s }
keys << 'debug'
re = Regexp.union(*keys)
diff --git a/lib/rubygems/core_ext/kernel_gem.rb b/lib/rubygems/core_ext/kernel_gem.rb
index 3405233ab1..edce4ee10b 100644
--- a/lib/rubygems/core_ext/kernel_gem.rb
+++ b/lib/rubygems/core_ext/kernel_gem.rb
@@ -26,6 +26,11 @@ module Kernel
# Kernel#gem should be called *before* any require statements (otherwise
# RubyGems may load a conflicting library version).
#
+ # Kernel#gem only loads prerelease versions when prerelease +requirements+
+ # are given:
+ #
+ # gem 'rake', '>= 1.1.a', '< 2'
+ #
# In older RubyGems versions, the environment variable GEM_SKIP could be
# used to skip activation of specified gems, for example to test out changes
# that haven't been installed yet. Now RubyGems defers to -I and the
@@ -51,7 +56,9 @@ module Kernel
end
spec = Gem::Dependency.new(gem_name, *requirements).to_spec
- spec.activate if spec
+ Gem::LOADED_SPECS_MUTEX.synchronize {
+ spec.activate
+ } if spec
end
private :gem
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index 84bb03f67d..bf9618d3bf 100755
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -50,12 +50,8 @@ module Kernel
# normal require handle loading a gem from the rescue below.
if Gem::Specification.unresolved_deps.empty? then
- begin
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.enter
- end
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(path)
end
# If +path+ is for a gem that has already been loaded, don't
@@ -71,8 +67,6 @@ module Kernel
begin
RUBYGEMS_ACTIVATION_MONITOR.exit
return gem_original_require(path)
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.enter
end if spec
# Attempt to find +path+ in any unresolved gems...
@@ -105,6 +99,7 @@ module Kernel
names = found_specs.map(&:name).uniq
if names.size > 1 then
+ RUBYGEMS_ACTIVATION_MONITOR.exit
raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
end
@@ -115,32 +110,27 @@ module Kernel
unless valid then
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
le.name = names.first
+ RUBYGEMS_ACTIVATION_MONITOR.exit
raise le
end
valid.activate
end
- begin
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.enter
- end
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(path)
rescue LoadError => load_error
+ RUBYGEMS_ACTIVATION_MONITOR.enter
+
if load_error.message.start_with?("Could not find") or
(load_error.message.end_with?(path) and Gem.try_activate(path)) then
- begin
- RUBYGEMS_ACTIVATION_MONITOR.exit
- return gem_original_require(path)
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.enter
- end
+ RUBYGEMS_ACTIVATION_MONITOR.exit
+ return gem_original_require(path)
+ else
+ RUBYGEMS_ACTIVATION_MONITOR.exit
end
raise load_error
- ensure
- RUBYGEMS_ACTIVATION_MONITOR.exit
end
private :require
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 6924f48e5a..55ca080c96 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -89,11 +89,11 @@ module Gem
# Default gem load path
def self.default_path
- if Gem.user_home && File.exist?(Gem.user_home) then
- [user_dir, default_dir]
- else
- [default_dir]
- end
+ path = []
+ path << user_dir if user_home && File.exist?(user_home)
+ path << default_dir
+ path << vendor_dir if vendor_dir and File.directory? vendor_dir
+ path
end
##
@@ -160,4 +160,18 @@ module Gem
true
end
+ ##
+ # Directory where vendor gems are installed.
+
+ def self.vendor_dir # :nodoc:
+ if vendor_dir = ENV['GEM_VENDOR'] then
+ return vendor_dir.dup
+ end
+
+ return nil unless RbConfig::CONFIG.key? 'vendordir'
+
+ File.join RbConfig::CONFIG['vendordir'], 'gems',
+ RbConfig::CONFIG['ruby_version']
+ end
+
end
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index a96d67c3e5..b72a540dc8 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -74,7 +74,7 @@ class Gem::Dependency
end
def inspect # :nodoc:
- if @prerelease
+ if prerelease? then
"<%s type=%p name=%p requirements=%p prerelease=ok>" %
[self.class, self.type, self.name, requirement.to_s]
else
@@ -145,7 +145,6 @@ class Gem::Dependency
@requirement = @version_requirements if defined?(@version_requirements)
end
- # DOC: this method needs documentation or :nodoc''d
def requirements_list
requirement.as_list
end
@@ -205,9 +204,19 @@ class Gem::Dependency
alias === =~
- # DOC: this method needs either documented or :nodoc'd
+ ##
+ # :call-seq:
+ # dep.match? name => true or false
+ # dep.match? name, version => true or false
+ # dep.match? spec => true or false
+ #
+ # Does this dependency match the specification described by +name+ and
+ # +version+ or match +spec+?
+ #
+ # NOTE: Unlike #matches_spec? this method does not return true when the
+ # version is a prerelease version unless this is a prerelease dependency.
- def match? obj, version=nil
+ def match? obj, version=nil, allow_prerelease=false
if !version
name = obj.name
version = obj.version
@@ -216,12 +225,23 @@ class Gem::Dependency
end
return false unless self.name === name
- return true if requirement.none?
- requirement.satisfied_by? Gem::Version.new(version)
+ version = Gem::Version.new version
+
+ return true if requirement.none? and not version.prerelease?
+ return false if version.prerelease? and
+ not allow_prerelease and
+ not prerelease?
+
+ requirement.satisfied_by? version
end
- # DOC: this method needs either documented or :nodoc'd
+ ##
+ # Does this dependency match +spec+?
+ #
+ # NOTE: This is not a convenience method. Unlike #match? this method
+ # returns true when +spec+ is a prerelease version even if this dependency
+ # is not a prerelease dependency.
def matches_spec? spec
return false unless name === spec.name
@@ -249,8 +269,6 @@ class Gem::Dependency
self.class.new name, self_req.as_list.concat(other_req.as_list)
end
- # DOC: this method needs either documented or :nodoc'd
-
def matching_specs platform_only = false
matches = Gem::Specification.stubs.find_all { |spec|
self.name === spec.name and # TODO: == instead of ===
@@ -273,8 +291,6 @@ class Gem::Dependency
@requirement.specific?
end
- # DOC: this method needs either documented or :nodoc'd
-
def to_specs
matches = matching_specs true
@@ -287,12 +303,13 @@ class Gem::Dependency
if specs.empty?
total = Gem::Specification.to_a.size
- error = Gem::LoadError.new \
- "Could not find '#{name}' (#{requirement}) among #{total} total gem(s)"
+ msg = "Could not find '#{name}' (#{requirement}) among #{total} total gem(s)\n"
else
- error = Gem::LoadError.new \
- "Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]"
+ msg = "Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]\n"
end
+ msg << "Checked in 'GEM_PATH=#{Gem.path.join(File::PATH_SEPARATOR)}', execute `gem env` for more information"
+
+ error = Gem::LoadError.new(msg)
error.name = self.name
error.requirement = self.requirement
raise error
@@ -303,11 +320,15 @@ class Gem::Dependency
matches
end
- # DOC: this method needs either documented or :nodoc'd
-
def to_spec
matches = self.to_specs
- matches.find { |spec| spec.activated? } or matches.last
+ active = matches.find { |spec| spec.activated? }
+
+ return active if active
+
+ matches.delete_if { |spec| spec.version.prerelease? } unless prerelease?
+
+ matches.last
end
end
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index da6994a9be..039d046da2 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -72,6 +72,7 @@ class Gem::DependencyInstaller
def initialize options = {}
@only_install_dir = !!options[:install_dir]
@install_dir = options[:install_dir] || Gem.dir
+ @build_root = options[:build_root]
options = DEFAULT_OPTIONS.merge options
@@ -102,7 +103,7 @@ class Gem::DependencyInstaller
@cache_dir = options[:cache_dir] || @install_dir
- @errors = nil
+ @errors = []
end
##
@@ -157,6 +158,7 @@ class Gem::DependencyInstaller
dependency_list.remove_specs_unsatisfied_by dependencies
end
+
##
# Creates an AvailableSet to install from based on +dep_or_name+ and
# +version+
@@ -243,9 +245,9 @@ class Gem::DependencyInstaller
# FIX if there is a problem talking to the network, we either need to always tell
# the user (no really_verbose) or fail hard, not silently tell them that we just
# couldn't find their requested gem.
- if Gem.configuration.really_verbose then
- say "Error fetching remote data:\t\t#{e.message}"
- say "Falling back to local-only install"
+ verbose do
+ "Error fetching remote data:\t\t#{e.message}\n" \
+ "Falling back to local-only install"
end
@domain = :local
end
@@ -375,13 +377,16 @@ class Gem::DependencyInstaller
options = {
:bin_dir => @bin_dir,
:build_args => @build_args,
+ :document => @document,
:env_shebang => @env_shebang,
:force => @force,
:format_executable => @format_executable,
:ignore_dependencies => @ignore_dependencies,
+ :prerelease => @prerelease,
:security_policy => @security_policy,
:user_install => @user_install,
:wrappers => @wrappers,
+ :build_root => @build_root,
:install_as_default => @install_as_default
}
options[:install_dir] = @install_dir if @only_install_dir
@@ -415,25 +420,59 @@ class Gem::DependencyInstaller
end
def resolve_dependencies dep_or_name, version # :nodoc:
- as = available_set_for dep_or_name, version
-
- request_set = as.to_request_set install_development_deps
+ request_set = Gem::RequestSet.new
+ request_set.development = @development
+ request_set.development_shallow = @dev_shallow
request_set.soft_missing = @force
+ request_set.prerelease = @prerelease
request_set.remote = false unless consider_remote?
installer_set = Gem::Resolver::InstallerSet.new @domain
- installer_set.always_install.concat request_set.always_install
installer_set.ignore_installed = @only_install_dir
+ if consider_local?
+ if dep_or_name =~ /\.gem$/ and File.file? dep_or_name then
+ src = Gem::Source::SpecificFile.new dep_or_name
+ installer_set.add_local dep_or_name, src.spec, src
+ version = src.spec.version if version == Gem::Requirement.default
+ elsif dep_or_name =~ /\.gem$/ then
+ Dir[dep_or_name].each do |name|
+ begin
+ src = Gem::Source::SpecificFile.new name
+ installer_set.add_local dep_or_name, src.spec, src
+ rescue Gem::Package::FormatError
+ end
+ end
+ # else This is a dependency. InstallerSet handles this case
+ end
+ end
+
+ dependency =
+ if spec = installer_set.local?(dep_or_name) then
+ Gem::Dependency.new spec.name, version
+ elsif String === dep_or_name then
+ Gem::Dependency.new dep_or_name, version
+ else
+ dep_or_name
+ end
+
+ dependency.prerelease = @prerelease
+
+ request_set.import [dependency]
+
+ installer_set.add_always_install dependency
+
+ request_set.always_install = installer_set.always_install
+
if @ignore_dependencies then
installer_set.ignore_dependencies = true
request_set.ignore_dependencies = true
request_set.soft_missing = true
end
- composed_set = Gem::Resolver.compose_sets as, installer_set
+ request_set.resolve installer_set
- request_set.resolve composed_set
+ @errors.concat request_set.errors
request_set
end
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
index 0aa0f7b79f..3c71fd5aa4 100644
--- a/lib/rubygems/doctor.rb
+++ b/lib/rubygems/doctor.rb
@@ -105,7 +105,7 @@ class Gem::Doctor
next if ent == "." || ent == ".."
child = File.join(directory, ent)
- next unless File.exists?(child)
+ next unless File.exist?(child)
basename = File.basename(child, extension)
next if installed_specs.include? basename
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index fc9bfbc0dc..9defb9ee9b 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -19,6 +19,36 @@ module Gem
attr_accessor :requirement
end
+ # Raised when there are conflicting gem specs loaded
+
+ class ConflictError < LoadError
+
+ ##
+ # A Hash mapping conflicting specifications to the dependencies that
+ # caused the conflict
+
+ attr_reader :conflicts
+
+ ##
+ # The specification that had the conflict
+
+ attr_reader :target
+
+ def initialize target, conflicts
+ @target = target
+ @conflicts = conflicts
+ @name = target.name
+
+ reason = conflicts.map { |act, dependencies|
+ "#{act.full_name} conflicts with #{dependencies.join(", ")}"
+ }.join ", "
+
+ # TODO: improve message by saying who activated `con`
+
+ super("Unable to activate #{target.full_name}, because #{reason}")
+ end
+ end
+
class ErrorReason; end
# Generated when trying to lookup a gem to indicate that the gem
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 6bd50eca2c..2a9875cd24 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -27,7 +27,7 @@ class Gem::DependencyRemovalException < Gem::Exception; end
# toplevel. Indicates which dependencies were incompatible through #conflict
# and #conflicting_dependencies
-class Gem::DependencyResolutionError < Gem::Exception
+class Gem::DependencyResolutionError < Gem::DependencyError
attr_reader :conflict
@@ -214,7 +214,7 @@ end
# Raised by Resolver when a dependency requests a gem for which
# there is no spec.
-class Gem::UnsatisfiableDependencyError < Gem::Exception
+class Gem::UnsatisfiableDependencyError < Gem::DependencyError
##
# The unsatisfiable dependency. This is a
@@ -223,6 +223,11 @@ class Gem::UnsatisfiableDependencyError < Gem::Exception
attr_reader :dependency
##
+ # Errors encountered which may have contributed to this exception
+
+ attr_accessor :errors
+
+ ##
# Creates a new UnsatisfiableDependencyError for the unsatisfiable
# Gem::Resolver::DependencyRequest +dep+
@@ -239,6 +244,21 @@ class Gem::UnsatisfiableDependencyError < Gem::Exception
end
@dependency = dep
+ @errors = []
+ end
+
+ ##
+ # The name of the unresolved dependency
+
+ def name
+ @dependency.name
+ end
+
+ ##
+ # The Requirement of the unresolved dependency (not Version).
+
+ def version
+ @dependency.requirement
end
end
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index cbae8234a1..548f1262a8 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -161,7 +161,7 @@ EOF
results = builder.build(extension, @gem_dir, dest_path,
results, @build_args, lib_dir)
- say results.join("\n") if Gem.configuration.really_verbose
+ verbose { results.join("\n") }
end
end
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index 05506b265b..213bdcb002 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -11,13 +11,15 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileEntry = FileUtils::Entry_ # :nodoc:
def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
- tmp_dest = Dir.mktmpdir(".gem.", ".")
+ # relative path required as some versions of mktmpdir return an absolute
+ # path which breaks make if it includes a space in the name
+ tmp_dest = get_relative_path(Dir.mktmpdir(".gem.", "."))
t = nil
Tempfile.open %w"siteconf .rb", "." do |siteconf|
t = siteconf
siteconf.puts "require 'rbconfig'"
- siteconf.puts "dest_path = #{(tmp_dest || dest_path).dump}"
+ siteconf.puts "dest_path = #{tmp_dest.dump}"
%w[sitearchdir sitelibdir].each do |dir|
siteconf.puts "RbConfig::MAKEFILE_CONFIG['#{dir}'] = dest_path"
siteconf.puts "RbConfig::CONFIG['#{dir}'] = dest_path"
@@ -25,14 +27,10 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
siteconf.flush
- siteconf_path = File.expand_path siteconf.path
-
- rubyopt = ENV["RUBYOPT"]
destdir = ENV["DESTDIR"]
begin
- ENV["RUBYOPT"] = ["-r#{siteconf_path}", rubyopt].compact.join(' ')
- cmd = [Gem.ruby, File.basename(extension), *args].join ' '
+ cmd = [Gem.ruby, "-r", get_relative_path(siteconf.path), File.basename(extension), *args].join ' '
begin
run cmd, results
@@ -42,7 +40,6 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
end
ENV["DESTDIR"] = nil
- ENV["RUBYOPT"] = rubyopt
make dest_path, results
@@ -57,11 +54,10 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileEntry.new(tmp_dest).traverse do |ent|
destent = ent.class.new(dest_path, ent.rel)
- destent.exist? or File.rename(ent.path, destent.path)
+ destent.exist? or FileUtils.mv(ent.path, destent.path)
end
end
ensure
- ENV["RUBYOPT"] = rubyopt
ENV["DESTDIR"] = destdir
end
end
@@ -72,5 +68,11 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileUtils.rm_rf tmp_dest if tmp_dest
end
+ private
+ def self.get_relative_path(path)
+ path[0..Dir.pwd.length-1] = '.' if path.start_with?(Dir.pwd)
+ path
+ end
+
end
diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index 6a4da9e983..4ecf7376e0 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -86,7 +86,7 @@ module Gem::GemcutterUtilities
def sign_in sign_in_host = nil
sign_in_host ||= self.host
- return if Gem.configuration.rubygems_api_key
+ return if api_key
pretty_host = if Gem::DEFAULT_HOST == sign_in_host then
'RubyGems.org'
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index d3f55cd5ea..a503b7fca4 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -59,6 +59,23 @@ module Gem::InstallUpdateOptions
end
end
+ add_option(:"Install/Update", '--build-root DIR',
+ 'Temporary installation root. Useful for building',
+ 'packages. Do not use this when installing remote gems.') do |value, options|
+ options[:build_root] = File.expand_path(value)
+ end
+
+ add_option(:"Install/Update", '--vendor',
+ 'Install gem into the vendor directory.',
+ 'Only for use by gem repackagers.') do |value, options|
+ unless Gem.vendor_dir then
+ raise OptionParser::InvalidOption.new 'your platform is not supported'
+ end
+
+ options[:vendor] = true
+ options[:install_dir] = Gem.vendor_dir
+ end
+
add_option(:"Install/Update", '-N', '--no-document',
'Disable documentation generation') do |value, options|
options[:document] = []
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index c80981682f..e5cfc0f536 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -39,7 +39,9 @@ class Gem::Installer
include Gem::UserInteraction
- # DOC: Missing docs or :nodoc:.
+ ##
+ # Filename of the gem being installed.
+
attr_reader :gem
##
@@ -47,6 +49,8 @@ class Gem::Installer
attr_reader :bin_dir
+ attr_reader :build_root # :nodoc:
+
##
# The gem repository the gem will be installed into
@@ -64,6 +68,8 @@ class Gem::Installer
@path_warning = false
+ @install_lock = Mutex.new
+
class << self
##
@@ -71,7 +77,19 @@ class Gem::Installer
attr_accessor :path_warning
- # DOC: Missing docs or :nodoc:.
+ ##
+ # Certain aspects of the install process are not thread-safe. This lock is
+ # used to allow multiple threads to install Gems at the same time.
+
+ attr_reader :install_lock
+
+ ##
+ # Overrides the executable format.
+ #
+ # This is a sprintf format with a "%s" which will be replaced with the
+ # executable name. It is based off the ruby executable name's difference
+ # from "ruby".
+
attr_writer :exec_format
# Defaults to use Ruby's program prefix and suffix.
@@ -240,7 +258,7 @@ class Gem::Installer
say spec.post_install_message unless spec.post_install_message.nil?
- Gem::Specification.add_spec spec unless Gem::Specification.include? spec
+ Gem::Installer.install_lock.synchronize { Gem::Specification.add_spec spec }
run_post_install_hooks
@@ -318,6 +336,7 @@ class Gem::Installer
# True if the gems in the system satisfy +dependency+.
def installation_satisfies_dependency?(dependency)
+ return true if @options[:development] and dependency.type == :development
return true if installed_specs.detect { |s| dependency.matches_spec? s }
return false if @only_install_dir
not dependency.matching_specs.empty?
@@ -382,12 +401,11 @@ class Gem::Installer
file.puts windows_stub_script(bindir, filename)
end
- say script_path if Gem.configuration.really_verbose
+ verbose script_path
end
end
- # DOC: Missing docs or :nodoc:.
- def generate_bin
+ def generate_bin # :nodoc:
return if spec.executables.nil? or spec.executables.empty?
Dir.mkdir @bin_dir unless File.exist? @bin_dir
@@ -433,7 +451,7 @@ class Gem::Installer
file.print app_script_text(filename)
end
- say bin_script_path if Gem.configuration.really_verbose
+ verbose bin_script_path
generate_windows_script filename, bindir
end
@@ -536,8 +554,7 @@ class Gem::Installer
end
end
- # DOC: Missing docs or :nodoc:.
- def ensure_required_ruby_version_met
+ def ensure_required_ruby_version_met # :nodoc:
if rrv = spec.required_ruby_version then
unless rrv.satisfied_by? Gem.ruby_version then
raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}."
@@ -545,8 +562,7 @@ class Gem::Installer
end
end
- # DOC: Missing docs or :nodoc:.
- def ensure_required_rubygems_version_met
+ def ensure_required_rubygems_version_met # :nodoc:
if rrgv = spec.required_rubygems_version then
unless rrgv.satisfied_by? Gem.rubygems_version then
raise Gem::InstallError,
@@ -556,8 +572,7 @@ class Gem::Installer
end
end
- # DOC: Missing docs or :nodoc:.
- def ensure_dependencies_met
+ def ensure_dependencies_met # :nodoc:
deps = spec.runtime_dependencies
deps |= spec.development_dependencies if @development
@@ -566,8 +581,7 @@ class Gem::Installer
end
end
- # DOC: Missing docs or :nodoc:.
- def process_options
+ def process_options # :nodoc:
@options = {
:bin_dir => nil,
:env_shebang => false,
@@ -590,12 +604,20 @@ class Gem::Installer
# (or use) a new bin dir under the gem_home.
@bin_dir = options[:bin_dir] || Gem.bindir(gem_home)
@development = options[:development]
+ @build_root = options[:build_root]
@build_args = options[:build_args] || Gem::Command.build_args
+
+ unless @build_root.nil?
+ require 'pathname'
+ @build_root = Pathname.new(@build_root).expand_path
+ @bin_dir = File.join(@build_root, options[:bin_dir] || Gem.bindir(@gem_home))
+ @gem_home = File.join(@build_root, @gem_home)
+ alert_warning "You build with buildroot.\n Build root: #{@build_root}\n Bin dir: #{@bin_dir}\n Gem home: #{@gem_home}"
+ end
end
- # DOC: Missing docs or :nodoc:.
- def check_that_user_bin_dir_is_in_path
+ def check_that_user_bin_dir_is_in_path # :nodoc:
user_bin_dir = @bin_dir || Gem.bindir(gem_home)
user_bin_dir = user_bin_dir.gsub(File::SEPARATOR, File::ALT_SEPARATOR) if
File::ALT_SEPARATOR
@@ -606,16 +628,19 @@ class Gem::Installer
user_bin_dir = user_bin_dir.downcase
end
- unless path.split(File::PATH_SEPARATOR).include? user_bin_dir then
- unless self.class.path_warning then
- alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
- self.class.path_warning = true
+ path = path.split(File::PATH_SEPARATOR)
+
+ unless path.include? user_bin_dir then
+ unless !Gem.win_platform? && (path.include? user_bin_dir.sub(ENV['HOME'], '~'))
+ unless self.class.path_warning then
+ alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
+ self.class.path_warning = true
+ end
end
end
end
- # DOC: Missing docs or :nodoc:.
- def verify_gem_home(unpack = false)
+ def verify_gem_home(unpack = false) # :nodoc:
FileUtils.mkdir_p gem_home
raise Gem::FilePermissionError, gem_home unless
unpack or File.writable?(gem_home)
@@ -660,10 +685,10 @@ TEXT
return <<-TEXT
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
-@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
+@"#{bindir.tr(File::SEPARATOR, File::ALT_SEPARATOR)}\\#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
-@"#{ruby}" "%~dpn0" %*
+@"%~dp0#{ruby}" "%~dpn0" %*
TEXT
end
diff --git a/lib/rubygems/installer_test_case.rb b/lib/rubygems/installer_test_case.rb
index d03e512451..549de011e4 100644
--- a/lib/rubygems/installer_test_case.rb
+++ b/lib/rubygems/installer_test_case.rb
@@ -101,6 +101,8 @@ class Gem::InstallerTestCase < Gem::TestCase
@installer = util_installer @spec, @gemhome
@user_installer = util_installer @user_spec, Gem.user_dir, :user
+
+ Gem::Installer.path_warning = false
end
def util_gem_bindir spec = @spec # :nodoc:
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index a1e106d9be..db23d9f974 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -100,8 +100,8 @@ module Gem::LocalRemoteOptions
def add_source_option
accept_uri_http
- add_option(:"Local/Remote", '--source URL', URI::HTTP,
- 'Add URL as a remote source for gems') do |source, options|
+ add_option(:"Local/Remote", '-s', '--source URL', URI::HTTP,
+ 'Append URL to list of remote gem sources') do |source, options|
source << '/' if source !~ /\/\z/
diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb
index f16ab369fa..60323db408 100644
--- a/lib/rubygems/name_tuple.rb
+++ b/lib/rubygems/name_tuple.rb
@@ -53,7 +53,7 @@ class Gem::NameTuple
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{@platform}"
- end
+ end.untaint
end
##
@@ -90,7 +90,9 @@ class Gem::NameTuple
alias to_s inspect # :nodoc:
def <=> other
- to_a <=> other.to_a
+ [@name, @version, @platform == Gem::Platform::RUBY ? -1 : 1] <=>
+ [other.name, other.version,
+ other.platform == Gem::Platform::RUBY ? -1 : 1]
end
include Comparable
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 0ed6e1b91f..417b34b79f 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -54,10 +54,12 @@ class Gem::Package
class FormatError < Error
attr_reader :path
- def initialize message, path = nil
- @path = path
+ def initialize message, source = nil
+ if source
+ @path = source.path
- message << " in #{path}" if path
+ message << " in #{path}" if path
+ end
super message
end
@@ -80,6 +82,7 @@ class Gem::Package
class TarInvalidError < Error; end
+
attr_accessor :build_time # :nodoc:
##
@@ -114,19 +117,26 @@ class Gem::Package
end
##
- # Creates a new Gem::Package for the file at +gem+.
+ # Creates a new Gem::Package for the file at +gem+. +gem+ can also be
+ # provided as an IO object.
#
# If +gem+ is an existing file in the old format a Gem::Package::Old will be
# returned.
def self.new gem
- return super unless Gem::Package == self
- return super unless File.exist? gem
+ gem = if gem.is_a?(Gem::Package::Source)
+ gem
+ elsif gem.respond_to? :read
+ Gem::Package::IOSource.new gem
+ else
+ Gem::Package::FileSource.new gem
+ end
- start = File.read gem, 20
+ return super(gem) unless Gem::Package == self
+ return super unless gem.present?
- return super unless start
- return super unless start.include? 'MD5SUM ='
+ return super unless gem.start
+ return super unless gem.start.include? 'MD5SUM ='
Gem::Package::Old.new gem
end
@@ -227,7 +237,7 @@ class Gem::Package
setup_signer
- open @gem, 'wb' do |gem_io|
+ @gem.with_write_io do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem|
add_metadata gem
add_contents gem
@@ -255,7 +265,7 @@ EOM
@contents = []
- open @gem, 'rb' do |io|
+ @gem.with_read_io do |io|
gem_tar = Gem::Package::TarReader.new io
gem_tar.each do |entry|
@@ -312,7 +322,7 @@ EOM
FileUtils.mkdir_p destination_dir
- open @gem, 'rb' do |io|
+ @gem.with_read_io do |io|
reader = Gem::Package::TarReader.new io
reader.each do |entry|
@@ -360,7 +370,7 @@ EOM
out.write entry.read
end if entry.file?
- say destination if Gem.configuration.really_verbose
+ verbose destination
end
end
end
@@ -490,7 +500,7 @@ EOM
@files = []
@spec = nil
- open @gem, 'rb' do |io|
+ @gem.with_read_io do |io|
Gem::Package::TarReader.new io do |reader|
read_checksums reader
@@ -592,6 +602,9 @@ EOM
end
require 'rubygems/package/digest_io'
+require 'rubygems/package/source'
+require 'rubygems/package/file_source'
+require 'rubygems/package/io_source'
require 'rubygems/package/old'
require 'rubygems/package/tar_header'
require 'rubygems/package/tar_reader'
diff --git a/lib/rubygems/package/file_source.rb b/lib/rubygems/package/file_source.rb
new file mode 100644
index 0000000000..85316f62e7
--- /dev/null
+++ b/lib/rubygems/package/file_source.rb
@@ -0,0 +1,33 @@
+##
+# The primary source of gems is a file on disk, including all usages
+# internal to rubygems.
+#
+# This is a private class, do not depend on it directly. Instead, pass a path
+# object to `Gem::Package.new`.
+
+class Gem::Package::FileSource < Gem::Package::Source # :nodoc: all
+
+ attr_reader :path
+
+ def initialize path
+ @path = path
+ end
+
+ def start
+ @start ||= File.read path, 20
+ end
+
+ def present?
+ File.exist? path
+ end
+
+ def with_write_io &block
+ open path, 'wb', &block
+ end
+
+ def with_read_io &block
+ open path, 'rb', &block
+ end
+
+end
+
diff --git a/lib/rubygems/package/io_source.rb b/lib/rubygems/package/io_source.rb
new file mode 100644
index 0000000000..f89593dd2d
--- /dev/null
+++ b/lib/rubygems/package/io_source.rb
@@ -0,0 +1,45 @@
+##
+# Supports reading and writing gems from/to a generic IO object. This is
+# useful for other applications built on top of rubygems, such as
+# rubygems.org.
+#
+# This is a private class, do not depend on it directly. Instead, pass an IO
+# object to `Gem::Package.new`.
+
+class Gem::Package::IOSource < Gem::Package::Source # :nodoc: all
+
+ attr_reader :io
+
+ def initialize io
+ @io = io
+ end
+
+ def start
+ @start ||= begin
+ if io.pos > 0
+ raise Gem::Package::Error, "Cannot read start unless IO is at start"
+ end
+
+ value = io.read 20
+ io.rewind
+ value
+ end
+ end
+
+ def present?
+ true
+ end
+
+ def with_read_io
+ yield io
+ end
+
+ def with_write_io
+ yield io
+ end
+
+ def path
+ end
+
+end
+
diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb
index 31c4111d33..65bcbb2283 100644
--- a/lib/rubygems/package/old.rb
+++ b/lib/rubygems/package/old.rb
@@ -37,7 +37,7 @@ class Gem::Package::Old < Gem::Package
return @contents if @contents
- open @gem, 'rb' do |io|
+ @gem.with_read_io do |io|
read_until_dashes io # spec
header = file_list io
@@ -53,7 +53,7 @@ class Gem::Package::Old < Gem::Package
errstr = "Error reading files from gem"
- open @gem, 'rb' do |io|
+ @gem.with_read_io do |io|
read_until_dashes io # spec
header = file_list io
raise Gem::Exception, errstr unless header
@@ -83,7 +83,7 @@ class Gem::Package::Old < Gem::Package
out.write file_data
end
- say destination if Gem.configuration.really_verbose
+ verbose destination
end
end
rescue Zlib::DataError
@@ -136,7 +136,7 @@ class Gem::Package::Old < Gem::Package
yaml = ''
- open @gem, 'rb' do |io|
+ @gem.with_read_io do |io|
skip_ruby io
read_until_dashes io do |line|
yaml << line
diff --git a/lib/rubygems/package/source.rb b/lib/rubygems/package/source.rb
new file mode 100644
index 0000000000..1f18d479da
--- /dev/null
+++ b/lib/rubygems/package/source.rb
@@ -0,0 +1,3 @@
+class Gem::Package::Source # :nodoc:
+end
+
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
index 7034e59210..737c7639c6 100644
--- a/lib/rubygems/package/tar_reader/entry.rb
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -129,6 +129,8 @@ class Gem::Package::TarReader::Entry
ret
end
+ alias readpartial read # :nodoc:
+
##
# Rewinds to the beginning of the tar file entry
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index 1bcd7549ad..fa56141631 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -17,7 +17,7 @@ class Gem::Platform
def self.local
arch = RbConfig::CONFIG['arch']
- arch = "#{arch}_60" if arch =~ /mswin32$/
+ arch = "#{arch}_60" if arch =~ /mswin(?:32|64)$/
@local ||= new(arch)
end
@@ -173,6 +173,7 @@ class Gem::Platform
when /^dalvik(\d+)?$/ then [nil, 'dalvik', $1 ]
when /dotnet(\-(\d+\.\d+))?/ then ['universal','dotnet', $2 ]
when /mswin32(\_(\d+))?/ then ['x86', 'mswin32', $2 ]
+ when /mswin64(\_(\d+))?/ then ['x64', 'mswin64', $2 ]
when 'powerpc-darwin' then ['powerpc', 'darwin', nil ]
when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1 ]
when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8' ]
diff --git a/lib/rubygems/rdoc.rb b/lib/rubygems/rdoc.rb
index 633bd893a5..180b95fbf3 100644
--- a/lib/rubygems/rdoc.rb
+++ b/lib/rubygems/rdoc.rb
@@ -263,7 +263,7 @@ class Gem::RDoc # :nodoc: all
Gem::Requirement.new('>= 2.4.0') =~ self.class.rdoc_version
r = new_rdoc
- say "rdoc #{args.join ' '}" if Gem.configuration.really_verbose
+ verbose { "rdoc #{args.join ' '}" }
Dir.chdir @spec.full_gem_path do
begin
@@ -279,7 +279,6 @@ class Gem::RDoc # :nodoc: all
ui.errs.puts "... RDOC args: #{args.join(' ')}"
ui.backtrace ex
ui.errs.puts "(continuing with the rest of the installation)"
- ensure
end
end
end
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index d5af458dfe..607827c47e 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -2,6 +2,7 @@ require 'rubygems'
require 'rubygems/request'
require 'rubygems/uri_formatter'
require 'rubygems/user_interaction'
+require 'rubygems/request/connection_pools'
require 'resolv'
##
@@ -73,6 +74,9 @@ class Gem::RemoteFetcher
Socket.do_not_reverse_lookup = true
@proxy = proxy
+ @pools = {}
+ @pool_lock = Mutex.new
+ @cert_files = Gem::Request.get_cert_files
@dns = dns
end
@@ -154,11 +158,10 @@ class Gem::RemoteFetcher
# REFACTOR: split this up and dispatch on scheme (eg download_http)
# REFACTOR: be sure to clean up fake fetcher when you do this... cleaner
case scheme
- when 'http', 'https' then
+ when 'http', 'https', 's3' then
unless File.exist? local_gem_path then
begin
- say "Downloading gem #{gem_file_name}" if
- Gem.configuration.really_verbose
+ verbose "Downloading gem #{gem_file_name}"
remote_gem_path = source_uri + "gems/#{gem_file_name}"
@@ -168,8 +171,7 @@ class Gem::RemoteFetcher
alternate_name = "#{spec.original_name}.gem"
- say "Failed, downloading gem #{alternate_name}" if
- Gem.configuration.really_verbose
+ verbose "Failed, downloading gem #{alternate_name}"
remote_gem_path = source_uri + "gems/#{alternate_name}"
@@ -188,8 +190,7 @@ class Gem::RemoteFetcher
local_gem_path = source_uri.to_s
end
- say "Using local gem #{local_gem_path}" if
- Gem.configuration.really_verbose
+ verbose "Using local gem #{local_gem_path}"
when nil then # TODO test for local overriding cache
source_path = if Gem.win_platform? && source_uri.scheme &&
!source_uri.path.include?(':') then
@@ -207,8 +208,7 @@ class Gem::RemoteFetcher
local_gem_path = source_uri.to_s
end
- say "Using local gem #{local_gem_path}" if
- Gem.configuration.really_verbose
+ verbose "Using local gem #{local_gem_path}"
else
raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}"
end
@@ -232,6 +232,7 @@ class Gem::RemoteFetcher
case response
when Net::HTTPOK, Net::HTTPNotModified then
+ response.uri = uri if response.respond_to? :uri
head ? response : response.body
when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
Net::HTTPTemporaryRedirect then
@@ -265,7 +266,7 @@ class Gem::RemoteFetcher
data = send "fetch_#{uri.scheme}", uri, mtime, head
- if data and !head and uri.to_s =~ /gz$/
+ if data and !head and uri.to_s =~ /\.gz$/
begin
data = Gem.gunzip data
rescue Zlib::GzipFile::Error
@@ -286,6 +287,11 @@ class Gem::RemoteFetcher
end
end
+ def fetch_s3(uri, mtime = nil, head = false)
+ public_uri = sign_s3_url(uri)
+ fetch_https public_uri, mtime, head
+ end
+
##
# Downloads +uri+ to +path+ if necessary. If no path is given, it just
# passes the data.
@@ -332,18 +338,57 @@ class Gem::RemoteFetcher
# connections to reduce connect overhead.
def request(uri, request_class, last_modified = nil)
- request = Gem::Request.new uri, request_class, last_modified, @proxy
+ proxy = proxy_for @proxy, uri
+ pool = pools_for(proxy).pool_for uri
+
+ request = Gem::Request.new uri, request_class, last_modified, pool
request.fetch do |req|
yield req if block_given?
end
- ensure
- request.close if request
end
def https?(uri)
uri.scheme.downcase == 'https'
end
+ protected
+
+ # we have our own signing code here to avoid a dependency on the aws-sdk gem
+ # fortunately, a simple GET request isn't too complex to sign properly
+ def sign_s3_url(uri, expiration = nil)
+ require 'base64'
+ require 'openssl'
+
+ unless uri.user && uri.password
+ raise FetchError.new("credentials needed in s3 source, like s3://key:secret@bucket-name/", uri.to_s)
+ end
+
+ expiration ||= s3_expiration
+ canonical_path = "/#{uri.host}#{uri.path}"
+ payload = "GET\n\n\n#{expiration}\n#{canonical_path}"
+ digest = OpenSSL::HMAC.digest('sha1', uri.password, payload)
+ # URI.escape is deprecated, and there isn't yet a replacement that does quite what we want
+ signature = Base64.encode64(digest).gsub("\n", '').gsub(/[\+\/=]/) { |c| BASE64_URI_TRANSLATE[c] }
+ URI.parse("https://#{uri.host}.s3.amazonaws.com#{uri.path}?AWSAccessKeyId=#{uri.user}&Expires=#{expiration}&Signature=#{signature}")
+ end
+
+ def s3_expiration
+ (Time.now + 3600).to_i # one hour from now
+ end
+
+ BASE64_URI_TRANSLATE = { '+' => '%2B', '/' => '%2F', '=' => '%3D' }.freeze
+
+ private
+
+ def proxy_for proxy, uri
+ Gem::Request.proxy_uri(proxy || Gem::Request.get_proxy_from_env(uri.scheme))
+ end
+
+ def pools_for proxy
+ @pool_lock.synchronize do
+ @pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files
+ end
+ end
end
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index cc99d30e92..702769c500 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -7,41 +7,43 @@ class Gem::Request
include Gem::UserInteraction
- attr_reader :proxy_uri
+ ###
+ # Legacy. This is used in tests.
+ def self.create_with_proxy uri, request_class, last_modified, proxy # :nodoc:
+ cert_files = get_cert_files
+ proxy ||= get_proxy_from_env(uri.scheme)
+ pool = ConnectionPools.new proxy_uri(proxy), cert_files
+
+ new(uri, request_class, last_modified, pool.pool_for(uri))
+ end
+
+ def self.proxy_uri proxy # :nodoc:
+ case proxy
+ when :no_proxy then nil
+ when URI::HTTP then proxy
+ else URI.parse(proxy)
+ end
+ end
- def initialize(uri, request_class, last_modified, proxy)
+ def initialize(uri, request_class, last_modified, pool)
@uri = uri
@request_class = request_class
@last_modified = last_modified
@requests = Hash.new 0
- @connections = {}
- @connections_mutex = Mutex.new
@user_agent = user_agent
- @proxy_uri =
- case proxy
- when :no_proxy then nil
- when nil then get_proxy_from_env uri.scheme
- when URI::HTTP then proxy
- else URI.parse(proxy)
- end
- @env_no_proxy = get_no_proxy_from_env
+ @connection_pool = pool
end
- def close
- @connections.each_value do |conn|
- conn.finish
- end
- end
+ def proxy_uri; @connection_pool.proxy_uri; end
+ def cert_files; @connection_pool.cert_files; end
- def add_rubygems_trusted_certs(store)
+ def self.get_cert_files
pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
- Dir.glob(pattern).each do |ssl_cert_file|
- store.add_file ssl_cert_file
- end
+ Dir.glob(pattern)
end
- def configure_connection_for_https(connection)
+ def self.configure_connection_for_https(connection, cert_files)
require 'net/https'
connection.use_ssl = true
connection.verify_mode =
@@ -55,7 +57,9 @@ class Gem::Request
end
store.set_default_paths
- add_rubygems_trusted_certs(store)
+ cert_files.each do |ssl_cert_file|
+ store.add_file ssl_cert_file
+ end
if Gem.configuration.ssl_ca_cert
if File.directory? Gem.configuration.ssl_ca_cert
store.add_path Gem.configuration.ssl_ca_cert
@@ -64,6 +68,7 @@ class Gem::Request
end
end
connection.cert_store = store
+ connection
rescue LoadError => e
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
e.message =~ / -- openssl$/
@@ -77,31 +82,7 @@ class Gem::Request
# connection, using a proxy if needed.
def connection_for(uri)
- net_http_args = [uri.host, uri.port]
-
- if @proxy_uri and not no_proxy?(uri.host) then
- net_http_args += [
- @proxy_uri.host,
- @proxy_uri.port,
- Gem::UriFormatter.new(@proxy_uri.user).unescape,
- Gem::UriFormatter.new(@proxy_uri.password).unescape,
- ]
- end
-
- connection_id = [Thread.current.object_id, *net_http_args].join ':'
-
- connection = @connections_mutex.synchronize do
- @connections[connection_id] ||= Net::HTTP.new(*net_http_args)
- @connections[connection_id]
- end
-
- if https?(uri) and not connection.started? then
- configure_connection_for_https(connection)
- end
-
- connection.start unless connection.started?
-
- connection
+ @connection_pool.checkout
rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
Errno::EHOSTDOWN => e
raise Gem::RemoteFetcher::FetchError.new(e.message, uri)
@@ -125,6 +106,37 @@ class Gem::Request
yield request if block_given?
+ perform_request request
+ end
+
+ ##
+ # Returns a proxy URI for the given +scheme+ if one is set in the
+ # environment variables.
+
+ def self.get_proxy_from_env scheme = 'http'
+ _scheme = scheme.downcase
+ _SCHEME = scheme.upcase
+ env_proxy = ENV["#{_scheme}_proxy"] || ENV["#{_SCHEME}_PROXY"]
+
+ no_env_proxy = env_proxy.nil? || env_proxy.empty?
+
+ return get_proxy_from_env 'http' if no_env_proxy and _scheme != 'http'
+ return :no_proxy if no_env_proxy
+
+ uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
+
+ if uri and uri.user.nil? and uri.password.nil? then
+ user = ENV["#{_scheme}_proxy_user"] || ENV["#{_SCHEME}_PROXY_USER"]
+ password = ENV["#{_scheme}_proxy_pass"] || ENV["#{_SCHEME}_PROXY_PASS"]
+
+ uri.user = Gem::UriFormatter.new(user).escape
+ uri.password = Gem::UriFormatter.new(password).escape
+ end
+
+ uri
+ end
+
+ def perform_request request # :nodoc:
connection = connection_for @uri
retried = false
@@ -133,8 +145,7 @@ class Gem::Request
begin
@requests[connection.object_id] += 1
- say "#{request.method} #{@uri}" if
- Gem.configuration.really_verbose
+ verbose "#{request.method} #{@uri}"
file_name = File.basename(@uri.path)
# perform download progress reporter only for gems
@@ -163,11 +174,10 @@ class Gem::Request
response = connection.request request
end
- say "#{response.code} #{response.message}" if
- Gem.configuration.really_verbose
+ verbose "#{response.code} #{response.message}"
rescue Net::HTTPBadResponse
- say "bad response" if Gem.configuration.really_verbose
+ verbose "bad response"
reset connection
@@ -182,8 +192,7 @@ class Gem::Request
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
requests = @requests[connection.object_id]
- say "connection reset after #{requests} requests, retrying" if
- Gem.configuration.really_verbose
+ verbose "connection reset after #{requests} requests, retrying"
raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried
@@ -194,57 +203,8 @@ class Gem::Request
end
response
- end
-
- ##
- # Returns list of no_proxy entries (if any) from the environment
-
- def get_no_proxy_from_env
- env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
-
- return [] if env_no_proxy.nil? or env_no_proxy.empty?
-
- env_no_proxy.split(/\s*,\s*/)
- end
-
- ##
- # Returns a proxy URI for the given +scheme+ if one is set in the
- # environment variables.
-
- def get_proxy_from_env scheme = 'http'
- _scheme = scheme.downcase
- _SCHEME = scheme.upcase
- env_proxy = ENV["#{_scheme}_proxy"] || ENV["#{_SCHEME}_PROXY"]
-
- no_env_proxy = env_proxy.nil? || env_proxy.empty?
-
- return get_proxy_from_env 'http' if no_env_proxy and _scheme != 'http'
- return nil if no_env_proxy
-
- uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
-
- if uri and uri.user.nil? and uri.password.nil? then
- user = ENV["#{_scheme}_proxy_user"] || ENV["#{_SCHEME}_PROXY_USER"]
- password = ENV["#{_scheme}_proxy_pass"] || ENV["#{_SCHEME}_PROXY_PASS"]
-
- uri.user = Gem::UriFormatter.new(user).escape
- uri.password = Gem::UriFormatter.new(password).escape
- end
-
- uri
- end
-
- def https?(uri)
- uri.scheme.downcase == 'https'
- end
-
- def no_proxy? host
- host = host.downcase
- @env_no_proxy.each do |pattern|
- pattern = pattern.downcase
- return true if host[-pattern.length, pattern.length ] == pattern
- end
- return false
+ ensure
+ @connection_pool.checkin connection
end
##
@@ -278,3 +238,7 @@ class Gem::Request
end
+require 'rubygems/request/http_pool'
+require 'rubygems/request/https_pool'
+require 'rubygems/request/connection_pools'
+
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
new file mode 100644
index 0000000000..27ee99c20d
--- /dev/null
+++ b/lib/rubygems/request/connection_pools.rb
@@ -0,0 +1,79 @@
+require 'thread'
+
+class Gem::Request::ConnectionPools # :nodoc:
+
+ @client = Net::HTTP
+
+ class << self
+ attr_accessor :client
+ end
+
+ def initialize proxy_uri, cert_files
+ @proxy_uri = proxy_uri
+ @cert_files = cert_files
+ @pools = {}
+ @pool_mutex = Mutex.new
+ end
+
+ def pool_for uri
+ http_args = net_http_args(uri, @proxy_uri)
+ key = http_args + [https?(uri)]
+ @pool_mutex.synchronize do
+ @pools[key] ||=
+ if https? uri then
+ Gem::Request::HTTPSPool.new(http_args, @cert_files, @proxy_uri)
+ else
+ Gem::Request::HTTPPool.new(http_args, @cert_files, @proxy_uri)
+ end
+ end
+ end
+
+ private
+
+ ##
+ # Returns list of no_proxy entries (if any) from the environment
+
+ def get_no_proxy_from_env
+ env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
+
+ return [] if env_no_proxy.nil? or env_no_proxy.empty?
+
+ env_no_proxy.split(/\s*,\s*/)
+ end
+
+ def https? uri
+ uri.scheme.downcase == 'https'
+ end
+
+ def no_proxy? host, env_no_proxy
+ host = host.downcase
+
+ env_no_proxy.any? do |pattern|
+ pattern = pattern.downcase
+
+ host[-pattern.length, pattern.length] == pattern or
+ (pattern.start_with? '.' and pattern[1..-1] == host)
+ end
+ end
+
+ def net_http_args uri, proxy_uri
+ net_http_args = [uri.host, uri.port]
+
+ no_proxy = get_no_proxy_from_env
+
+ if proxy_uri and not no_proxy?(uri.host, no_proxy) then
+ net_http_args + [
+ proxy_uri.host,
+ proxy_uri.port,
+ Gem::UriFormatter.new(proxy_uri.user).unescape,
+ Gem::UriFormatter.new(proxy_uri.password).unescape,
+ ]
+ elsif no_proxy? uri.host, no_proxy then
+ net_http_args += [nil, nil]
+ else
+ net_http_args
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/request/http_pool.rb b/lib/rubygems/request/http_pool.rb
new file mode 100644
index 0000000000..61c8884af7
--- /dev/null
+++ b/lib/rubygems/request/http_pool.rb
@@ -0,0 +1,38 @@
+##
+# A connection "pool" that only manages one connection for now. Provides
+# thread safe `checkout` and `checkin` methods. The pool consists of one
+# connection that corresponds to `http_args`. This class is private, do not
+# use it.
+
+class Gem::Request::HTTPPool # :nodoc:
+ attr_reader :cert_files, :proxy_uri
+
+ def initialize http_args, cert_files, proxy_uri
+ @http_args = http_args
+ @cert_files = cert_files
+ @proxy_uri = proxy_uri
+ @queue = SizedQueue.new 1
+ @queue << nil
+ end
+
+ def checkout
+ @queue.pop || make_connection
+ end
+
+ def checkin connection
+ @queue.push connection
+ end
+
+ private
+
+ def make_connection
+ setup_connection Gem::Request::ConnectionPools.client.new(*@http_args)
+ end
+
+ def setup_connection connection
+ connection.start
+ connection
+ end
+
+end
+
diff --git a/lib/rubygems/request/https_pool.rb b/lib/rubygems/request/https_pool.rb
new file mode 100644
index 0000000000..2e3da0a44e
--- /dev/null
+++ b/lib/rubygems/request/https_pool.rb
@@ -0,0 +1,10 @@
+class Gem::Request::HTTPSPool < Gem::Request::HTTPPool # :nodoc:
+ private
+
+ def setup_connection connection
+ Gem::Request.configure_connection_for_https(connection, @cert_files)
+ super
+ end
+end
+
+
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index fb54e344bd..d12e06358d 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -1,4 +1,3 @@
-require 'rubygems'
require 'tsort'
##
@@ -21,13 +20,23 @@ class Gem::RequestSet
##
# Array of gems to install even if already installed
- attr_reader :always_install
+ attr_accessor :always_install
attr_reader :dependencies
attr_accessor :development
##
+ # Errors fetching gems during resolution.
+
+ attr_reader :errors
+
+ ##
+ # Set to true if you want to install only direct development dependencies.
+
+ attr_accessor :development_shallow
+
+ ##
# The set of git gems imported via load_gemdeps.
attr_reader :git_set # :nodoc:
@@ -38,11 +47,20 @@ class Gem::RequestSet
attr_accessor :ignore_dependencies
+ attr_reader :install_dir # :nodoc:
+
+ ##
+ # If true, allow dependencies to match prerelease gems.
+
+ attr_accessor :prerelease
+
##
# When false no remote sets are used for resolving gems.
attr_accessor :remote
+ attr_reader :resolver # :nodoc:
+
##
# Sets used for resolution
@@ -71,11 +89,15 @@ class Gem::RequestSet
@dependencies = deps
@always_install = []
+ @conservative = false
@dependency_names = {}
@development = false
+ @development_shallow = false
+ @errors = []
@git_set = nil
@ignore_dependencies = false
@install_dir = Gem.dir
+ @prerelease = false
@remote = true
@requests = []
@sets = []
@@ -116,12 +138,14 @@ class Gem::RequestSet
def install options, &block # :yields: request, installer
if dir = options[:install_dir]
- return install_into dir, false, options, &block
+ requests = install_into dir, false, options, &block
+ return requests
end
cache_dir = options[:cache_dir] || Gem.dir
+ @prerelease = options[:prerelease]
- specs = []
+ requests = []
sorted_requests.each do |req|
if req.installed? then
@@ -139,10 +163,30 @@ class Gem::RequestSet
yield req, inst if block_given?
- specs << inst.install
+ requests << inst.install
+ end
+
+ requests
+ ensure
+ raise if $!
+ return requests if options[:gemdeps]
+
+ specs = requests.map do |request|
+ case request
+ when Gem::Resolver::ActivationRequest then
+ request.spec.spec
+ else
+ request
+ end
end
- specs
+ require 'rubygems/dependency_installer'
+ inst = Gem::DependencyInstaller.new options
+ inst.installed_gems.replace specs
+
+ Gem.done_installing_hooks.each do |hook|
+ hook.call inst, specs
+ end unless Gem.done_installing_hooks.empty?
end
##
@@ -156,17 +200,19 @@ class Gem::RequestSet
gemdeps = options[:gemdeps]
@install_dir = options[:install_dir] || Gem.dir
+ @prerelease = options[:prerelease]
@remote = options[:domain] != :local
+ @conservative = true if options[:conservative]
- load_gemdeps gemdeps, options[:without_groups]
+ gem_deps_api = load_gemdeps gemdeps, options[:without_groups], true
resolve
if options[:explain]
puts "Gems to install:"
- specs.map { |s| s.full_name }.sort.each do |s|
- puts " #{s}"
+ sorted_requests.each do |spec|
+ puts " #{spec.full_name}"
end
if Gem.configuration.really_verbose
@@ -175,8 +221,11 @@ class Gem::RequestSet
else
installed = install options, &block
- lockfile = Gem::RequestSet::Lockfile.new self, gemdeps
- lockfile.write
+ if options.fetch :lock, true then
+ lockfile =
+ Gem::RequestSet::Lockfile.new self, gemdeps, gem_deps_api.dependencies
+ lockfile.write
+ end
installed
end
@@ -192,8 +241,10 @@ class Gem::RequestSet
installed = []
+ options[:development] = false
options[:install_dir] = dir
options[:only_install_dir] = true
+ @prerelease = options[:prerelease]
sorted_requests.each do |request|
spec = request.spec
@@ -218,7 +269,7 @@ class Gem::RequestSet
##
# Load a dependency management file.
- def load_gemdeps path, without_groups = []
+ def load_gemdeps path, without_groups = [], installing = false
@git_set = Gem::Resolver::GitSet.new
@vendor_set = Gem::Resolver::VendorSet.new
@@ -228,6 +279,7 @@ class Gem::RequestSet
lockfile.parse
gf = Gem::RequestSet::GemDependencyAPI.new self, path
+ gf.installing = installing
gf.without_groups = without_groups if without_groups
gf.load
end
@@ -243,15 +295,29 @@ class Gem::RequestSet
set = Gem::Resolver.compose_sets(*@sets)
set.remote = @remote
+ set.prerelease = @prerelease
resolver = Gem::Resolver.new @dependencies, set
resolver.development = @development
+ resolver.development_shallow = @development_shallow
resolver.ignore_dependencies = @ignore_dependencies
resolver.soft_missing = @soft_missing
+ if @conservative
+ installed_gems = {}
+ Gem::Specification.find_all do |spec|
+ (installed_gems[spec.name] ||= []) << spec
+ end
+ resolver.skip_gems = installed_gems
+ end
+
@resolver = resolver
@requests = resolver.resolve
+
+ @errors = set.errors
+
+ @requests
end
##
@@ -284,16 +350,20 @@ class Gem::RequestSet
node.spec.dependencies.each do |dep|
next if dep.type == :development and not @development
- match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
- if match
- begin
- yield match
- rescue TSort::Cyclic
- end
- else
- unless @soft_missing
- raise Gem::DependencyError, "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
- end
+ match = @requests.find { |r|
+ dep.match? r.spec.name, r.spec.version, @prerelease
+ }
+
+ unless match then
+ next if dep.type == :development and @development_shallow
+ next if @soft_missing
+ raise Gem::DependencyError,
+ "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
+ end
+
+ begin
+ yield match
+ rescue TSort::Cyclic
end
end
end
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index efce979177..24179dd1ed 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -1,5 +1,33 @@
##
-# A semi-compatible DSL for the Bundler Gemfile and Isolate formats.
+# A semi-compatible DSL for the Bundler Gemfile and Isolate gem dependencies
+# files.
+#
+# To work with both the Bundler Gemfile and Isolate formats this
+# implementation takes some liberties to allow compatibility with each, most
+# notably in #source.
+#
+# A basic gem dependencies file will look like the following:
+#
+# source 'https://rubygems.org'
+#
+# gem 'rails', '3.2.14a
+# gem 'devise', '~> 2.1', '>= 2.1.3'
+# gem 'cancan'
+# gem 'airbrake'
+# gem 'pg'
+#
+# RubyGems recommends saving this as gem.deps.rb over Gemfile or Isolate.
+#
+# To install the gems in this Gemfile use `gem install -g` to install it and
+# create a lockfile. The lockfile will ensure that when you make changes to
+# your gem dependencies file a minimum amount of change is made to the
+# dependencies of your gems.
+#
+# RubyGems can activate all the gems in your dependencies file at startup
+# using the RUBYGEMS_GEMDEPS environment variable or through Gem.use_gemdeps.
+# See Gem.use_gemdeps for details and warnings.
+#
+# See `gem help install` and `gem help gem_dependencies` for further details.
class Gem::RequestSet::GemDependencyAPI
@@ -21,6 +49,8 @@ class Gem::RequestSet::GemDependencyAPI
:ruby_21 => %w[ruby rbx maglev],
}
+ mswin = Gem::Platform.new 'x86-mswin32'
+ mswin64 = Gem::Platform.new 'x64-mswin64'
x86_mingw = Gem::Platform.new 'x86-mingw32'
x64_mingw = Gem::Platform.new 'x64-mingw32'
@@ -39,7 +69,15 @@ class Gem::RequestSet::GemDependencyAPI
:mri_19 => Gem::Platform::RUBY,
:mri_20 => Gem::Platform::RUBY,
:mri_21 => Gem::Platform::RUBY,
- :mswin => Gem::Platform::RUBY,
+ :mswin => mswin,
+ :mswin_18 => mswin,
+ :mswin_19 => mswin,
+ :mswin_20 => mswin,
+ :mswin_21 => mswin,
+ :mswin64 => mswin64,
+ :mswin64_19 => mswin64,
+ :mswin64_20 => mswin64,
+ :mswin64_21 => mswin64,
:rbx => Gem::Platform::RUBY,
:ruby => Gem::Platform::RUBY,
:ruby_18 => Gem::Platform::RUBY,
@@ -73,6 +111,14 @@ class Gem::RequestSet::GemDependencyAPI
:mri_20 => tilde_gt_2_0_0,
:mri_21 => tilde_gt_2_1_0,
:mswin => gt_eq_0,
+ :mswin_18 => tilde_gt_1_8_0,
+ :mswin_19 => tilde_gt_1_9_0,
+ :mswin_20 => tilde_gt_2_0_0,
+ :mswin_21 => tilde_gt_2_1_0,
+ :mswin64 => gt_eq_0,
+ :mswin64_19 => tilde_gt_1_9_0,
+ :mswin64_20 => tilde_gt_2_0_0,
+ :mswin64_21 => tilde_gt_2_1_0,
:rbx => gt_eq_0,
:ruby => gt_eq_0,
:ruby_18 => tilde_gt_1_8_0,
@@ -96,6 +142,14 @@ class Gem::RequestSet::GemDependencyAPI
:mri_20 => :never,
:mri_21 => :never,
:mswin => :only,
+ :mswin_18 => :only,
+ :mswin_19 => :only,
+ :mswin_20 => :only,
+ :mswin_21 => :only,
+ :mswin64 => :only,
+ :mswin64_19 => :only,
+ :mswin64_20 => :only,
+ :mswin64_21 => :only,
:rbx => :never,
:ruby => :never,
:ruby_18 => :never,
@@ -108,6 +162,11 @@ class Gem::RequestSet::GemDependencyAPI
}
##
+ # The gems required by #gem statements in the gem.deps.rb file
+
+ attr_reader :dependencies
+
+ ##
# A set of gems that are loaded via the +:git+ option to #gem
attr_reader :git_set # :nodoc:
@@ -136,14 +195,31 @@ class Gem::RequestSet::GemDependencyAPI
@path = path
@current_groups = nil
- @current_platform = nil
+ @current_platforms = nil
@current_repository = nil
+ @dependencies = {}
@default_sources = true
@git_set = @set.git_set
+ @git_sources = {}
+ @installing = false
@requires = Hash.new { |h, name| h[name] = [] }
@vendor_set = @set.vendor_set
@gem_sources = {}
@without_groups = []
+
+ git_source :github do |repo_name|
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/"
+
+ "git://github.com/#{repo_name}.git"
+ end
+
+ git_source :bitbucket do |repo_name|
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include? "/"
+
+ user, = repo_name.split "/", 2
+
+ "https://#{user}@bitbucket.org/#{repo_name}.git"
+ end
end
##
@@ -187,14 +263,26 @@ class Gem::RequestSet::GemDependencyAPI
end
##
- # Loads the gem dependency file
+ # Changes the behavior of gem dependency file loading to installing mode.
+ # In installing mode certain restrictions are ignored such as ruby version
+ # mismatch checks.
+
+ def installing= installing # :nodoc:
+ @installing = installing
+ end
+
+ ##
+ # Loads the gem dependency file and returns self.
def load
instance_eval File.read(@path).untaint, @path, 1
+
+ self
end
##
# :category: Gem Dependencies DSL
+ #
# :call-seq:
# gem(name)
# gem(name, *requirements)
@@ -202,6 +290,66 @@ class Gem::RequestSet::GemDependencyAPI
#
# Specifies a gem dependency with the given +name+ and +requirements+. You
# may also supply +options+ following the +requirements+
+ #
+ # +options+ include:
+ #
+ # require: ::
+ # RubyGems does not provide any autorequire features so requires in a gem
+ # dependencies file are recorded but ignored.
+ #
+ # In bundler the require: option overrides the file to require during
+ # Bundler.require. By default the name of the dependency is required in
+ # Bundler. A single file or an Array of files may be given.
+ #
+ # To disable requiring any file give +false+:
+ #
+ # gem 'rake', require: false
+ #
+ # group: ::
+ # Place the dependencies in the given dependency group. A single group or
+ # an Array of groups may be given.
+ #
+ # See also #group
+ #
+ # platform: ::
+ # Only install the dependency on the given platform. A single platform or
+ # an Array of platforms may be given.
+ #
+ # See #platform for a list of platforms available.
+ #
+ # path: ::
+ # Install this dependency from an unpacked gem in the given directory.
+ #
+ # gem 'modified_gem', path: 'vendor/modified_gem'
+ #
+ # git: ::
+ # Install this dependency from a git repository:
+ #
+ # gem 'private_gem', git: git@my.company.example:private_gem.git'
+ #
+ # gist: ::
+ # Install this dependency from the gist ID:
+ #
+ # gem 'bang', gist: '1232884'
+ #
+ # github: ::
+ # Install this dependency from a github git repository:
+ #
+ # gem 'private_gem', github: 'my_company/private_gem'
+ #
+ # submodules: ::
+ # Set to +true+ to include submodules when fetching the git repository for
+ # git:, gist: and github: dependencies.
+ #
+ # ref: ::
+ # Use the given commit name or SHA for git:, gist: and github:
+ # dependencies.
+ #
+ # branch: ::
+ # Use the given branch for git:, gist: and github: dependencies.
+ #
+ # tag: ::
+ # Use the given tag for git:, gist: and github: dependencies.
def gem name, *requirements
options = requirements.pop if requirements.last.kind_of?(Hash)
@@ -211,9 +359,20 @@ class Gem::RequestSet::GemDependencyAPI
source_set = false
- source_set ||= gem_path name, options
- source_set ||= gem_git name, options
- source_set ||= gem_github name, options
+ source_set ||= gem_path name, options
+ source_set ||= gem_git name, options
+ source_set ||= gem_git_source name, options
+
+ duplicate = @dependencies.include? name
+
+ @dependencies[name] =
+ if requirements.empty? and not source_set then
+ nil
+ elsif source_set then
+ '!'
+ else
+ requirements
+ end
return unless gem_platforms options
@@ -225,6 +384,12 @@ class Gem::RequestSet::GemDependencyAPI
gem_requires name, options
+ if duplicate then
+ warn <<-WARNING
+Gem dependencies file #{@path} requires #{name} more than once.
+ WARNING
+ end
+
@set.gem name, *requirements
end
@@ -258,21 +423,27 @@ class Gem::RequestSet::GemDependencyAPI
private :gem_git
##
- # Handles the github: option from +options+ for gem +name+.
+ # Handles a git gem option from +options+ for gem +name+ for a git source
+ # registered through git_source.
#
- # Returns +true+ if the path option was handled.
+ # Returns +true+ if the custom source option was handled.
- def gem_github name, options # :nodoc:
- return unless path = options.delete(:github)
+ def gem_git_source name, options # :nodoc:
+ return unless git_source = (@git_sources.keys & options.keys).last
- options[:git] = "git://github.com/#{path}.git"
+ source_callback = @git_sources[git_source]
+ source_param = options.delete git_source
+
+ git_url = source_callback.call source_param
+
+ options[:git] = git_url
gem_git name, options
true
end
- private :gem_github
+ private :gem_git_source
##
# Handles the :group and :groups +options+ for the gem with the given
@@ -314,8 +485,9 @@ class Gem::RequestSet::GemDependencyAPI
# platform matches the current platform.
def gem_platforms options # :nodoc:
- platform_names = Array(options.delete :platforms)
- platform_names << @current_platform if @current_platform
+ platform_names = Array(options.delete :platform)
+ platform_names.concat Array(options.delete :platforms)
+ platform_names.concat @current_platforms if @current_platforms
return true if platform_names.empty?
@@ -343,7 +515,7 @@ class Gem::RequestSet::GemDependencyAPI
private :gem_platforms
##
- # Handles the require: option from +options+ and adds those files, or the
+ # Records the require: option from +options+ and adds those files, or the
# default file to the require list for +name+.
def gem_requires name, options # :nodoc:
@@ -362,6 +534,11 @@ class Gem::RequestSet::GemDependencyAPI
# :category: Gem Dependencies DSL
#
# Block form for specifying gems from a git +repository+.
+ #
+ # git 'https://github.com/rails/rails.git' do
+ # gem 'activesupport'
+ # gem 'activerecord'
+ # end
def git repository
@current_repository = repository
@@ -373,6 +550,15 @@ class Gem::RequestSet::GemDependencyAPI
end
##
+ # Defines a custom git source that uses +name+ to expand git repositories
+ # for use in gems built from git repositories. You must provide a block
+ # that accepts a git repository name for expansion.
+
+ def git_source name, &callback
+ @git_sources[name] = callback
+ end
+
+ ##
# Returns the basename of the file the dependencies were loaded from
def gem_deps_file # :nodoc:
@@ -383,6 +569,23 @@ class Gem::RequestSet::GemDependencyAPI
# :category: Gem Dependencies DSL
#
# Loads dependencies from a gemspec file.
+ #
+ # +options+ include:
+ #
+ # name: ::
+ # The name portion of the gemspec file. Defaults to searching for any
+ # gemspec file in the current directory.
+ #
+ # gemspec name: 'my_gem'
+ #
+ # path: ::
+ # The path the gemspec lives in. Defaults to the current directory:
+ #
+ # gemspec 'my_gem', path: 'gemspecs', name: 'my_gem'
+ #
+ # development_group: ::
+ # The group to add development dependencies to. By default this is
+ # :development. Only one group may be specified.
def gemspec options = {}
name = options.delete(:name) || '{,*}'
@@ -404,7 +607,20 @@ class Gem::RequestSet::GemDependencyAPI
##
# :category: Gem Dependencies DSL
+ #
# Block form for placing a dependency in the given +groups+.
+ #
+ # group :development do
+ # gem 'debugger'
+ # end
+ #
+ # group :development, :test do
+ # gem 'minitest'
+ # end
+ #
+ # Groups can be excluded at install time using `gem install -g --without
+ # development`. See `gem help install` and `gem help gem_dependencies` for
+ # further details.
def group *groups
@current_groups = groups
@@ -440,28 +656,72 @@ class Gem::RequestSet::GemDependencyAPI
##
# :category: Gem Dependencies DSL
#
- # Block form for restricting gems to a particular platform.
+ # Block form for restricting gems to a set of platforms.
+ #
+ # The gem dependencies platform is different from Gem::Platform. A platform
+ # gem.deps.rb platform matches on the ruby engine, the ruby version and
+ # whether or not windows is allowed.
+ #
+ # :ruby, :ruby_XY ::
+ # Matches non-windows, non-jruby implementations where X and Y can be used
+ # to match releases in the 1.8, 1.9, 2.0 or 2.1 series.
+ #
+ # :mri, :mri_XY ::
+ # Matches non-windows C Ruby (Matz Ruby) or only the 1.8, 1.9, 2.0 or
+ # 2.1 series.
+ #
+ # :mingw, :mingw_XY ::
+ # Matches 32 bit C Ruby on MinGW or only the 1.8, 1.9, 2.0 or 2.1 series.
+ #
+ # :x64_mingw, :x64_mingw_XY ::
+ # Matches 64 bit C Ruby on MinGW or only the 1.8, 1.9, 2.0 or 2.1 series.
+ #
+ # :mswin, :mswin_XY ::
+ # Matches 32 bit C Ruby on Microsoft Windows or only the 1.8, 1.9, 2.0 or
+ # 2.1 series.
+ #
+ # :mswin64, :mswin64_XY ::
+ # Matches 64 bit C Ruby on Microsoft Windows or only the 1.8, 1.9, 2.0 or
+ # 2.1 series.
+ #
+ # :jruby, :jruby_XY ::
+ # Matches JRuby or JRuby in 1.8 or 1.9 mode.
+ #
+ # :maglev ::
+ # Matches Maglev
+ #
+ # :rbx ::
+ # Matches non-windows Rubinius
+ #
+ # NOTE: There is inconsistency in what environment a platform matches. You
+ # may need to read the source to know the exact details.
- def platform what
- @current_platform = what
+ def platform *platforms
+ @current_platforms = platforms
yield
ensure
- @current_platform = nil
+ @current_platforms = nil
end
##
# :category: Gem Dependencies DSL
#
- # Block form for restricting gems to a particular platform.
+ # Block form for restricting gems to a particular set of platforms. See
+ # #platform.
alias :platforms :platform
##
# :category: Gem Dependencies DSL
- # Restricts this gem dependencies file to the given ruby +version+. The
- # +:engine+ options from Bundler are currently ignored.
+ #
+ # Restricts this gem dependencies file to the given ruby +version+.
+ #
+ # You may also provide +engine:+ and +engine_version:+ options to restrict
+ # this gem dependencies file to a particular ruby engine and its engine
+ # version. This matching is performed by using the RUBY_ENGINE and
+ # engine_specific VERSION constants. (For JRuby, JRUBY_VERSION).
def ruby version, options = {}
engine = options[:engine]
@@ -471,6 +731,8 @@ class Gem::RequestSet::GemDependencyAPI
'you must specify engine_version along with the ruby engine' if
engine and not engine_version
+ return true if @installing
+
unless RUBY_VERSION == version then
message = "Your Ruby version is #{RUBY_VERSION}, " +
"but your #{gem_deps_file} requires #{version}"
@@ -503,7 +765,16 @@ class Gem::RequestSet::GemDependencyAPI
##
# :category: Gem Dependencies DSL
#
- # Sets +url+ as a source for gems for this dependency API.
+ # Sets +url+ as a source for gems for this dependency API. RubyGems uses
+ # the default configured sources if no source was given. If a source is set
+ # only that source is used.
+ #
+ # This method differs in behavior from Bundler:
+ #
+ # * The +:gemcutter+, # +:rubygems+ and +:rubyforge+ sources are not
+ # supported as they are deprecated in bundler.
+ # * The +prepend:+ option is not supported. If you wish to order sources
+ # then list them in your preferred order.
def source url
Gem.sources.clear if @default_sources
diff --git a/lib/rubygems/request_set/lockfile.rb b/lib/rubygems/request_set/lockfile.rb
index 0433d2a7fc..2901dba871 100644
--- a/lib/rubygems/request_set/lockfile.rb
+++ b/lib/rubygems/request_set/lockfile.rb
@@ -49,11 +49,14 @@ class Gem::RequestSet::Lockfile
# Creates a new Lockfile for the given +request_set+ and +gem_deps_file+
# location.
- def initialize request_set, gem_deps_file
+ def initialize request_set, gem_deps_file, dependencies = nil
@set = request_set
+ @dependencies = dependencies
@gem_deps_file = File.expand_path(gem_deps_file)
@gem_deps_dir = File.dirname(@gem_deps_file)
+ @gem_deps_file.untaint unless gem_deps_file.tainted?
+
@current_token = nil
@line = 0
@line_pos = 0
@@ -64,19 +67,42 @@ class Gem::RequestSet::Lockfile
def add_DEPENDENCIES out # :nodoc:
out << "DEPENDENCIES"
- @requests.sort_by { |r| r.name }.each do |request|
- spec = request.spec
-
- if [Gem::Resolver::VendorSpecification,
- Gem::Resolver::GitSpecification].include? spec.class then
- out << " #{request.name}!"
+ dependencies =
+ if @dependencies then
+ @dependencies.sort_by { |name,| name }.map do |name, requirement|
+ requirement_string =
+ if '!' == requirement then
+ requirement
+ else
+ Gem::Requirement.new(requirement).for_lockfile
+ end
+
+ [name, requirement_string]
+ end
else
- requirement = request.request.dependency.requirement
-
- out << " #{request.name}#{requirement.for_lockfile}"
+ @requests.sort_by { |r| r.name }.map do |request|
+ spec = request.spec
+ name = request.name
+ requirement = request.request.dependency.requirement
+
+ requirement_string =
+ if [Gem::Resolver::VendorSpecification,
+ Gem::Resolver::GitSpecification].include? spec.class then
+ "!"
+ else
+ requirement.for_lockfile
+ end
+
+ [name, requirement_string]
+ end
end
+
+ dependencies = dependencies.map do |name, requirement_string|
+ " #{name}#{requirement_string}"
end
+ out.concat dependencies
+
out << nil
end
@@ -93,12 +119,15 @@ class Gem::RequestSet::Lockfile
out << " specs:"
requests.sort_by { |request| request.name }.each do |request|
+ next if request.spec.name == 'bundler'
platform = "-#{request.spec.platform}" unless
Gem::Platform::RUBY == request.spec.platform
out << " #{request.name} (#{request.version}#{platform})"
request.full_spec.dependencies.sort.each do |dependency|
+ next if dependency.type == :development
+
requirement = dependency.requirement
out << " #{dependency.name}#{requirement.for_lockfile}"
end
@@ -166,9 +195,8 @@ class Gem::RequestSet::Lockfile
out << "PLATFORMS"
platforms = @requests.map { |request| request.spec.platform }.uniq
- platforms.delete Gem::Platform::RUBY if platforms.length > 1
- platforms.each do |platform|
+ platforms.sort.each do |platform|
out << " #{platform}"
end
@@ -250,7 +278,7 @@ class Gem::RequestSet::Lockfile
Gem::Resolver::VendorSet === set
}.map { |set|
set.specs[name]
- }.first
+ }.compact.first
requirements << spec.version
when :l_paren then
@@ -277,26 +305,33 @@ class Gem::RequestSet::Lockfile
end
def parse_GEM # :nodoc:
- get :entry, 'remote'
- _, data, = get :text
+ sources = []
+
+ while [:entry, 'remote'] == peek.first(2) do
+ get :entry, 'remote'
+ _, data, = get :text
+ skip :newline
- source = Gem::Source.new data
+ sources << Gem::Source.new(data)
+ end
- skip :newline
+ sources << Gem::Source.new(Gem::DEFAULT_HOST) if sources.empty?
get :entry, 'specs'
skip :newline
- set = Gem::Resolver::LockSet.new source
- last_spec = nil
+ set = Gem::Resolver::LockSet.new sources
+ last_specs = nil
while not @tokens.empty? and :text == peek.first do
_, name, column, = get :text
case peek[0]
when :newline then
- last_spec.add_dependency Gem::Dependency.new name if column == 6
+ last_specs.each do |spec|
+ spec.add_dependency Gem::Dependency.new name if column == 6
+ end
when :l_paren then
get :l_paren
@@ -308,11 +343,13 @@ class Gem::RequestSet::Lockfile
platform =
platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
- last_spec = set.add name, version, platform
+ last_specs = set.add name, version, platform
else
dependency = parse_dependency name, data
- last_spec.add_dependency dependency
+ last_specs.each do |spec|
+ spec.add_dependency dependency
+ end
end
get :r_paren
@@ -337,11 +374,21 @@ class Gem::RequestSet::Lockfile
skip :newline
+ type, value = peek.first 2
+ if type == :entry and %w[branch ref tag].include? value then
+ get
+ get :text
+
+ skip :newline
+ end
+
get :entry, 'specs'
skip :newline
set = Gem::Resolver::GitSet.new
+ set.root_dir = @set.install_dir
+
last_spec = nil
while not @tokens.empty? and :text == peek.first do
@@ -360,7 +407,7 @@ class Gem::RequestSet::Lockfile
else
dependency = parse_dependency name, data
- last_spec.spec.dependencies << dependency
+ last_spec.add_dependency dependency
end
get :r_paren
@@ -403,7 +450,7 @@ class Gem::RequestSet::Lockfile
else
dependency = parse_dependency name, data
- last_spec.spec.dependencies << dependency
+ last_spec.dependencies << dependency
end
get :r_paren
@@ -432,7 +479,7 @@ class Gem::RequestSet::Lockfile
# the first token of the requirements and returns a Gem::Dependency object.
def parse_dependency name, op # :nodoc:
- return Gem::Dependency.new name unless peek[0] == :text
+ return Gem::Dependency.new name, op unless peek[0] == :text
_, version, = get :text
@@ -575,8 +622,10 @@ class Gem::RequestSet::Lockfile
# Writes the lock file alongside the gem dependencies file
def write
+ content = to_s
+
open "#{@gem_deps_file}.lock", 'w' do |io|
- io.write to_s
+ io.write content
end
end
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index ece9d00f38..8b6a81612c 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -8,6 +8,9 @@ Gem.load_yaml if defined? ::YAML
##
# A Requirement is a set of one or more version restrictions. It supports a
# few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators.
+#
+# See Gem::Version for a description on how versions and requirements work
+# together in RubyGems.
class Gem::Requirement
OPS = { #:nodoc:
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 65e92bbf29..ef17d682ac 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -1,4 +1,3 @@
-require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/exceptions'
require 'rubygems/util/list'
@@ -21,17 +20,24 @@ class Gem::Resolver
DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil?
+ require 'pp' if DEBUG_RESOLVER
+
##
# Contains all the conflicts encountered while doing resolution
attr_reader :conflicts
##
- # Set to true if development dependencies should be considered.
+ # Set to true if all development dependencies should be considered.
attr_accessor :development
##
+ # Set to true if immediate development dependencies should be considered.
+
+ attr_accessor :development_shallow
+
+ ##
# When true, no dependencies are looked up for requested gems.
attr_accessor :ignore_dependencies
@@ -44,6 +50,12 @@ class Gem::Resolver
attr_reader :stats
##
+ # Hash of gems to skip resolution. Keyed by gem name, with arrays of
+ # gem specifications as values.
+
+ attr_accessor :skip_gems
+
+ ##
# When a missing dependency, don't stop. Just go on and record what was
# missing.
@@ -100,26 +112,27 @@ class Gem::Resolver
@conflicts = []
@development = false
+ @development_shallow = false
@ignore_dependencies = false
@missing = []
+ @skip_gems = {}
@soft_missing = false
@stats = Gem::Resolver::Stats.new
end
def explain stage, *data # :nodoc:
- if DEBUG_RESOLVER
- d = data.map { |x| x.inspect }.join(", ")
- STDOUT.printf "%20s %s\n", stage.to_s.upcase, d
- end
+ return unless DEBUG_RESOLVER
+
+ d = data.map { |x| x.pretty_inspect }.join(", ")
+ $stderr.printf "%10s %s\n", stage.to_s.upcase, d
end
- def explain_list stage, data # :nodoc:
- if DEBUG_RESOLVER
- STDOUT.printf "%20s (%d entries)\n", stage.to_s.upcase, data.size
- data.each do |d|
- STDOUT.printf "%20s %s\n", "", d
- end
- end
+ def explain_list stage # :nodoc:
+ return unless DEBUG_RESOLVER
+
+ data = yield
+ $stderr.printf "%10s (%d entries)\n", stage.to_s.upcase, data.size
+ PP.pp data, $stderr unless data.empty?
end
##
@@ -132,6 +145,7 @@ class Gem::Resolver
spec = possible.pop
explain :activate, [spec.full_name, possible.size]
+ explain :possible, possible
activation_request =
Gem::Resolver::ActivationRequest.new spec, dep, possible
@@ -142,8 +156,15 @@ class Gem::Resolver
def requests s, act, reqs=nil # :nodoc:
return reqs if @ignore_dependencies
+ s.fetch_development_dependencies if @development
+
s.dependencies.reverse_each do |d|
next if d.type == :development and not @development
+ next if d.type == :development and @development_shallow and
+ act.development?
+ next if d.type == :development and @development_shallow and
+ act.parent
+
reqs.add Gem::Resolver::DependencyRequest.new(d, act)
@stats.requirement!
end
@@ -186,6 +207,15 @@ class Gem::Resolver
def find_possible dependency # :nodoc:
all = @set.find_all dependency
+
+ if (skip_dep_gems = skip_gems[dependency.name]) && !skip_dep_gems.empty?
+ matching = all.select do |api_spec|
+ skip_dep_gems.any? { |s| api_spec.version == s.version }
+ end
+
+ all = matching unless matching.empty?
+ end
+
matching_platform = select_local_platforms all
return matching_platform, all
@@ -270,8 +300,8 @@ class Gem::Resolver
dep = needed.remove
explain :try, [dep, dep.requester ? dep.requester.request : :toplevel]
- explain_list :next5, needed.next5
- explain_list :specs, Array(specs).map { |x| x.full_name }.sort
+ explain_list(:next5) { needed.next5 }
+ explain_list(:specs) { Array(specs).map { |x| x.full_name }.sort }
# If there is already a spec activated for the requested name...
if specs && existing = specs.find { |s| dep.name == s.name }
@@ -403,7 +433,10 @@ class Gem::Resolver
@missing << dep
unless @soft_missing
- raise Gem::UnsatisfiableDependencyError.new(dep, platform_mismatch)
+ exc = Gem::UnsatisfiableDependencyError.new dep, platform_mismatch
+ exc.errors = @set.errors
+
+ raise exc
end
end
diff --git a/lib/rubygems/resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb
index 2d48cfa927..56c6363e4f 100644
--- a/lib/rubygems/resolver/activation_request.rb
+++ b/lib/rubygems/resolver/activation_request.rb
@@ -39,6 +39,13 @@ class Gem::Resolver::ActivationRequest
end
##
+ # Is this activation request for a development dependency?
+
+ def development?
+ @request.development?
+ end
+
+ ##
# Downloads a gem at +path+ and returns the file path.
def download path
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
index 5475e626e6..dda3579878 100644
--- a/lib/rubygems/resolver/api_set.rb
+++ b/lib/rubygems/resolver/api_set.rb
@@ -34,6 +34,8 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
@data = Hash.new { |h,k| h[k] = [] }
@source = Gem::Source.new @uri
+
+ @to_fetch = []
end
##
@@ -45,6 +47,10 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
return res unless @remote
+ if @to_fetch.include?(req.name)
+ prefetch_now
+ end
+
versions(req.name).each do |ver|
if req.dependency.match? req.name, ver[:number]
res << Gem::Resolver::APISpecification.new(self, ver)
@@ -61,9 +67,13 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
def prefetch reqs
return unless @remote
names = reqs.map { |r| r.dependency.name }
- needed = names - @data.keys
+ needed = names - @data.keys - @to_fetch
+
+ @to_fetch += needed
+ end
- return if needed.empty?
+ def prefetch_now
+ needed, @to_fetch = @to_fetch, []
uri = @dep_uri + "?gems=#{needed.sort.join ','}"
str = Gem::RemoteFetcher.fetcher.fetch_path uri
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index 67052af82e..bbd5a6427b 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -34,6 +34,12 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@dependencies == other.dependencies
end
+ def fetch_development_dependencies # :nodoc:
+ spec = source.fetch_spec Gem::NameTuple.new @name, @version, @platform
+
+ @dependencies = spec.dependencies
+ end
+
def installable_platform? # :nodoc:
Gem::Platform.match @platform
end
diff --git a/lib/rubygems/resolver/best_set.rb b/lib/rubygems/resolver/best_set.rb
index 20bb94827b..7e2d7e2647 100644
--- a/lib/rubygems/resolver/best_set.rb
+++ b/lib/rubygems/resolver/best_set.rb
@@ -28,6 +28,10 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
pick_sets if @remote and @sets.empty?
super
+ rescue Gem::RemoteFetcher::FetchError => e
+ replace_failed_api_set e
+
+ retry
end
def prefetch reqs # :nodoc:
@@ -46,5 +50,29 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
end
end
+ ##
+ # Replaces a failed APISet for the URI in +error+ with an IndexSet.
+ #
+ # If no matching APISet can be found the original +error+ is raised.
+ #
+ # The calling method must retry the exception to repeat the lookup.
+
+ def replace_failed_api_set error # :nodoc:
+ uri = error.uri
+ uri = URI uri unless URI === uri
+ uri.query = nil
+
+ raise error unless api_set = @sets.find { |set|
+ Gem::Resolver::APISet === set and set.dep_uri == uri
+ }
+
+ index_set = Gem::Resolver::IndexSet.new api_set.source
+
+ @sets.map! do |set|
+ next set unless set == api_set
+ index_set
+ end
+ end
+
end
diff --git a/lib/rubygems/resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb
index 6f912b0afe..5b08f128ed 100644
--- a/lib/rubygems/resolver/composed_set.rb
+++ b/lib/rubygems/resolver/composed_set.rb
@@ -22,6 +22,18 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
end
##
+ # When +allow_prerelease+ is set to +true+ prereleases gems are allowed to
+ # match dependencies.
+
+ def prerelease= allow_prerelease
+ super
+
+ sets.each do |set|
+ set.prerelease = allow_prerelease
+ end
+ end
+
+ ##
# Sets the remote network access for all composed sets.
def remote= remote
@@ -30,6 +42,10 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
@sets.each { |set| set.remote = remote }
end
+ def errors
+ @errors + @sets.map { |set| set.errors }.flatten
+ end
+
##
# Finds all specs matching +req+ in all sets.
diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb
index 8830e8d1fb..902c286b6b 100644
--- a/lib/rubygems/resolver/conflict.rb
+++ b/lib/rubygems/resolver/conflict.rb
@@ -52,11 +52,40 @@ class Gem::Resolver::Conflict
def explanation
activated = @activated.spec.full_name
- requirement = @failed_dep.dependency.requirement
+ dependency = @failed_dep.dependency
+ requirement = dependency.requirement
+ alternates = dependency.matching_specs.map { |spec| spec.full_name }
- " Activated %s via:\n %s\n instead of (%s) via:\n %s\n" % [
- activated, request_path(@activated).join(', '),
- requirement, request_path(requester).join(', '),
+ unless alternates.empty? then
+ matching = <<-MATCHING.chomp
+
+ Gems matching %s:
+ %s
+ MATCHING
+
+ matching = matching % [
+ dependency,
+ alternates.join(', '),
+ ]
+ end
+
+ explanation = <<-EXPLANATION
+ Activated %s
+ which does not match conflicting dependency (%s)
+
+ Conflicting dependency chains:
+ %s
+
+ versus:
+ %s
+%s
+ EXPLANATION
+
+ explanation % [
+ activated, requirement,
+ request_path(@activated).reverse.join(", depends on\n "),
+ request_path(@failed_dep).reverse.join(", depends on\n "),
+ matching,
]
end
@@ -95,10 +124,19 @@ class Gem::Resolver::Conflict
path = []
while current do
- requirement = current.request.dependency.requirement
- path << "#{current.spec.full_name} (#{requirement})"
+ case current
+ when Gem::Resolver::ActivationRequest then
+ path <<
+ "#{current.request.dependency}, #{current.spec.version} activated"
+
+ current = current.parent
+ when Gem::Resolver::DependencyRequest then
+ path << "#{current.dependency}"
- current = current.parent
+ current = current.requester
+ else
+ raise Gem::Exception, "[BUG] unknown request class #{current.class}"
+ end
end
path = ['user request (gem command or Gemfile)'] if path.empty?
diff --git a/lib/rubygems/resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb
index 1d51db4945..79690bec4c 100644
--- a/lib/rubygems/resolver/dependency_request.rb
+++ b/lib/rubygems/resolver/dependency_request.rb
@@ -35,7 +35,26 @@ class Gem::Resolver::DependencyRequest
end
##
- # Does this dependency request match +spec+
+ # Is this dependency a development dependency?
+
+ def development?
+ @dependency.type == :development
+ end
+
+ ##
+ # Does this dependency request match +spec+?
+ #
+ # NOTE: #match? only matches prerelease versions when #dependency is a
+ # prerelease dependency.
+
+ def match? spec, allow_prerelease = false
+ @dependency.match? spec, nil, allow_prerelease
+ end
+
+ ##
+ # Does this dependency request match +spec+?
+ #
+ # NOTE: #matches_spec? matches prerelease versions. See also #match?
def matches_spec?(spec)
@dependency.matches_spec? spec
diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb
index d32710e3d6..5f1b368ac1 100644
--- a/lib/rubygems/resolver/git_set.rb
+++ b/lib/rubygems/resolver/git_set.rb
@@ -80,7 +80,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
prefetch nil
specs.values.select do |spec|
- req.matches_spec? spec
+ req.match? spec
end
end
diff --git a/lib/rubygems/resolver/git_specification.rb b/lib/rubygems/resolver/git_specification.rb
index 113e7ea9de..55e180e525 100644
--- a/lib/rubygems/resolver/git_specification.rb
+++ b/lib/rubygems/resolver/git_specification.rb
@@ -12,11 +12,15 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
@source == other.source
end
+ def add_dependency dependency # :nodoc:
+ spec.dependencies << dependency
+ end
+
##
# Installing a git gem only involves building the extensions and generating
# the executables.
- def install options
+ def install options = {}
require 'rubygems/installer'
installer = Gem::Installer.new '', options
@@ -31,5 +35,25 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
installer.run_post_install_hooks
end
+ def pretty_print q # :nodoc:
+ q.group 2, '[GitSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp dependencies
+
+ q.breakable
+ q.text "source:"
+ q.breakable
+ q.pp @source
+ end
+ end
+
end
diff --git a/lib/rubygems/resolver/index_set.rb b/lib/rubygems/resolver/index_set.rb
index ef01f0f0ad..7c56c2bf99 100644
--- a/lib/rubygems/resolver/index_set.rb
+++ b/lib/rubygems/resolver/index_set.rb
@@ -18,7 +18,9 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
@all = Hash.new { |h,k| h[k] = [] }
- list, = @f.available_specs :released
+ list, errors = @f.available_specs :complete
+
+ @errors.concat errors
list.each do |uri, specs|
specs.each do |n|
@@ -41,7 +43,7 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
name = req.dependency.name
@all[name].each do |uri, n|
- if req.dependency.match? n then
+ if req.match? n, @prerelease then
res << Gem::Resolver::IndexSpecification.new(
self, n.name, n.version, uri, n.platform)
end
diff --git a/lib/rubygems/resolver/installed_specification.rb b/lib/rubygems/resolver/installed_specification.rb
index a9438129fb..2a2b89a6c2 100644
--- a/lib/rubygems/resolver/installed_specification.rb
+++ b/lib/rubygems/resolver/installed_specification.rb
@@ -14,7 +14,7 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
# This is a null install as this specification is already installed.
# +options+ are ignored.
- def install options
+ def install options = {}
yield nil
end
@@ -29,6 +29,24 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
super
end
+ def pretty_print q # :nodoc:
+ q.group 2, '[InstalledSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp spec.dependencies
+ end
+ end
+
##
# The source for this specification
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index 045c893fdc..f53b496dc7 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -21,6 +21,11 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
attr_accessor :ignore_installed # :nodoc:
##
+ # The remote_set looks up remote gems for installation.
+
+ attr_reader :remote_set # :nodoc:
+
+ ##
# Creates a new InstallerSet that will look for gems in +domain+.
def initialize domain
@@ -34,11 +39,53 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
@always_install = []
@ignore_dependencies = false
@ignore_installed = false
+ @local = {}
@remote_set = Gem::Resolver::BestSet.new
@specs = {}
end
##
+ # Looks up the latest specification for +dependency+ and adds it to the
+ # always_install list.
+
+ def add_always_install dependency
+ request = Gem::Resolver::DependencyRequest.new dependency, nil
+
+ found = find_all request
+
+ found.delete_if { |s|
+ s.version.prerelease? and not s.local?
+ } unless dependency.prerelease?
+
+ found = found.select do |s|
+ Gem::Source::SpecificFile === s.source or
+ Gem::Platform::RUBY == s.platform or
+ Gem::Platform.local === s.platform
+ end
+
+ if found.empty? then
+ exc = Gem::UnsatisfiableDependencyError.new request
+ exc.errors = errors
+
+ raise exc
+ end
+
+ newest = found.max_by do |s|
+ [s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
+ end
+
+ @always_install << newest.spec
+ end
+
+ ##
+ # Adds a local gem requested using +dep_name+ with the given +spec+ that can
+ # be loaded and installed using the +source+.
+
+ def add_local dep_name, spec, source
+ @local[dep_name] = [spec, source]
+ end
+
+ ##
# Should local gems should be considered?
def consider_local? # :nodoc:
@@ -53,6 +100,13 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
##
+ # Errors encountered while resolving gems
+
+ def errors
+ @errors + @remote_set.errors
+ end
+
+ ##
# Returns an array of IndexSpecification objects matching DependencyRequest
# +req+.
@@ -62,30 +116,53 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
dep = req.dependency
return res if @ignore_dependencies and
- @always_install.none? { |spec| dep.matches_spec? spec }
+ @always_install.none? { |spec| dep.match? spec }
name = dep.name
dep.matching_specs.each do |gemspec|
- next if @always_install.include? gemspec
+ next if @always_install.any? { |spec| spec.name == gemspec.name }
res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
end unless @ignore_installed
if consider_local? then
+ matching_local = @local.values.select do |spec, _|
+ req.match? spec
+ end.map do |spec, source|
+ Gem::Resolver::LocalSpecification.new self, spec, source
+ end
+
+ res.concat matching_local
+
local_source = Gem::Source::Local.new
- if spec = local_source.find_gem(name, dep.requirement) then
+ if local_spec = local_source.find_gem(name, dep.requirement) then
res << Gem::Resolver::IndexSpecification.new(
- self, spec.name, spec.version, local_source, spec.platform)
+ self, local_spec.name, local_spec.version,
+ local_source, local_spec.platform)
end
end
+ res.delete_if do |spec|
+ spec.version.prerelease? and not dep.prerelease?
+ end
+
res.concat @remote_set.find_all req if consider_remote?
res
end
+ def prefetch(reqs)
+ @remote_set.prefetch(reqs)
+ end
+
+ def prerelease= allow_prerelease
+ super
+
+ @remote_set.prerelease = allow_prerelease
+ end
+
def inspect # :nodoc:
always_install = @always_install.map { |s| s.full_name }
@@ -108,6 +185,15 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
end
+ ##
+ # Has a local gem for +dep_name+ been added to this set?
+
+ def local? dep_name # :nodoc:
+ spec, = @local[dep_name]
+
+ spec
+ end
+
def pretty_print q # :nodoc:
q.group 2, '[InstallerSet', ']' do
q.breakable
diff --git a/lib/rubygems/resolver/local_specification.rb b/lib/rubygems/resolver/local_specification.rb
index dcca6c736a..20a283f0ba 100644
--- a/lib/rubygems/resolver/local_specification.rb
+++ b/lib/rubygems/resolver/local_specification.rb
@@ -12,5 +12,30 @@ class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
super
end
+ def local? # :nodoc:
+ true
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[LocalSpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp dependencies
+
+ q.breakable
+ q.text "source: #{@source.path}"
+ end
+ end
+
end
diff --git a/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb
index f4987576ec..4ede5971fb 100644
--- a/lib/rubygems/resolver/lock_set.rb
+++ b/lib/rubygems/resolver/lock_set.rb
@@ -6,13 +6,16 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
attr_reader :specs # :nodoc:
##
- # Creates a new LockSet from the given +source+
+ # Creates a new LockSet from the given +sources+
- def initialize source
+ def initialize sources
super()
- @source = Gem::Source::Lock.new source
- @specs = []
+ @sources = sources.map do |source|
+ Gem::Source::Lock.new source
+ end
+
+ @specs = []
end
##
@@ -25,13 +28,14 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
def add name, version, platform # :nodoc:
version = Gem::Version.new version
- spec =
- Gem::Resolver::LockSpecification.new self, name, version, @source,
+ specs = @sources.map do |source|
+ Gem::Resolver::LockSpecification.new self, name, version, source,
platform
+ end
- @specs << spec
+ @specs.concat specs
- spec
+ specs
end
##
@@ -40,7 +44,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
def find_all req
@specs.select do |spec|
- req.matches_spec? spec
+ req.match? spec
end
end
diff --git a/lib/rubygems/resolver/lock_specification.rb b/lib/rubygems/resolver/lock_specification.rb
index 4bc21b9402..0013171469 100644
--- a/lib/rubygems/resolver/lock_specification.rb
+++ b/lib/rubygems/resolver/lock_specification.rb
@@ -23,7 +23,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
# This is a null install as a locked specification is considered installed.
# +options+ are ignored.
- def install options
+ def install options = {}
destination = options[:install_dir] || Gem.dir
if File.exist? File.join(destination, 'specifications', spec.spec_name) then
@@ -41,10 +41,36 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
@dependencies << dependency
end
+ def pretty_print q # :nodoc:
+ q.group 2, '[LockSpecification', ']' do
+ q.breakable
+ q.text "name: #{@name}"
+
+ q.breakable
+ q.text "version: #{@version}"
+
+ unless @platform == Gem::Platform::RUBY then
+ q.breakable
+ q.text "platform: #{@platform}"
+ end
+
+ unless @dependencies.empty? then
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp @dependencies
+ end
+ end
+ end
+
##
# A specification constructed from the lockfile is returned
def spec
+ @spec ||= Gem::Specification.find { |spec|
+ spec.name == @name and spec.version == @version
+ }
+
@spec ||= Gem::Specification.new do |s|
s.name = @name
s.version = @version
diff --git a/lib/rubygems/resolver/set.rb b/lib/rubygems/resolver/set.rb
index f053b65e15..b26dc45c7b 100644
--- a/lib/rubygems/resolver/set.rb
+++ b/lib/rubygems/resolver/set.rb
@@ -9,8 +9,20 @@ class Gem::Resolver::Set
attr_accessor :remote
+ ##
+ # Errors encountered when resolving gems
+
+ attr_accessor :errors
+
+ ##
+ # When true, allows matching of requests to prerelease gems.
+
+ attr_accessor :prerelease
+
def initialize # :nodoc:
- @remote = true
+ @prerelease = false
+ @remote = true
+ @errors = []
end
##
diff --git a/lib/rubygems/resolver/spec_specification.rb b/lib/rubygems/resolver/spec_specification.rb
index 0c411bdf5f..1350e8a7ab 100644
--- a/lib/rubygems/resolver/spec_specification.rb
+++ b/lib/rubygems/resolver/spec_specification.rb
@@ -4,8 +4,6 @@
class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
- attr_reader :spec # :nodoc:
-
##
# A SpecSpecification is created for a +set+ for a Gem::Specification in
# +spec+. The +source+ is either where the +spec+ came from, or should be
diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb
index d158225474..4d77293262 100644
--- a/lib/rubygems/resolver/specification.rb
+++ b/lib/rubygems/resolver/specification.rb
@@ -31,6 +31,14 @@ class Gem::Resolver::Specification
attr_reader :source
##
+ # The Gem::Specification for this Resolver::Specification.
+ #
+ # Implementers, note that #install updates @spec, so be sure to cache the
+ # Gem::Specification in @spec when overriding.
+
+ attr_reader :spec
+
+ ##
# The version of the gem for this specification.
attr_reader :version
@@ -48,6 +56,13 @@ class Gem::Resolver::Specification
end
##
+ # Fetches development dependencies if the source does not provide them by
+ # default (see APISpecification).
+
+ def fetch_development_dependencies # :nodoc:
+ end
+
+ ##
# The name and version of the specification.
#
# Unlike Gem::Specification#full_name, the platform is not included.
@@ -61,8 +76,11 @@ class Gem::Resolver::Specification
# install method yields a Gem::Installer instance, which indicates the
# gem will be installed, or +nil+, which indicates the gem is already
# installed.
+ #
+ # After installation #spec is updated to point to the just-installed
+ # specification.
- def install options
+ def install options = {}
require 'rubygems/installer'
destination = options[:install_dir] || Gem.dir
@@ -75,7 +93,7 @@ class Gem::Resolver::Specification
yield installer if block_given?
- installer.install
+ @spec = installer.install
end
##
@@ -85,5 +103,8 @@ class Gem::Resolver::Specification
Gem::Platform.match spec.platform
end
+ def local? # :nodoc:
+ false
+ end
end
diff --git a/lib/rubygems/resolver/vendor_set.rb b/lib/rubygems/resolver/vendor_set.rb
index 6e867073be..614bd05382 100644
--- a/lib/rubygems/resolver/vendor_set.rb
+++ b/lib/rubygems/resolver/vendor_set.rb
@@ -43,6 +43,8 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
@specs[spec.name] = spec
@directories[spec] = directory
+
+ spec
end
##
@@ -51,7 +53,7 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
def find_all req
@specs.values.select do |spec|
- req.matches_spec? spec
+ req.match? spec
end.map do |spec|
source = Gem::Source::Vendor.new @directories[spec]
Gem::Resolver::VendorSpecification.new self, spec, source
diff --git a/lib/rubygems/resolver/vendor_specification.rb b/lib/rubygems/resolver/vendor_specification.rb
index c6a8e58d9b..a99b5f3cc1 100644
--- a/lib/rubygems/resolver/vendor_specification.rb
+++ b/lib/rubygems/resolver/vendor_specification.rb
@@ -16,7 +16,7 @@ class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
# This is a null install as this gem was unpacked into a directory.
# +options+ are ignored.
- def install options
+ def install options = {}
yield nil
end
diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb
index 7238b2e477..b9bcb17525 100644
--- a/lib/rubygems/security/policy.rb
+++ b/lib/rubygems/security/policy.rb
@@ -218,6 +218,7 @@ class Gem::Security::Policy
# against
else
alert_warning "#{full_name} is not signed"
+ return
end
end
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index ca6dc683f5..7655be22ce 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -530,6 +530,36 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
end
+ def prerelease_specs req, res
+ reset_gems
+
+ res['content-type'] = 'application/x-gzip'
+
+ add_date res
+
+ specs = Gem::Specification.select do |spec|
+ spec.version.prerelease?
+ end.sort.map do |spec|
+ platform = spec.original_platform || Gem::Platform::RUBY
+ [spec.name, spec.version, platform]
+ end
+
+ specs = Marshal.dump specs
+
+ if req.path =~ /\.gz$/ then
+ specs = Gem.gzip specs
+ res['content-type'] = 'application/x-gzip'
+ else
+ res['content-type'] = 'application/octet-stream'
+ end
+
+ if req.request_method == 'HEAD' then
+ res['content-length'] = specs.length
+ else
+ res.body << specs
+ end
+ end
+
def quick(req, res)
reset_gems
@@ -537,7 +567,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
add_date res
case req.request_uri.path
- when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then
+ when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+[^-]*?)(-.*?)?\.gemspec\.rz$| then
marshal_format, name, version, platform = $1, $2, $3, $4
specs = Gem::Specification.find_all_by_name name, version
@@ -757,6 +787,11 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
@server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
method(:latest_specs)
+ @server.mount_proc "/prerelease_specs.#{Gem.marshal_version}",
+ method(:prerelease_specs)
+ @server.mount_proc "/prerelease_specs.#{Gem.marshal_version}.gz",
+ method(:prerelease_specs)
+
@server.mount_proc "/quick/", method(:quick)
@server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 7f34e43528..4858ffb5ba 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -78,15 +78,21 @@ class Gem::Source
# Returns a Set that can fetch specifications from this source.
def dependency_resolver_set # :nodoc:
+ return Gem::Resolver::IndexSet.new self if 'file' == api_uri.scheme
+
bundler_api_uri = api_uri + './api/v1/dependencies'
begin
fetcher = Gem::RemoteFetcher.fetcher
- fetcher.fetch_path bundler_api_uri, nil, true
+ response = fetcher.fetch_path bundler_api_uri, nil, true
rescue Gem::RemoteFetcher::FetchError
Gem::Resolver::IndexSet.new self
else
- Gem::Resolver::APISet.new bundler_api_uri
+ if response.respond_to? :uri then
+ Gem::Resolver::APISet.new response.uri
+ else
+ Gem::Resolver::APISet.new bundler_api_uri
+ end
end
end
@@ -100,6 +106,8 @@ class Gem::Source
def cache_dir(uri)
# Correct for windows paths
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
+ escaped_path.untaint
+
File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index 2e3fa03730..fb9cbce2fb 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -51,7 +51,7 @@ class Gem::Source::Git < Gem::Source
# will be checked out when the gem is installed.
def initialize name, repository, reference, submodules = false
- super(nil)
+ super repository
@name = name
@repository = repository
@@ -67,7 +67,7 @@ class Gem::Source::Git < Gem::Source
case other
when Gem::Source::Git then
0
- when Gem::Source::Installed,
+ when Gem::Source::Vendor,
Gem::Source::Lock then
-1
when Gem::Source then
@@ -101,7 +101,7 @@ class Gem::Source::Git < Gem::Source
Dir.chdir install_dir do
system @git, 'fetch', '--quiet', '--force', '--tags', install_dir
- success = system @git, 'reset', '--quiet', '--hard', @reference
+ success = system @git, 'reset', '--quiet', '--hard', rev_parse
success &&=
Gem::Util.silent_system @git, 'submodule', 'update',
diff --git a/lib/rubygems/source/installed.rb b/lib/rubygems/source/installed.rb
index 6d343c2439..bd05c75af1 100644
--- a/lib/rubygems/source/installed.rb
+++ b/lib/rubygems/source/installed.rb
@@ -12,7 +12,8 @@ class Gem::Source::Installed < Gem::Source
def <=> other
case other
- when Gem::Source::Lock,
+ when Gem::Source::Git,
+ Gem::Source::Lock,
Gem::Source::Vendor then
-1
when Gem::Source::Installed then
@@ -31,5 +32,9 @@ class Gem::Source::Installed < Gem::Source
nil
end
+ def pretty_print q # :nodoc:
+ q.text '[Installed]'
+ end
+
end
diff --git a/lib/rubygems/source/specific_file.rb b/lib/rubygems/source/specific_file.rb
index a7b6c53542..250a839203 100644
--- a/lib/rubygems/source/specific_file.rb
+++ b/lib/rubygems/source/specific_file.rb
@@ -5,6 +5,11 @@
class Gem::Source::SpecificFile < Gem::Source
##
+ # The path to the gem for this specific file.
+
+ attr_reader :path
+
+ ##
# Creates a new SpecificFile for the gem in +file+
def initialize(file)
@@ -37,7 +42,7 @@ class Gem::Source::SpecificFile < Gem::Source
end
def pretty_print q # :nodoc:
- q.group 2, '[Local:', ']' do
+ q.group 2, '[SpecificFile:', ']' do
q.breakable
q.text @path
end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 12b8fb27d8..4967c4a40b 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -186,7 +186,7 @@ class Gem::SpecFetcher
def suggest_gems_from_name gem_name
gem_name = gem_name.downcase.tr('_-', '')
max = gem_name.size / 2
- names = available_specs(:complete).first.values.flatten(1)
+ names = available_specs(:latest).first.values.flatten(1)
matches = names.map { |n|
next unless n.match_platform?
@@ -258,18 +258,11 @@ class Gem::SpecFetcher
# etc.). If +gracefully_ignore+ is true, errors are ignored.
def tuples_for(source, type, gracefully_ignore=false) # :nodoc:
- cache = @caches[type]
-
- tuples =
- begin
- cache[source.uri] ||=
- source.load_specs(type).sort_by { |tup| tup.name }
- rescue Gem::RemoteFetcher::FetchError
- raise unless gracefully_ignore
- []
- end
-
- tuples
+ @caches[type][source.uri] ||=
+ source.load_specs(type).sort_by { |tup| tup.name }
+ rescue Gem::RemoteFetcher::FetchError
+ raise unless gracefully_ignore
+ []
end
end
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 3b803fa785..975a2e3f9a 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -14,12 +14,6 @@ require 'rubygems/basic_specification'
require 'rubygems/stub_specification'
require 'rubygems/util/stringio'
-# :stopdoc:
-# date.rb can't be loaded for `make install` due to miniruby
-# Date is needed for old gems that stored #date as Date instead of Time.
-class Date; end
-# :startdoc:
-
##
# The Specification class contains the information for a Gem. Typically
# defined in a .gemspec file or a Rakefile, and looks like this:
@@ -218,9 +212,11 @@ class Gem::Specification < Gem::BasicSpecification
# Usage:
#
# # If all library files are in the root directory...
- # spec.require_path = '.'
+ # spec.require_paths = ['.']
- attr_writer :require_paths
+ def require_paths=(val)
+ @require_paths = Array(val)
+ end
##
# The version of RubyGems used to create this gem.
@@ -373,7 +369,9 @@ class Gem::Specification < Gem::BasicSpecification
##
# A long description of this gem
#
- # The description should be more detailed than the summary.
+ # The description should be more detailed than the summary but not
+ # excessively long. A few paragraphs is a recommended length with no
+ # examples or formatting.
#
# Usage:
#
@@ -413,6 +411,16 @@ class Gem::Specification < Gem::BasicSpecification
attr_accessor :post_install_message
##
+ # The version of Ruby required by this gem
+
+ attr_reader :required_ruby_version
+
+ ##
+ # The RubyGems version required by this gem
+
+ attr_reader :required_rubygems_version
+
+ ##
# The key used to sign this gem. See Gem::Security for details.
attr_accessor :signing_key
@@ -472,6 +480,9 @@ class Gem::Specification < Gem::BasicSpecification
# found in bindir. These files must be executable Ruby files. Files that
# use bash or other interpreters will not work.
#
+ # Executables included may only be ruby scripts, not scripts for other
+ # languages or compiled binaries.
+ #
# Usage:
#
# spec.executables << 'rake'
@@ -532,20 +543,26 @@ class Gem::Specification < Gem::BasicSpecification
##
# :category: Recommended gemspec attributes
+ #
# The license for this gem.
#
- # The license must be a short name, no more than 64 characters.
+ # The license must be no more than 64 characters.
#
- # This should just be the name of your license. The full
- # text of the license should be inside of the gem when you build it.
+ # This should just be the name of your license. The full text of the license
+ # should be inside of the gem (at the top level) when you build it.
+ #
+ # The simplest way, is to specify the standard SPDX ID
+ # https://spdx.org/licenses/ for the license.
+ # Ideally you should pick one that is OSI (Open Source Initiative)
+ # http://opensource.org/licenses/alphabetical approved.
#
- # See http://opensource.org/licenses/alphabetical for a list of licenses and
- # their abbreviations (or short names). GitHub also provides a
- # license picker at http://choosealicense.com/
+ # The most commonly used OSI approved licenses are BSD-3-Clause and MIT.
+ # GitHub also provides a license picker at http://choosealicense.com/.
#
- # According to copyright law, not having an OSI-approved open source license
- # means you have no rights to use the code for any purpose-- in other words,
- # "all rights reserved".
+ # You should specify a license for your gem so that people know how they are
+ # permitted to use it, and any restrictions you're placing on it. Not
+ # specifying a license means all rights are reserved; others have no rights
+ # to use the code for any purpose.
#
# You can set multiple licenses with #licenses=
#
@@ -608,6 +625,13 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # The RubyGems version required by this gem
+
+ def required_rubygems_version= req
+ @required_rubygems_version = Gem::Requirement.create req
+ end
+
+ ##
# Lists the external (to RubyGems) requirements that must be met for this gem
# to work. It's simply information for the user.
#
@@ -628,7 +652,7 @@ class Gem::Specification < Gem::BasicSpecification
# spec.test_files = Dir.glob('test/tc_*.rb')
# spec.test_files = ['tests/test-suite.rb']
- def test_files= files
+ def test_files= files # :nodoc:
@test_files = Array files
end
@@ -662,16 +686,6 @@ class Gem::Specification < Gem::BasicSpecification
attr_writer :original_platform # :nodoc:
##
- # The version of Ruby required by this gem
-
- attr_reader :required_ruby_version
-
- ##
- # The RubyGems version required by this gem
-
- attr_reader :required_rubygems_version
-
- ##
# The rubyforge project this gem lives under. i.e. RubyGems'
# rubyforge_project is "rubygems".
#
@@ -1259,9 +1273,13 @@ class Gem::Specification < Gem::BasicSpecification
# there are conflicts upon activation.
def activate
- raise_if_conflicts
+ other = Gem.loaded_specs[self.name]
+ if other then
+ check_version_conflict other
+ return false
+ end
- return false if Gem.loaded_specs[self.name]
+ raise_if_conflicts
activate_dependencies
add_self_to_load_path
@@ -1403,7 +1421,10 @@ class Gem::Specification < Gem::BasicSpecification
def build_args
if File.exist? build_info_file
- File.readlines(build_info_file).map { |x| x.strip }
+ build_info = File.readlines build_info_file
+ build_info = build_info.map { |x| x.strip }
+ build_info.delete ""
+ build_info
else
[]
end
@@ -1418,8 +1439,8 @@ class Gem::Specification < Gem::BasicSpecification
return if extensions.empty?
return if installed_by_version < Gem::Version.new('2.2.0.preview.2')
return if File.exist? gem_build_complete_path
- return if !File.writable?(base_dir) &&
- !File.exist?(File.join(base_dir, 'extensions'))
+ return if !File.writable?(base_dir)
+ return if !File.exist?(File.join(base_dir, 'extensions'))
begin
# We need to require things in $LOAD_PATH without looking for the
@@ -1477,13 +1498,12 @@ class Gem::Specification < Gem::BasicSpecification
def conflicts
conflicts = {}
- Gem.loaded_specs.values.each do |spec|
- bad = self.runtime_dependencies.find_all { |dep|
- spec.name == dep.name and not spec.satisfies_requirement? dep
- }
-
- conflicts[spec] = bad unless bad.empty?
- end
+ self.runtime_dependencies.each { |dep|
+ spec = Gem.loaded_specs[dep.name]
+ if spec and not spec.satisfies_requirement? dep
+ (conflicts[spec] ||= []) << dep
+ end
+ }
conflicts
end
@@ -1496,6 +1516,11 @@ class Gem::Specification < Gem::BasicSpecification
@date ||= TODAY
end
+ DateLike = Object.new # :nodoc:
+ def DateLike.===(obj) # :nodoc:
+ defined?(::Date) and Date === obj
+ end
+
DateTimeFormat = # :nodoc:
/\A
(\d{4})-(\d{2})-(\d{2})
@@ -1525,7 +1550,7 @@ class Gem::Specification < Gem::BasicSpecification
raise(Gem::InvalidSpecificationException,
"invalid date format in specification: #{date.inspect}")
end
- when Time, Date then
+ when Time, DateLike then
Time.utc(date.year, date.month, date.day)
else
TODAY
@@ -1779,7 +1804,7 @@ class Gem::Specification < Gem::BasicSpecification
##
# True if this gem has files in test_files
- def has_unit_tests?
+ def has_unit_tests? # :nodoc:
not test_files.empty?
end
@@ -1953,6 +1978,19 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # Is this specification missing its extensions? When this returns true you
+ # probably want to build_extensions
+
+ def missing_extensions?
+ return false if default_gem?
+ return false if extensions.empty?
+ return false if installed_by_version < Gem::Version.new('2.2.0.preview.2')
+ return false if File.exist? gem_build_complete_path
+
+ true
+ end
+
+ ##
# Normalize the list of files so that:
# * All file lists have redundancies removed.
# * Files referenced in the extra_rdoc_files are included in the package
@@ -2036,34 +2074,34 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # Check the spec for possible conflicts and freak out if there are any.
+ # Raise an exception if the version of this spec conflicts with the one
+ # that is already loaded (+other+)
- def raise_if_conflicts
- other = Gem.loaded_specs[self.name]
+ def check_version_conflict other # :nodoc:
+ return if self.version == other.version
- if other and self.version != other.version then
- # This gem is already loaded. If the currently loaded gem is not in the
- # list of candidate gems, then we have a version conflict.
+ # This gem is already loaded. If the currently loaded gem is not in the
+ # list of candidate gems, then we have a version conflict.
- msg = "can't activate #{full_name}, already activated #{other.full_name}"
+ msg = "can't activate #{full_name}, already activated #{other.full_name}"
- e = Gem::LoadError.new msg
- e.name = self.name
- # TODO: e.requirement = dep.requirement
+ e = Gem::LoadError.new msg
+ e.name = self.name
+ # TODO: e.requirement = dep.requirement
- raise e
- end
+ raise e
+ end
- conf = self.conflicts
+ private :check_version_conflict
- unless conf.empty? then
- y = conf.map { |act,con|
- "#{act.full_name} conflicts with #{con.join(", ")}"
- }.join ", "
+ ##
+ # Check the spec for possible conflicts and freak out if there are any.
- # TODO: improve message by saying who activated `con`
+ def raise_if_conflicts # :nodoc:
+ conf = self.conflicts
- raise Gem::LoadError, "Unable to activate #{self.full_name}, because #{y}"
+ unless conf.empty? then
+ raise Gem::ConflictError.new self, conf
end
end
@@ -2087,14 +2125,7 @@ class Gem::Specification < Gem::BasicSpecification
# Singular accessor for #require_paths
def require_path= path
- self.require_paths = [path]
- end
-
- ##
- # The RubyGems version required by this gem
-
- def required_rubygems_version= req
- @required_rubygems_version = Gem::Requirement.create req
+ self.require_paths = Array(path)
end
##
@@ -2129,7 +2160,7 @@ class Gem::Specification < Gem::BasicSpecification
seg = obj.keys.sort.map { |k| "#{k.to_s.dump} => #{obj[k].to_s.dump}" }
"{ #{seg.join(', ')} }"
when Gem::Version then obj.to_s.dump
- when Date then obj.strftime('%Y-%m-%d').dump
+ when DateLike then obj.strftime('%Y-%m-%d').dump
when Time then obj.strftime('%Y-%m-%d').dump
when Numeric then obj.inspect
when true, false, nil then obj.inspect
@@ -2217,14 +2248,14 @@ class Gem::Specification < Gem::BasicSpecification
##
# Singular accessor for #test_files
- def test_file
+ def test_file # :nodoc:
val = test_files and val.first
end
##
# Singular mutator for #test_files
- def test_file= file
+ def test_file= file # :nodoc:
self.test_files = [file]
end
@@ -2232,7 +2263,7 @@ class Gem::Specification < Gem::BasicSpecification
# Test files included in this gem. You cannot append to this accessor, you
# must assign to it.
- def test_files
+ def test_files # :nodoc:
# Handle the possibility that we have @test_suite_file but not
# @test_files. This will happen when an old gem is loaded via
# YAML.
@@ -2256,7 +2287,7 @@ class Gem::Specification < Gem::BasicSpecification
mark_version
result = []
result << "# -*- encoding: utf-8 -*-"
- result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{@require_paths.join("\0")}"
+ result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{raw_require_paths.join("\0")}"
result << "#{Gem::StubSpecification::PREFIX}#{extensions.join "\0"}" unless
extensions.empty?
result << nil
@@ -2273,7 +2304,7 @@ class Gem::Specification < Gem::BasicSpecification
if metadata and !metadata.empty?
result << " s.metadata = #{ruby_code metadata} if s.respond_to? :metadata="
end
- result << " s.require_paths = #{ruby_code @require_paths}"
+ result << " s.require_paths = #{ruby_code raw_require_paths}"
handled = [
:dependencies,
@@ -2443,7 +2474,7 @@ class Gem::Specification < Gem::BasicSpecification
"invalid value for attribute name: \"#{name.inspect}\""
end
- if @require_paths.empty? then
+ if raw_require_paths.empty? then
raise Gem::InvalidSpecificationException,
'specification must have at least one require_path'
end
@@ -2627,7 +2658,8 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
dep.requirement.requirements.any? do |op, version|
op == '~>' and
not version.prerelease? and
- version.segments.length > 2
+ version.segments.length > 2 and
+ version.segments.first != 0
end
if overly_strict then
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index 221dc1d404..49a6df43a3 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -120,6 +120,13 @@ class Gem::StubSpecification < Gem::BasicSpecification
super
end
+ def missing_extensions?
+ return false if default_gem?
+ return false if extensions.empty?
+
+ to_spec.missing_extensions?
+ end
+
##
# Name of the gem
@@ -147,7 +154,14 @@ class Gem::StubSpecification < Gem::BasicSpecification
# The full Gem::Specification for this gem, loaded from evalling its gemspec
def to_spec
+ @spec ||= Gem.loaded_specs.values.find { |spec|
+ spec.name == @name and spec.version == @version
+ }
+
@spec ||= Gem::Specification.load(loaded_from)
+ @spec.ignored = @ignored if instance_variable_defined? :@ignored
+
+ @spec
end
##
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index 74cb4c98d1..e9916dad61 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -218,8 +218,11 @@ class Gem::TestCase < MiniTest::Unit::TestCase
def setup
super
- @orig_gem_home = ENV['GEM_HOME']
- @orig_gem_path = ENV['GEM_PATH']
+ @orig_gem_home = ENV['GEM_HOME']
+ @orig_gem_path = ENV['GEM_PATH']
+ @orig_gem_vendor = ENV['GEM_VENDOR']
+
+ ENV['GEM_VENDOR'] = nil
@current_dir = Dir.pwd
@fetcher = nil
@@ -347,8 +350,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase
FileUtils.rm_rf @tempdir unless ENV['KEEP_FILES']
- ENV['GEM_HOME'] = @orig_gem_home
- ENV['GEM_PATH'] = @orig_gem_path
+ ENV['GEM_HOME'] = @orig_gem_home
+ ENV['GEM_PATH'] = @orig_gem_path
+ ENV['GEM_VENDOR'] = @orig_gem_vendor
Gem.ruby = @orig_ruby if @orig_ruby
@@ -1263,7 +1267,7 @@ Also, a list:
# The StaticSet is a static set of gem specifications used for testing only.
# It is available by requiring Gem::TestCase.
- class StaticSet
+ class StaticSet < Gem::Resolver::Set
##
# A StaticSet ignores remote because it has a fixed set of gems.
@@ -1274,6 +1278,8 @@ Also, a list:
# Creates a new StaticSet for the given +specs+
def initialize(specs)
+ super()
+
@specs = specs
@remote = true
@@ -1299,7 +1305,7 @@ Also, a list:
# Finds all gems matching +dep+ in this set.
def find_all(dep)
- @specs.find_all { |s| dep.matches_spec? s }
+ @specs.find_all { |s| dep.match? s, @prerelease }
end
##
diff --git a/lib/rubygems/test_utilities.rb b/lib/rubygems/test_utilities.rb
index 3deb71fa46..25786e6a21 100644
--- a/lib/rubygems/test_utilities.rb
+++ b/lib/rubygems/test_utilities.rb
@@ -38,6 +38,8 @@ class Gem::FakeFetcher
end
def find_data(path)
+ return File.read path.path if URI === path and 'file' == path.scheme
+
if URI === path and "URI::#{path.scheme.upcase}" != path.class.name then
raise ArgumentError,
"mismatch for scheme #{path.scheme} and class #{path.class}"
diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb
index 793cd953cb..8b26bebec8 100644
--- a/lib/rubygems/text.rb
+++ b/lib/rubygems/text.rb
@@ -26,6 +26,16 @@ module Gem::Text
result.join("\n").gsub(/^/, " " * indent)
end
+ def min3 a, b, c # :nodoc:
+ if a < b && a < c
+ a
+ elsif b < a && b < c
+ b
+ else
+ c
+ end
+ end
+
# This code is based directly on the Text gem implementation
# Returns a value representing the "cost" of transforming str1 into str2
def levenshtein_distance str1, str2
@@ -42,16 +52,16 @@ module Gem::Text
d = (0..m).to_a
x = nil
- n.times do |i|
+ str1.each_char.each_with_index do |char1,i|
e = i+1
- m.times do |j|
- cost = (s[i] == t[j]) ? 0 : 1
- x = [
+ str2.each_char.each_with_index do |char2,j|
+ cost = (char1 == char2) ? 0 : 1
+ x = min3(
d[j+1] + 1, # insertion
e + 1, # deletion
d[j] + cost # substitution
- ].min
+ )
d[j] = e
e = x
end
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index fa838333cd..2a6edc6131 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -96,6 +96,8 @@ class Gem::Uninstaller
(@user_install and spec.base_dir == Gem.user_dir)
end
+ list.sort!
+
if list.empty? then
if other_repo_specs.empty?
if default_specs.empty?
@@ -120,7 +122,8 @@ class Gem::Uninstaller
remove_all list
elsif list.size > 1 then
- gem_names = list.collect {|gem| gem.full_name} + ["All versions"]
+ gem_names = list.map { |gem| gem.full_name }
+ gem_names << "All versions"
say
_, index = choose_from_list "Select gem to uninstall:", gem_names
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index f7788cedf2..917b4ea5aa 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -157,6 +157,14 @@ module Gem::UserInteraction
def terminate_interaction exit_code = 0
ui.terminate_interaction exit_code
end
+
+ ##
+ # Calls +say+ with +msg+ or the results of the block if really_verbose
+ # is true.
+
+ def verbose msg = nil
+ say(msg || yield) if Gem.configuration.really_verbose
+ end
end
##
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index 20663326ef..8335ebe182 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -143,6 +143,10 @@
# "~> 3.0.0" 3.0.0 ... 3.1
# "~> 3.5" 3.5 ... 4.0
# "~> 3.5.0" 3.5.0 ... 3.6
+# "~> 3" 3.0 ... 4.0
+#
+# For the last example, single-digit versions are automatically extended with
+# a zero to give a sensible result.
class Gem::Version
autoload :Requirement, 'rubygems/requirement'
@@ -189,7 +193,7 @@ class Gem::Version
@@all = {}
def self.new version # :nodoc:
- return super unless Gem::VERSION == self.class
+ return super unless Gem::Version == self
@@all[version] ||= super
end
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index e33742049c..0da28bb9fb 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -75,6 +75,21 @@ class TestGem < Gem::TestCase
end
end
+ def test_self_install
+ spec_fetcher do |f|
+ f.gem 'a', 1
+ f.spec 'a', 2
+ end
+
+ gemhome2 = "#{@gemhome}2"
+
+ installed = Gem.install 'a', '= 1', :install_dir => gemhome2
+
+ assert_equal %w[a-1], installed.map { |spec| spec.full_name }
+
+ assert_path_exists File.join(gemhome2, 'gems', 'a-1')
+ end
+
def test_require_missing
save_loaded_features do
assert_raises ::LoadError do
@@ -216,6 +231,58 @@ class TestGem < Gem::TestCase
end
end
+ def test_default_path
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG['vendordir'] = File.join @tempdir, 'vendor'
+
+ FileUtils.rm_rf Gem.user_home
+
+ expected = [Gem.default_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_default_path_missing_vendor
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ FileUtils.rm_rf Gem.user_home
+
+ expected = [Gem.default_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_default_path_user_home
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG['vendordir'] = File.join @tempdir, 'vendor'
+
+ expected = [Gem.user_dir, Gem.default_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
+ def test_default_path_vendor_dir
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG['vendordir'] = File.join @tempdir, 'vendor'
+
+ FileUtils.mkdir_p Gem.vendor_dir
+
+ FileUtils.rm_rf Gem.user_home
+
+ expected = [Gem.default_dir, Gem.vendor_dir]
+
+ assert_equal expected, Gem.default_path
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
def test_self_default_sources
assert_equal %w[https://rubygems.org/], Gem.default_sources
end
@@ -816,6 +883,23 @@ class TestGem < Gem::TestCase
assert_match %r%Could not find 'b' %, e.message
end
+ def test_self_try_activate_missing_extensions
+ util_spec 'ext', '1' do |s|
+ s.extensions = %w[ext/extconf.rb]
+ s.mark_version
+ s.installed_by_version = v('2.2')
+ end
+
+ _, err = capture_io do
+ refute Gem.try_activate 'nonexistent'
+ end
+
+ expected = "Ignoring ext-1 because its extensions are not built. " +
+ "Try: gem pristine ext-1\n"
+
+ assert_equal expected, err
+ end
+
def test_self_use_paths
util_ensure_gem_dirs
@@ -951,6 +1035,30 @@ class TestGem < Gem::TestCase
end
end
+ def test_self_vendor_dir
+ expected =
+ File.join RbConfig::CONFIG['vendordir'], 'gems',
+ RbConfig::CONFIG['ruby_version']
+
+ assert_equal expected, Gem.vendor_dir
+ end
+
+ def test_self_vendor_dir_ENV_GEM_VENDOR
+ ENV['GEM_VENDOR'] = File.join @tempdir, 'vendor', 'gems'
+
+ assert_equal ENV['GEM_VENDOR'], Gem.vendor_dir
+ refute Gem.vendor_dir.frozen?
+ end
+
+ def test_self_vendor_dir_missing
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ assert_nil Gem.vendor_dir
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
def test_load_plugins
skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
plugin_path = File.join "lib", "rubygems_plugin.rb"
@@ -1251,13 +1359,28 @@ class TestGem < Gem::TestCase
end
def test_use_gemdeps
+ gem_deps_file = 'gem.deps.rb'.untaint
+ spec = util_spec 'a', 1
+
+ refute spec.activated?
+
+ open gem_deps_file, 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ Gem.use_gemdeps gem_deps_file
+
+ assert spec.activated?
+ end
+
+ def test_use_gemdeps_ENV
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
spec = util_spec 'a', 1
refute spec.activated?
- open 'Gemfile', 'w' do |io|
+ open 'gem.deps.rb', 'w' do |io|
io.write 'gem "a"'
end
@@ -1268,6 +1391,29 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
+ def test_use_gemdeps_argument_missing
+ e = assert_raises ArgumentError do
+ Gem.use_gemdeps 'gem.deps.rb'
+ end
+
+ assert_equal 'Unable to find gem dependencies file at gem.deps.rb',
+ e.message
+ end
+
+ def test_use_gemdeps_argument_missing_match_ENV
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] =
+ ENV['RUBYGEMS_GEMDEPS'], 'gem.deps.rb'
+
+ e = assert_raises ArgumentError do
+ Gem.use_gemdeps 'gem.deps.rb'
+ end
+
+ assert_equal 'Unable to find gem dependencies file at gem.deps.rb',
+ e.message
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
def test_use_gemdeps_automatic
skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
@@ -1287,6 +1433,17 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
+ def test_use_gemdeps_automatic_missing
+ skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
+
+ Gem.use_gemdeps
+
+ assert true # count
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
def test_use_gemdeps_disabled
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], ''
@@ -1294,7 +1451,7 @@ class TestGem < Gem::TestCase
refute spec.activated?
- open 'Gemfile', 'w' do |io|
+ open 'gem.deps.rb', 'w' do |io|
io.write 'gem "a"'
end
@@ -1305,6 +1462,27 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end
+ def test_use_gemdeps_missing_gem
+ skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7"
+ rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
+
+ open 'x', 'w' do |io|
+ io.write 'gem "a"'
+ end
+
+ expected = <<-EXPECTED
+Unable to resolve dependency: user requested 'a (>= 0)'
+You may need to `gem install -g` to install missing gems
+
+ EXPECTED
+
+ assert_output nil, expected do
+ Gem.use_gemdeps
+ end
+ ensure
+ ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
+ end
+
def test_use_gemdeps_specific
skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'
diff --git a/test/rubygems/test_gem_available_set.rb b/test/rubygems/test_gem_available_set.rb
index 9c204d046b..2b515447a3 100644
--- a/test/rubygems/test_gem_available_set.rb
+++ b/test/rubygems/test_gem_available_set.rb
@@ -3,6 +3,7 @@ require 'rubygems/available_set'
require 'rubygems/security'
class TestGemAvailableSet < Gem::TestCase
+
def setup
super
@@ -23,22 +24,24 @@ class TestGemAvailableSet < Gem::TestCase
end
def test_find_all
- a1, a1_gem = util_gem 'a', 1
+ a1, a1_gem = util_gem 'a', 1
+ a1a, a1a_gem = util_gem 'a', '1.a'
- source = Gem::Source::SpecificFile.new a1_gem
+ a1_source = Gem::Source::SpecificFile.new a1_gem
+ a1a_source = Gem::Source::SpecificFile.new a1a_gem
set = Gem::AvailableSet.new
- set.add a1, source
+ set.add a1, a1_source
+ set.add a1a, a1a_source
dep = Gem::Resolver::DependencyRequest.new dep('a'), nil
- specs = set.find_all dep
-
- spec = specs.first
+ assert_equal %w[a-1], set.find_all(dep).map { |spec| spec.full_name }
- assert_kind_of Gem::Resolver::LocalSpecification, spec
+ dep = Gem::Resolver::DependencyRequest.new dep('a', '>= 0.a'), nil
- assert_equal 'a-1', spec.full_name
+ assert_equal %w[a-1 a-1.a],
+ set.find_all(dep).map { |spec| spec.full_name }.sort
end
def test_match_platform
diff --git a/test/rubygems/test_gem_command.rb b/test/rubygems/test_gem_command.rb
index 712259c6cd..48cbc98d8c 100644
--- a/test/rubygems/test_gem_command.rb
+++ b/test/rubygems/test_gem_command.rb
@@ -184,5 +184,60 @@ class TestGemCommand < Gem::TestCase
assert_equal ['-h', 'command'], args
end
+ def test_show_lookup_failure_suggestions_local
+ correct = "non_existent_with_hint"
+ misspelled = "nonexistent_with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correct, 2
+ end
+
+ use_ui @ui do
+ @cmd.show_lookup_failure misspelled, Gem::Requirement.default, [], :local
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any repository
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_show_lookup_failure_suggestions_none
+ spec_fetcher do |fetcher|
+ fetcher.spec 'correct', 2
+ end
+
+ use_ui @ui do
+ @cmd.show_lookup_failure 'other', Gem::Requirement.default, [], :remote
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'other' (>= 0) in any repository
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
+ def test_show_lookup_failure_suggestions_remote
+ correct = "non_existent_with_hint"
+ misspelled = "nonexistent_with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correct, 2
+ end
+
+ use_ui @ui do
+ @cmd.show_lookup_failure misspelled, Gem::Requirement.default, [], :remote
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any repository
+ERROR: Possible alternatives: non_existent_with_hint
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
end
diff --git a/test/rubygems/test_gem_commands_cert_command.rb b/test/rubygems/test_gem_commands_cert_command.rb
index 03063859f5..4c1dcc25c0 100644
--- a/test/rubygems/test_gem_commands_cert_command.rb
+++ b/test/rubygems/test_gem_commands_cert_command.rb
@@ -179,6 +179,7 @@ Added '/CN=alternate/DC=example'
assert_empty @ui.error
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
+ refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
end
def test_execute_build_encrypted_key
diff --git a/test/rubygems/test_gem_commands_contents_command.rb b/test/rubygems/test_gem_commands_contents_command.rb
index 7f5cf22223..fb6906afd2 100644
--- a/test/rubygems/test_gem_commands_contents_command.rb
+++ b/test/rubygems/test_gem_commands_contents_command.rb
@@ -9,8 +9,8 @@ class TestGemCommandsContentsCommand < Gem::TestCase
@cmd = Gem::Commands::ContentsCommand.new
end
- def gem name
- spec = quick_gem name do |gem|
+ def gem name, version = 2
+ spec = quick_gem name, version do |gem|
gem.files = %W[lib/#{name}.rb Rakefile]
end
write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
@@ -135,6 +135,40 @@ class TestGemCommandsContentsCommand < Gem::TestCase
assert_equal "", @ui.error
end
+ def test_execute_show_install_dir
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:show_install_dir] = true
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = File.join @gemhome, 'gems', 'foo-2'
+
+ assert_equal "#{expected}\n", @ui.output
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_show_install_dir_version
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:show_install_dir] = true
+ @cmd.options[:version] = Gem::Requirement.new '= 1'
+
+ gem 'foo', 1
+ gem 'foo', 2
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = File.join @gemhome, 'gems', 'foo-1'
+
+ assert_equal "#{expected}\n", @ui.output
+ assert_equal "", @ui.error
+ end
+
def test_execute_no_prefix
@cmd.options[:args] = %w[foo]
@cmd.options[:prefix] = false
@@ -183,13 +217,22 @@ lib/foo.rb
assert @cmd.options[:prefix]
assert_empty @cmd.options[:specdirs]
assert_nil @cmd.options[:version]
+ refute @cmd.options[:show_install_dir]
- @cmd.send :handle_options, %w[-l -s foo --version 0.0.2 --no-prefix]
+ @cmd.send :handle_options, %w[
+ -l
+ -s
+ foo
+ --version 0.0.2
+ --no-prefix
+ --show-install-dir
+ ]
assert @cmd.options[:lib_only]
refute @cmd.options[:prefix]
assert_equal %w[foo], @cmd.options[:specdirs]
assert_equal Gem::Requirement.new('0.0.2'), @cmd.options[:version]
+ assert @cmd.options[:show_install_dir]
end
end
diff --git a/test/rubygems/test_gem_commands_environment_command.rb b/test/rubygems/test_gem_commands_environment_command.rb
index bb7589f50d..81ff55d357 100644
--- a/test/rubygems/test_gem_commands_environment_command.rb
+++ b/test/rubygems/test_gem_commands_environment_command.rb
@@ -28,6 +28,7 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
assert_match %r|RUBYGEMS PREFIX: |, @ui.output
assert_match %r|RUBY EXECUTABLE:.*#{RbConfig::CONFIG['ruby_install_name']}|,
@ui.output
+ assert_match %r|SYSTEM CONFIGURATION DIRECTORY:|, @ui.output
assert_match %r|EXECUTABLE DIRECTORY:|, @ui.output
assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output
assert_match %r|- #{Gem::Platform.local}|, @ui.output
diff --git a/test/rubygems/test_gem_commands_help_command.rb b/test/rubygems/test_gem_commands_help_command.rb
index 3a6f2fa523..bed6095827 100644
--- a/test/rubygems/test_gem_commands_help_command.rb
+++ b/test/rubygems/test_gem_commands_help_command.rb
@@ -22,6 +22,13 @@ class TestGemCommandsHelpCommand < Gem::TestCase
end
end
+ def test_gem_help_gem_dependencies
+ util_gem 'gem_dependencies' do |out, err|
+ assert_match 'gem.deps.rb', out
+ assert_equal '', err
+ end
+ end
+
def test_gem_help_platforms
util_gem 'platforms' do |out, err|
assert_match(/x86-freebsd/, out)
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index 6315cb5d95..f03285ae85 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -24,6 +24,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
Gem::Command.build_args = @orig_args
File.unlink @gemdeps if File.file? @gemdeps
+ File.unlink "#{@gemdeps}.lock" if File.file? "#{@gemdeps}.lock"
end
def test_execute_exclude_prerelease
@@ -194,6 +195,32 @@ class TestGemCommandsInstallCommand < Gem::TestCase
assert_match(%r!Unable to download data from http://not-there.nothing!, errs.shift)
end
+ def test_execute_nonexistent_hint_disabled
+ misspelled = "nonexistent_with_hint"
+ correctly_spelled = "non_existent_with_hint"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec correctly_spelled, 2
+ end
+
+ @cmd.options[:args] = [misspelled]
+ @cmd.options[:suggest_alternate] = false
+
+ use_ui @ui do
+ e = assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+
+ assert_equal 2, e.exit_code
+ end
+
+ expected = <<-EXPECTED
+ERROR: Could not find a valid gem 'nonexistent_with_hint' (>= 0) in any repository
+ EXPECTED
+
+ assert_equal expected, @ui.error
+ end
+
def test_execute_nonexistent_with_hint
misspelled = "nonexistent_with_hint"
correctly_spelled = "non_existent_with_hint"
@@ -238,7 +265,10 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal 2, e.exit_code
end
- expected = ["ERROR: Could not find a valid gem 'non-existent_with-hint' (>= 0) in any repository", "ERROR: Possible alternatives: nonexistent-with_hint"]
+ expected = [
+ "ERROR: Could not find a valid gem 'non-existent_with-hint' (>= 0) in any repository",
+ "ERROR: Possible alternatives: nonexistent-with_hint"
+ ]
output = @ui.error.split "\n"
@@ -535,6 +565,11 @@ ERROR: Possible alternatives: non_existent_with_hint
end
def test_install_gem_ignore_dependencies_both
+ done_installing = false
+ Gem.done_installing do
+ done_installing = true
+ end
+
spec = quick_spec 'a', 2
util_build_gem spec
@@ -546,6 +581,8 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.install_gem 'a', '>= 0'
assert_equal %w[a-2], @cmd.installed_specs.map { |s| s.full_name }
+
+ assert done_installing, 'documentation was not generated'
end
def test_install_gem_ignore_dependencies_remote
@@ -622,8 +659,8 @@ ERROR: Possible alternatives: non_existent_with_hint
end
assert_equal 2, e.exit_code
- assert_match %r!Could not find a valid gem 'blah' \(>= 0\)!, @ui.error
- assert_match %r!Unable to download data from http://not-there\.nothing!, @ui.error
+
+ assert_match 'Unable to download data', @ui.error
end
def test_show_source_problems_even_on_success
@@ -648,7 +685,7 @@ ERROR: Possible alternatives: non_existent_with_hint
e = @ui.error
- x = "WARNING: Unable to pull data from 'http://nonexistent.example': no data for http://nonexistent.example/latest_specs.4.8.gz (http://nonexistent.example/latest_specs.4.8.gz)\n"
+ x = "WARNING: Unable to pull data from 'http://nonexistent.example': no data for http://nonexistent.example/specs.4.8.gz (http://nonexistent.example/specs.4.8.gz)\n"
assert_equal x, e
end
@@ -672,6 +709,56 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name }
assert_match "Using a (2)", @ui.output
+ assert File.exist?("#{@gemdeps}.lock")
+ end
+
+ def test_execute_uses_from_a_gemdeps_with_no_lock
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'a'"
+ end
+
+ @cmd.handle_options %w[--no-lock]
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "Using a (2)", @ui.output
+ assert !File.exist?("#{@gemdeps}.lock")
+ end
+
+ def test_execute_installs_from_a_gemdeps_with_conservative
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.clear
+ fetcher.gem 'a', 1
+ end
+
+ File.open @gemdeps, "w" do |f|
+ f << "gem 'a'"
+ end
+
+ @cmd.handle_options %w[--conservative]
+ @cmd.options[:gemdeps] = @gemdeps
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
+ @cmd.execute
+ end
+ end
+
+ assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name }
+
+ assert_match "Using a (1)", @ui.output
end
def test_execute_installs_from_a_gemdeps
@@ -885,6 +972,18 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal 'gem.deps.rb', @cmd.options[:gemdeps]
end
+ def test_handle_options_suggest
+ assert @cmd.options[:suggest_alternate]
+
+ @cmd.handle_options %w[--no-suggestions]
+
+ refute @cmd.options[:suggest_alternate]
+
+ @cmd.handle_options %w[--suggestions]
+
+ assert @cmd.options[:suggest_alternate]
+ end
+
def test_handle_options_without
@cmd.handle_options %w[--without test]
diff --git a/test/rubygems/test_gem_commands_open_command.rb b/test/rubygems/test_gem_commands_open_command.rb
new file mode 100644
index 0000000000..25f22c81b3
--- /dev/null
+++ b/test/rubygems/test_gem_commands_open_command.rb
@@ -0,0 +1,46 @@
+require 'rubygems/test_case'
+require 'rubygems/commands/open_command'
+
+class TestGemCommandsOpenCommand < Gem::TestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::OpenCommand.new
+ end
+
+ def gem name
+ spec = quick_gem name do |gem|
+ gem.files = %W[lib/#{name}.rb Rakefile]
+ end
+ write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
+ write_file File.join(*%W[gems #{spec.full_name} Rakefile])
+ end
+
+ def test_execute
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:editor] = "#{Gem.ruby} -e0 --"
+
+ gem 'foo'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "", @ui.error
+ end
+
+ def test_execute_bad_gem
+ @cmd.options[:args] = %w[foo]
+
+ assert_raises Gem::MockGemUi::TermError do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_match %r|Unable to find gem 'foo'|, @ui.output
+ assert_equal "", @ui.error
+ end
+
+end
diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb
index 9c5ee7a5a8..2fe3384954 100644
--- a/test/rubygems/test_gem_commands_setup_command.rb
+++ b/test/rubygems/test_gem_commands_setup_command.rb
@@ -111,6 +111,12 @@ class TestGemCommandsSetupCommand < Gem::TestCase
end
expected = <<-EXPECTED
+=== #{Gem::VERSION} / 2013-03-26
+
+* Bug fixes:
+ * Fixed release note display for LANG=C when installing rubygems
+ * π is tasty
+
=== 2.0.2 / 2013-03-06
* Bug fixes:
@@ -118,6 +124,9 @@ class TestGemCommandsSetupCommand < Gem::TestCase
EXPECTED
+ output = @ui.output
+ output.force_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
+
assert_equal expected, @ui.output
ensure
capture_io do
diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb
index 6f9ec104a5..4f045c5e3d 100644
--- a/test/rubygems/test_gem_commands_uninstall_command.rb
+++ b/test/rubygems/test_gem_commands_uninstall_command.rb
@@ -239,6 +239,42 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
assert_equal nil, @cmd.options[:install_dir]
assert_equal true, @cmd.options[:user_install]
assert_equal Gem::Requirement.default, @cmd.options[:version]
+ assert_equal false, @cmd.options[:vendor]
+ end
+
+ def test_handle_options_vendor
+ use_ui @ui do
+ @cmd.handle_options %w[--vendor]
+ end
+
+ assert @cmd.options[:vendor]
+ assert_equal Gem.vendor_dir, @cmd.options[:install_dir]
+
+ assert_empty @ui.output
+
+ expected = <<-EXPECTED
+WARNING: Use your OS package manager to uninstall vendor gems
+ EXPECTED
+
+ assert_match expected, @ui.error
+ end
+
+ def test_handle_options_vendor_missing
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ e = assert_raises OptionParser::InvalidOption do
+ @cmd.handle_options %w[--vendor]
+ end
+
+ assert_equal 'invalid option: --vendor your platform is not supported',
+ e.message
+
+ refute @cmd.options[:vendor]
+ refute @cmd.options[:install_dir]
+
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
end
end
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index d259383ba2..2b3bb56855 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -52,6 +52,30 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
assert_empty out
end
+ def test_execute_multiple
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.gem 'ab', 2
+
+ fetcher.clear
+
+ fetcher.spec 'a', 1
+ fetcher.spec 'ab', 1
+ end
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a", out.shift
+ assert_empty out
+ end
+
def test_execute_system
spec_fetcher do |fetcher|
fetcher.gem 'rubygems-update', 9 do |s| s.files = %w[setup.rb] end
@@ -266,6 +290,30 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
assert_empty out
end
+ def test_execute_named_some_up_to_date
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ fetcher.clear
+ fetcher.spec 'a', 1
+
+ fetcher.spec 'b', 2
+ end
+
+ @cmd.options[:args] = %w[a b]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ out = @ui.output.split "\n"
+ assert_equal "Updating installed gems", out.shift
+ assert_equal "Updating a", out.shift
+ assert_equal "Gems updated: a", out.shift
+ assert_equal "Gems already up-to-date: b", out.shift
+
+ assert_empty out
+ end
+
def test_execute_named_up_to_date
spec_fetcher do |fetcher|
fetcher.spec 'a', 2
@@ -437,6 +485,21 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
assert_equal expected, @cmd.options
end
+ def test_update_gem_unresolved_dependency
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.gem 'a', 2 do |s|
+ s.add_dependency 'b', '>= 2'
+ end
+
+ fetcher.spec 'b', 1
+ end
+
+ @cmd.update_gem 'a'
+
+ assert_empty @cmd.updated
+ end
+
def test_update_rubygems_arguments
@cmd.options[:system] = true
diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb
index e627452243..469fd15bd9 100644
--- a/test/rubygems/test_gem_commands_yank_command.rb
+++ b/test/rubygems/test_gem_commands_yank_command.rb
@@ -18,7 +18,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
@cmd.handle_options %w[a --version 1.0 --platform x86-darwin -k KEY]
assert_equal %w[a], @cmd.options[:args]
- assert_equal 'KEY', @cmd.options[:key]
+ assert_equal :KEY, @cmd.options[:key]
assert_nil @cmd.options[:platform]
assert_equal req('= 1.0'), @cmd.options[:version]
end
@@ -61,7 +61,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
@cmd.options[:args] = %w[a]
@cmd.options[:version] = req('= 1.0')
- @cmd.options[:key] = 'KEY'
+ @cmd.options[:key] = :KEY
use_ui @ui do
@cmd.execute
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index e9cd33579d..5077f37359 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -234,16 +234,19 @@ if you believe they were disclosed to a third party.
end
def test_handle_arguments_debug
- old_dollar_DEBUG = $DEBUG
assert_equal false, $DEBUG
args = %w[--debug]
- @cfg.handle_arguments args
+ _, err = capture_io do
+ @cfg.handle_arguments args
+ end
+
+ assert_match 'NOTE', err
assert_equal true, $DEBUG
ensure
- $DEBUG = old_dollar_DEBUG
+ $DEBUG = false
end
def test_handle_arguments_override
@@ -377,6 +380,9 @@ if you believe they were disclosed to a third party.
fp.puts ":verbose: false"
fp.puts ":sources:"
fp.puts " - http://more-gems.example.com"
+ fp.puts ":ssl_verify_mode: 2"
+ fp.puts ":ssl_ca_cert: /nonexistent/ca_cert.pem"
+ fp.puts ":ssl_client_cert: /nonexistent/client_cert.pem"
fp.puts "install: --wrappers"
end
@@ -399,6 +405,10 @@ if you believe they were disclosed to a third party.
assert_equal false, @cfg.update_sources, 'update_sources'
assert_equal false, @cfg.verbose, 'verbose'
+ assert_equal 2, @cfg.ssl_verify_mode
+ assert_equal '/nonexistent/ca_cert.pem', @cfg.ssl_ca_cert
+ assert_equal '/nonexistent/client_cert.pem', @cfg.ssl_client_cert
+
assert_equal '--wrappers --no-rdoc', @cfg[:install], 'install'
assert_equal %w[http://even-more-gems.example.com], Gem.sources
@@ -409,11 +419,13 @@ if you believe they were disclosed to a third party.
fp.puts "some-non-yaml-hash-string"
end
- # Avoid writing stuff to output when running tests
- Gem::ConfigFile.class_eval { def warn(args); end }
+ begin
+ verbose, $VERBOSE = $VERBOSE, nil
- # This should not raise exception
- util_config_file
+ util_config_file
+ ensure
+ $VERBOSE = verbose
+ end
end
def test_load_ssl_verify_mode_from_config
diff --git a/test/rubygems/test_gem_dependency.rb b/test/rubygems/test_gem_dependency.rb
index 7fc4dbbebf..c43ff0e03d 100644
--- a/test/rubygems/test_gem_dependency.rb
+++ b/test/rubygems/test_gem_dependency.rb
@@ -2,6 +2,7 @@ require 'rubygems/test_case'
require 'rubygems/dependency'
class TestGemDependency < Gem::TestCase
+
def test_initialize
d = dep "pkg", "> 1.0"
@@ -28,6 +29,16 @@ class TestGemDependency < Gem::TestCase
assert_equal req(">= 0"), d.requirement
end
+ def test_initialize_prerelease
+ d = dep 'd', '1.a'
+
+ assert d.prerelease?
+
+ d = dep 'd', '= 1.a'
+
+ assert d.prerelease?
+ end
+
def test_initialize_type
assert_equal :runtime, dep("pkg").type
assert_equal :development, dep("pkg", [], :development).type
@@ -115,6 +126,106 @@ class TestGemDependency < Gem::TestCase
refute_equal dep("pkg", :development), dep("pkg", :runtime), "type"
end
+ def test_match_eh_name_tuple
+ a_dep = dep 'a'
+
+ a_tup = Gem::NameTuple.new 'a', 1
+ b_tup = Gem::NameTuple.new 'b', 2
+ c_tup = Gem::NameTuple.new 'c', '2.a'
+
+ assert a_dep.match? a_tup
+ refute a_dep.match? b_tup
+
+ b_dep = dep 'b', '>= 3'
+
+ refute b_dep.match? b_tup
+
+ c_dep = dep 'c', '>= 1'
+
+ refute c_dep.match? c_tup
+
+ c_dep = dep 'c'
+
+ refute c_dep.match? c_tup
+
+ c_dep = dep 'c', '2.a'
+
+ assert c_dep.match? c_tup
+ end
+
+ def test_match_eh_allow_prerelease
+ a_dep = dep 'a'
+
+ a_tup = Gem::NameTuple.new 'a', 1
+ b_tup = Gem::NameTuple.new 'b', 2
+ c_tup = Gem::NameTuple.new 'c', '2.a'
+
+ assert a_dep.match? a_tup, nil, true
+ refute a_dep.match? b_tup, nil, true
+
+ b_dep = dep 'b', '>= 3'
+
+ refute b_dep.match? b_tup, nil, true
+
+ c_dep = dep 'c', '>= 1'
+
+ assert c_dep.match? c_tup, nil, true
+
+ c_dep = dep 'c'
+
+ assert c_dep.match? c_tup, nil, true
+
+ c_dep = dep 'c', '2.a'
+
+ assert c_dep.match? c_tup, nil, true
+ end
+
+ def test_match_eh_specification
+ a_dep = dep 'a'
+
+ a_spec = util_spec 'a', 1
+ b_spec = util_spec 'b', 2
+ c_spec = util_spec 'c', '2.a'
+
+ assert a_dep.match? a_spec
+ refute a_dep.match? b_spec
+
+ b_dep = dep 'b', '>= 3'
+
+ refute b_dep.match? b_spec
+
+ c_dep = dep 'c', '>= 1'
+
+ refute c_dep.match? c_spec
+
+ c_dep = dep 'c'
+
+ refute c_dep.match? c_spec
+
+ c_dep = dep 'c', '2.a'
+
+ assert c_dep.match? c_spec
+ end
+
+ def test_matches_spec_eh
+ spec = util_spec 'b', 2
+
+ refute dep('a') .matches_spec?(spec), 'name mismatch'
+ assert dep('b') .matches_spec?(spec), 'name match'
+ refute dep('b', '= 1') .matches_spec?(spec), 'requirement mismatch'
+ assert dep('b', '~> 2').matches_spec?(spec), 'requirement match'
+ end
+
+ def test_matches_spec_eh_prerelease
+ spec = util_spec 'b', '2.1.a'
+
+ refute dep('a') .matches_spec?(spec), 'name mismatch'
+ assert dep('b') .matches_spec?(spec), 'name match'
+ refute dep('b', '= 1') .matches_spec?(spec), 'requirement mismatch'
+ assert dep('b', '~> 2') .matches_spec?(spec), 'requirement match'
+ assert dep('b', '~> 2.a').matches_spec?(spec), 'prerelease requirement'
+ end
+
def test_merge
a1 = dep 'a', '~> 1.0'
a2 = dep 'a', '= 1.0'
@@ -182,6 +293,29 @@ class TestGemDependency < Gem::TestCase
assert dep('a', '= 1').specific?
end
+ def test_to_spec
+ util_spec 'a', '1'
+ a_2 = util_spec 'a', '2'
+
+ a_dep = dep 'a', '>= 0'
+
+ assert_equal a_2, a_dep.to_spec
+ end
+
+ def test_to_spec_prerelease
+ a_1 = util_spec 'a', '1'
+ a_1_1_a = util_spec 'a', '1.1.a'
+
+ a_dep = dep 'a', '>= 0'
+
+ assert_equal a_1, a_dep.to_spec
+
+ a_pre_dep = dep 'a', '>= 0'
+ a_pre_dep.prerelease = true
+
+ assert_equal a_1_1_a, a_pre_dep.to_spec
+ end
+
def test_to_specs_suggests_other_versions
a = util_spec 'a', '1.0', 'b' => '>= 1.0'
@@ -197,7 +331,7 @@ class TestGemDependency < Gem::TestCase
dep.to_specs
end
- assert_equal "Could not find 'a' (= 2.0) - did find: [a-1.0]", e.message
+ assert_match "Could not find 'a' (= 2.0) - did find: [a-1.0]", e.message
end
def test_to_specs_indicates_total_gem_set_size
@@ -215,7 +349,7 @@ class TestGemDependency < Gem::TestCase
dep.to_specs
end
- assert_equal "Could not find 'b' (= 2.0) among 1 total gem(s)", e.message
+ assert_match "Could not find 'b' (= 2.0) among 1 total gem(s)", e.message
end
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index 352ebbb54f..8a0470761e 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -126,6 +126,27 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal [p1a], inst.installed_gems
end
+ def test_install_prerelease_bug_990
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '1.b' do |s|
+ s.add_dependency 'b', '~> 1.a'
+ end
+
+ fetcher.gem 'b', '1.b' do |s|
+ s.add_dependency 'c', '>= 1'
+ end
+
+ fetcher.gem 'c', '1.1.b'
+ end
+
+ dep = Gem::Dependency.new 'a'
+
+ inst = Gem::DependencyInstaller.new :prerelease => true
+ inst.install dep
+
+ assert_equal %w[a-1.b b-1.b c-1.1.b], Gem::Specification.map(&:full_name)
+ end
+
def test_install_when_only_prerelease
p1a, gem = util_gem 'p', '1.a'
@@ -138,7 +159,9 @@ class TestGemDependencyInstaller < Gem::TestCase
dep = Gem::Dependency.new "p"
inst = Gem::DependencyInstaller.new
- inst.install dep
+ assert_raises Gem::UnsatisfiableDependencyError do
+ inst.install dep
+ end
assert_equal %w[], Gem::Specification.map(&:full_name)
assert_equal [], inst.installed_gems
@@ -291,7 +314,7 @@ class TestGemDependencyInstaller < Gem::TestCase
Gem.done_installing do |installer, specs|
done_installing_ran = true
- assert_equal inst, installer
+ refute_nil installer
assert_equal [@a1, @b1], specs
end
@@ -455,6 +478,20 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end
+ def test_install_local_prerelease
+ util_setup_gems
+
+ FileUtils.mv @a1_pre_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'a-1.a.gem'
+ end
+
+ assert_equal %w[a-1.a], inst.installed_gems.map { |s| s.full_name }
+ end
+
def test_install_local_dependency
util_setup_gems
@@ -538,6 +575,23 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[a-1 e-1], inst.installed_gems.map { |s| s.full_name }
end
+ def test_install_no_document
+ util_setup_gems
+
+ done_installing_called = false
+
+ Gem.done_installing do |dep_installer, specs|
+ done_installing_called = true
+ assert_empty dep_installer.document
+ end
+
+ inst = Gem::DependencyInstaller.new :domain => :local, :document => []
+
+ inst.install @a1_gem
+
+ assert done_installing_called
+ end
+
def test_install_env_shebang
util_setup_gems
@@ -682,7 +736,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst.install 'b'
end
- expected = "Unable to resolve dependency: 'b (= 1)' requires 'a (>= 0)'"
+ expected = "Unable to resolve dependency: 'b (>= 0)' requires 'a (>= 0)'"
assert_equal expected, e.message
end
@@ -796,10 +850,10 @@ class TestGemDependencyInstaller < Gem::TestCase
s.platform = Gem::Platform.new %w[cpu other_platform 1]
end
- util_clear_gems
-
si = util_setup_spec_fetcher @a1, a2_o
+ util_clear_gems
+
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
a1_data = nil
@@ -1043,165 +1097,6 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_kind_of Gem::SourceFetchProblem, installer.errors.first
end
- def assert_resolve expected, *specs
- util_clear_gems
- util_setup_spec_fetcher(*specs)
- Gem::Specification.reset
-
- inst = Gem::DependencyInstaller.new
- inst.find_spec_by_name_and_version specs.first.name
- inst.gather_dependencies
-
- actual = inst.gems_to_install.map { |s| s.full_name }
- assert_equal expected, actual
- end
-
- def assert_resolve_pre expected, *specs
- util_clear_gems
-
- util_setup_spec_fetcher(*specs)
- Gem::Specification.reset
-
- spec = specs.first
-
- inst = Gem::DependencyInstaller.new :prerelease => true
- inst.find_spec_by_name_and_version spec.name, spec.version
- inst.gather_dependencies
-
- actual = inst.gems_to_install.map { |s| s.full_name }
- assert_equal expected, actual
- end
-
- def test_gather_dependencies
- util_setup_gems
- util_reset_gems
-
- inst = Gem::DependencyInstaller.new
- inst.find_spec_by_name_and_version 'b'
- inst.gather_dependencies
-
- assert_equal %w[a-1 b-1], inst.gems_to_install.map { |s| s.full_name }
- end
-
- ##
- # [A1] depends on
- # [B] > 0 (satisfied by 2.0)
- # [B1] depends on
- # [C] > 0 (satisfied by 1.0)
- # [B2] depends on nothing!
- # [C1] depends on nothing
-
- def test_gather_dependencies_dropped
- a1, = util_spec 'a', '1', 'b' => nil
- b1, = util_spec 'b', '1', 'c' => nil
- b2, = util_spec 'b', '2'
- c1, = util_spec 'c', '1'
-
- assert_resolve %w[b-2 a-1], a1, b1, b2, c1
- end
-
- ##
- # [A] depends on
- # [B] >= 1.0 (satisfied by 1.1) depends on
- # [Z]
- # [C] >= 1.0 depends on
- # [B] = 1.0
- #
- # and should backtrack to resolve using b-1.0, pruning Z from the
- # resolve.
-
- def test_gather_dependencies_raggi_the_edgecase_generator
- a, _ = util_spec 'a', '1.0', 'b' => '>= 1.0', 'c' => '>= 1.0'
- b1, _ = util_spec 'b', '1.0'
- b2, _ = util_spec 'b', '1.1', 'z' => '>= 1.0'
- c, _ = util_spec 'c', '1.0', 'b' => '= 1.0'
-
- assert_resolve %w[b-1.0 c-1.0 a-1.0], a, b1, b2, c
- end
-
- ##
- # [A] depends on
- # [B] >= 1.0 (satisfied by 2.0)
- # [C] = 1.0 depends on
- # [B] ~> 1.0
- #
- # and should resolve using b-1.0
-
- def test_gather_dependencies_over
- a, _ = util_spec 'a', '1.0', 'b' => '>= 1.0', 'c' => '= 1.0'
- b1, _ = util_spec 'b', '1.0'
- b2, _ = util_spec 'b', '2.0'
- c, _ = util_spec 'c', '1.0', 'b' => '~> 1.0'
-
- assert_resolve %w[b-1.0 c-1.0 a-1.0], a, b1, b2, c
- end
-
- ##
- # [A] depends on
- # [B] ~> 1.0 (satisfied by 1.1)
- # [C] = 1.0 depends on
- # [B] = 1.0
- #
- # and should resolve using b-1.0
- #
- # TODO: this is not under, but over... under would require depth
- # first resolve through a dependency that is later pruned.
-
- def test_gather_dependencies_under
- a, _ = util_spec 'a', '1.0', 'b' => '~> 1.0', 'c' => '= 1.0'
- b10, _ = util_spec 'b', '1.0'
- b11, _ = util_spec 'b', '1.1'
- c, _ = util_spec 'c', '1.0', 'b' => '= 1.0'
-
- assert_resolve %w[b-1.0 c-1.0 a-1.0], a, b10, b11, c
- end
-
- # under
- #
- # [A] depends on
- # [B] ~> 1.0 (satisfied by 1.0)
- # [C] = 1.0 depends on
- # [B] = 2.0
-
- def test_gather_dependencies_divergent
- a, _ = util_spec 'a', '1.0', 'b' => '~> 1.0', 'c' => '= 1.0'
- b1, _ = util_spec 'b', '1.0'
- b2, _ = util_spec 'b', '2.0'
- c, _ = util_spec 'c', '1.0', 'b' => '= 2.0'
-
- assert_raises Gem::DependencyError do
- assert_resolve :ignored, a, b1, b2, c
- end
- end
-
- def test_gather_dependencies_platform_alternate
- util_setup_wxyz
- util_set_arch 'cpu-my_platform1'
-
- assert_resolve %w[x-1-cpu-my_platform-1 w-1], @w1, @x1_m
- end
-
- def test_gather_dependencies_platform_bump
- util_setup_wxyz
-
- assert_resolve %w[y-1 z-1], @z1, @y1
- end
-
- def test_gather_dependencies_prerelease
- util_setup_gems
- util_setup_c1_pre
-
- assert_resolve_pre %w[a-1.a b-1 c-1.a], @c1_pre, @a1_pre, @b1
- end
-
- def test_gather_dependencies_old_required
- util_setup_d
- e1, = util_spec 'e', '1', 'd' => '= 1'
- util_clear_gems
-
- assert_resolve %w[d-1 e-1], e1, @d1, @d2
- end
-
def test_resolve_dependencies
util_setup_gems
@@ -1232,6 +1127,21 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[b-1], requests
end
+ def test_resolve_dependencies_local
+ util_setup_gems
+
+ @a2, @a2_gem = util_gem 'a', '2'
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @a2_gem, @tempdir
+
+ inst = Gem::DependencyInstaller.new
+ request_set = inst.resolve_dependencies 'a-1.gem', req('>= 0')
+
+ requests = request_set.sorted_requests.map { |req| req.full_name }
+
+ assert_equal %w[a-1], requests
+ end
+
def util_write_a1_bin
write_file File.join('gems', 'a-1', 'bin', 'a_bin') do |fp|
fp.puts "#!/usr/bin/ruby"
diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb
index eb86f53872..5607096a0b 100644
--- a/test/rubygems/test_gem_ext_builder.rb
+++ b/test/rubygems/test_gem_ext_builder.rb
@@ -132,6 +132,17 @@ install:
assert_path_exists File.join @spec.gem_dir, 'lib', 'a', 'b.rb'
end
+ def test_build_extensions_with_gemhome_with_space
+ new_gemhome = File.join @tempdir, 'gem home'
+ File.rename(@gemhome, new_gemhome)
+ @gemhome = new_gemhome
+ Gem.use_paths(@gemhome)
+ @spec = util_spec 'a'
+ @builder = Gem::Ext::Builder.new @spec, ''
+
+ test_build_extensions
+ end
+
def test_build_extensions_install_ext_only
class << Gem
alias orig_install_extension_in_lib install_extension_in_lib
@@ -226,7 +237,7 @@ install:
gem_make_out = File.join @spec.extension_dir, 'gem_make.out'
- assert_match %r%#{Regexp.escape Gem.ruby} extconf\.rb%,
+ assert_match %r%#{Regexp.escape Gem.ruby}.* extconf\.rb%,
File.read(gem_make_out)
assert_match %r%: No such file%,
File.read(gem_make_out)
diff --git a/test/rubygems/test_gem_ext_ext_conf_builder.rb b/test/rubygems/test_gem_ext_ext_conf_builder.rb
index 367c933a4c..f2f467e871 100644
--- a/test/rubygems/test_gem_ext_ext_conf_builder.rb
+++ b/test/rubygems/test_gem_ext_ext_conf_builder.rb
@@ -33,7 +33,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_same result, output
end
- assert_match(/^#{Gem.ruby} extconf.rb/, output[0])
+ assert_match(/^#{Gem.ruby}.* extconf.rb/, output[0])
assert_equal "creating Makefile\n", output[1]
assert_contains_make_command 'clean', output[2]
assert_contains_make_command '', output[4]
@@ -106,7 +106,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
assert_equal 'extconf failed, exit code 1', error.message
- assert_equal("#{Gem.ruby} extconf.rb", output[0])
+ assert_match(/^#{Gem.ruby}.* extconf.rb/, output[0])
assert_path_exists File.join @dest_path, 'mkmf.log'
end
diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb
index d70ac35beb..c117c8f826 100644
--- a/test/rubygems/test_gem_gemcutter_utilities.rb
+++ b/test/rubygems/test_gem_gemcutter_utilities.rb
@@ -148,6 +148,15 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_equal "", @sign_in_ui.output
end
+ def test_sign_in_skips_with_key_override
+ api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
+ Gem.configuration.api_keys[:KEY] = 'other'
+ @cmd.options[:key] = :KEY
+ util_sign_in [api_key, 200, 'OK']
+
+ assert_equal "", @sign_in_ui.output
+ end
+
def test_sign_in_with_other_credentials_doesnt_overwrite_other_keys
api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
other_api_key = 'f46dbb18bb6a9c97cdc61b5b85c186a17403cdcbf'
diff --git a/test/rubygems/test_gem_impossible_dependencies_error.rb b/test/rubygems/test_gem_impossible_dependencies_error.rb
index 577ee580ec..9c9825ca9a 100644
--- a/test/rubygems/test_gem_impossible_dependencies_error.rb
+++ b/test/rubygems/test_gem_impossible_dependencies_error.rb
@@ -28,14 +28,30 @@ class TestGemImpossibleDependenciesError < Gem::TestCase
expected = <<-EXPECTED
rye-0.9.8 requires net-ssh (>= 2.0.13) but it conflicted:
- Activated net-ssh-2.6.5 via:
- net-ssh-2.6.5 (>= 2.0.13), rye-0.9.8 (= 0.9.8)
- instead of (~> 2.2.2) via:
- net-ssh-2.6.5 (>= 2.0.13), rye-0.9.8 (= 0.9.8)
- Activated net-ssh-2.2.2 via:
- net-ssh-2.2.2 (>= 2.0.13), rye-0.9.8 (= 0.9.8)
- instead of (>= 2.6.5) via:
- net-ssh-2.2.2 (>= 2.0.13), rye-0.9.8 (= 0.9.8)
+ Activated net-ssh-2.6.5
+ which does not match conflicting dependency (~> 2.2.2)
+
+ Conflicting dependency chains:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.6.5 activated
+
+ versus:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.6.5 activated, depends on
+ net-ssh (~> 2.2.2)
+
+ Activated net-ssh-2.2.2
+ which does not match conflicting dependency (>= 2.6.5)
+
+ Conflicting dependency chains:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.2.2 activated
+
+ versus:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.2.2 activated, depends on
+ net-ssh (>= 2.6.5)
+
EXPECTED
assert_equal expected, error.message
diff --git a/test/rubygems/test_gem_install_update_options.rb b/test/rubygems/test_gem_install_update_options.rb
index 3f63896999..de09d7a171 100644
--- a/test/rubygems/test_gem_install_update_options.rb
+++ b/test/rubygems/test_gem_install_update_options.rb
@@ -17,6 +17,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
def test_add_install_update_options
args = %w[
--document
+ --build-root build_root
--format-exec
--ignore-dependencies
--rdoc
@@ -25,6 +26,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
-f
-i /install_to
-w
+ --vendor
]
args.concat %w[-P HighSecurity] if defined?(OpenSSL::SSL)
@@ -32,6 +34,12 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
assert @cmd.handles?(args)
end
+ def test_build_root
+ @cmd.handle_options %w[--build-root build_root]
+
+ assert_equal File.expand_path('build_root'), @cmd.options[:build_root]
+ end
+
def test_doc
@cmd.handle_options %w[--doc]
@@ -147,4 +155,30 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
ensure
FileUtils.chmod 0755, @gemhome
end
+
+ def test_vendor
+ @cmd.handle_options %w[--vendor]
+
+ assert @cmd.options[:vendor]
+ assert_equal Gem.vendor_dir, @cmd.options[:install_dir]
+ end
+
+ def test_vendor_missing
+ orig_vendordir = RbConfig::CONFIG['vendordir']
+ RbConfig::CONFIG.delete 'vendordir'
+
+ e = assert_raises OptionParser::InvalidOption do
+ @cmd.handle_options %w[--vendor]
+ end
+
+ assert_equal 'invalid option: --vendor your platform is not supported',
+ e.message
+
+ refute @cmd.options[:vendor]
+ refute @cmd.options[:install_dir]
+
+ ensure
+ RbConfig::CONFIG['vendordir'] = orig_vendordir
+ end
+
end
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index eff62ab28b..6f8012feb8 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -188,6 +188,56 @@ gem 'other', version
assert_match %r|generated by RubyGems|, wrapper
end unless Gem.win_platform?
+ def test_check_that_user_bin_dir_is_in_path
+ bin_dir = @installer.bin_dir
+
+ if Gem.win_platform?
+ bin_dir = bin_dir.downcase.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
+ end
+
+ orig_PATH, ENV['PATH'] =
+ ENV['PATH'], [ENV['PATH'], bin_dir].join(File::PATH_SEPARATOR)
+
+ use_ui @ui do
+ @installer.check_that_user_bin_dir_is_in_path
+ end
+
+ assert_empty @ui.error
+ ensure
+ ENV['PATH'] = orig_PATH
+ end
+
+ def test_check_that_user_bin_dir_is_in_path_tilde
+ skip "Tilde is PATH is not supported under MS Windows" if win_platform?
+
+ orig_PATH, ENV['PATH'] =
+ ENV['PATH'], [ENV['PATH'], '~/bin'].join(File::PATH_SEPARATOR)
+
+ @installer.bin_dir.replace File.join @userhome, 'bin'
+
+ use_ui @ui do
+ @installer.check_that_user_bin_dir_is_in_path
+ end
+
+ assert_empty @ui.error
+ ensure
+ ENV['PATH'] = orig_PATH unless win_platform?
+ end
+
+ def test_check_that_user_bin_dir_is_in_path_not_in_path
+ use_ui @ui do
+ @installer.check_that_user_bin_dir_is_in_path
+ end
+
+ expected = @installer.bin_dir
+
+ if Gem.win_platform? then
+ expected = expected.downcase.gsub(File::SEPARATOR, File::ALT_SEPARATOR)
+ end
+
+ assert_match expected, @ui.error
+ end
+
def test_ensure_dependency
util_spec 'a'
@@ -574,6 +624,9 @@ gem 'other', version
def test_generate_bin_symlink_win32
old_win_platform = Gem.win_platform?
Gem.win_platform = true
+ old_alt_separator = File::ALT_SEPARATOR
+ File.__send__(:remove_const, :ALT_SEPARATOR)
+ File.const_set(:ALT_SEPARATOR, '\\')
@installer.wrappers = false
util_make_exec
@installer.gem_dir = util_gem_dir
@@ -592,6 +645,8 @@ gem 'other', version
wrapper = File.read installed_exec
assert_match(/generated by RubyGems/, wrapper)
ensure
+ File.__send__(:remove_const, :ALT_SEPARATOR)
+ File.const_set(:ALT_SEPARATOR, old_alt_separator)
Gem.win_platform = old_win_platform
end
@@ -1053,7 +1108,7 @@ gem 'other', version
path = File.join(@gemhome, 'gems', 'a-2', 'gem_make.out')
- if File.exists?(path)
+ if File.exist?(path)
puts File.read(path)
puts '-' * 78
end
@@ -1071,6 +1126,16 @@ gem 'other', version
refute @installer.installation_satisfies_dependency?(dep)
end
+ def test_installation_satisfies_dependency_eh_development
+ @installer.options[:development] = true
+ @installer.options[:dev_shallow] = true
+
+ util_spec 'a'
+
+ dep = Gem::Dependency.new 'a', :development
+ assert @installer.installation_satisfies_dependency?(dep)
+ end
+
def test_pre_install_checks_dependencies
@spec.add_dependency 'b', '> 5'
util_setup_gem
@@ -1157,6 +1222,22 @@ gem 'other', version
assert_equal "#!#{Gem.ruby}", shebang
end
+ def test_process_options
+ assert_nil @installer.build_root
+ assert_equal File.join(@gemhome, 'bin'), @installer.bin_dir
+ assert_equal @gemhome, @installer.gem_home
+ end
+
+ def test_process_options_build_root
+ build_root = File.join @tempdir, 'build_root'
+
+ @installer = Gem::Installer.new @gem, :build_root => build_root
+
+ assert_equal Pathname(build_root), @installer.build_root
+ assert_equal File.join(build_root, @gemhome, 'bin'), @installer.bin_dir
+ assert_equal File.join(build_root, @gemhome), @installer.gem_home
+ end
+
def test_shebang_arguments
util_make_exec @spec, "#!/usr/bin/ruby -ws"
diff --git a/test/rubygems/test_gem_local_remote_options.rb b/test/rubygems/test_gem_local_remote_options.rb
index 2b4ca806da..1a0338bfe1 100644
--- a/test/rubygems/test_gem_local_remote_options.rb
+++ b/test/rubygems/test_gem_local_remote_options.rb
@@ -90,6 +90,19 @@ class TestGemLocalRemoteOptions < Gem::TestCase
assert_equal original_sources, Gem.sources
end
+ def test_short_source_option
+ @cmd.add_source_option
+
+ original_sources = Gem.sources.dup
+
+ source = URI.parse 'http://more-gems.example.com/'
+ @cmd.handle_options %W[-s #{source}]
+
+ original_sources << source
+
+ assert_equal original_sources, Gem.sources
+ end
+
def test_update_sources_option
@cmd.add_update_sources_option
diff --git a/test/rubygems/test_gem_name_tuple.rb b/test/rubygems/test_gem_name_tuple.rb
index 170a9c2ae0..38320f7590 100644
--- a/test/rubygems/test_gem_name_tuple.rb
+++ b/test/rubygems/test_gem_name_tuple.rb
@@ -33,5 +33,12 @@ class TestGemNameTuple < Gem::TestCase
assert_equal "a-0.gemspec", n.spec_name
end
+ def test_spaceship
+ a = Gem::NameTuple.new 'a', Gem::Version.new(0), Gem::Platform::RUBY
+ a_p = Gem::NameTuple.new 'a', Gem::Version.new(0), Gem::Platform.local
+
+ assert_equal 1, a_p.<=>(a)
+ end
+
end
diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb
index 1e592fcb05..128dcdb1c4 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -638,7 +638,7 @@ class TestGemPackage < Gem::Package::TarTestCase
e.message
io
end
- tf.close!
+ tf.close! if tf.respond_to? :close!
end
def test_verify_empty
@@ -780,6 +780,23 @@ class TestGemPackage < Gem::Package::TarTestCase
assert_equal @spec, package.spec
end
+ def test_spec_from_io
+ # This functionality is used by rubygems.org to extract spec data from an
+ # uploaded gem before it is written to storage.
+ io = StringIO.new Gem.read_binary @gem
+ package = Gem::Package.new io
+
+ assert_equal @spec, package.spec
+ end
+
+ def test_spec_from_io_raises_gem_error_for_io_not_at_start
+ io = StringIO.new Gem.read_binary @gem
+ io.read(1)
+ assert_raises(Gem::Package::Error) do
+ Gem::Package.new io
+ end
+ end
+
def util_tar
tar_io = StringIO.new
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index ea78d07e0b..3769911522 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -244,6 +244,36 @@ gems:
assert File.exist?(a1_cache_gem)
end
+ def test_download_with_auth
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ a1_cache_gem = @a1.cache_file
+ assert_equal a1_cache_gem, fetcher.download(@a1, 'http://user:password@gems.example.com')
+ assert_equal("http://user:password@gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(a1_cache_gem)
+ end
+
+ def test_download_with_encoded_auth
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ a1_cache_gem = @a1.cache_file
+ assert_equal a1_cache_gem, fetcher.download(@a1, 'http://user:%25pas%25sword@gems.example.com')
+ assert_equal("http://user:%25pas%25sword@gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(a1_cache_gem)
+ end
+
def test_download_cached
FileUtils.mv @a1_gem, @cache_dir
@@ -557,6 +587,40 @@ gems:
assert_equal "too many redirects (#{url})", e.message
end
+ def test_fetch_s3
+ fetcher = Gem::RemoteFetcher.new nil
+ url = 's3://testuser:testpass@my-bucket/gems/specs.4.8.gz'
+ $fetched_uri = nil
+
+ def fetcher.request(uri, request_class, last_modified = nil)
+ $fetched_uri = uri
+ res = Net::HTTPOK.new nil, 200, nil
+ def res.body() 'success' end
+ res
+ end
+
+ def fetcher.s3_expiration
+ 1395098371
+ end
+
+ data = fetcher.fetch_s3 URI.parse(url)
+
+ assert_equal 'https://my-bucket.s3.amazonaws.com/gems/specs.4.8.gz?AWSAccessKeyId=testuser&Expires=1395098371&Signature=eUTr7NkpZEet%2BJySE%2BfH6qukroI%3D', $fetched_uri.to_s
+ assert_equal 'success', data
+ ensure
+ $fetched_uri = nil
+ end
+
+ def test_fetch_s3_no_creds
+ fetcher = Gem::RemoteFetcher.new nil
+ url = 's3://my-bucket/gems/specs.4.8.gz'
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ fetcher.fetch_s3 URI.parse(url)
+ end
+
+ assert_match "credentials needed", e.message
+ end
+
def test_observe_no_proxy_env_single_host
use_ui @ui do
ENV["http_proxy"] = @proxy_uri
@@ -711,6 +775,8 @@ gems:
@proxy_server ||= start_server(PROXY_DATA)
@enable_yaml = true
@enable_zip = false
+ @ssl_server = nil
+ @ssl_server_thread = nil
end
def stop_servers
diff --git a/test/rubygems/test_gem_request.rb b/test/rubygems/test_gem_request.rb
index bcbbcf1f99..c45d5d86fd 100644
--- a/test/rubygems/test_gem_request.rb
+++ b/test/rubygems/test_gem_request.rb
@@ -11,6 +11,10 @@ class TestGemRequest < Gem::TestCase
PUBLIC_CERT_FILE = cert_path 'public'
SSL_CERT = load_cert 'ssl'
+ def make_request uri, request_class, last_modified, proxy
+ Gem::Request.create_with_proxy uri, request_class, last_modified, proxy
+ end
+
def setup
@proxies = %w[http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY]
@old_proxies = @proxies.map {|k| ENV[k] }
@@ -21,7 +25,7 @@ class TestGemRequest < Gem::TestCase
@proxy_uri = "http://localhost:1234"
@uri = URI('http://example')
- @request = Gem::Request.new @uri, nil, nil, nil
+ @request = make_request @uri, nil, nil, nil
end
def teardown
@@ -33,7 +37,7 @@ class TestGemRequest < Gem::TestCase
def test_initialize_proxy
proxy_uri = 'http://proxy.example.com'
- request = Gem::Request.new @uri, nil, nil, proxy_uri
+ request = make_request @uri, nil, nil, proxy_uri
assert_equal proxy_uri, request.proxy_uri.to_s
end
@@ -41,7 +45,7 @@ class TestGemRequest < Gem::TestCase
def test_initialize_proxy_URI
proxy_uri = 'http://proxy.example.com'
- request = Gem::Request.new @uri, nil, nil, URI(proxy_uri)
+ request = make_request @uri, nil, nil, URI(proxy_uri)
assert_equal proxy_uri, request.proxy_uri.to_s
end
@@ -51,7 +55,7 @@ class TestGemRequest < Gem::TestCase
ENV['http_proxy_user'] = 'foo'
ENV['http_proxy_pass'] = 'bar'
- request = Gem::Request.new @uri, nil, nil, nil
+ request = make_request @uri, nil, nil, nil
proxy = request.proxy_uri
@@ -62,7 +66,7 @@ class TestGemRequest < Gem::TestCase
def test_initialize_proxy_ENV_https
ENV['https_proxy'] = @proxy_uri
- request = Gem::Request.new URI('https://example'), nil, nil, nil
+ request = make_request URI('https://example'), nil, nil, nil
proxy = request.proxy_uri
@@ -72,13 +76,13 @@ class TestGemRequest < Gem::TestCase
def test_configure_connection_for_https
connection = Net::HTTP.new 'localhost', 443
- request = Gem::Request.new URI('https://example'), nil, nil, nil
-
- def request.add_rubygems_trusted_certs store
- store.add_cert TestGemRequest::PUBLIC_CERT
- end
+ request = Class.new(Gem::Request) {
+ def self.get_cert_files
+ [TestGemRequest::PUBLIC_CERT_FILE]
+ end
+ }.create_with_proxy URI('https://example'), nil, nil, nil
- request.configure_connection_for_https connection
+ Gem::Request.configure_connection_for_https connection, request.cert_files
cert_store = connection.cert_store
@@ -91,13 +95,13 @@ class TestGemRequest < Gem::TestCase
connection = Net::HTTP.new 'localhost', 443
- request = Gem::Request.new URI('https://example'), nil, nil, nil
+ request = Class.new(Gem::Request) {
+ def self.get_cert_files
+ [TestGemRequest::PUBLIC_CERT_FILE]
+ end
+ }.create_with_proxy URI('https://example'), nil, nil, nil
- def request.add_rubygems_trusted_certs store
- store.add_cert TestGemRequest::PUBLIC_CERT
- end
-
- request.configure_connection_for_https connection
+ Gem::Request.configure_connection_for_https connection, request.cert_files
cert_store = connection.cert_store
@@ -109,16 +113,18 @@ class TestGemRequest < Gem::TestCase
def test_get_proxy_from_env_fallback
ENV['http_proxy'] = @proxy_uri
-
- proxy = @request.get_proxy_from_env 'https'
+ request = make_request @uri, nil, nil, nil
+ proxy = request.proxy_uri
assert_equal URI(@proxy_uri), proxy
end
def test_get_proxy_from_env_https
ENV['https_proxy'] = @proxy_uri
+ uri = URI('https://example')
+ request = make_request uri, nil, nil, nil
- proxy = @request.get_proxy_from_env 'https'
+ proxy = request.proxy_uri
assert_equal URI(@proxy_uri), proxy
end
@@ -127,8 +133,9 @@ class TestGemRequest < Gem::TestCase
ENV['http_proxy'] = @proxy_uri
ENV['http_proxy_user'] = 'foo\user'
ENV['http_proxy_pass'] = 'my bar'
+ request = make_request @uri, nil, nil, nil
- proxy = @request.get_proxy_from_env
+ proxy = request.proxy_uri
assert_equal 'foo\user', Gem::UriFormatter.new(proxy.user).unescape
assert_equal 'my bar', Gem::UriFormatter.new(proxy.password).unescape
@@ -138,8 +145,9 @@ class TestGemRequest < Gem::TestCase
ENV['http_proxy'] = @proxy_uri
ENV['http_proxy_user'] = 'foo@user'
ENV['http_proxy_pass'] = 'my@bar'
+ request = make_request @uri, nil, nil, nil
- proxy = @request.get_proxy_from_env
+ proxy = request.proxy_uri
assert_equal 'foo%40user', proxy.user
assert_equal 'my%40bar', proxy.password
@@ -147,23 +155,26 @@ class TestGemRequest < Gem::TestCase
def test_get_proxy_from_env_normalize
ENV['HTTP_PROXY'] = 'fakeurl:12345'
+ request = make_request @uri, nil, nil, nil
- assert_equal 'http://fakeurl:12345', @request.get_proxy_from_env.to_s
+ assert_equal 'http://fakeurl:12345', request.proxy_uri.to_s
end
def test_get_proxy_from_env_empty
ENV['HTTP_PROXY'] = ''
ENV.delete 'http_proxy'
+ request = make_request @uri, nil, nil, nil
- assert_nil @request.get_proxy_from_env
+ assert_nil request.proxy_uri
end
def test_fetch
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
- @request = Gem::Request.new(uri, Net::HTTP::Get, nil, nil)
- util_stub_connection_for :body => :junk, :code => 200
+ response = util_stub_net_http(:body => :junk, :code => 200) do
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
- response = @request.fetch
+ @request.fetch
+ end
assert_equal 200, response.code
assert_equal :junk, response.body
@@ -171,34 +182,34 @@ class TestGemRequest < Gem::TestCase
def test_fetch_basic_auth
uri = URI.parse "https://user:pass@example.rubygems/specs.#{Gem.marshal_version}"
- @request = Gem::Request.new(uri, Net::HTTP::Get, nil, nil)
- conn = util_stub_connection_for :body => :junk, :code => 200
-
- @request.fetch
+ conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ c
+ end
auth_header = conn.payload['Authorization']
-
assert_equal "Basic #{Base64.encode64('user:pass')}".strip, auth_header
end
def test_fetch_basic_auth_encoded
uri = URI.parse "https://user:%7BDEScede%7Dpass@example.rubygems/specs.#{Gem.marshal_version}"
- @request = Gem::Request.new(uri, Net::HTTP::Get, nil, nil)
- conn = util_stub_connection_for :body => :junk, :code => 200
-
- @request.fetch
+ conn = util_stub_net_http(:body => :junk, :code => 200) do |c|
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ c
+ end
auth_header = conn.payload['Authorization']
-
assert_equal "Basic #{Base64.encode64('user:{DEScede}pass')}".strip, auth_header
end
def test_fetch_head
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
- @request = Gem::Request.new(uri, Net::HTTP::Get, nil, nil)
- util_stub_connection_for :body => '', :code => 200
-
- response = @request.fetch
+ response = util_stub_net_http(:body => '', :code => 200) do |conn|
+ @request = make_request(uri, Net::HTTP::Get, nil, nil)
+ @request.fetch
+ end
assert_equal 200, response.code
assert_equal '', response.body
@@ -207,10 +218,10 @@ class TestGemRequest < Gem::TestCase
def test_fetch_unmodified
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
t = Time.utc(2013, 1, 2, 3, 4, 5)
- @request = Gem::Request.new(uri, Net::HTTP::Get, t, nil)
- conn = util_stub_connection_for :body => '', :code => 304
-
- response = @request.fetch
+ conn, response = util_stub_net_http(:body => '', :code => 304) do |c|
+ @request = make_request(uri, Net::HTTP::Get, t, nil)
+ [c, @request.fetch]
+ end
assert_equal 304, response.code
assert_equal '', response.body
@@ -221,7 +232,7 @@ class TestGemRequest < Gem::TestCase
end
def test_user_agent
- ua = Gem::Request.new(@uri, nil, nil, nil).user_agent
+ ua = make_request(@uri, nil, nil, nil).user_agent
assert_match %r%^RubyGems/\S+ \S+ Ruby/\S+ \(.*?\)%, ua
assert_match %r%RubyGems/#{Regexp.escape Gem::VERSION}%, ua
@@ -236,7 +247,7 @@ class TestGemRequest < Gem::TestCase
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
Object.send :const_set, :RUBY_ENGINE, 'vroom'
- ua = Gem::Request.new(@uri, nil, nil, nil).user_agent
+ ua = make_request(@uri, nil, nil, nil).user_agent
assert_match %r%\) vroom%, ua
ensure
@@ -249,7 +260,7 @@ class TestGemRequest < Gem::TestCase
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
Object.send :const_set, :RUBY_ENGINE, 'ruby'
- ua = Gem::Request.new(@uri, nil, nil, nil).user_agent
+ ua = make_request(@uri, nil, nil, nil).user_agent
assert_match %r%\)%, ua
ensure
@@ -262,7 +273,7 @@ class TestGemRequest < Gem::TestCase
Object.send :remove_const, :RUBY_PATCHLEVEL
Object.send :const_set, :RUBY_PATCHLEVEL, 5
- ua = Gem::Request.new(@uri, nil, nil, nil).user_agent
+ ua = make_request(@uri, nil, nil, nil).user_agent
assert_match %r% patchlevel 5\)%, ua
ensure
@@ -277,7 +288,7 @@ class TestGemRequest < Gem::TestCase
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
Object.send :const_set, :RUBY_REVISION, 6
- ua = Gem::Request.new(@uri, nil, nil, nil).user_agent
+ ua = make_request(@uri, nil, nil, nil).user_agent
assert_match %r% revision 6\)%, ua
assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}dev%, ua
@@ -292,7 +303,7 @@ class TestGemRequest < Gem::TestCase
Object.send :const_set, :RUBY_PATCHLEVEL, -1
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
- ua = Gem::Request.new(@uri, nil, nil, nil).user_agent
+ ua = make_request(@uri, nil, nil, nil).user_agent
assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE}\)%, ua
ensure
@@ -318,21 +329,24 @@ class TestGemRequest < Gem::TestCase
@orig_RUBY_REVISION = RUBY_REVISION if defined? RUBY_REVISION
end
- def util_stub_connection_for hash
- def @request.connection= conn
- @conn = conn
- end
-
- def @request.connection_for uri
- @conn
- end
-
- @request.connection = Conn.new OpenStruct.new(hash)
+ def util_stub_net_http hash
+ old_client = Gem::Request::ConnectionPools.client
+ conn = Conn.new OpenStruct.new(hash)
+ Gem::Request::ConnectionPools.client = conn
+ yield conn
+ ensure
+ Gem::Request::ConnectionPools.client = old_client
end
class Conn
attr_accessor :payload
+ def new *args; self; end
+ def use_ssl=(bool); end
+ def verify_mode=(setting); end
+ def cert_store=(setting); end
+ def start; end
+
def initialize(response)
@response = response
self.payload = nil
diff --git a/test/rubygems/test_gem_request_connection_pools.rb b/test/rubygems/test_gem_request_connection_pools.rb
new file mode 100644
index 0000000000..1cf6b27979
--- /dev/null
+++ b/test/rubygems/test_gem_request_connection_pools.rb
@@ -0,0 +1,120 @@
+require 'rubygems/test_case'
+require 'rubygems/request'
+require 'timeout'
+
+class TestGemRequestConnectionPool < Gem::TestCase
+ class FakeHttp
+ def initialize *args
+ end
+
+ def start
+ end
+ end
+
+ def setup
+ super
+ @old_client = Gem::Request::ConnectionPools.client
+ Gem::Request::ConnectionPools.client = FakeHttp
+
+ @proxy = URI 'http://proxy.example'
+ end
+
+ def teardown
+ Gem::Request::ConnectionPools.client = @old_client
+ super
+ end
+
+ def test_checkout_same_connection
+ uri = URI.parse('http://example/some_endpoint')
+
+ pools = Gem::Request::ConnectionPools.new nil, []
+ pool = pools.pool_for uri
+ conn = pool.checkout
+ pool.checkin conn
+
+ assert_equal conn, pool.checkout
+ end
+
+ def test_to_proxy_eh
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ env_no_proxy = %w[
+ 1.no-proxy.example
+ 2.no-proxy.example
+ ]
+
+ no_proxy = pools.send :no_proxy?, '2.no-proxy.example', env_no_proxy
+
+ assert no_proxy, 'match'
+
+ no_proxy = pools.send :no_proxy?, 'proxy.example', env_no_proxy
+
+ refute no_proxy, 'mismatch'
+ end
+
+ def test_to_proxy_eh_wildcard
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ env_no_proxy = %w[
+ .no-proxy.example
+ ]
+
+ no_proxy = pools.send :no_proxy?, '2.no-proxy.example', env_no_proxy
+
+ assert no_proxy, 'wildcard matching subdomain'
+
+ no_proxy = pools.send :no_proxy?, 'no-proxy.example', env_no_proxy
+
+ assert no_proxy, 'wildcard matching dotless domain'
+
+ no_proxy = pools.send :no_proxy?, 'proxy.example', env_no_proxy
+
+ refute no_proxy, 'wildcard mismatch'
+ end
+
+ def test_net_http_args
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ net_http_args = pools.send :net_http_args, URI('http://example'), nil
+
+ assert_equal ['example', 80], net_http_args
+ end
+
+ def test_net_http_args_proxy
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ net_http_args = pools.send :net_http_args, URI('http://example'), @proxy
+
+ assert_equal ['example', 80, 'proxy.example', 80, nil, nil], net_http_args
+ end
+
+ def test_net_http_args_no_proxy
+ orig_no_proxy, ENV['no_proxy'] = ENV['no_proxy'], 'example'
+
+ pools = Gem::Request::ConnectionPools.new nil, []
+
+ net_http_args = pools.send :net_http_args, URI('http://example'), @proxy
+
+ assert_equal ['example', 80, nil, nil], net_http_args
+
+ ensure
+ ENV['no_proxy'] = orig_no_proxy
+ end
+
+ def test_thread_waits_for_connection
+ uri = URI.parse('http://example/some_endpoint')
+ pools = Gem::Request::ConnectionPools.new nil, []
+ pool = pools.pool_for uri
+
+ pool.checkout
+
+ t1 = Thread.new {
+ timeout(1) do
+ pool.checkout
+ end
+ }
+ assert_raises(Timeout::Error) do
+ t1.join
+ end
+ end
+end
diff --git a/test/rubygems/test_gem_request_set.rb b/test/rubygems/test_gem_request_set.rb
index 130728e249..3c1d5ac1d1 100644
--- a/test/rubygems/test_gem_request_set.rb
+++ b/test/rubygems/test_gem_request_set.rb
@@ -42,6 +42,12 @@ class TestGemRequestSet < Gem::TestCase
fetcher.gem 'a', 2
end
+ done_installing_ran = false
+
+ Gem.done_installing do |installer, specs|
+ done_installing_ran = true
+ end
+
rs = Gem::RequestSet.new
installed = []
@@ -61,6 +67,29 @@ class TestGemRequestSet < Gem::TestCase
assert_path_exists 'gem.deps.rb.lock'
assert rs.remote
+ refute done_installing_ran
+ end
+
+ def test_install_from_gemdeps_explain
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ rs = Gem::RequestSet.new
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts 'gem "a"'
+ io.flush
+
+ expected = <<-EXPECTED
+Gems to install:
+ a-2
+ EXPECTED
+
+ assert_output expected do
+ rs.install_from_gemdeps :gemdeps => io.path, :explain => true
+ end
+ end
end
def test_install_from_gemdeps_install_dir
@@ -153,6 +182,30 @@ DEPENDENCIES
assert_path_exists File.join @gemhome, 'specifications', 'b-1.gemspec'
end
+ def test_install_from_gemdeps_version_mismatch
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 2
+ end
+
+ rs = Gem::RequestSet.new
+ installed = []
+
+ open 'gem.deps.rb', 'w' do |io|
+ io.puts <<-GEM_DEPS
+gem "a"
+ruby "0"
+ GEM_DEPS
+
+ io.flush
+
+ rs.install_from_gemdeps :gemdeps => io.path do |req, installer|
+ installed << req.full_name
+ end
+ end
+
+ assert_includes installed, 'a-2'
+ end
+
def test_load_gemdeps
rs = Gem::RequestSet.new
@@ -160,10 +213,12 @@ DEPENDENCIES
io.puts 'gem "a"'
io.flush
- rs.load_gemdeps io.path
+ gem_deps = rs.load_gemdeps io.path
+
+ assert_kind_of Gem::RequestSet::GemDependencyAPI, gem_deps
io
end
- tf.close!
+ tf.close! if tf.respond_to? :close!
assert_equal [dep('a')], rs.dependencies
@@ -171,6 +226,24 @@ DEPENDENCIES
assert rs.vendor_set
end
+ def test_load_gemdeps_installing
+ rs = Gem::RequestSet.new
+
+ tf = Tempfile.open 'gem.deps.rb' do |io|
+ io.puts 'ruby "0"'
+ io.puts 'gem "a"'
+ io.flush
+
+ gem_deps = rs.load_gemdeps io.path, [], true
+
+ assert_kind_of Gem::RequestSet::GemDependencyAPI, gem_deps
+ io
+ end
+ tf.close! if tf.respond_to? :close!
+
+ assert_equal [dep('a')], rs.dependencies
+ end
+
def test_load_gemdeps_without_groups
rs = Gem::RequestSet.new
@@ -181,7 +254,7 @@ DEPENDENCIES
rs.load_gemdeps io.path, [:test]
io
end
- tf.close!
+ tf.close! if tf.respond_to? :close!
assert_empty rs.dependencies
end
@@ -193,12 +266,69 @@ DEPENDENCIES
rs = Gem::RequestSet.new
rs.gem "a"
+ orig_errors = rs.errors
+
res = rs.resolve StaticSet.new([a, b])
assert_equal 2, res.size
names = res.map { |s| s.full_name }.sort
assert_equal ["a-2", "b-2"], names
+
+ refute_same orig_errors, rs.errors
+ end
+
+ def test_bug_bug_990
+ a = util_spec 'a', '1.b', 'b' => '~> 1.a'
+ b = util_spec 'b', '1.b', 'c' => '>= 1'
+ c = util_spec 'c', '1.1.b'
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.prerelease = true
+
+ res = rs.resolve StaticSet.new([a, b, c])
+ assert_equal 3, res.size
+
+ names = res.map { |s| s.full_name }.sort
+
+ assert_equal %w[a-1.b b-1.b c-1.1.b], names
+ end
+
+ def test_resolve_development
+ a = util_spec 'a', 1
+ spec = Gem::Resolver::SpecSpecification.new nil, a
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+
+ res = rs.resolve StaticSet.new [spec]
+ assert_equal 1, res.size
+
+ assert rs.resolver.development
+ refute rs.resolver.development_shallow
+ end
+
+ def test_resolve_development_shallow
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ res = rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+ assert_equal 2, res.size
+
+ assert rs.resolver.development
+ assert rs.resolver.development_shallow
end
def test_resolve_git
@@ -216,7 +346,7 @@ DEPENDENCIES
rs.load_gemdeps io.path
io
end
- tf.close!
+ tf.close! if tf.respond_to? :close!
res = rs.resolve
assert_equal 1, res.size
@@ -280,7 +410,7 @@ DEPENDENCIES
rs.load_gemdeps io.path
io
end
- tf.close!
+ tf.close! if tf.respond_to? :close!
res = rs.resolve
assert_equal 2, res.size
@@ -308,6 +438,12 @@ DEPENDENCIES
end
def test_install
+ done_installing_ran = false
+
+ Gem.done_installing do
+ done_installing_ran = true
+ end
+
spec_fetcher do |fetcher|
fetcher.gem "a", "1", "b" => "= 1"
fetcher.gem "b", "1"
@@ -336,6 +472,8 @@ DEPENDENCIES
assert_path_exists File.join @gemhome, 'specifications', 'b-1.gemspec'
assert_equal %w[b-1 a-1], installed.map { |s| s.full_name }
+
+ assert done_installing_ran
end
def test_install_into
@@ -358,4 +496,102 @@ DEPENDENCIES
assert_equal %w!b-1 a-1!, installed.map { |s| s.full_name }
end
+
+ def test_install_into_development_shallow
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '1' do |s|
+ s.add_development_dependency 'b', '= 1'
+ end
+
+ fetcher.gem 'b', '1' do |s|
+ s.add_development_dependency 'c', '= 1'
+ end
+
+ fetcher.spec 'c', '1'
+ end
+
+ rs = Gem::RequestSet.new
+ rs.development = true
+ rs.development_shallow = true
+ rs.gem 'a'
+
+ rs.resolve
+
+ options = {
+ :development => true,
+ :development_shallow => true,
+ }
+
+ installed = rs.install_into @tempdir, true, options do
+ assert_equal @tempdir, ENV['GEM_HOME']
+ end
+
+ assert_equal %w[a-1 b-1], installed.map { |s| s.full_name }.sort
+ end
+
+ def test_sorted_requests_development_shallow
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+
+ assert_equal %w[b-1 a-1], rs.sorted_requests.map { |req| req.full_name }
+ end
+
+ def test_tsort_each_child_development
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+
+ a_req = Gem::Resolver::ActivationRequest.new a_spec, nil
+
+ deps = rs.enum_for(:tsort_each_child, a_req).to_a
+
+ assert_equal %w[b], deps.map { |dep| dep.name }
+ end
+
+ def test_tsort_each_child_development_shallow
+ a = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c = util_spec 'c', 1
+
+ rs = Gem::RequestSet.new
+ rs.gem 'a'
+ rs.development = true
+ rs.development_shallow = true
+
+ a_spec = Gem::Resolver::SpecSpecification.new nil, a
+ b_spec = Gem::Resolver::SpecSpecification.new nil, b
+ c_spec = Gem::Resolver::SpecSpecification.new nil, c
+
+ rs.resolve StaticSet.new [a_spec, b_spec, c_spec]
+
+ b_req = Gem::Resolver::ActivationRequest.new b_spec, nil
+
+ deps = rs.enum_for(:tsort_each_child, b_req).to_a
+
+ assert_empty deps
+ end
+
end
diff --git a/test/rubygems/test_gem_request_set_gem_dependency_api.rb b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
index 08649ebff1..c2bf5ff378 100644
--- a/test/rubygems/test_gem_request_set_gem_dependency_api.rb
+++ b/test/rubygems/test_gem_request_set_gem_dependency_api.rb
@@ -68,6 +68,22 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal [dep('a')], @set.dependencies
assert_equal %w[a], @gda.requires['a']
+
+ expected = { 'a' => nil }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_duplicate
+ @gda.gem 'a'
+
+ _, err = capture_io do
+ @gda.gem 'a'
+ end
+
+ expected = "Gem dependencies file gem.deps.rb requires a more than once."
+
+ assert_match expected, err
end
def test_gem_git
@@ -76,6 +92,36 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal [dep('a')], @set.dependencies
assert_equal %w[git/a master], @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_bitbucket
+ @gda.gem 'a', :bitbucket => 'example/repository'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[https://example@bitbucket.org/example/repository.git master],
+ @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_bitbucket_expand_path
+ @gda.gem 'a', :bitbucket => 'example'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[https://example@bitbucket.org/example/example.git master],
+ @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
end
def test_gem_git_branch
@@ -127,6 +173,23 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal %w[git://github.com/example/repository.git master],
@git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
+ end
+
+ def test_gem_github_expand_path
+ @gda.gem 'a', :github => 'example'
+
+ assert_equal [dep('a')], @set.dependencies
+
+ assert_equal %w[git://github.com/example/example.git master],
+ @git_set.repositories['a']
+
+ expected = { 'a' => '!' }
+
+ assert_equal expected, @gda.dependencies
end
def test_gem_group
@@ -141,6 +204,10 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
@gda.gem 'a', :group => :test
assert_empty @set.dependencies
+
+ expected = { 'a' => nil }
+
+ assert_equal expected, @gda.dependencies
end
def test_gem_groups
@@ -159,6 +226,10 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
loaded = @vendor_set.load_spec(name, version, Gem::Platform::RUBY, nil)
assert_equal "#{name}-#{version}", loaded.full_name
+
+ expected = { name => '!' }
+
+ assert_equal expected, @gda.dependencies
end
def test_gem_platforms
@@ -254,6 +325,18 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
Gem.win_platform = win_platform
end
+ def test_gem_platforms_platform
+ win_platform, Gem.win_platform = Gem.win_platform?, false
+
+ with_engine_version 'ruby', '2.0.0' do
+ @gda.gem 'a', :platforms => :jruby, :platform => :ruby
+
+ refute_empty @set.dependencies
+ end
+ ensure
+ Gem.win_platform = win_platform
+ end
+
def test_gem_platforms_version
with_engine_version 'ruby', '2.0.0' do
@gda.gem 'a', :platforms => :ruby_18
@@ -270,7 +353,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal 'unknown platform :unknown', e.message
end
- def test_gem_require
+ def test_gem_requires
@gda.gem 'a', :require => %w[b c]
@gda.gem 'd', :require => 'e'
@@ -280,7 +363,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal %w[e], @gda.requires['d']
end
- def test_gem_require_false
+ def test_gem_requires_false
@gda.gem 'a', :require => false
assert_equal [dep('a')], @set.dependencies
@@ -288,7 +371,7 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_empty @gda.requires
end
- def test_gem_require_without_group
+ def test_gem_requires_without_group
@gda.without_groups << :test
@gda.gem 'a', :group => :test
@@ -302,12 +385,20 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
@gda.gem 'a', '~> 1.0'
assert_equal [dep('a', '~> 1.0')], @set.dependencies
+
+ expected = { 'a' => ['~> 1.0'] }
+
+ assert_equal expected, @gda.dependencies
end
def test_gem_requirements
@gda.gem 'b', '~> 1.0', '>= 1.0.2'
assert_equal [dep('b', '~> 1.0', '>= 1.0.2')], @set.dependencies
+
+ expected = { 'b' => ['~> 1.0', '>= 1.0.2'] }
+
+ assert_equal expected, @gda.dependencies
end
def test_gem_requirements_options
@@ -483,6 +574,16 @@ class TestGemRequestSetGemDependencyAPI < Gem::TestCase
assert_equal %w[git://example/repo.git master], @git_set.repositories['b']
end
+ def test_git_source
+ @gda.git_source :example do |repo_name|
+ "git://example/#{repo_name}.git"
+ end
+
+ @gda.gem 'a', :example => 'repo'
+
+ assert_equal %w[git://example/repo.git master], @git_set.repositories['a']
+ end
+
def test_group
@gda.group :test do
@gda.gem 'a'
@@ -504,12 +605,12 @@ end
gda = @GDA.new @set, io.path
- gda.load
+ assert_equal gda, gda.load
assert_equal [dep('a'), dep('b')], @set.dependencies
io
end
- tf.close!
+ tf.close! if tf.respond_to? :close!
end
def test_name_typo
@@ -538,18 +639,42 @@ end
end
def test_platform_mswin
+ util_set_arch 'i686-darwin8.10.1' do
+ @gda.platform :mswin do
+ @gda.gem 'a'
+ end
+
+ assert_empty @set.dependencies
+ end
+
+ util_set_arch 'x86-mswin32-60' do
+ @gda.platform :mswin do
+ @gda.gem 'a'
+ end
+
+ refute_empty @set.dependencies
+ end
+ end
+
+ def test_platform_multiple
win_platform, Gem.win_platform = Gem.win_platform?, false
- @gda.platform :mswin do
- @gda.gem 'a'
+ gda = @GDA.new @set, nil
+
+ with_engine_version 'ruby', '1.8.7' do
+ gda.platform :mri_19, :mri_20 do
+ gda.gem 'a'
+ end
end
assert_empty @set.dependencies
- Gem.win_platform = true
+ gda = @GDA.new @set, nil
- @gda.platform :mswin do
- @gda.gem 'a'
+ with_engine_version 'ruby', '2.0.0' do
+ gda.platform :mri_19, :mri_20 do
+ gda.gem 'a'
+ end
end
refute_empty @set.dependencies
@@ -570,30 +695,27 @@ end
end
def test_platforms
- win_platform, Gem.win_platform = Gem.win_platform?, false
+ util_set_arch 'i686-darwin8.10.1' do
+ @gda.platforms :ruby do
+ @gda.gem 'a'
+ end
- @gda.platforms :ruby do
- @gda.gem 'a'
- end
+ assert_equal [dep('a')], @set.dependencies
- assert_equal [dep('a')], @set.dependencies
+ @gda.platforms :mswin do
+ @gda.gem 'b'
+ end
- @gda.platforms :mswin do
- @gda.gem 'b'
+ assert_equal [dep('a')], @set.dependencies
end
- assert_equal [dep('a')], @set.dependencies
-
- Gem.win_platform = true
+ util_set_arch 'x86-mswin32-60' do
+ @gda.platforms :mswin do
+ @gda.gem 'c'
+ end
- @gda.platforms :mswin do
- @gda.gem 'c'
+ assert_equal [dep('a'), dep('c')], @set.dependencies
end
-
- assert_equal [dep('a'), dep('c')], @set.dependencies
-
- ensure
- Gem.win_platform = win_platform
end
def test_ruby
@@ -647,6 +769,12 @@ end
assert_equal "Your Ruby version is #{RUBY_VERSION}, but your gem.deps.rb requires 1.8.0", e.message
end
+ def test_ruby_mismatch_installing
+ @gda.installing = true
+
+ assert @gda.ruby '1.8.0'
+ end
+
def test_source
sources = Gem.sources
diff --git a/test/rubygems/test_gem_request_set_lockfile.rb b/test/rubygems/test_gem_request_set_lockfile.rb
index 0be69bf92b..96cc12e164 100644
--- a/test/rubygems/test_gem_request_set_lockfile.rb
+++ b/test/rubygems/test_gem_request_set_lockfile.rb
@@ -38,6 +38,127 @@ class TestGemRequestSetLockfile < Gem::TestCase
end
end
+ def test_add_DEPENDENCIES
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_development_dependency 'b'
+ end
+ end
+
+ @set.gem 'a'
+ @set.resolve
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ out = []
+
+ @lockfile.add_DEPENDENCIES out
+
+ expected = [
+ 'DEPENDENCIES',
+ ' a',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_add_DEPENDENCIES_from_gem_deps
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_development_dependency 'b'
+ end
+ end
+
+ dependencies = { 'a' => '~> 2.0' }
+
+ @set.gem 'a'
+ @set.resolve
+ @lockfile =
+ Gem::RequestSet::Lockfile.new @set, @gem_deps_file, dependencies
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ out = []
+
+ @lockfile.add_DEPENDENCIES out
+
+ expected = [
+ 'DEPENDENCIES',
+ ' a (~> 2.0)',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_add_GEM
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_dependency 'b'
+ s.add_development_dependency 'c'
+ end
+
+ fetcher.spec 'b', 2
+
+ fetcher.spec 'bundler', 1
+ end
+
+ @set.gem 'a'
+ @set.gem 'bundler'
+ @set.resolve
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ spec_groups = @set.sorted_requests.group_by do |request|
+ request.spec.class
+ end
+ @lockfile.instance_variable_set :@spec_groups, spec_groups
+
+
+ out = []
+
+ @lockfile.add_GEM out
+
+ expected = [
+ 'GEM',
+ ' remote: http://gems.example.com/',
+ ' specs:',
+ ' a (2)',
+ ' b',
+ ' b (2)',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
+ def test_add_PLATFORMS
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2 do |s|
+ s.add_dependency 'b'
+ end
+
+ fetcher.spec 'b', 2 do |s|
+ s.platform = Gem::Platform::CURRENT
+ end
+ end
+
+ @set.gem 'a'
+ @set.resolve
+ @lockfile.instance_variable_set :@requests, @set.sorted_requests
+
+ out = []
+
+ @lockfile.add_PLATFORMS out
+
+ expected = [
+ 'PLATFORMS',
+ ' ruby',
+ ' x86-darwin-8',
+ nil
+ ]
+
+ assert_equal expected, out
+ end
+
def test_get
@lockfile.instance_variable_set :@tokens, [:token]
@@ -142,7 +263,106 @@ DEPENDENCIES
assert_equal %w[a-2], lockfile_set.specs.map { |tuple| tuple.full_name }
end
+ def test_parse_DEPENDENCIES_git
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://git.example/josevalim/rails-footnotes.git
+ revision: 3a6ac1971e91d822f057650cc5916ebfcbd6ee37
+ specs:
+ rails-footnotes (3.7.9)
+ rails (>= 3.0.0)
+
+GIT
+ remote: git://git.example/svenfuchs/i18n-active_record.git
+ revision: 55507cf59f8f2173d38e07e18df0e90d25b1f0f6
+ specs:
+ i18n-active_record (0.0.2)
+ i18n (>= 0.5.0)
+
+GEM
+ remote: http://gems.example/
+ specs:
+ i18n (0.6.9)
+ rails (4.0.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ i18n-active_record!
+ rails-footnotes!
+ LOCKFILE
+
+ @lockfile.parse
+
+ expected = [
+ dep('i18n-active_record', '= 0.0.2'),
+ dep('rails-footnotes', '= 3.7.9'),
+ ]
+
+ assert_equal expected, @set.dependencies
+ end
+
+ def test_parse_GEM
+ write_lockfile <<-LOCKFILE
+GEM
+ specs:
+ a (2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '>= 0')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ assert lockfile_set, 'found a LockSet'
+
+ assert_equal %w[a-2], lockfile_set.specs.map { |s| s.full_name }
+ end
+
+ def test_parse_GEM_remote_multiple
+ write_lockfile <<-LOCKFILE
+GEM
+ remote: https://gems.example/
+ remote: https://other.example/
+ specs:
+ a (2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ a
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '>= 0')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ assert lockfile_set, 'found a LockSet'
+
+ assert_equal %w[a-2 a-2], lockfile_set.specs.map { |s| s.full_name }
+
+ assert_equal %w[https://gems.example/ https://other.example/],
+ lockfile_set.specs.map { |s| s.source.uri.to_s }
+ end
+
def test_parse_GIT
+ @set.instance_variable_set :@install_dir, 'install_dir'
+
write_lockfile <<-LOCKFILE
GIT
remote: git://example/a.git
@@ -150,6 +370,7 @@ GIT
specs:
a (2)
b (>= 3)
+ c
DEPENDENCIES
a!
@@ -173,7 +394,126 @@ DEPENDENCIES
assert_equal %w[a-2], git_set.specs.values.map { |s| s.full_name }
- assert_equal [dep('b', '>= 3')], git_set.specs.values.first.dependencies
+ assert_equal [dep('b', '>= 3'), dep('c')],
+ git_set.specs.values.first.dependencies
+
+ expected = {
+ 'a' => %w[git://example/a.git master],
+ }
+
+ assert_equal expected, git_set.repositories
+ assert_equal 'install_dir', git_set.root_dir
+ end
+
+ def test_parse_GIT_branch
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://example/a.git
+ revision: 1234abc
+ branch: 0-9-12-stable
+ specs:
+ a (2)
+ b (>= 3)
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 2')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'fount a LockSet'
+
+ git_set = @set.sets.find do |set|
+ Gem::Resolver::GitSet === set
+ end
+
+ assert git_set, 'could not find a GitSet'
+
+ expected = {
+ 'a' => %w[git://example/a.git 1234abc],
+ }
+
+ assert_equal expected, git_set.repositories
+ end
+
+ def test_parse_GIT_ref
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://example/a.git
+ revision: 1234abc
+ ref: 1234abc
+ specs:
+ a (2)
+ b (>= 3)
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 2')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'fount a LockSet'
+
+ git_set = @set.sets.find do |set|
+ Gem::Resolver::GitSet === set
+ end
+
+ assert git_set, 'could not find a GitSet'
+
+ expected = {
+ 'a' => %w[git://example/a.git 1234abc],
+ }
+
+ assert_equal expected, git_set.repositories
+ end
+
+ def test_parse_GIT_tag
+ write_lockfile <<-LOCKFILE
+GIT
+ remote: git://example/a.git
+ revision: 1234abc
+ tag: v0.9.12
+ specs:
+ a (2)
+ b (>= 3)
+
+DEPENDENCIES
+ a!
+ LOCKFILE
+
+ @lockfile.parse
+
+ assert_equal [dep('a', '= 2')], @set.dependencies
+
+ lockfile_set = @set.sets.find do |set|
+ Gem::Resolver::LockSet === set
+ end
+
+ refute lockfile_set, 'fount a LockSet'
+
+ git_set = @set.sets.find do |set|
+ Gem::Resolver::GitSet === set
+ end
+
+ assert git_set, 'could not find a GitSet'
+
+ expected = {
+ 'a' => %w[git://example/a.git 1234abc],
+ }
+
+ assert_equal expected, git_set.repositories
end
def test_parse_PATH
@@ -184,6 +524,7 @@ PATH
remote: #{directory}
specs:
a (1)
+ b (2)
DEPENDENCIES
a!
@@ -206,6 +547,28 @@ DEPENDENCIES
assert vendor_set, 'could not find a VendorSet'
assert_equal %w[a-1], vendor_set.specs.values.map { |s| s.full_name }
+
+ spec = vendor_set.load_spec 'a', nil, nil, nil
+
+ assert_equal [dep('b', '= 2')], spec.dependencies
+ end
+
+ def test_parse_dependency
+ write_lockfile ' 1)'
+
+ @lockfile.tokenize
+
+ parsed = @lockfile.parse_dependency 'a', '='
+
+ assert_equal dep('a', '= 1'), parsed
+
+ write_lockfile ')'
+
+ @lockfile.tokenize
+
+ parsed = @lockfile.parse_dependency 'a', '2'
+
+ assert_equal dep('a', '= 2'), parsed
end
def test_parse_gem_specs_dependency
@@ -853,5 +1216,23 @@ DEPENDENCIES
refute_empty File.read gem_deps_lock_file
end
+ def test_write_error
+ @set.gem 'nonexistent'
+
+ gem_deps_lock_file = "#{@gem_deps_file}.lock"
+
+ open gem_deps_lock_file, 'w' do |io|
+ io.write 'hello'
+ end
+
+ assert_raises Gem::UnsatisfiableDependencyError do
+ @lockfile.write
+ end
+
+ assert_path_exists gem_deps_lock_file
+
+ assert_equal 'hello', File.read(gem_deps_lock_file)
+ end
+
end
diff --git a/test/rubygems/test_gem_requirement.rb b/test/rubygems/test_gem_requirement.rb
index 8adaf898bc..6974ff0810 100644
--- a/test/rubygems/test_gem_requirement.rb
+++ b/test/rubygems/test_gem_requirement.rb
@@ -207,6 +207,14 @@ class TestGemRequirement < Gem::TestCase
end
end
+ def test_satisfied_by_eh_tilde_gt_v0
+ r = req "~> 0.0.1"
+
+ refute_satisfied_by "0.1.1", r
+ assert_satisfied_by "0.0.2", r
+ assert_satisfied_by "0.0.1", r
+ end
+
def test_satisfied_by_eh_good
assert_satisfied_by "0.2.33", "= 0.2.33"
assert_satisfied_by "0.2.34", "> 0.2.33"
@@ -277,6 +285,11 @@ class TestGemRequirement < Gem::TestCase
refute_satisfied_by "1.1.pre", "~> 1.1"
refute_satisfied_by "2.0.a", "~> 1.0"
refute_satisfied_by "2.0.a", "~> 2.0"
+
+ refute_satisfied_by "0.9", "~> 1"
+ assert_satisfied_by "1.0", "~> 1"
+ assert_satisfied_by "1.1", "~> 1"
+ refute_satisfied_by "2.0", "~> 1"
end
def test_satisfied_by_eh_multiple
diff --git a/test/rubygems/test_gem_resolver.rb b/test/rubygems/test_gem_resolver.rb
index c97e9e710c..2b9e9fe137 100644
--- a/test/rubygems/test_gem_resolver.rb
+++ b/test/rubygems/test_gem_resolver.rb
@@ -13,6 +13,12 @@ class TestGemResolver < Gem::TestCase
end
def set(*specs)
+ source = Gem::Source.new URI @gem_repo
+
+ specs = specs.map do |spec|
+ Gem::Resolver::SpecSpecification.new nil, spec, source
+ end
+
StaticSet.new(specs)
end
@@ -20,7 +26,7 @@ class TestGemResolver < Gem::TestCase
actual = resolver.resolve
exp = expected.sort_by { |s| s.full_name }
- act = actual.map { |a| a.spec }.sort_by { |s| s.full_name }
+ act = actual.map { |a| a.spec.spec }.sort_by { |s| s.full_name }
msg = "Set of gems was not the same: #{exp.map { |x| x.full_name}.inspect} != #{act.map { |x| x.full_name}.inspect}"
@@ -123,6 +129,30 @@ class TestGemResolver < Gem::TestCase
assert_equal ['b (= 2)'], reqs.to_a.map { |req| req.to_s }
end
+ def test_requests_development
+ a1 = util_spec 'a', 1, 'b' => 2
+
+ spec = Gem::Resolver::SpecSpecification.new nil, a1
+ def spec.fetch_development_dependencies
+ @called = true
+ end
+
+ r1 = Gem::Resolver::DependencyRequest.new dep('a', '= 1'), nil
+
+ act = Gem::Resolver::ActivationRequest.new spec, r1, false
+
+ res = Gem::Resolver.new [act]
+ res.development = true
+
+ reqs = Gem::Resolver::RequirementList.new
+
+ res.requests spec, act, reqs
+
+ assert_equal ['b (= 2)'], reqs.to_a.map { |req| req.to_s }
+
+ assert spec.instance_variable_defined? :@called
+ end
+
def test_requests_ignore_dependencies
a1 = util_spec 'a', 1, 'b' => 2
@@ -140,6 +170,104 @@ class TestGemResolver < Gem::TestCase
assert_empty reqs
end
+ def test_resolve_conservative
+ a1_spec = util_spec 'a', 1
+ a2_spec = util_spec 'a', 2 do |s|
+ s.add_dependency 'b', 2
+ s.add_dependency 'c'
+ end
+ b1_spec = util_spec 'b', 1
+ b2_spec = util_spec 'b', 2
+ c1_spec = util_spec 'c', 1 do |s| s.add_dependency 'd', 2 end
+ c2_spec = util_spec 'c', 2 do |s| s.add_dependency 'd', 2 end
+ d1_spec = util_spec 'd', 1 do |s| s.add_dependency 'e' end
+ d2_spec = util_spec 'd', 2 do |s| s.add_dependency 'e' end
+ e1_spec = util_spec 'e', 1
+ e2_spec = util_spec 'e', 2
+
+ a_dep = make_dep 'a', '= 2'
+ e_dep = make_dep 'e'
+
+ # When requesting to install:
+ # a-2, e
+ deps = [a_dep, e_dep]
+
+ s = set a1_spec, a2_spec, b1_spec, b2_spec, c1_spec, c2_spec, d1_spec, d2_spec, e1_spec, e2_spec
+
+ res = Gem::Resolver.new deps, s
+
+ # With the following gems already installed:
+ # a-1, b-1, c-1, e-1
+ res.skip_gems = {'a'=>[a1_spec], 'b'=>[b1_spec], 'c'=>[c1_spec], 'e'=>[e1_spec]}
+
+ # Make sure the following gems end up getting used/installed/upgraded:
+ # a-2 (upgraded)
+ # b-2 (upgraded), specific dependency from a-2
+ # c-1 (used, not upgraded), open dependency from a-2
+ # d-2 (installed), specific dependency from c-2
+ # e-1 (used, not upgraded), open dependency from request
+ assert_resolves_to [a2_spec, b2_spec, c1_spec, d2_spec, e1_spec], res
+ end
+
+ def test_resolve_development
+ a_spec = util_spec 'a', 1 do |s| s.add_development_dependency 'b' end
+ b_spec = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c_spec = util_spec 'c', 1
+
+ a_dep = make_dep 'a', '= 1'
+
+ deps = [a_dep]
+
+ s = set a_spec, b_spec, c_spec
+
+ res = Gem::Resolver.new deps, s
+
+ res.development = true
+
+ assert_resolves_to [a_spec, b_spec, c_spec], res
+ end
+
+ def test_resolve_development_shallow
+ a_spec = util_spec 'a', 1 do |s|
+ s.add_development_dependency 'b'
+ s.add_runtime_dependency 'd'
+ end
+
+ b_spec = util_spec 'b', 1 do |s| s.add_development_dependency 'c' end
+ c_spec = util_spec 'c', 1
+
+ d_spec = util_spec 'd', 1 do |s| s.add_development_dependency 'e' end
+ e_spec = util_spec 'e', 1
+
+ a_dep = make_dep 'a', '= 1'
+
+ deps = [a_dep]
+
+ s = set a_spec, b_spec, c_spec, d_spec, e_spec
+
+ res = Gem::Resolver.new deps, s
+
+ res.development = true
+ res.development_shallow = true
+
+ assert_resolves_to [a_spec, b_spec, d_spec], res
+ end
+
+ def test_resolve_remote_missing_dependency
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+
+ a_dep = make_dep 'a', '= 1'
+
+ res = Gem::Resolver.new [a_dep], Gem::Resolver::IndexSet.new
+
+ e = assert_raises Gem::UnsatisfiableDepedencyError do
+ res.resolve
+ end
+
+ refute_empty e.errors
+ end
+
def test_no_overlap_specificly
a = util_spec "a", '1'
b = util_spec "b", "1"
@@ -215,7 +343,7 @@ class TestGemResolver < Gem::TestCase
res = Gem::Resolver.new([ad], s)
- assert_resolves_to [a2_p1], res
+ assert_resolves_to [a2_p1.spec], res
end
def test_only_returns_spec_once
diff --git a/test/rubygems/test_gem_resolver_activation_request.rb b/test/rubygems/test_gem_resolver_activation_request.rb
index 54de6bf16a..c9163e25de 100644
--- a/test/rubygems/test_gem_resolver_activation_request.rb
+++ b/test/rubygems/test_gem_resolver_activation_request.rb
@@ -19,6 +19,16 @@ class TestGemResolverActivationRequest < Gem::TestCase
@req = @DR::ActivationRequest.new @a3, @dep, [@a1, @a2]
end
+ def test_development_eh
+ refute @req.development?
+
+ dep_req = @DR::DependencyRequest.new dep('a', '>= 0', :development), nil
+
+ act_req = @DR::ActivationRequest.new @a3, dep_req, [@a1, @a2]
+
+ assert act_req.development?
+ end
+
def test_inspect
assert_match 'a-3', @req.inspect
assert_match 'from a (>= 0)', @req.inspect
diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb
index 245f6c1d24..4ae54d71b3 100644
--- a/test/rubygems/test_gem_resolver_api_set.rb
+++ b/test/rubygems/test_gem_resolver_api_set.rb
@@ -73,13 +73,13 @@ class TestGemResolverAPISet < Gem::TestCase
set.prefetch [a_dep]
- @fetcher.data.delete "#{@dep_uri}?gems=a"
-
expected = [
@DR::APISpecification.new(set, data.first)
]
assert_equal expected, set.find_all(a_dep)
+
+ @fetcher.data.delete "#{@dep_uri}?gems=a"
end
def test_find_all_local
diff --git a/test/rubygems/test_gem_resolver_api_specification.rb b/test/rubygems/test_gem_resolver_api_specification.rb
index 6b21a9d4d8..cfeca43453 100644
--- a/test/rubygems/test_gem_resolver_api_specification.rb
+++ b/test/rubygems/test_gem_resolver_api_specification.rb
@@ -28,6 +28,46 @@ class TestGemResolverAPISpecification < Gem::TestCase
assert_equal expected, spec.dependencies
end
+ def test_fetch_development_dependencies
+ specs = spec_fetcher do |fetcher|
+ fetcher.spec 'rails', '3.0.3' do |s|
+ s.add_runtime_dependency 'bundler', '~> 1.0'
+ s.add_runtime_dependency 'railties', '= 3.0.3'
+ s.add_development_dependency 'a', '= 1'
+ end
+ end
+
+ rails = specs['rails-3.0.3']
+
+ repo = @gem_repo + 'api/v1/dependencies'
+
+ set = Gem::Resolver::APISet.new repo
+
+ data = {
+ :name => 'rails',
+ :number => '3.0.3',
+ :platform => 'ruby',
+ :dependencies => [
+ ['bundler', '~> 1.0'],
+ ['railties', '= 3.0.3'],
+ ],
+ }
+
+ util_setup_spec_fetcher rails
+
+ spec = Gem::Resolver::APISpecification.new set, data
+
+ spec.fetch_development_dependencies
+
+ expected = [
+ Gem::Dependency.new('bundler', '~> 1.0'),
+ Gem::Dependency.new('railties', '= 3.0.3'),
+ Gem::Dependency.new('a', '= 1', :development),
+ ]
+
+ assert_equal expected, spec.dependencies
+ end
+
def test_installable_platform_eh
set = Gem::Resolver::APISet.new
data = {
diff --git a/test/rubygems/test_gem_resolver_best_set.rb b/test/rubygems/test_gem_resolver_best_set.rb
index 2d16f8b701..055438c89b 100644
--- a/test/rubygems/test_gem_resolver_best_set.rb
+++ b/test/rubygems/test_gem_resolver_best_set.rb
@@ -32,6 +32,26 @@ class TestGemResolverBestSet < Gem::TestCase
assert_equal %w[a-1], found.map { |s| s.full_name }
end
+ def test_find_all_fallback
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ end
+
+ set = @DR::BestSet.new
+
+ api_uri = URI(@gem_repo) + './api/v1/dependencies'
+
+ set.sets << Gem::Resolver::APISet.new(api_uri)
+
+ dependency = dep 'a', '~> 1'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_equal %w[a-1], found.map { |s| s.full_name }
+ end
+
def test_find_all_local
spec_fetcher do |fetcher|
fetcher.spec 'a', 1
@@ -76,5 +96,42 @@ class TestGemResolverBestSet < Gem::TestCase
assert_empty set.sets
end
+ def test_replace_failed_api_set
+ set = @DR::BestSet.new
+
+ api_uri = URI(@gem_repo) + './api/v1/dependencies'
+ api_set = Gem::Resolver::APISet.new api_uri
+
+ set.sets << api_set
+
+ error_uri = api_uri + '?gems=a'
+
+ error = Gem::RemoteFetcher::FetchError.new 'bogus', error_uri
+
+ set.replace_failed_api_set error
+
+ assert_equal 1, set.sets.size
+
+ refute_includes set.sets, api_set
+
+ assert_kind_of Gem::Resolver::IndexSet, set.sets.first
+ end
+
+ def test_replace_failed_api_set_no_api_set
+ set = @DR::BestSet.new
+
+ index_set = Gem::Resolver::IndexSet.new Gem::Source.new @gem_repo
+
+ set.sets << index_set
+
+ error = Gem::RemoteFetcher::FetchError.new 'bogus', @gem_repo
+
+ e = assert_raises Gem::RemoteFetcher::FetchError do
+ set.replace_failed_api_set error
+ end
+
+ assert_equal error, e
+ end
+
end
diff --git a/test/rubygems/test_gem_resolver_composed_set.rb b/test/rubygems/test_gem_resolver_composed_set.rb
index 85026f7eb9..f8455e1aa4 100644
--- a/test/rubygems/test_gem_resolver_composed_set.rb
+++ b/test/rubygems/test_gem_resolver_composed_set.rb
@@ -2,6 +2,33 @@ require 'rubygems/test_case'
class TestGemResolverComposedSet < Gem::TestCase
+ def test_errors
+ index_set = Gem::Resolver::IndexSet.new
+ current_set = Gem::Resolver::CurrentSet.new
+
+ set = Gem::Resolver::ComposedSet.new index_set, current_set
+
+ set.instance_variable_get(:@errors) << :a
+ current_set.errors << :b
+
+ assert_includes set.errors, :a
+ assert_includes set.errors, :b
+ assert_includes set.errors, index_set.errors.first
+ end
+
+ def test_prerelease_equals
+ best_set = Gem::Resolver::BestSet.new
+ current_set = Gem::Resolver::CurrentSet.new
+
+ set = Gem::Resolver::ComposedSet.new best_set, current_set
+
+ set.prerelease = true
+
+ assert set.prerelease
+ assert best_set.prerelease
+ assert current_set.prerelease
+ end
+
def test_remote_equals
best_set = Gem::Resolver::BestSet.new
current_set = Gem::Resolver::CurrentSet.new
diff --git a/test/rubygems/test_gem_resolver_conflict.rb b/test/rubygems/test_gem_resolver_conflict.rb
index f8bba3f16f..0cef0cad2a 100644
--- a/test/rubygems/test_gem_resolver_conflict.rb
+++ b/test/rubygems/test_gem_resolver_conflict.rb
@@ -22,10 +22,17 @@ class TestGemResolverConflict < Gem::TestCase
Gem::Resolver::Conflict.new child, active
expected = <<-EXPECTED
- Activated net-ssh-2.2.2 via:
- net-ssh-2.2.2 (>= 2.0.13)
- instead of (>= 2.6.5) via:
- net-ssh-2.2.2 (>= 2.0.13), rye-0.9.8 (= 0.9.8)
+ Activated net-ssh-2.2.2
+ which does not match conflicting dependency (>= 2.6.5)
+
+ Conflicting dependency chains:
+ net-ssh (>= 2.0.13), 2.2.2 activated
+
+ versus:
+ rye (= 0.9.8), 0.9.8 activated, depends on
+ net-ssh (>= 2.0.13), 2.2.2 activated, depends on
+ net-ssh (>= 2.6.5)
+
EXPECTED
assert_equal expected, conflict.explanation
@@ -44,10 +51,15 @@ class TestGemResolverConflict < Gem::TestCase
conflict = @DR::Conflict.new a1_req, activated
expected = <<-EXPECTED
- Activated a-2 via:
- a-2 (= 2)
- instead of (= 1) via:
- user request (gem command or Gemfile)
+ Activated a-2
+ which does not match conflicting dependency (= 1)
+
+ Conflicting dependency chains:
+ a (= 2), 2 activated
+
+ versus:
+ a (= 1)
+
EXPECTED
assert_equal expected, conflict.explanation
@@ -64,8 +76,8 @@ class TestGemResolverConflict < Gem::TestCase
Gem::Resolver::Conflict.new nil, nil
expected = [
- 'net-ssh-2.2.2 (>= 2.0.13)',
- 'rye-0.9.8 (= 0.9.8)'
+ 'net-ssh (>= 2.0.13), 2.2.2 activated',
+ 'rye (= 0.9.8), 0.9.8 activated'
]
assert_equal expected, conflict.request_path(child.requester)
diff --git a/test/rubygems/test_gem_resolver_dependency_request.rb b/test/rubygems/test_gem_resolver_dependency_request.rb
index e1a98826fb..0665883971 100644
--- a/test/rubygems/test_gem_resolver_dependency_request.rb
+++ b/test/rubygems/test_gem_resolver_dependency_request.rb
@@ -8,6 +8,70 @@ class TestGemResolverDependencyRequest < Gem::TestCase
@DR = Gem::Resolver::DependencyRequest
end
+ def test_development_eh
+ a_dep = dep 'a', '>= 1'
+
+ a_dep_req = @DR.new a_dep, nil
+
+ refute a_dep_req.development?
+
+ b_dep = dep 'b', '>= 1', :development
+
+ b_dep_req = @DR.new b_dep, nil
+
+ assert b_dep_req.development?
+ end
+
+ def test_match_eh
+ spec = util_spec 'a', 1
+ dependency = dep 'a', '>= 1'
+
+ dr = @DR.new dependency, nil
+
+ assert dr.match? spec
+ end
+
+ def test_match_eh_prerelease
+ spec = util_spec 'a', '1.a'
+
+ a_dep = dep 'a', '>= 1'
+ a_dr = @DR.new a_dep, nil
+
+ refute a_dr.match? spec
+
+ a_pre_dep = dep 'a', '>= 1.a'
+ a_pre_dr = @DR.new a_pre_dep, nil
+
+ assert a_pre_dr.match? spec
+ end
+
+ def test_match_eh_prerelease_allow_prerelease
+ spec = util_spec 'a', '2.a'
+
+ a_dep = dep 'a', '>= 1'
+ a_dr = @DR.new a_dep, nil
+
+ assert a_dr.match? spec, true
+ end
+
+ def test_matches_spec_eh
+ spec = util_spec 'a', 1
+ dependency = dep 'a', '>= 1'
+
+ dr = @DR.new dependency, nil
+
+ assert dr.matches_spec? spec
+ end
+
+ def test_matches_spec_eh_prerelease
+ spec = util_spec 'a', '1.a'
+
+ dependency = dep 'a', '>= 0'
+ dr = @DR.new dependency, nil
+
+ assert dr.matches_spec? spec
+ end
+
def test_requirement
dependency = dep 'a', '>= 1'
diff --git a/test/rubygems/test_gem_resolver_git_set.rb b/test/rubygems/test_gem_resolver_git_set.rb
index f82b942777..3659193456 100644
--- a/test/rubygems/test_gem_resolver_git_set.rb
+++ b/test/rubygems/test_gem_resolver_git_set.rb
@@ -85,6 +85,32 @@ class TestGemResolverGitSet < Gem::TestCase
assert_empty @set.find_all dependency
end
+ def test_find_all_prerelease
+ name, _, repository, = git_gem 'a', '1.a'
+
+ @set.add_git_gem name, repository, 'master', false
+
+ dependency = dep 'a', '>= 0'
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ found = @set.find_all dependency
+
+ assert_empty found
+
+ dependency = dep 'a', '>= 0.a'
+ req = Gem::Resolver::DependencyRequest.new dependency, nil
+ @reqs.add req
+
+ @set.prefetch @reqs
+
+ found = @set.find_all dependency
+
+ refute_empty found
+ end
+
def test_root_dir
assert_equal Gem.dir, @set.root_dir
diff --git a/test/rubygems/test_gem_resolver_git_specification.rb b/test/rubygems/test_gem_resolver_git_specification.rb
index c9b040c117..a31674a218 100644
--- a/test/rubygems/test_gem_resolver_git_specification.rb
+++ b/test/rubygems/test_gem_resolver_git_specification.rb
@@ -32,6 +32,18 @@ class TestGemResolverGitSpecification < Gem::TestCase
refute_equal g_spec_a, i_spec
end
+ def test_add_dependency
+ git_gem 'a', 1
+
+ git_spec = Gem::Resolver::GitSpecification.new @set, @spec
+
+ b_dep = dep 'b'
+
+ git_spec.add_dependency b_dep
+
+ assert_equal [b_dep], git_spec.dependencies
+ end
+
def test_install
git_gem 'a', 1
diff --git a/test/rubygems/test_gem_resolver_index_set.rb b/test/rubygems/test_gem_resolver_index_set.rb
index b0adc511c9..04ef8844c1 100644
--- a/test/rubygems/test_gem_resolver_index_set.rb
+++ b/test/rubygems/test_gem_resolver_index_set.rb
@@ -22,6 +22,8 @@ class TestGemResolverIndexSet < Gem::TestCase
fetcher = set.instance_variable_get :@f
refute_same Gem::SpecFetcher.fetcher, fetcher
+
+ refute_empty set.errors
end
def test_find_all
@@ -31,7 +33,7 @@ class TestGemResolverIndexSet < Gem::TestCase
fetcher.spec 'b', 1
end
- set = @DR::BestSet.new
+ set = @DR::IndexSet.new
dependency = dep 'a', '~> 1'
@@ -49,7 +51,7 @@ class TestGemResolverIndexSet < Gem::TestCase
fetcher.spec 'b', 1
end
- set = @DR::BestSet.new
+ set = @DR::IndexSet.new
set.remote = false
dependency = dep 'a', '~> 1'
@@ -59,5 +61,29 @@ class TestGemResolverIndexSet < Gem::TestCase
assert_empty set.find_all req
end
+ def test_find_all_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', '1.a'
+ end
+
+ set = @DR::IndexSet.new
+
+ dependency = dep 'a'
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_empty found
+
+ dependency.prerelease = true
+
+ req = @DR::DependencyRequest.new dependency, nil
+
+ found = set.find_all req
+
+ assert_equal %w[a-1.a], found.map { |s| s.full_name }
+ end
+
end
diff --git a/test/rubygems/test_gem_resolver_installer_set.rb b/test/rubygems/test_gem_resolver_installer_set.rb
index 258f9bc803..3096a23011 100644
--- a/test/rubygems/test_gem_resolver_installer_set.rb
+++ b/test/rubygems/test_gem_resolver_installer_set.rb
@@ -2,6 +2,101 @@ require 'rubygems/test_case'
class TestGemResolverInstallerSet < Gem::TestCase
+ def test_add_always_install
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2
+ fetcher.clear
+ end
+
+ util_gem 'a', 1
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep('a')
+
+ assert_equal %w[a-2], set.always_install.map { |s| s.full_name }
+
+ e = assert_raises Gem::UnsatisfiableDependencyError do
+ set.add_always_install dep('b')
+ end
+
+ assert_equal dep('b'), e.dependency.dependency
+ end
+
+ def test_add_always_install_errors
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ e = assert_raises Gem::UnsatisfiableDependencyError do
+ set.add_always_install dep 'a'
+ end
+
+ refute_empty e.errors
+ end
+
+ def test_add_always_install_platform
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 1
+ fetcher.spec 'a', 2 do |s|
+ s.platform = Gem::Platform.new 'x86-freebsd-9'
+ end
+ fetcher.clear
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep('a')
+
+ assert_equal %w[a-1], set.always_install.map { |s| s.full_name }
+ end
+
+ def test_add_always_install_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ fetcher.gem 'a', '3.a'
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep('a')
+
+ assert_equal %w[a-1], set.always_install.map { |s| s.full_name }
+ end
+
+ def test_add_always_install_prerelease_only
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', '3.a'
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ assert_raises Gem::UnsatisfiableDependencyError do
+ set.add_always_install dep('a')
+ end
+ end
+
+ def test_add_local
+ a_1, a_1_gem = util_gem 'a', 1
+
+ a_1_source = Gem::Source::SpecificFile.new a_1_gem
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_local File.basename(a_1_gem), a_1, a_1_source
+
+ assert set.local? File.basename(a_1_gem)
+
+ FileUtils.rm a_1_gem
+ util_clear_gems
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_equal %w[a-1], set.find_all(req).map { |spec| spec.full_name }
+ end
+
def test_consider_local_eh
set = Gem::Resolver::InstallerSet.new :remote
@@ -30,6 +125,54 @@ class TestGemResolverInstallerSet < Gem::TestCase
refute set.consider_remote?
end
+ def test_errors
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.instance_variable_get(:@errors) << :a
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ set.find_all req
+
+ assert_equal [:a, set.remote_set.errors.first], set.errors
+ end
+
+ def test_find_all_always_install
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', 2
+ fetcher.clear
+ end
+
+ util_gem 'a', 1
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ set.add_always_install dep 'a'
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_equal %w[a-2], set.find_all(req).map { |spec| spec.full_name }
+ end
+
+ def test_find_all_prerelease
+ spec_fetcher do |fetcher|
+ fetcher.spec 'a', '1'
+ fetcher.spec 'a', '1.a'
+ fetcher.clear
+ end
+
+ set = Gem::Resolver::InstallerSet.new :both
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_equal %w[a-1], set.find_all(req).map { |spec| spec.full_name }
+
+ req = Gem::Resolver::DependencyRequest.new dep('a', '>= 0.a'), nil
+
+ assert_equal %w[a-1 a-1.a],
+ set.find_all(req).map { |spec| spec.full_name }.sort
+ end
+
def test_load_spec
specs = spec_fetcher do |fetcher|
fetcher.spec 'a', 2
@@ -46,6 +189,18 @@ class TestGemResolverInstallerSet < Gem::TestCase
assert_equal specs["a-2-#{Gem::Platform.local}"].full_name, spec.full_name
end
+ def test_prerelease_equals
+ set = Gem::Resolver::InstallerSet.new :remote
+
+ refute set.prerelease
+ refute set.remote_set.prerelease
+
+ set.prerelease = true
+
+ assert set.prerelease
+ assert set.remote_set.prerelease
+ end
+
def test_remote_equals_both
set = Gem::Resolver::InstallerSet.new :both
set.remote = true
diff --git a/test/rubygems/test_gem_resolver_lock_set.rb b/test/rubygems/test_gem_resolver_lock_set.rb
index 51ddad42f0..fdcb8ffa98 100644
--- a/test/rubygems/test_gem_resolver_lock_set.rb
+++ b/test/rubygems/test_gem_resolver_lock_set.rb
@@ -5,14 +5,15 @@ class TestGemResolverLockSet < Gem::TestCase
def setup
super
- @source = Gem::Source.new @gem_repo
- @lock_source = Gem::Source::Lock.new @source
+ @sources = [Gem::Source.new(@gem_repo)]
+ @lock_source = Gem::Source::Lock.new @sources.first
- @set = Gem::Resolver::LockSet.new @source
+ @set = Gem::Resolver::LockSet.new @sources
end
def test_add
- spec = @set.add 'a', '2', Gem::Platform::RUBY
+ specs = @set.add 'a', '2', Gem::Platform::RUBY
+ spec = specs.first
assert_equal %w[a-2], @set.specs.map { |t| t.full_name }
@@ -26,12 +27,17 @@ class TestGemResolverLockSet < Gem::TestCase
end
def test_find_all
- @set.add 'a', '2', Gem::Platform::RUBY
- @set.add 'b', '2', Gem::Platform::RUBY
+ @set.add 'a', '1.a', Gem::Platform::RUBY
+ @set.add 'a', '2', Gem::Platform::RUBY
+ @set.add 'b', '2', Gem::Platform::RUBY
found = @set.find_all dep 'a'
assert_equal %w[a-2], found.map { |s| s.full_name }
+
+ found = @set.find_all dep 'a', '>= 0.a'
+
+ assert_equal %w[a-1.a a-2], found.map { |s| s.full_name }
end
def test_load_spec
diff --git a/test/rubygems/test_gem_resolver_lock_specification.rb b/test/rubygems/test_gem_resolver_lock_specification.rb
index 5741950fe0..f8a336e658 100644
--- a/test/rubygems/test_gem_resolver_lock_specification.rb
+++ b/test/rubygems/test_gem_resolver_lock_specification.rb
@@ -9,7 +9,7 @@ class TestGemResolverLockSpecification < Gem::TestCase
@LS = Gem::Resolver::LockSpecification
@source = Gem::Source.new @gem_repo
- @set = Gem::Resolver::LockSet.new @source
+ @set = Gem::Resolver::LockSet.new [@source]
end
def test_initialize
@@ -83,5 +83,16 @@ class TestGemResolverLockSpecification < Gem::TestCase
assert_equal [b_dep, c_dep], l_spec.spec.dependencies
end
+ def test_spec_loaded
+ real_spec = util_spec 'a', 2
+ real_spec.activate
+
+ version = v(2)
+
+ l_spec = @LS.new @set, 'a', version, @source, Gem::Platform::RUBY
+
+ assert_same real_spec, l_spec.spec
+ end
+
end
diff --git a/test/rubygems/test_gem_resolver_specification.rb b/test/rubygems/test_gem_resolver_specification.rb
index 2c9c143cee..e1ec68a22c 100644
--- a/test/rubygems/test_gem_resolver_specification.rb
+++ b/test/rubygems/test_gem_resolver_specification.rb
@@ -3,6 +3,7 @@ require 'rubygems/test_case'
class TestGemResolverSpecification < Gem::TestCase
class TestSpec < Gem::Resolver::Specification
+ attr_writer :source
attr_reader :spec
def initialize spec
@@ -12,6 +13,26 @@ class TestGemResolverSpecification < Gem::TestCase
end
end
+ def test_install
+ gemhome = "#{@gemhome}2"
+ spec_fetcher do |fetcher|
+ fetcher.gem 'a', 1
+ end
+
+ a = util_spec 'a', 1
+
+ a_spec = TestSpec.new a
+ a_spec.source = Gem::Source.new @gem_repo
+
+ a_spec.install :install_dir => gemhome
+
+ assert_path_exists File.join gemhome, 'gems', a.full_name
+
+ expected = File.join gemhome, 'specifications', a.spec_name
+
+ assert_equal expected, a_spec.spec.loaded_from
+ end
+
def test_installable_platform_eh
a = util_spec 'a', 1
@@ -28,5 +49,16 @@ class TestGemResolverSpecification < Gem::TestCase
refute b_spec.installable_platform?
end
+ def test_source
+ a = util_spec 'a', 1
+
+ source = Gem::Source.new @gem_repo
+
+ a_spec = TestSpec.new a
+ a_spec.source = source
+
+ assert_equal source, a_spec.source
+ end
+
end
diff --git a/test/rubygems/test_gem_resolver_vendor_set.rb b/test/rubygems/test_gem_resolver_vendor_set.rb
index 4c1e14609d..618e251de2 100644
--- a/test/rubygems/test_gem_resolver_vendor_set.rb
+++ b/test/rubygems/test_gem_resolver_vendor_set.rb
@@ -11,10 +11,12 @@ class TestGemResolverVendorSet < Gem::TestCase
def test_add_vendor_gem
name, version, directory = vendor_gem
- @set.add_vendor_gem name, directory
+ added = @set.add_vendor_gem name, directory
spec = @set.load_spec name, version, Gem::Platform::RUBY, nil
+ assert_equal spec, added
+
assert_equal "#{name}-#{version}", spec.full_name
assert_equal File.expand_path(directory), spec.full_gem_path
@@ -55,6 +57,20 @@ class TestGemResolverVendorSet < Gem::TestCase
assert_equal expected, found
end
+ def test_find_all_prerelease
+ name, _, directory = vendor_gem 'a', '1.a'
+
+ @set.add_vendor_gem name, directory
+
+ req = Gem::Resolver::DependencyRequest.new dep('a'), nil
+
+ assert_empty @set.find_all req
+
+ req = Gem::Resolver::DependencyRequest.new dep('a', '>= 0.a'), nil
+
+ refute_empty @set.find_all req
+ end
+
def test_load_spec
error = Object.const_defined?(:KeyError) ? KeyError : IndexError
diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb
index a2115e709a..d708306e79 100644
--- a/test/rubygems/test_gem_security_policy.rb
+++ b/test/rubygems/test_gem_security_policy.rb
@@ -347,7 +347,7 @@ class TestGemSecurityPolicy < Gem::TestCase
assert_match "WARNING: some_gem is not signed\n", @ui.error
assert_raises Gem::Security::Exception do
- @almost_no.verify [PUBLIC_CERT], nil, digests, {}
+ @high.verify [PUBLIC_CERT], nil, digests, {}
end
end
@@ -513,7 +513,7 @@ class TestGemSecurityPolicy < Gem::TestCase
digests['SHA1']['data.tar.gz'] = OpenSSL::Digest.new 'SHA1', 'hello'
assert_raises Gem::Security::Exception do
- @almost_no.verify_signatures @spec, digests, {}
+ @high.verify_signatures @spec, digests, {}
end
end
diff --git a/test/rubygems/test_gem_server.rb b/test/rubygems/test_gem_server.rb
index b55b019268..964048615c 100644
--- a/test/rubygems/test_gem_server.rb
+++ b/test/rubygems/test_gem_server.rb
@@ -10,8 +10,9 @@ class TestGemServer < Gem::TestCase
def setup
super
- @a1 = quick_gem 'a', '1'
- @a2 = quick_gem 'a', '2'
+ @a1 = quick_gem 'a', '1'
+ @a2 = quick_gem 'a', '2'
+ @a3_p = quick_gem 'a', '3.a'
@server = Gem::Server.new Gem.dir, process_based_port, false
@req = WEBrick::HTTPRequest.new :Logger => nil
@@ -144,6 +145,36 @@ class TestGemServer < Gem::TestCase
assert_equal 2, @server.server.listeners.length
end
+ def test_prerelease_specs
+ data = StringIO.new "GET /prerelease_specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ Gem::Deprecate.skip_during do
+ @server.prerelease_specs @req, @res
+ end
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/octet-stream', @res['content-type']
+ assert_equal [['a', v('3.a'), Gem::Platform::RUBY]],
+ Marshal.load(@res.body)
+ end
+
+ def test_prerelease_specs_gz
+ data = StringIO.new "GET /prerelease_specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ Gem::Deprecate.skip_during do
+ @server.prerelease_specs @req, @res
+ end
+
+ assert_equal 200, @res.status, @res.body
+ assert_match %r| \d\d:\d\d:\d\d |, @res['date']
+ assert_equal 'application/x-gzip', @res['content-type']
+ assert_equal [['a', v('3.a'), Gem::Platform::RUBY]],
+ Marshal.load(Gem.gunzip(@res.body))
+ end
+
def test_quick_gemdirs
data = StringIO.new "GET /quick/Marshal.4.8/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
dir = "#{@gemhome}2"
@@ -223,6 +254,38 @@ class TestGemServer < Gem::TestCase
assert_equal Gem::Platform.local, spec.platform
end
+ def test_quick_marshal_a_3_a_gemspec_rz
+ data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-3.a.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert @res['date']
+ assert_equal 'application/x-deflate', @res['content-type']
+
+ spec = Marshal.load Gem.inflate(@res.body)
+ assert_equal 'a', spec.name
+ assert_equal v('3.a'), spec.version
+ end
+
+ def test_quick_marshal_a_b_3_a_gemspec_rz
+ quick_gem 'a-b', '3.a'
+
+ data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-b-3.a.gemspec.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @req, @res
+
+ assert_equal 200, @res.status, @res.body
+ assert @res['date']
+ assert_equal 'application/x-deflate', @res['content-type']
+
+ spec = Marshal.load Gem.inflate(@res.body)
+ assert_equal 'a-b', spec.name
+ assert_equal v('3.a'), spec.version
+ end
+
def test_rdoc
data = StringIO.new "GET /rdoc?q=a HTTP/1.0\r\n\r\n"
@req.parse data
@@ -279,7 +342,8 @@ class TestGemServer < Gem::TestCase
assert_equal 'application/octet-stream', @res['content-type']
assert_equal [['a', Gem::Version.new(1), Gem::Platform::RUBY],
- ['a', Gem::Version.new(2), Gem::Platform::RUBY]],
+ ['a', Gem::Version.new(2), Gem::Platform::RUBY],
+ ['a', v('3.a'), Gem::Platform::RUBY]],
Marshal.load(@res.body)
end
@@ -318,7 +382,8 @@ class TestGemServer < Gem::TestCase
assert_equal 'application/x-gzip', @res['content-type']
assert_equal [['a', Gem::Version.new(1), Gem::Platform::RUBY],
- ['a', Gem::Version.new(2), Gem::Platform::RUBY]],
+ ['a', Gem::Version.new(2), Gem::Platform::RUBY],
+ ['a', v('3.a'), Gem::Platform::RUBY]],
Marshal.load(Gem.gunzip(@res.body))
end
diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb
index d207bcac7f..9ded7f7c03 100644
--- a/test/rubygems/test_gem_source.rb
+++ b/test/rubygems/test_gem_source.rb
@@ -1,5 +1,6 @@
require 'rubygems/test_case'
require 'rubygems/source'
+require 'rubygems/indexer'
class TestGemSource < Gem::TestCase
@@ -40,13 +41,28 @@ class TestGemSource < Gem::TestCase
end
def test_dependency_resolver_set_bundler_api
- @fetcher.data["#{@gem_repo}api/v1/dependencies"] = 'data'
+ response = Net::HTTPResponse.new '1.1', 200, 'OK'
+ response.uri = URI('http://example') if response.respond_to? :uri
+
+ @fetcher.data["#{@gem_repo}api/v1/dependencies"] = response
set = @source.dependency_resolver_set
assert_kind_of Gem::Resolver::APISet, set
end
+ def test_dependency_resolver_set_file_uri
+ skip 'install builder gem' unless defined? Builder::XChar
+
+ Gem::Indexer.new(@tempdir).generate_index
+
+ source = Gem::Source.new "file://#{@tempdir}/"
+
+ set = source.dependency_resolver_set
+
+ assert_kind_of Gem::Resolver::IndexSet, set
+ end
+
def test_dependency_resolver_set_marshal_api
set = @source.dependency_resolver_set
diff --git a/test/rubygems/test_gem_source_git.rb b/test/rubygems/test_gem_source_git.rb
index 58bff84490..cf5f088c03 100644
--- a/test/rubygems/test_gem_source_git.rb
+++ b/test/rubygems/test_gem_source_git.rb
@@ -27,6 +27,21 @@ class TestGemSourceGit < Gem::TestCase
assert_path_exists File.join @source.install_dir, 'a.gemspec'
end
+ def test_checkout_master
+ Dir.chdir @repository do
+ system @git, 'checkout', '-q', '-b', 'other'
+ system @git, 'mv', 'a.gemspec', 'b.gemspec'
+ system @git, 'commit', '-q', '-a', '-m', 'rename gemspec'
+ system @git, 'checkout', '-q', 'master'
+ end
+
+ @source = Gem::Source::Git.new @name, @repository, 'other', false
+
+ @source.checkout
+
+ assert_path_exists File.join @source.install_dir, 'b.gemspec'
+ end
+
def test_checkout_local
@source.remote = false
@@ -179,14 +194,18 @@ class TestGemSourceGit < Gem::TestCase
git = Gem::Source::Git.new 'a', 'git/a', 'master', false
remote = Gem::Source.new @gem_repo
installed = Gem::Source::Installed.new
+ vendor = Gem::Source::Vendor.new 'vendor/foo'
assert_equal( 0, git. <=>(git), 'git <=> git')
assert_equal( 1, git. <=>(remote), 'git <=> remote')
assert_equal(-1, remote. <=>(git), 'remote <=> git')
- assert_equal( 1, installed.<=>(git), 'installed <=> git')
- assert_equal(-1, git. <=>(installed), 'git <=> installed')
+ assert_equal( 1, git. <=>(installed), 'git <=> installed')
+ assert_equal(-1, installed.<=>(git), 'installed <=> git')
+
+ assert_equal(-1, git. <=>(vendor), 'git <=> vendor')
+ assert_equal( 1, vendor. <=>(git), 'vendor <=> git')
end
def test_specs
@@ -254,6 +273,10 @@ class TestGemSourceGit < Gem::TestCase
end
end
+ def test_uri
+ assert_equal URI(@repository), @source.uri
+ end
+
def test_uri_hash
assert_equal @hash, @source.uri_hash
diff --git a/test/rubygems/test_gem_source_installed.rb b/test/rubygems/test_gem_source_installed.rb
index 1119ad0c2b..9eaddf72fd 100644
--- a/test/rubygems/test_gem_source_installed.rb
+++ b/test/rubygems/test_gem_source_installed.rb
@@ -11,6 +11,8 @@ class TestGemSourceInstalled < Gem::TestCase
specific = Gem::Source::SpecificFile.new a1.cache_file
installed = Gem::Source::Installed.new
local = Gem::Source::Local.new
+ git = Gem::Source::Git.new 'a', 'a', 'master'
+ vendor = Gem::Source::Vendor.new 'a'
assert_equal( 0, installed.<=>(installed), 'installed <=> installed')
@@ -22,6 +24,12 @@ class TestGemSourceInstalled < Gem::TestCase
assert_equal(-1, specific. <=>(installed), 'specific <=> installed')
assert_equal( 1, installed.<=>(specific), 'installed <=> specific')
+
+ assert_equal( 1, git. <=>(installed), 'git <=> installed')
+ assert_equal(-1, installed.<=>(git), 'installed <=> git')
+
+ assert_equal( 1, vendor. <=>(installed), 'vendor <=> installed')
+ assert_equal(-1, installed.<=>(vendor), 'installed <=> vendor')
end
end
diff --git a/test/rubygems/test_gem_source_lock.rb b/test/rubygems/test_gem_source_lock.rb
index c7c4b8ca3f..23f063da92 100644
--- a/test/rubygems/test_gem_source_lock.rb
+++ b/test/rubygems/test_gem_source_lock.rb
@@ -43,8 +43,8 @@ class TestGemSourceLock < Gem::TestCase
assert_equal( 0, i_lock.<=>(i_lock), 'i_lock <=> i_lock')
assert_equal( 0, v_lock.<=>(v_lock), 'v_lock <=> v_lock')
- assert_equal(-1, g_lock.<=>(i_lock), 'g_lock <=> i_lock')
- assert_equal( 1, i_lock.<=>(g_lock), 'i_lock <=> g_lock')
+ assert_equal( 1, g_lock.<=>(i_lock), 'g_lock <=> i_lock')
+ assert_equal(-1, i_lock.<=>(g_lock), 'i_lock <=> g_lock')
assert_equal(-1, g_lock.<=>(v_lock), 'g_lock <=> v_lock')
assert_equal( 1, v_lock.<=>(g_lock), 'v_lock <=> g_lock')
diff --git a/test/rubygems/test_gem_source_specific_file.rb b/test/rubygems/test_gem_source_specific_file.rb
index fd1f4491ac..12ef7f5b7c 100644
--- a/test/rubygems/test_gem_source_specific_file.rb
+++ b/test/rubygems/test_gem_source_specific_file.rb
@@ -9,6 +9,10 @@ class TestGemSourceSpecificFile < Gem::TestCase
@sf = Gem::Source::SpecificFile.new(@a_gem)
end
+ def test_path
+ assert_equal @a_gem, @sf.path
+ end
+
def test_spec
assert_equal @a, @sf.spec
end
diff --git a/test/rubygems/test_gem_source_vendor.rb b/test/rubygems/test_gem_source_vendor.rb
index 3f4121e5f6..1d9ae35e84 100644
--- a/test/rubygems/test_gem_source_vendor.rb
+++ b/test/rubygems/test_gem_source_vendor.rb
@@ -12,6 +12,7 @@ class TestGemSourceVendor < Gem::TestCase
def test_spaceship
vendor = Gem::Source::Vendor.new 'vendor/foo'
remote = Gem::Source.new @gem_repo
+ git = Gem::Source::Git.new 'a', 'a', 'master'
installed = Gem::Source::Installed.new
assert_equal( 0, vendor. <=>(vendor), 'vendor <=> vendor')
@@ -19,6 +20,9 @@ class TestGemSourceVendor < Gem::TestCase
assert_equal( 1, vendor. <=>(remote), 'vendor <=> remote')
assert_equal(-1, remote. <=>(vendor), 'remote <=> vendor')
+ assert_equal( 1, vendor. <=>(git), 'vendor <=> git')
+ assert_equal(-1, git. <=>(vendor), 'git <=> vendor')
+
assert_equal( 1, vendor. <=>(installed), 'vendor <=> installed')
assert_equal(-1, installed.<=>(vendor), 'installed <=> vendor')
end
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index e1c6a1b549..146e249381 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -225,7 +225,7 @@ end
util_spec 'b', '2.0'
c, _ = util_spec 'c', '1.0', 'b' => '= 2.0'
- e = assert_raises Gem::LoadError do
+ e = assert_raises Gem::ConflictError do
assert_activate nil, a, c, "b"
end
@@ -1135,6 +1135,24 @@ dependencies: []
assert_equal %w[lib/code.rb], @a2.files
end
+ def test_build_args
+ ext_spec
+
+ assert_empty @ext.build_args
+
+ open @ext.build_info_file, 'w' do |io|
+ io.puts
+ end
+
+ assert_empty @ext.build_args
+
+ open @ext.build_info_file, 'w' do |io|
+ io.puts '--with-foo-dir=wherever'
+ end
+
+ assert_equal %w[--with-foo-dir=wherever], @ext.build_args
+ end
+
def test_build_extensions
ext_spec
@@ -1231,11 +1249,10 @@ dependencies: []
FileUtils.chmod 0555, @ext.base_dir
FileUtils.chmod 0555, File.join(@ext.base_dir, 'extensions')
- assert_raises Errno::EACCES do
- @ext.build_extensions
- end
+ @ext.build_extensions
+ refute_path_exists @ext.extension_dir
ensure
- unless Gem.win_platform? then
+ unless ($DEBUG or win_platform?) then
FileUtils.chmod 0755, File.join(@ext.base_dir, 'extensions')
FileUtils.chmod 0755, @ext.base_dir
end
@@ -1331,22 +1348,14 @@ dependencies: []
def test_contains_requirable_file_eh_extension
ext_spec
- extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
- FileUtils.mkdir_p File.dirname extconf_rb
-
- open extconf_rb, 'w' do |f|
- f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
- f.puts "clean:\n\techo cleaned"
- f.puts "default:\n\techo built"
- f.puts "install:\n\techo installed"
- end
- RUBY
+ _, err = capture_io do
+ refute @ext.contains_requirable_file? 'nonexistent'
end
- refute @ext.contains_requirable_file? 'nonexistent'
+ expected = "Ignoring ext-1 because its extensions are not built. " +
+ "Try: gem pristine ext-1\n"
- assert_path_exists @ext.extension_dir
+ assert_equal expected, err
end
def test_date
@@ -1791,13 +1800,33 @@ dependencies: []
enable_shared 'no' do
ext_spec
- @ext.require_path = 'lib'
+ @ext.require_paths = 'lib'
- ext_install_dir = Pathname(@ext.extension_dir)
- full_gem_path = Pathname(@ext.full_gem_path)
- relative_install_dir = ext_install_dir.relative_path_from full_gem_path
+ assert_equal [@ext.extension_dir, 'lib'], @ext.require_paths
+ end
+ end
- assert_equal [relative_install_dir.to_s, 'lib'], @ext.require_paths
+ def test_require_paths_default_ext_dir_for
+ class << Gem
+ send :alias_method, :orig_default_ext_dir_for, :default_ext_dir_for
+ end
+
+ def Gem.default_ext_dir_for base_dir
+ '/foo'
+ end
+
+ enable_shared 'no' do
+ ext_spec
+
+ @ext.require_paths = 'lib'
+
+ assert_equal ['/foo/ext-1', 'lib'], @ext.require_paths
+ end
+ ensure
+ class << Gem
+ send :remove_method, :default_ext_dir_for
+ send :alias_method, :default_ext_dir_for, :orig_default_ext_dir_for
+ send :remove_method, :orig_default_ext_dir_for
end
end
@@ -1824,7 +1853,7 @@ dependencies: []
def test_full_require_paths
ext_spec
- @ext.require_path = 'lib'
+ @ext.require_paths = 'lib'
expected = [
@ext.extension_dir,
@@ -2268,6 +2297,7 @@ end
@a1.add_runtime_dependency 'k', '> 1.2'
@a1.add_runtime_dependency 'l', '> 1.2.3'
@a1.add_runtime_dependency 'm', '~> 2.1.0'
+ @a1.add_runtime_dependency 'n', '~> 0.1.0'
use_ui @ui do
@a1.validate
@@ -2874,14 +2904,76 @@ end
assert_equal @m1.to_ruby, valid_ruby_spec
end
+ def test_missing_extensions_eh
+ ext_spec
+
+ assert @ext.missing_extensions?
+
+ extconf_rb = File.join @ext.gem_dir, @ext.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+
+ @ext.build_extensions
+
+ refute @ext.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_default_gem
+ spec = new_default_spec 'default', 1
+ spec.extensions << 'extconf.rb'
+
+ refute spec.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_legacy
+ ext_spec
+
+ @ext.installed_by_version = v '2.2.0.preview.2'
+
+ assert @ext.missing_extensions?
+
+ @ext.installed_by_version = v '2.2.0.preview.1'
+
+ refute @ext.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_none
+ refute @a1.missing_extensions?
+ end
+
def test_find_by_name
- util_make_gems
- assert(Gem::Specification.find_by_name("a"))
- assert(Gem::Specification.find_by_name("a", "1"))
- assert(Gem::Specification.find_by_name("a", ">1"))
- assert_raises(Gem::LoadError) do
- Gem::Specification.find_by_name("monkeys")
+ util_spec "a"
+
+ assert Gem::Specification.find_by_name "a"
+ assert Gem::Specification.find_by_name "a", "1"
+ assert Gem::Specification.find_by_name "a", ">1"
+
+ assert_raises Gem::LoadError do
+ Gem::Specification.find_by_name "monkeys"
+ end
+ end
+
+ def test_find_by_name_prerelease
+ b = util_spec "b", "2.a"
+
+ b.activate
+
+ assert Gem::Specification.find_by_name "b"
+
+ assert_raises Gem::LoadError do
+ Gem::Specification.find_by_name "b", "1"
end
+
+ assert Gem::Specification.find_by_name "b", ">1"
end
def test_find_by_path
diff --git a/test/rubygems/test_gem_stub_specification.rb b/test/rubygems/test_gem_stub_specification.rb
index c4c07597f5..d992567a5c 100644
--- a/test/rubygems/test_gem_stub_specification.rb
+++ b/test/rubygems/test_gem_stub_specification.rb
@@ -23,16 +23,11 @@ class TestStubSpecification < Gem::TestCase
def test_initialize_extension
stub = stub_with_extension
- ext_install_dir = Pathname(stub.extension_dir)
- full_gem_path = Pathname(stub.full_gem_path)
- relative_install_dir = ext_install_dir.relative_path_from full_gem_path
- relative_install_dir = relative_install_dir.to_s
-
- assert_equal 'stub_e', stub.name
- assert_equal v(2), stub.version
- assert_equal Gem::Platform::RUBY, stub.platform
- assert_equal [relative_install_dir, 'lib'], stub.require_paths
- assert_equal %w[ext/stub_e/extconf.rb], stub.extensions
+ assert_equal 'stub_e', stub.name
+ assert_equal v(2), stub.version
+ assert_equal Gem::Platform::RUBY, stub.platform
+ assert_equal [stub.extension_dir, 'lib'], stub.require_paths
+ assert_equal %w[ext/stub_e/extconf.rb], stub.extensions
end
def test_initialize_missing_stubline
@@ -55,22 +50,14 @@ class TestStubSpecification < Gem::TestCase
def test_contains_requirable_file_eh_extension
stub_with_extension do |stub|
- extconf_rb = File.join stub.gem_dir, stub.extensions.first
- FileUtils.mkdir_p File.dirname extconf_rb
-
- open extconf_rb, 'w' do |f|
- f.write <<-'RUBY'
- open 'Makefile', 'w' do |f|
- f.puts "clean:\n\techo cleaned"
- f.puts "default:\n\techo built"
- f.puts "install:\n\techo installed"
- end
- RUBY
+ _, err = capture_io do
+ refute stub.contains_requirable_file? 'nonexistent'
end
- refute stub.contains_requirable_file? 'nonexistent'
+ expected = "Ignoring stub_e-2 because its extensions are not built. " +
+ "Try: gem pristine stub_e-2\n"
- assert_path_exists stub.extension_dir
+ assert_equal expected, err
end
end
@@ -85,9 +72,70 @@ class TestStubSpecification < Gem::TestCase
assert_equal expected, stub.full_require_paths
end
+ def test_missing_extensions_eh
+ stub = stub_with_extension do |s|
+ extconf_rb = File.join s.gem_dir, s.extensions.first
+ FileUtils.mkdir_p File.dirname extconf_rb
+
+ open extconf_rb, 'w' do |f|
+ f.write <<-'RUBY'
+ open 'Makefile', 'w' do |f|
+ f.puts "clean:\n\techo clean"
+ f.puts "default:\n\techo built"
+ f.puts "install:\n\techo installed"
+ end
+ RUBY
+ end
+ end
+
+ assert stub.missing_extensions?
+
+ stub.build_extensions
+
+ refute stub.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_default_gem
+ spec = new_default_spec 'default', 1
+ spec.extensions << 'extconf.rb'
+
+ open spec.loaded_from, 'w' do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ default_spec = Gem::StubSpecification.new spec.loaded_from
+
+ refute default_spec.missing_extensions?
+ end
+
+ def test_missing_extensions_eh_none
+ refute @foo.missing_extensions?
+ end
+
def test_to_spec
+ real_foo = util_spec @foo.name, @foo.version
+ real_foo.activate
+
+ assert_equal @foo.version, Gem.loaded_specs[@foo.name].version,
+ 'sanity check'
+
+ assert_same real_foo, @foo.to_spec
+ end
+
+ def test_to_spec_activated
assert @foo.to_spec.is_a?(Gem::Specification)
assert_equal "foo", @foo.to_spec.name
+ refute @foo.to_spec.instance_variable_defined? :@ignored
+ end
+
+ def test_to_spec_missing_extensions
+ stub = stub_with_extension
+
+ capture_io do
+ stub.contains_requirable_file? 'nonexistent'
+ end
+
+ assert stub.to_spec.instance_variable_get :@ignored
end
def stub_with_extension
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index 11fdaf68e2..2a9a15d5b1 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -325,6 +325,29 @@ create_makefile '#{@spec.name}'
assert_equal expected, e.message
end
+ def test_uninstall_selection
+ util_make_gems
+
+ list = Gem::Specification.find_all_by_name 'a'
+
+ uninstaller = Gem::Uninstaller.new 'a'
+
+ ui = Gem::MockGemUi.new "1\ny\n"
+
+ use_ui ui do
+ uninstaller.uninstall
+ end
+
+ updated_list = Gem::Specification.find_all_by_name('a')
+ assert_equal list.length - 1, updated_list.length
+
+ assert_match ' 1. a-1', ui.output
+ assert_match ' 2. a-2', ui.output
+ assert_match ' 3. a-3.a', ui.output
+ assert_match ' 4. All versions', ui.output
+ assert_match 'uninstalled a-1', ui.output
+ end
+
def test_uninstall_selection_greater_than_one
util_make_gems
diff --git a/test/rubygems/test_gem_unsatisfiable_dependency_error.rb b/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
new file mode 100644
index 0000000000..6418c5d20e
--- /dev/null
+++ b/test/rubygems/test_gem_unsatisfiable_dependency_error.rb
@@ -0,0 +1,32 @@
+require 'rubygems/test_case'
+
+class TestGemUnsatisfiableDependencyError < Gem::TestCase
+
+ def setup
+ super
+
+ @a_dep = dep 'a', '~> 1'
+
+ @req = Gem::Resolver::DependencyRequest.new @a_dep, nil
+
+ @e = Gem::UnsatisfiableDependencyError.new @req
+ end
+
+ def test_errors
+ assert_equal [], @e.errors
+
+ @e.errors << :a
+
+ assert_equal [:a], @e.errors
+ end
+
+ def test_name
+ assert_equal 'a', @e.name
+ end
+
+ def test_version
+ assert_equal @a_dep.requirement, @e.version
+ end
+
+end
+
diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb
index 6210422506..6d25c48bc1 100644
--- a/test/rubygems/test_kernel.rb
+++ b/test/rubygems/test_kernel.rb
@@ -33,6 +33,12 @@ class TestKernel < Gem::TestCase
assert_equal 1, $:.select { |p| %r{a-1/lib} =~ p }.size
end
+ def test_gem_prerelease
+ quick_gem 'd', '1.1.a'
+ refute gem('d', '>= 1'), 'release requirement must not load prerelease'
+ assert gem('d', '>= 1.a'), 'prerelease requirement may load prerelease'
+ end
+
def test_gem_conflicting
assert gem('a', '= 1'), "Should load"
diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb
index c52c9937fe..dec5285d21 100644
--- a/test/rubygems/test_require.rb
+++ b/test/rubygems/test_require.rb
@@ -2,6 +2,26 @@ require 'rubygems/test_case'
require 'rubygems'
class TestGemRequire < Gem::TestCase
+ class Latch
+ def initialize count = 1
+ @count = count
+ @lock = Monitor.new
+ @cv = @lock.new_cond
+ end
+
+ def release
+ @lock.synchronize do
+ @count -= 1 if @count > 0
+ @cv.broadcast if @count.zero?
+ end
+ end
+
+ def await
+ @lock.synchronize do
+ @cv.wait_while { @count > 0 }
+ end
+ end
+ end
def setup
super
@@ -17,6 +37,46 @@ class TestGemRequire < Gem::TestCase
assert require(path), "'#{path}' was already required"
end
+ def append_latch spec
+ dir = spec.gem_dir
+ Dir.chdir dir do
+ spec.files.each do |file|
+ File.open file, 'a' do |fp|
+ fp.puts "FILE_ENTERED_LATCH.release"
+ fp.puts "FILE_EXIT_LATCH.await"
+ end
+ end
+ end
+ end
+
+ def test_concurrent_require
+ Object.const_set :FILE_ENTERED_LATCH, Latch.new(2)
+ Object.const_set :FILE_EXIT_LATCH, Latch.new(1)
+
+ a1 = new_spec "a", "1", nil, "lib/a.rb"
+ b1 = new_spec "b", "1", nil, "lib/b.rb"
+
+ install_specs a1, b1
+
+ append_latch a1
+ append_latch b1
+
+ t1 = Thread.new { assert_require 'a' }
+ t2 = Thread.new { assert_require 'b' }
+
+ # wait until both files are waiting on the exit latch
+ FILE_ENTERED_LATCH.await
+
+ # now let them finish
+ FILE_EXIT_LATCH.release
+
+ assert t1.join, "thread 1 should exit"
+ assert t2.join, "thread 2 should exit"
+ ensure
+ Object.send :remove_const, :FILE_ENTERED_LATCH
+ Object.send :remove_const, :FILE_EXIT_LATCH
+ end
+
def test_require_is_not_lazy_with_exact_req
a1 = new_spec "a", "1", {"b" => "= 1"}, "lib/test_gem_require_a.rb"
b1 = new_spec "b", "1", nil, "lib/b/c.rb"