* 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
This commit is contained in:
hsbt 2014-09-14 03:30:02 +00:00
Родитель e548c09d42
Коммит 4de117a615
153 изменённых файлов: 5400 добавлений и 981 удалений

Просмотреть файл

@ -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> Sun Sep 14 11:03:24 2014 Aaron Patterson <aaron@tenderlovemaking.com>
* ext/psych/lib/psych.rb: update version * ext/psych/lib/psych.rb: update version

Просмотреть файл

@ -6,9 +6,10 @@
#++ #++
require 'rbconfig' require 'rbconfig'
require 'thread'
module Gem module Gem
VERSION = '2.2.2' VERSION = '2.4.1'
end end
# Must be first since it unloads the prelude from 1.9.2 # 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 defaults are stored in rubygems/defaults.rb. If you're packaging
# RubyGems or implementing Ruby you can change RubyGems' defaults. # RubyGems or implementing Ruby you can change RubyGems' defaults.
# #
# For RubyGems packagers, provide lib/rubygems/operating_system.rb and # For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb
# override any defaults from lib/rubygems/defaults.rb. # and override any defaults from lib/rubygems/defaults.rb.
# #
# For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and # For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and
# override any defaults from lib/rubygems/defaults.rb. # override any defaults from lib/rubygems/defaults.rb.
@ -83,7 +84,7 @@ require 'rubygems/errors'
# * Chad Fowler -- chad(at)chadfowler.com # * Chad Fowler -- chad(at)chadfowler.com
# * David Black -- dblack(at)wobblini.net # * David Black -- dblack(at)wobblini.net
# * Paul Brannan -- paul(at)atdesk.com # * Paul Brannan -- paul(at)atdesk.com
# * Jim Weirich -- jim(at)weirichhouse.org # * Jim Weirich -- jim(at)weirichhouse.org
# #
# Contributors: # Contributors:
# #
@ -156,6 +157,7 @@ module Gem
@configuration = nil @configuration = nil
@loaded_specs = {} @loaded_specs = {}
LOADED_SPECS_MUTEX = Mutex.new
@path_to_default_spec_map = {} @path_to_default_spec_map = {}
@platforms = [] @platforms = []
@ruby = nil @ruby = nil
@ -298,7 +300,7 @@ module Gem
end 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. # package is not available as a gem, return nil.
def self.datadir(gem_name) def self.datadir(gem_name)
@ -544,9 +546,9 @@ module Gem
# Fetching: minitest-3.0.1.gem (100%) # Fetching: minitest-3.0.1.gem (100%)
# => [#<Gem::Specification:0x1013b4528 @name="minitest", ...>] # => [#<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" require "rubygems/dependency_installer"
inst = Gem::DependencyInstaller.new(**options) inst = Gem::DependencyInstaller.new(*options)
inst.install name, version inst.install name, version
inst.installed_gems inst.installed_gems
end end
@ -995,19 +997,31 @@ module Gem
end end
## ##
# Looks for gem dependency files (gem.deps.rb, Gemfile, Isolate) from the # Looks for a gem dependency file at +path+ and activates the gems in the
# current directory up and activates the gems in the first file found. # 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 # You can run this automatically when rubygems starts. To enable, set
# the <code>RUBYGEMS_GEMDEPS</code> environment variable to either the path # 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 # NOTE: Enabling automatic discovery on multiuser systems can lead to
# execution of arbitrary code when used from directories outside your # execution of arbitrary code when used from directories outside your
# control. # control.
def self.use_gemdeps def self.use_gemdeps path = nil
return unless path = ENV['RUBYGEMS_GEMDEPS'] raise_exception = path
path ||= ENV['RUBYGEMS_GEMDEPS']
return unless path
path = path.dup path = path.dup
if path == "-" then if path == "-" then
@ -1025,7 +1039,11 @@ module Gem
path.untaint 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 = Gem::RequestSet.new
rs.load_gemdeps path rs.load_gemdeps path
@ -1035,6 +1053,10 @@ module Gem
sp.activate sp.activate
sp sp
end end
rescue Gem::LoadError, Gem::UnsatisfiableDependencyError => e
warn e.message
warn "You may need to `gem install -g` to install missing gems"
warn ""
end end
class << self class << self

Просмотреть файл

@ -126,7 +126,7 @@ class Gem::AvailableSet
dep = req.dependency dep = req.dependency
match = @set.find_all do |t| match = @set.find_all do |t|
dep.matches_spec? t.spec dep.match? t.spec
end end
match.map do |t| match.map do |t|

Просмотреть файл

@ -14,6 +14,11 @@ class Gem::BasicSpecification
attr_writer :extension_dir # :nodoc: 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. # The path this gemspec was loaded from. This attribute is not persisted.
@ -53,7 +58,16 @@ class Gem::BasicSpecification
# Return true if this spec can require +file+. # Return true if this spec can require +file+.
def contains_requirable_file? 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 suffixes = Gem.suffixes
@ -120,11 +134,11 @@ class Gem::BasicSpecification
# activated. # activated.
def full_require_paths 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 File.join full_gem_path, path
end end
full_paths.unshift extension_dir unless @extensions.empty? full_paths.unshift extension_dir unless @extensions.nil? || @extensions.empty?
full_paths full_paths
end end
@ -176,7 +190,7 @@ class Gem::BasicSpecification
end end
def raw_require_paths # :nodoc: def raw_require_paths # :nodoc:
@require_paths Array(@require_paths)
end end
## ##
@ -197,13 +211,9 @@ class Gem::BasicSpecification
# spec.require_path = '.' # spec.require_path = '.'
def require_paths def require_paths
return @require_paths if @extensions.empty? return raw_require_paths if @extensions.nil? || @extensions.empty?
relative_extension_dir = [extension_dir].concat raw_require_paths
File.join '..', '..', 'extensions', Gem::Platform.local.to_s,
Gem.extension_api_version, full_name
[relative_extension_dir].concat @require_paths
end end
## ##

Просмотреть файл

@ -148,6 +148,8 @@ class Gem::Command
## ##
# Display to the user that a gem couldn't be found and reasons why # 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) def show_lookup_failure(gem_name, version, errors, domain)
if errors and !errors.empty? if errors and !errors.empty?
@ -557,7 +559,8 @@ basic help message containing pointers to more information.
Further help: Further help:
gem help commands list all 'gem' commands gem help commands list all 'gem' commands
gem help examples show some examples of usage 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 gem help <COMMAND> show help on COMMAND
(e.g. 'gem help install') (e.g. 'gem help install')
gem server present a web page at gem server present a web page at

Просмотреть файл

@ -48,6 +48,7 @@ class Gem::CommandManager
:list, :list,
:lock, :lock,
:mirror, :mirror,
:open,
:outdated, :outdated,
:owner, :owner,
:pristine, :pristine,

Просмотреть файл

@ -129,23 +129,21 @@ class Gem::Commands::CertCommand < Gem::Command
end end
def build_key # :nodoc: def build_key # :nodoc:
if options[:key] then return options[:key] if options[:key]
options[:key]
else
passphrase = ask_for_password 'Passphrase for your Private Key:'
say "\n"
passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:' passphrase = ask_for_password 'Passphrase for your Private Key:'
say "\n" say "\n"
raise Gem::CommandLineError, passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:'
"Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation say "\n"
key = Gem::Security.create_key raise Gem::CommandLineError,
key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase "Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation
return key, key_path key = Gem::Security.create_key
end key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase
return key, key_path
end end
def certificates_matching filter def certificates_matching filter

Просмотреть файл

@ -67,10 +67,10 @@ If no gems are named all gems in GEM_HOME are cleaned.
say "Clean Up Complete" say "Clean Up Complete"
if Gem.configuration.really_verbose then verbose do
skipped = @default_gems.map { |spec| spec.full_name } skipped = @default_gems.map { |spec| spec.full_name }
say "Skipped default gems: #{skipped.join ', '}" "Skipped default gems: #{skipped.join ', '}"
end end
end end

Просмотреть файл

@ -8,7 +8,8 @@ class Gem::Commands::ContentsCommand < Gem::Command
def initialize def initialize
super 'contents', 'Display the contents of the installed gems', 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 add_version_option
@ -32,6 +33,11 @@ class Gem::Commands::ContentsCommand < Gem::Command
options[:prefix] = prefix options[:prefix] = prefix
end 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 @path_kind = nil
@spec_dirs = nil @spec_dirs = nil
@version = nil @version = nil
@ -65,7 +71,12 @@ prefix or only the files that are requireable.
names = gem_names names = gem_names
names.each do |name| 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 terminate_interaction 1 unless found or names.length > 1
end end
@ -115,6 +126,16 @@ prefix or only the files that are requireable.
true true
end end
def gem_install_dir name
spec = spec_for name
return false unless spec
say spec.gem_dir
true
end
def gem_names # :nodoc: def gem_names # :nodoc:
if options[:all] then if options[:all] then
Gem::Specification.map(&:name) Gem::Specification.map(&:name)

Просмотреть файл

@ -31,7 +31,7 @@ class Gem::Commands::DependencyCommand < Gem::Command
end end
def arguments # :nodoc: def arguments # :nodoc:
"GEMNAME name of gem to show dependencies for" "REGEXP show dependencies for gems whose names start with REGEXP"
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
@ -50,7 +50,7 @@ use with other commands.
end end
def usage # :nodoc: def usage # :nodoc:
"#{program_name} GEMNAME" "#{program_name} REGEXP"
end end
def fetch_remote_specs dependency # :nodoc: def fetch_remote_specs dependency # :nodoc:

Просмотреть файл

@ -28,8 +28,9 @@ The RubyGems environment can be controlled through command line arguments,
gemrc files, environment variables and built-in defaults. gemrc files, environment variables and built-in defaults.
Command line argument defaults and some RubyGems defaults can be set in a 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 ~/.gemrc file for individual users and a gemrc in the SYSTEM CONFIGURATION
files are YAML files with the following YAML keys: 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 :sources: A YAML array of remote gem repositories to install gems from
:verbose: Verbosity of the gem command. false, true, and :really are the :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 << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n"
out << " - SYSTEM CONFIGURATION DIRECTORY: #{Gem::ConfigFile::SYSTEM_CONFIG_PATH}\n"
out << " - RUBYGEMS PLATFORMS:\n" out << " - RUBYGEMS PLATFORMS:\n"
Gem.platforms.each do |platform| Gem.platforms.each do |platform|
out << " - #{platform}\n" out << " - #{platform}\n"

Просмотреть файл

@ -52,6 +52,183 @@ Some examples of 'gem' usage.
gem update --system gem update --system
EOF 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' PLATFORMS = <<-'EOF'
RubyGems platforms are composed of three parts, a CPU, an OS, and a 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 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 Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's
platform. platform.
EOF 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: # :startdoc:
def initialize def initialize
@ -98,15 +285,6 @@ platform.
@command_manager = Gem::CommandManager.instance @command_manager = Gem::CommandManager.instance
end 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: def usage # :nodoc:
"#{program_name} ARGUMENT" "#{program_name} ARGUMENT"
end end
@ -114,19 +292,20 @@ platform.
def execute def execute
arg = options[:args][0] arg = options[:args][0]
if begins? "commands", arg then _, help = SUBCOMMANDS.find do |command,|
show_commands begins? command, arg
end
elsif begins? "options", arg then if help then
say Gem::Command::HELP if Symbol === help then
send help
else
say help
end
return
end
elsif begins? "examples", arg then if options[:help] then
say EXAMPLES
elsif begins? "platforms", arg then
say PLATFORMS
elsif options[:help] then
show_help show_help
elsif arg then elsif arg then

Просмотреть файл

@ -21,6 +21,8 @@ class Gem::Commands::InstallCommand < Gem::Command
def initialize def initialize
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:format_executable => false, :format_executable => false,
:lock => true,
:suggest_alternate => true,
:version => Gem::Requirement.default, :version => Gem::Requirement.default,
:without_groups => [], :without_groups => [],
}) })
@ -69,6 +71,16 @@ class Gem::Commands::InstallCommand < Gem::Command
o[:explain] = v o[:explain] = v
end 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 = [] @installed_specs = []
end end
@ -78,7 +90,7 @@ class Gem::Commands::InstallCommand < Gem::Command
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--both --version '#{Gem::Requirement.default}' --document --no-force\n" + "--both --version '#{Gem::Requirement.default}' --document --no-force\n" +
"--install-dir #{Gem.dir}" "--install-dir #{Gem.dir} --lock"
end end
def description # :nodoc: 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 For example `rake _0.7.3_ --version` will run rake version 0.7.3 if a newer
version is also installed. 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 If an extension fails to compile during gem installation the gem
specification is not written out, but the gem remains unpacked in the 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 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 install_gem_without_dependencies name, req
else else
inst = Gem::DependencyInstaller.new options inst = Gem::DependencyInstaller.new options
request_set = inst.resolve_dependencies name, req
if options[:explain] if options[:explain]
request_set = inst.resolve_dependencies name, req
puts "Gems to install:" puts "Gems to install:"
request_set.specs.map { |s| s.full_name }.sort.each do |s| request_set.sorted_requests.each do |s|
puts " #{s}" puts " #{s.full_name}"
end end
return return
else else
inst.install name, req @installed_specs.concat request_set.install options
end end
@installed_specs.push(*inst.installed_gems)
show_install_errors inst.errors show_install_errors inst.errors
end end
end end
@ -250,6 +278,14 @@ to write the specification by hand. For example:
inst = Gem::Installer.new gem, options inst = Gem::Installer.new gem, options
inst.install 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) @installed_specs.push(inst.spec)
end end
@ -264,8 +300,10 @@ to write the specification by hand. For example:
rescue Gem::InstallError => e rescue Gem::InstallError => e
alert_error "Error installing #{gem_name}:\n\t#{e.message}" alert_error "Error installing #{gem_name}:\n\t#{e.message}"
exit_code |= 1 exit_code |= 1
rescue Gem::GemNotFoundException => e rescue Gem::GemNotFoundException, Gem::UnsatisfiableDependencyError => e
show_lookup_failure e.name, e.version, e.errors, options[:domain] domain = options[:domain]
domain = :local unless options[:suggest_alternate]
show_lookup_failure e.name, e.version, e.errors, domain
exit_code |= 2 exit_code |= 2
end end

Просмотреть файл

@ -8,13 +8,13 @@ require 'rubygems/commands/query_command'
class Gem::Commands::ListCommand < Gem::Commands::QueryCommand class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
def initialize 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') remove_option('--name-matches')
end end
def arguments # :nodoc: def arguments # :nodoc:
"STRING start of gem name to look for" "REGEXP regexp to look for in gem name"
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:

Просмотреть файл

@ -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

Просмотреть файл

@ -86,7 +86,9 @@ permission to.
request.add_field "Authorization", api_key request.add_field "Authorization", api_key
end end
with_response response, "Removing #{owner}" action = method == :delete ? "Removing" : "Adding"
with_response response, "#{action} #{owner}"
rescue rescue
# ignore # ignore
end end

Просмотреть файл

@ -4,7 +4,7 @@ require 'rubygems/commands/query_command'
class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
def initialize def initialize
super 'search', 'Display remote gems whose name contains STRING' super 'search', 'Display remote gems whose name matches REGEXP'
remove_option '--name-matches' remove_option '--name-matches'
@ -12,7 +12,7 @@ class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
end end
def arguments # :nodoc: def arguments # :nodoc:
"STRING fragment of gem name to search for" "REGEXP regexp to search for in gem name"
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
@ -21,8 +21,8 @@ class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
def description # :nodoc: def description # :nodoc:
<<-EOF <<-EOF
The search command displays remote gems whose name contains the given The search command displays remote gems whose name matches the given
string. regexp.
The --details option displays additional details from the gem but will The --details option displays additional details from the gem but will
take a little longer to complete as it must download the information 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 end
def usage # :nodoc: def usage # :nodoc:
"#{program_name} [STRING]" "#{program_name} [REGEXP]"
end end
end end

Просмотреть файл

@ -446,7 +446,7 @@ abort "#{deprecation_message}"
history.force_encoding Encoding::UTF_8 if history.force_encoding Encoding::UTF_8 if
Object.const_defined? :Encoding Object.const_defined? :Encoding
history = history.sub(/^# coding:.*?^=/m, '') history = history.sub(/^# coding:.*?(?=^=)/m, '')
text = history.split(HISTORY_HEADER) text = history.split(HISTORY_HEADER)
text.shift # correct an off-by-one generated by split text.shift # correct an off-by-one generated by split

Просмотреть файл

@ -15,7 +15,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
def initialize def initialize
super 'uninstall', 'Uninstall gems from the local repository', super 'uninstall', 'Uninstall gems from the local repository',
:version => Gem::Requirement.default, :user_install => true, :version => Gem::Requirement.default, :user_install => true,
:check_dev => false :check_dev => false, :vendor => false
add_option('-a', '--[no-]all', add_option('-a', '--[no-]all',
'Uninstall all matching versions' 'Uninstall all matching versions'
@ -76,6 +76,18 @@ class Gem::Commands::UninstallCommand < Gem::Command
add_version_option add_version_option
add_platform_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 end
def arguments # :nodoc: def arguments # :nodoc:

Просмотреть файл

@ -16,6 +16,8 @@ class Gem::Commands::UpdateCommand < Gem::Command
attr_reader :installer # :nodoc: attr_reader :installer # :nodoc:
attr_reader :updated # :nodoc:
def initialize def initialize
super 'update', 'Update installed gems to the latest version', super 'update', 'Update installed gems to the latest version',
:document => %w[rdoc ri], :document => %w[rdoc ri],
@ -45,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
end end
def arguments # :nodoc: def arguments # :nodoc:
"GEMNAME name of gem to update" "REGEXP regexp to search for in gem name"
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
@ -56,13 +58,13 @@ class Gem::Commands::UpdateCommand < Gem::Command
<<-EOF <<-EOF
The update command will update your gems to the latest version. 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. command to remove old versions.
EOF EOF
end end
def usage # :nodoc: def usage # :nodoc:
"#{program_name} GEMNAME [GEMNAME ...]" "#{program_name} REGEXP [REGEXP ...]"
end end
def check_latest_rubygems version # :nodoc: def check_latest_rubygems version # :nodoc:
@ -97,10 +99,14 @@ command to remove old versions.
updated = update_gems gems_to_update 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 if updated.empty? then
say "Nothing to update" say "Nothing to update"
else 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
end end
@ -199,15 +205,11 @@ command to remove old versions.
@installer ||= Gem::DependencyInstaller.new options @installer ||= Gem::DependencyInstaller.new options
success = false
say "Updating #{name}" say "Updating #{name}"
begin begin
@installer.install name, Gem::Requirement.new(version) @installer.install name, Gem::Requirement.new(version)
success = true rescue Gem::InstallError, Gem::DependencyError => e
rescue Gem::InstallError => e
alert_error "Error installing #{name}:\n\t#{e.message}" alert_error "Error installing #{name}:\n\t#{e.message}"
success = false
end end
@installer.installed_gems.each do |spec| @installer.installed_gems.each do |spec|
@ -259,7 +261,7 @@ command to remove old versions.
highest_installed_gems.each do |l_name, l_spec| highest_installed_gems.each do |l_name, l_spec|
next if not gem_names.empty? and 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 highest_remote_ver = highest_remote_version l_spec
@ -272,4 +274,3 @@ command to remove old versions.
end end
end end

Просмотреть файл

@ -44,10 +44,7 @@ as the reason for the removal request.
options[:undo] = true options[:undo] = true
end end
add_option('-k', '--key KEY_NAME', add_key_option
'Use API key from your gem credentials file') do |value, options|
options[:key] = value
end
end end
def execute def execute
@ -55,14 +52,12 @@ as the reason for the removal request.
version = get_version_from_requirements(options[:version]) version = get_version_from_requirements(options[:version])
platform = get_platform_from_requirements(options) 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 version then
if options[:undo] then if options[:undo] then
unyank_gem(version, platform, api_key) unyank_gem(version, platform)
else else
yank_gem(version, platform, api_key) yank_gem(version, platform)
end end
else else
say "A version argument is required: #{usage}" say "A version argument is required: #{usage}"
@ -70,19 +65,19 @@ as the reason for the removal request.
end end
end end
def yank_gem(version, platform, api_key) def yank_gem(version, platform)
say "Yanking gem from #{self.host}..." 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 end
def unyank_gem(version, platform, api_key) def unyank_gem(version, platform)
say "Unyanking gem from #{host}..." 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 end
private private
def yank_api_request(method, version, platform, api, api_key) def yank_api_request(method, version, platform, api)
name = get_one_gem_name name = get_one_gem_name
response = rubygems_api_request(method, api) do |request| response = rubygems_api_request(method, api) do |request|
request.add_field("Authorization", api_key) request.add_field("Authorization", api_key)

Просмотреть файл

@ -57,7 +57,7 @@ class Gem::ConfigFile
# :stopdoc: # :stopdoc:
system_config_path = SYSTEM_CONFIG_PATH =
begin begin
require "etc" require "etc"
Etc.sysconfdir Etc.sysconfdir
@ -86,7 +86,7 @@ class Gem::ConfigFile
# :startdoc: # :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. # 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 @backtrace = true
when /^--debug$/ then when /^--debug$/ then
$DEBUG = true $DEBUG = true
warn 'NOTE: Debugging mode prints all exceptions even when rescued'
else else
@args << arg @args << arg
end end
@ -428,6 +430,15 @@ if you believe they were disclosed to a third party.
DEFAULT_VERBOSITY DEFAULT_VERBOSITY
end 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 = yaml_hash.keys.map { |key| key.to_s }
keys << 'debug' keys << 'debug'
re = Regexp.union(*keys) re = Regexp.union(*keys)

Просмотреть файл

@ -26,6 +26,11 @@ module Kernel
# Kernel#gem should be called *before* any require statements (otherwise # Kernel#gem should be called *before* any require statements (otherwise
# RubyGems may load a conflicting library version). # 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 # In older RubyGems versions, the environment variable GEM_SKIP could be
# used to skip activation of specified gems, for example to test out changes # 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 # that haven't been installed yet. Now RubyGems defers to -I and the
@ -51,7 +56,9 @@ module Kernel
end end
spec = Gem::Dependency.new(gem_name, *requirements).to_spec spec = Gem::Dependency.new(gem_name, *requirements).to_spec
spec.activate if spec Gem::LOADED_SPECS_MUTEX.synchronize {
spec.activate
} if spec
end end
private :gem private :gem

Просмотреть файл

@ -50,12 +50,8 @@ module Kernel
# normal require handle loading a gem from the rescue below. # normal require handle loading a gem from the rescue below.
if Gem::Specification.unresolved_deps.empty? then if Gem::Specification.unresolved_deps.empty? then
begin RUBYGEMS_ACTIVATION_MONITOR.exit
RUBYGEMS_ACTIVATION_MONITOR.exit return gem_original_require(path)
return gem_original_require(path)
ensure
RUBYGEMS_ACTIVATION_MONITOR.enter
end
end end
# If +path+ is for a gem that has already been loaded, don't # If +path+ is for a gem that has already been loaded, don't
@ -71,8 +67,6 @@ module Kernel
begin begin
RUBYGEMS_ACTIVATION_MONITOR.exit RUBYGEMS_ACTIVATION_MONITOR.exit
return gem_original_require(path) return gem_original_require(path)
ensure
RUBYGEMS_ACTIVATION_MONITOR.enter
end if spec end if spec
# Attempt to find +path+ in any unresolved gems... # Attempt to find +path+ in any unresolved gems...
@ -105,6 +99,7 @@ module Kernel
names = found_specs.map(&:name).uniq names = found_specs.map(&:name).uniq
if names.size > 1 then if names.size > 1 then
RUBYGEMS_ACTIVATION_MONITOR.exit
raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}" raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
end end
@ -115,32 +110,27 @@ module Kernel
unless valid then unless valid then
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate" le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
le.name = names.first le.name = names.first
RUBYGEMS_ACTIVATION_MONITOR.exit
raise le raise le
end end
valid.activate valid.activate
end end
begin RUBYGEMS_ACTIVATION_MONITOR.exit
RUBYGEMS_ACTIVATION_MONITOR.exit return gem_original_require(path)
return gem_original_require(path)
ensure
RUBYGEMS_ACTIVATION_MONITOR.enter
end
rescue LoadError => load_error rescue LoadError => load_error
RUBYGEMS_ACTIVATION_MONITOR.enter
if load_error.message.start_with?("Could not find") or if load_error.message.start_with?("Could not find") or
(load_error.message.end_with?(path) and Gem.try_activate(path)) then (load_error.message.end_with?(path) and Gem.try_activate(path)) then
begin RUBYGEMS_ACTIVATION_MONITOR.exit
RUBYGEMS_ACTIVATION_MONITOR.exit return gem_original_require(path)
return gem_original_require(path) else
ensure RUBYGEMS_ACTIVATION_MONITOR.exit
RUBYGEMS_ACTIVATION_MONITOR.enter
end
end end
raise load_error raise load_error
ensure
RUBYGEMS_ACTIVATION_MONITOR.exit
end end
private :require private :require

Просмотреть файл

@ -89,11 +89,11 @@ module Gem
# Default gem load path # Default gem load path
def self.default_path def self.default_path
if Gem.user_home && File.exist?(Gem.user_home) then path = []
[user_dir, default_dir] path << user_dir if user_home && File.exist?(user_home)
else path << default_dir
[default_dir] path << vendor_dir if vendor_dir and File.directory? vendor_dir
end path
end end
## ##
@ -160,4 +160,18 @@ module Gem
true true
end 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 end

Просмотреть файл

@ -74,7 +74,7 @@ class Gem::Dependency
end end
def inspect # :nodoc: def inspect # :nodoc:
if @prerelease if prerelease? then
"<%s type=%p name=%p requirements=%p prerelease=ok>" % "<%s type=%p name=%p requirements=%p prerelease=ok>" %
[self.class, self.type, self.name, requirement.to_s] [self.class, self.type, self.name, requirement.to_s]
else else
@ -145,7 +145,6 @@ class Gem::Dependency
@requirement = @version_requirements if defined?(@version_requirements) @requirement = @version_requirements if defined?(@version_requirements)
end end
# DOC: this method needs documentation or :nodoc''d
def requirements_list def requirements_list
requirement.as_list requirement.as_list
end end
@ -205,9 +204,19 @@ class Gem::Dependency
alias === =~ 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 if !version
name = obj.name name = obj.name
version = obj.version version = obj.version
@ -216,12 +225,23 @@ class Gem::Dependency
end end
return false unless self.name === name 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 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 def matches_spec? spec
return false unless name === spec.name 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) self.class.new name, self_req.as_list.concat(other_req.as_list)
end end
# DOC: this method needs either documented or :nodoc'd
def matching_specs platform_only = false def matching_specs platform_only = false
matches = Gem::Specification.stubs.find_all { |spec| matches = Gem::Specification.stubs.find_all { |spec|
self.name === spec.name and # TODO: == instead of === self.name === spec.name and # TODO: == instead of ===
@ -273,8 +291,6 @@ class Gem::Dependency
@requirement.specific? @requirement.specific?
end end
# DOC: this method needs either documented or :nodoc'd
def to_specs def to_specs
matches = matching_specs true matches = matching_specs true
@ -287,12 +303,13 @@ class Gem::Dependency
if specs.empty? if specs.empty?
total = Gem::Specification.to_a.size total = Gem::Specification.to_a.size
error = Gem::LoadError.new \ msg = "Could not find '#{name}' (#{requirement}) among #{total} total gem(s)\n"
"Could not find '#{name}' (#{requirement}) among #{total} total gem(s)"
else else
error = Gem::LoadError.new \ msg = "Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]\n"
"Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]"
end 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.name = self.name
error.requirement = self.requirement error.requirement = self.requirement
raise error raise error
@ -303,11 +320,15 @@ class Gem::Dependency
matches matches
end end
# DOC: this method needs either documented or :nodoc'd
def to_spec def to_spec
matches = self.to_specs 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
end end

Просмотреть файл

@ -72,6 +72,7 @@ class Gem::DependencyInstaller
def initialize options = {} def initialize options = {}
@only_install_dir = !!options[:install_dir] @only_install_dir = !!options[:install_dir]
@install_dir = options[:install_dir] || Gem.dir @install_dir = options[:install_dir] || Gem.dir
@build_root = options[:build_root]
options = DEFAULT_OPTIONS.merge options options = DEFAULT_OPTIONS.merge options
@ -102,7 +103,7 @@ class Gem::DependencyInstaller
@cache_dir = options[:cache_dir] || @install_dir @cache_dir = options[:cache_dir] || @install_dir
@errors = nil @errors = []
end end
## ##
@ -157,6 +158,7 @@ class Gem::DependencyInstaller
dependency_list.remove_specs_unsatisfied_by dependencies dependency_list.remove_specs_unsatisfied_by dependencies
end end
## ##
# Creates an AvailableSet to install from based on +dep_or_name+ and # Creates an AvailableSet to install from based on +dep_or_name+ and
# +version+ # +version+
@ -243,9 +245,9 @@ class Gem::DependencyInstaller
# FIX if there is a problem talking to the network, we either need to always tell # 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 # the user (no really_verbose) or fail hard, not silently tell them that we just
# couldn't find their requested gem. # couldn't find their requested gem.
if Gem.configuration.really_verbose then verbose do
say "Error fetching remote data:\t\t#{e.message}" "Error fetching remote data:\t\t#{e.message}\n" \
say "Falling back to local-only install" "Falling back to local-only install"
end end
@domain = :local @domain = :local
end end
@ -375,13 +377,16 @@ class Gem::DependencyInstaller
options = { options = {
:bin_dir => @bin_dir, :bin_dir => @bin_dir,
:build_args => @build_args, :build_args => @build_args,
:document => @document,
:env_shebang => @env_shebang, :env_shebang => @env_shebang,
:force => @force, :force => @force,
:format_executable => @format_executable, :format_executable => @format_executable,
:ignore_dependencies => @ignore_dependencies, :ignore_dependencies => @ignore_dependencies,
:prerelease => @prerelease,
:security_policy => @security_policy, :security_policy => @security_policy,
:user_install => @user_install, :user_install => @user_install,
:wrappers => @wrappers, :wrappers => @wrappers,
:build_root => @build_root,
:install_as_default => @install_as_default :install_as_default => @install_as_default
} }
options[:install_dir] = @install_dir if @only_install_dir options[:install_dir] = @install_dir if @only_install_dir
@ -415,25 +420,59 @@ class Gem::DependencyInstaller
end end
def resolve_dependencies dep_or_name, version # :nodoc: def resolve_dependencies dep_or_name, version # :nodoc:
as = available_set_for dep_or_name, version request_set = Gem::RequestSet.new
request_set.development = @development
request_set = as.to_request_set install_development_deps request_set.development_shallow = @dev_shallow
request_set.soft_missing = @force request_set.soft_missing = @force
request_set.prerelease = @prerelease
request_set.remote = false unless consider_remote? request_set.remote = false unless consider_remote?
installer_set = Gem::Resolver::InstallerSet.new @domain installer_set = Gem::Resolver::InstallerSet.new @domain
installer_set.always_install.concat request_set.always_install
installer_set.ignore_installed = @only_install_dir 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 if @ignore_dependencies then
installer_set.ignore_dependencies = true installer_set.ignore_dependencies = true
request_set.ignore_dependencies = true request_set.ignore_dependencies = true
request_set.soft_missing = true request_set.soft_missing = true
end 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 request_set
end end

Просмотреть файл

@ -105,7 +105,7 @@ class Gem::Doctor
next if ent == "." || ent == ".." next if ent == "." || ent == ".."
child = File.join(directory, ent) child = File.join(directory, ent)
next unless File.exists?(child) next unless File.exist?(child)
basename = File.basename(child, extension) basename = File.basename(child, extension)
next if installed_specs.include? basename next if installed_specs.include? basename

Просмотреть файл

@ -19,6 +19,36 @@ module Gem
attr_accessor :requirement attr_accessor :requirement
end 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 class ErrorReason; end
# Generated when trying to lookup a gem to indicate that the gem # Generated when trying to lookup a gem to indicate that the gem

Просмотреть файл

@ -27,7 +27,7 @@ class Gem::DependencyRemovalException < Gem::Exception; end
# toplevel. Indicates which dependencies were incompatible through #conflict # toplevel. Indicates which dependencies were incompatible through #conflict
# and #conflicting_dependencies # and #conflicting_dependencies
class Gem::DependencyResolutionError < Gem::Exception class Gem::DependencyResolutionError < Gem::DependencyError
attr_reader :conflict attr_reader :conflict
@ -214,7 +214,7 @@ end
# Raised by Resolver when a dependency requests a gem for which # Raised by Resolver when a dependency requests a gem for which
# there is no spec. # there is no spec.
class Gem::UnsatisfiableDependencyError < Gem::Exception class Gem::UnsatisfiableDependencyError < Gem::DependencyError
## ##
# The unsatisfiable dependency. This is a # The unsatisfiable dependency. This is a
@ -222,6 +222,11 @@ class Gem::UnsatisfiableDependencyError < Gem::Exception
attr_reader :dependency attr_reader :dependency
##
# Errors encountered which may have contributed to this exception
attr_accessor :errors
## ##
# Creates a new UnsatisfiableDependencyError for the unsatisfiable # Creates a new UnsatisfiableDependencyError for the unsatisfiable
# Gem::Resolver::DependencyRequest +dep+ # Gem::Resolver::DependencyRequest +dep+
@ -239,6 +244,21 @@ class Gem::UnsatisfiableDependencyError < Gem::Exception
end end
@dependency = dep @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
end end

Просмотреть файл

@ -161,7 +161,7 @@ EOF
results = builder.build(extension, @gem_dir, dest_path, results = builder.build(extension, @gem_dir, dest_path,
results, @build_args, lib_dir) results, @build_args, lib_dir)
say results.join("\n") if Gem.configuration.really_verbose verbose { results.join("\n") }
end end
end end

Просмотреть файл

@ -11,13 +11,15 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileEntry = FileUtils::Entry_ # :nodoc: FileEntry = FileUtils::Entry_ # :nodoc:
def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil) 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 t = nil
Tempfile.open %w"siteconf .rb", "." do |siteconf| Tempfile.open %w"siteconf .rb", "." do |siteconf|
t = siteconf t = siteconf
siteconf.puts "require 'rbconfig'" 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| %w[sitearchdir sitelibdir].each do |dir|
siteconf.puts "RbConfig::MAKEFILE_CONFIG['#{dir}'] = dest_path" siteconf.puts "RbConfig::MAKEFILE_CONFIG['#{dir}'] = dest_path"
siteconf.puts "RbConfig::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.flush
siteconf_path = File.expand_path siteconf.path
rubyopt = ENV["RUBYOPT"]
destdir = ENV["DESTDIR"] destdir = ENV["DESTDIR"]
begin begin
ENV["RUBYOPT"] = ["-r#{siteconf_path}", rubyopt].compact.join(' ') cmd = [Gem.ruby, "-r", get_relative_path(siteconf.path), File.basename(extension), *args].join ' '
cmd = [Gem.ruby, File.basename(extension), *args].join ' '
begin begin
run cmd, results run cmd, results
@ -42,7 +40,6 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
end end
ENV["DESTDIR"] = nil ENV["DESTDIR"] = nil
ENV["RUBYOPT"] = rubyopt
make dest_path, results make dest_path, results
@ -57,11 +54,10 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileEntry.new(tmp_dest).traverse do |ent| FileEntry.new(tmp_dest).traverse do |ent|
destent = ent.class.new(dest_path, ent.rel) 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
end end
ensure ensure
ENV["RUBYOPT"] = rubyopt
ENV["DESTDIR"] = destdir ENV["DESTDIR"] = destdir
end end
end end
@ -72,5 +68,11 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileUtils.rm_rf tmp_dest if tmp_dest FileUtils.rm_rf tmp_dest if tmp_dest
end end
private
def self.get_relative_path(path)
path[0..Dir.pwd.length-1] = '.' if path.start_with?(Dir.pwd)
path
end
end end

Просмотреть файл

@ -86,7 +86,7 @@ module Gem::GemcutterUtilities
def sign_in sign_in_host = nil def sign_in sign_in_host = nil
sign_in_host ||= self.host 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 pretty_host = if Gem::DEFAULT_HOST == sign_in_host then
'RubyGems.org' 'RubyGems.org'

Просмотреть файл

@ -59,6 +59,23 @@ module Gem::InstallUpdateOptions
end end
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', add_option(:"Install/Update", '-N', '--no-document',
'Disable documentation generation') do |value, options| 'Disable documentation generation') do |value, options|
options[:document] = [] options[:document] = []

Просмотреть файл

@ -39,7 +39,9 @@ class Gem::Installer
include Gem::UserInteraction include Gem::UserInteraction
# DOC: Missing docs or :nodoc:. ##
# Filename of the gem being installed.
attr_reader :gem attr_reader :gem
## ##
@ -47,6 +49,8 @@ class Gem::Installer
attr_reader :bin_dir attr_reader :bin_dir
attr_reader :build_root # :nodoc:
## ##
# The gem repository the gem will be installed into # The gem repository the gem will be installed into
@ -64,6 +68,8 @@ class Gem::Installer
@path_warning = false @path_warning = false
@install_lock = Mutex.new
class << self class << self
## ##
@ -71,7 +77,19 @@ class Gem::Installer
attr_accessor :path_warning 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 attr_writer :exec_format
# Defaults to use Ruby's program prefix and suffix. # 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? 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 run_post_install_hooks
@ -318,6 +336,7 @@ class Gem::Installer
# True if the gems in the system satisfy +dependency+. # True if the gems in the system satisfy +dependency+.
def installation_satisfies_dependency?(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 true if installed_specs.detect { |s| dependency.matches_spec? s }
return false if @only_install_dir return false if @only_install_dir
not dependency.matching_specs.empty? not dependency.matching_specs.empty?
@ -382,12 +401,11 @@ class Gem::Installer
file.puts windows_stub_script(bindir, filename) file.puts windows_stub_script(bindir, filename)
end end
say script_path if Gem.configuration.really_verbose verbose script_path
end end
end end
# DOC: Missing docs or :nodoc:. def generate_bin # :nodoc:
def generate_bin
return if spec.executables.nil? or spec.executables.empty? return if spec.executables.nil? or spec.executables.empty?
Dir.mkdir @bin_dir unless File.exist? @bin_dir Dir.mkdir @bin_dir unless File.exist? @bin_dir
@ -433,7 +451,7 @@ class Gem::Installer
file.print app_script_text(filename) file.print app_script_text(filename)
end end
say bin_script_path if Gem.configuration.really_verbose verbose bin_script_path
generate_windows_script filename, bindir generate_windows_script filename, bindir
end end
@ -536,8 +554,7 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:. def ensure_required_ruby_version_met # :nodoc:
def ensure_required_ruby_version_met
if rrv = spec.required_ruby_version then if rrv = spec.required_ruby_version then
unless rrv.satisfied_by? Gem.ruby_version then unless rrv.satisfied_by? Gem.ruby_version then
raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}." raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}."
@ -545,8 +562,7 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:. def ensure_required_rubygems_version_met # :nodoc:
def ensure_required_rubygems_version_met
if rrgv = spec.required_rubygems_version then if rrgv = spec.required_rubygems_version then
unless rrgv.satisfied_by? Gem.rubygems_version then unless rrgv.satisfied_by? Gem.rubygems_version then
raise Gem::InstallError, raise Gem::InstallError,
@ -556,8 +572,7 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:. def ensure_dependencies_met # :nodoc:
def ensure_dependencies_met
deps = spec.runtime_dependencies deps = spec.runtime_dependencies
deps |= spec.development_dependencies if @development deps |= spec.development_dependencies if @development
@ -566,8 +581,7 @@ class Gem::Installer
end end
end end
# DOC: Missing docs or :nodoc:. def process_options # :nodoc:
def process_options
@options = { @options = {
:bin_dir => nil, :bin_dir => nil,
:env_shebang => false, :env_shebang => false,
@ -590,12 +604,20 @@ class Gem::Installer
# (or use) a new bin dir under the gem_home. # (or use) a new bin dir under the gem_home.
@bin_dir = options[:bin_dir] || Gem.bindir(gem_home) @bin_dir = options[:bin_dir] || Gem.bindir(gem_home)
@development = options[:development] @development = options[:development]
@build_root = options[:build_root]
@build_args = options[:build_args] || Gem::Command.build_args @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 end
# DOC: Missing docs or :nodoc:. def check_that_user_bin_dir_is_in_path # :nodoc:
def check_that_user_bin_dir_is_in_path
user_bin_dir = @bin_dir || Gem.bindir(gem_home) user_bin_dir = @bin_dir || Gem.bindir(gem_home)
user_bin_dir = user_bin_dir.gsub(File::SEPARATOR, File::ALT_SEPARATOR) if user_bin_dir = user_bin_dir.gsub(File::SEPARATOR, File::ALT_SEPARATOR) if
File::ALT_SEPARATOR File::ALT_SEPARATOR
@ -606,16 +628,19 @@ class Gem::Installer
user_bin_dir = user_bin_dir.downcase user_bin_dir = user_bin_dir.downcase
end end
unless path.split(File::PATH_SEPARATOR).include? user_bin_dir then path = path.split(File::PATH_SEPARATOR)
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." unless path.include? user_bin_dir then
self.class.path_warning = true 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 end
end end
# DOC: Missing docs or :nodoc:. def verify_gem_home(unpack = false) # :nodoc:
def verify_gem_home(unpack = false)
FileUtils.mkdir_p gem_home FileUtils.mkdir_p gem_home
raise Gem::FilePermissionError, gem_home unless raise Gem::FilePermissionError, gem_home unless
unpack or File.writable?(gem_home) unpack or File.writable?(gem_home)
@ -660,10 +685,10 @@ TEXT
return <<-TEXT return <<-TEXT
@ECHO OFF @ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT 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 GOTO :EOF
:WinNT :WinNT
@"#{ruby}" "%~dpn0" %* @"%~dp0#{ruby}" "%~dpn0" %*
TEXT TEXT
end end

Просмотреть файл

@ -101,6 +101,8 @@ class Gem::InstallerTestCase < Gem::TestCase
@installer = util_installer @spec, @gemhome @installer = util_installer @spec, @gemhome
@user_installer = util_installer @user_spec, Gem.user_dir, :user @user_installer = util_installer @user_spec, Gem.user_dir, :user
Gem::Installer.path_warning = false
end end
def util_gem_bindir spec = @spec # :nodoc: def util_gem_bindir spec = @spec # :nodoc:

Просмотреть файл

@ -100,8 +100,8 @@ module Gem::LocalRemoteOptions
def add_source_option def add_source_option
accept_uri_http accept_uri_http
add_option(:"Local/Remote", '--source URL', URI::HTTP, add_option(:"Local/Remote", '-s', '--source URL', URI::HTTP,
'Add URL as a remote source for gems') do |source, options| 'Append URL to list of remote gem sources') do |source, options|
source << '/' if source !~ /\/\z/ source << '/' if source !~ /\/\z/

Просмотреть файл

@ -53,7 +53,7 @@ class Gem::NameTuple
"#{@name}-#{@version}" "#{@name}-#{@version}"
else else
"#{@name}-#{@version}-#{@platform}" "#{@name}-#{@version}-#{@platform}"
end end.untaint
end end
## ##
@ -90,7 +90,9 @@ class Gem::NameTuple
alias to_s inspect # :nodoc: alias to_s inspect # :nodoc:
def <=> other 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 end
include Comparable include Comparable

Просмотреть файл

@ -54,10 +54,12 @@ class Gem::Package
class FormatError < Error class FormatError < Error
attr_reader :path attr_reader :path
def initialize message, path = nil def initialize message, source = nil
@path = path if source
@path = source.path
message << " in #{path}" if path message << " in #{path}" if path
end
super message super message
end end
@ -80,6 +82,7 @@ class Gem::Package
class TarInvalidError < Error; end class TarInvalidError < Error; end
attr_accessor :build_time # :nodoc: attr_accessor :build_time # :nodoc:
## ##
@ -114,19 +117,26 @@ class Gem::Package
end 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 # If +gem+ is an existing file in the old format a Gem::Package::Old will be
# returned. # returned.
def self.new gem def self.new gem
return super unless Gem::Package == self gem = if gem.is_a?(Gem::Package::Source)
return super unless File.exist? gem 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 gem.start
return super unless start.include? 'MD5SUM =' return super unless gem.start.include? 'MD5SUM ='
Gem::Package::Old.new gem Gem::Package::Old.new gem
end end
@ -227,7 +237,7 @@ class Gem::Package
setup_signer setup_signer
open @gem, 'wb' do |gem_io| @gem.with_write_io do |gem_io|
Gem::Package::TarWriter.new gem_io do |gem| Gem::Package::TarWriter.new gem_io do |gem|
add_metadata gem add_metadata gem
add_contents gem add_contents gem
@ -255,7 +265,7 @@ EOM
@contents = [] @contents = []
open @gem, 'rb' do |io| @gem.with_read_io do |io|
gem_tar = Gem::Package::TarReader.new io gem_tar = Gem::Package::TarReader.new io
gem_tar.each do |entry| gem_tar.each do |entry|
@ -312,7 +322,7 @@ EOM
FileUtils.mkdir_p destination_dir FileUtils.mkdir_p destination_dir
open @gem, 'rb' do |io| @gem.with_read_io do |io|
reader = Gem::Package::TarReader.new io reader = Gem::Package::TarReader.new io
reader.each do |entry| reader.each do |entry|
@ -360,7 +370,7 @@ EOM
out.write entry.read out.write entry.read
end if entry.file? end if entry.file?
say destination if Gem.configuration.really_verbose verbose destination
end end
end end
end end
@ -490,7 +500,7 @@ EOM
@files = [] @files = []
@spec = nil @spec = nil
open @gem, 'rb' do |io| @gem.with_read_io do |io|
Gem::Package::TarReader.new io do |reader| Gem::Package::TarReader.new io do |reader|
read_checksums reader read_checksums reader
@ -592,6 +602,9 @@ EOM
end end
require 'rubygems/package/digest_io' 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/old'
require 'rubygems/package/tar_header' require 'rubygems/package/tar_header'
require 'rubygems/package/tar_reader' require 'rubygems/package/tar_reader'

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -37,7 +37,7 @@ class Gem::Package::Old < Gem::Package
return @contents if @contents return @contents if @contents
open @gem, 'rb' do |io| @gem.with_read_io do |io|
read_until_dashes io # spec read_until_dashes io # spec
header = file_list io header = file_list io
@ -53,7 +53,7 @@ class Gem::Package::Old < Gem::Package
errstr = "Error reading files from gem" errstr = "Error reading files from gem"
open @gem, 'rb' do |io| @gem.with_read_io do |io|
read_until_dashes io # spec read_until_dashes io # spec
header = file_list io header = file_list io
raise Gem::Exception, errstr unless header raise Gem::Exception, errstr unless header
@ -83,7 +83,7 @@ class Gem::Package::Old < Gem::Package
out.write file_data out.write file_data
end end
say destination if Gem.configuration.really_verbose verbose destination
end end
end end
rescue Zlib::DataError rescue Zlib::DataError
@ -136,7 +136,7 @@ class Gem::Package::Old < Gem::Package
yaml = '' yaml = ''
open @gem, 'rb' do |io| @gem.with_read_io do |io|
skip_ruby io skip_ruby io
read_until_dashes io do |line| read_until_dashes io do |line|
yaml << line yaml << line

Просмотреть файл

@ -0,0 +1,3 @@
class Gem::Package::Source # :nodoc:
end

Просмотреть файл

@ -129,6 +129,8 @@ class Gem::Package::TarReader::Entry
ret ret
end end
alias readpartial read # :nodoc:
## ##
# Rewinds to the beginning of the tar file entry # Rewinds to the beginning of the tar file entry

Просмотреть файл

@ -17,7 +17,7 @@ class Gem::Platform
def self.local def self.local
arch = RbConfig::CONFIG['arch'] arch = RbConfig::CONFIG['arch']
arch = "#{arch}_60" if arch =~ /mswin32$/ arch = "#{arch}_60" if arch =~ /mswin(?:32|64)$/
@local ||= new(arch) @local ||= new(arch)
end end
@ -173,6 +173,7 @@ class Gem::Platform
when /^dalvik(\d+)?$/ then [nil, 'dalvik', $1 ] when /^dalvik(\d+)?$/ then [nil, 'dalvik', $1 ]
when /dotnet(\-(\d+\.\d+))?/ then ['universal','dotnet', $2 ] when /dotnet(\-(\d+\.\d+))?/ then ['universal','dotnet', $2 ]
when /mswin32(\_(\d+))?/ then ['x86', 'mswin32', $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' then ['powerpc', 'darwin', nil ]
when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1 ] when /powerpc-darwin(\d)/ then ['powerpc', 'darwin', $1 ]
when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8' ] when /sparc-solaris2.8/ then ['sparc', 'solaris', '2.8' ]

Просмотреть файл

@ -263,7 +263,7 @@ class Gem::RDoc # :nodoc: all
Gem::Requirement.new('>= 2.4.0') =~ self.class.rdoc_version Gem::Requirement.new('>= 2.4.0') =~ self.class.rdoc_version
r = new_rdoc r = new_rdoc
say "rdoc #{args.join ' '}" if Gem.configuration.really_verbose verbose { "rdoc #{args.join ' '}" }
Dir.chdir @spec.full_gem_path do Dir.chdir @spec.full_gem_path do
begin begin
@ -279,7 +279,6 @@ class Gem::RDoc # :nodoc: all
ui.errs.puts "... RDOC args: #{args.join(' ')}" ui.errs.puts "... RDOC args: #{args.join(' ')}"
ui.backtrace ex ui.backtrace ex
ui.errs.puts "(continuing with the rest of the installation)" ui.errs.puts "(continuing with the rest of the installation)"
ensure
end end
end end
end end

Просмотреть файл

@ -2,6 +2,7 @@ require 'rubygems'
require 'rubygems/request' require 'rubygems/request'
require 'rubygems/uri_formatter' require 'rubygems/uri_formatter'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'rubygems/request/connection_pools'
require 'resolv' require 'resolv'
## ##
@ -73,6 +74,9 @@ class Gem::RemoteFetcher
Socket.do_not_reverse_lookup = true Socket.do_not_reverse_lookup = true
@proxy = proxy @proxy = proxy
@pools = {}
@pool_lock = Mutex.new
@cert_files = Gem::Request.get_cert_files
@dns = dns @dns = dns
end end
@ -154,11 +158,10 @@ class Gem::RemoteFetcher
# REFACTOR: split this up and dispatch on scheme (eg download_http) # REFACTOR: split this up and dispatch on scheme (eg download_http)
# REFACTOR: be sure to clean up fake fetcher when you do this... cleaner # REFACTOR: be sure to clean up fake fetcher when you do this... cleaner
case scheme case scheme
when 'http', 'https' then when 'http', 'https', 's3' then
unless File.exist? local_gem_path then unless File.exist? local_gem_path then
begin begin
say "Downloading gem #{gem_file_name}" if verbose "Downloading gem #{gem_file_name}"
Gem.configuration.really_verbose
remote_gem_path = source_uri + "gems/#{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" alternate_name = "#{spec.original_name}.gem"
say "Failed, downloading gem #{alternate_name}" if verbose "Failed, downloading gem #{alternate_name}"
Gem.configuration.really_verbose
remote_gem_path = source_uri + "gems/#{alternate_name}" remote_gem_path = source_uri + "gems/#{alternate_name}"
@ -188,8 +190,7 @@ class Gem::RemoteFetcher
local_gem_path = source_uri.to_s local_gem_path = source_uri.to_s
end end
say "Using local gem #{local_gem_path}" if verbose "Using local gem #{local_gem_path}"
Gem.configuration.really_verbose
when nil then # TODO test for local overriding cache when nil then # TODO test for local overriding cache
source_path = if Gem.win_platform? && source_uri.scheme && source_path = if Gem.win_platform? && source_uri.scheme &&
!source_uri.path.include?(':') then !source_uri.path.include?(':') then
@ -207,8 +208,7 @@ class Gem::RemoteFetcher
local_gem_path = source_uri.to_s local_gem_path = source_uri.to_s
end end
say "Using local gem #{local_gem_path}" if verbose "Using local gem #{local_gem_path}"
Gem.configuration.really_verbose
else else
raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}" raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}"
end end
@ -232,6 +232,7 @@ class Gem::RemoteFetcher
case response case response
when Net::HTTPOK, Net::HTTPNotModified then when Net::HTTPOK, Net::HTTPNotModified then
response.uri = uri if response.respond_to? :uri
head ? response : response.body head ? response : response.body
when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther, when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
Net::HTTPTemporaryRedirect then Net::HTTPTemporaryRedirect then
@ -265,7 +266,7 @@ class Gem::RemoteFetcher
data = send "fetch_#{uri.scheme}", uri, mtime, head 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 begin
data = Gem.gunzip data data = Gem.gunzip data
rescue Zlib::GzipFile::Error rescue Zlib::GzipFile::Error
@ -286,6 +287,11 @@ class Gem::RemoteFetcher
end end
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 # Downloads +uri+ to +path+ if necessary. If no path is given, it just
# passes the data. # passes the data.
@ -332,18 +338,57 @@ class Gem::RemoteFetcher
# connections to reduce connect overhead. # connections to reduce connect overhead.
def request(uri, request_class, last_modified = nil) 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| request.fetch do |req|
yield req if block_given? yield req if block_given?
end end
ensure
request.close if request
end end
def https?(uri) def https?(uri)
uri.scheme.downcase == 'https' uri.scheme.downcase == 'https'
end 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 end

Просмотреть файл

@ -7,41 +7,43 @@ class Gem::Request
include Gem::UserInteraction 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
def initialize(uri, request_class, last_modified, proxy) 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, pool)
@uri = uri @uri = uri
@request_class = request_class @request_class = request_class
@last_modified = last_modified @last_modified = last_modified
@requests = Hash.new 0 @requests = Hash.new 0
@connections = {}
@connections_mutex = Mutex.new
@user_agent = user_agent @user_agent = user_agent
@proxy_uri = @connection_pool = pool
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
end end
def close def proxy_uri; @connection_pool.proxy_uri; end
@connections.each_value do |conn| def cert_files; @connection_pool.cert_files; end
conn.finish
end
end
def add_rubygems_trusted_certs(store) def self.get_cert_files
pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__)) pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
Dir.glob(pattern).each do |ssl_cert_file| Dir.glob(pattern)
store.add_file ssl_cert_file
end
end end
def configure_connection_for_https(connection) def self.configure_connection_for_https(connection, cert_files)
require 'net/https' require 'net/https'
connection.use_ssl = true connection.use_ssl = true
connection.verify_mode = connection.verify_mode =
@ -55,7 +57,9 @@ class Gem::Request
end end
store.set_default_paths 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 Gem.configuration.ssl_ca_cert
if File.directory? Gem.configuration.ssl_ca_cert if File.directory? Gem.configuration.ssl_ca_cert
store.add_path Gem.configuration.ssl_ca_cert store.add_path Gem.configuration.ssl_ca_cert
@ -64,6 +68,7 @@ class Gem::Request
end end
end end
connection.cert_store = store connection.cert_store = store
connection
rescue LoadError => e rescue LoadError => e
raise unless (e.respond_to?(:path) && e.path == 'openssl') || raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
e.message =~ / -- openssl$/ e.message =~ / -- openssl$/
@ -77,31 +82,7 @@ class Gem::Request
# connection, using a proxy if needed. # connection, using a proxy if needed.
def connection_for(uri) def connection_for(uri)
net_http_args = [uri.host, uri.port] @connection_pool.checkout
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
rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN, rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
Errno::EHOSTDOWN => e Errno::EHOSTDOWN => e
raise Gem::RemoteFetcher::FetchError.new(e.message, uri) raise Gem::RemoteFetcher::FetchError.new(e.message, uri)
@ -125,6 +106,37 @@ class Gem::Request
yield request if block_given? 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 connection = connection_for @uri
retried = false retried = false
@ -133,8 +145,7 @@ class Gem::Request
begin begin
@requests[connection.object_id] += 1 @requests[connection.object_id] += 1
say "#{request.method} #{@uri}" if verbose "#{request.method} #{@uri}"
Gem.configuration.really_verbose
file_name = File.basename(@uri.path) file_name = File.basename(@uri.path)
# perform download progress reporter only for gems # perform download progress reporter only for gems
@ -163,11 +174,10 @@ class Gem::Request
response = connection.request request response = connection.request request
end end
say "#{response.code} #{response.message}" if verbose "#{response.code} #{response.message}"
Gem.configuration.really_verbose
rescue Net::HTTPBadResponse rescue Net::HTTPBadResponse
say "bad response" if Gem.configuration.really_verbose verbose "bad response"
reset connection reset connection
@ -182,8 +192,7 @@ class Gem::Request
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
requests = @requests[connection.object_id] requests = @requests[connection.object_id]
say "connection reset after #{requests} requests, retrying" if verbose "connection reset after #{requests} requests, retrying"
Gem.configuration.really_verbose
raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried
@ -194,57 +203,8 @@ class Gem::Request
end end
response response
end ensure
@connection_pool.checkin connection
##
# 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
end end
## ##
@ -278,3 +238,7 @@ class Gem::Request
end end
require 'rubygems/request/http_pool'
require 'rubygems/request/https_pool'
require 'rubygems/request/connection_pools'

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -1,4 +1,3 @@
require 'rubygems'
require 'tsort' require 'tsort'
## ##
@ -21,12 +20,22 @@ class Gem::RequestSet
## ##
# Array of gems to install even if already installed # Array of gems to install even if already installed
attr_reader :always_install attr_accessor :always_install
attr_reader :dependencies attr_reader :dependencies
attr_accessor :development 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. # The set of git gems imported via load_gemdeps.
@ -38,11 +47,20 @@ class Gem::RequestSet
attr_accessor :ignore_dependencies 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. # When false no remote sets are used for resolving gems.
attr_accessor :remote attr_accessor :remote
attr_reader :resolver # :nodoc:
## ##
# Sets used for resolution # Sets used for resolution
@ -71,11 +89,15 @@ class Gem::RequestSet
@dependencies = deps @dependencies = deps
@always_install = [] @always_install = []
@conservative = false
@dependency_names = {} @dependency_names = {}
@development = false @development = false
@development_shallow = false
@errors = []
@git_set = nil @git_set = nil
@ignore_dependencies = false @ignore_dependencies = false
@install_dir = Gem.dir @install_dir = Gem.dir
@prerelease = false
@remote = true @remote = true
@requests = [] @requests = []
@sets = [] @sets = []
@ -116,12 +138,14 @@ class Gem::RequestSet
def install options, &block # :yields: request, installer def install options, &block # :yields: request, installer
if dir = options[:install_dir] if dir = options[:install_dir]
return install_into dir, false, options, &block requests = install_into dir, false, options, &block
return requests
end end
cache_dir = options[:cache_dir] || Gem.dir cache_dir = options[:cache_dir] || Gem.dir
@prerelease = options[:prerelease]
specs = [] requests = []
sorted_requests.each do |req| sorted_requests.each do |req|
if req.installed? then if req.installed? then
@ -139,10 +163,30 @@ class Gem::RequestSet
yield req, inst if block_given? yield req, inst if block_given?
specs << inst.install requests << inst.install
end end
specs 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
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 end
## ##
@ -156,17 +200,19 @@ class Gem::RequestSet
gemdeps = options[:gemdeps] gemdeps = options[:gemdeps]
@install_dir = options[:install_dir] || Gem.dir @install_dir = options[:install_dir] || Gem.dir
@prerelease = options[:prerelease]
@remote = options[:domain] != :local @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 resolve
if options[:explain] if options[:explain]
puts "Gems to install:" puts "Gems to install:"
specs.map { |s| s.full_name }.sort.each do |s| sorted_requests.each do |spec|
puts " #{s}" puts " #{spec.full_name}"
end end
if Gem.configuration.really_verbose if Gem.configuration.really_verbose
@ -175,8 +221,11 @@ class Gem::RequestSet
else else
installed = install options, &block installed = install options, &block
lockfile = Gem::RequestSet::Lockfile.new self, gemdeps if options.fetch :lock, true then
lockfile.write lockfile =
Gem::RequestSet::Lockfile.new self, gemdeps, gem_deps_api.dependencies
lockfile.write
end
installed installed
end end
@ -192,8 +241,10 @@ class Gem::RequestSet
installed = [] installed = []
options[:development] = false
options[:install_dir] = dir options[:install_dir] = dir
options[:only_install_dir] = true options[:only_install_dir] = true
@prerelease = options[:prerelease]
sorted_requests.each do |request| sorted_requests.each do |request|
spec = request.spec spec = request.spec
@ -218,7 +269,7 @@ class Gem::RequestSet
## ##
# Load a dependency management file. # 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 @git_set = Gem::Resolver::GitSet.new
@vendor_set = Gem::Resolver::VendorSet.new @vendor_set = Gem::Resolver::VendorSet.new
@ -228,6 +279,7 @@ class Gem::RequestSet
lockfile.parse lockfile.parse
gf = Gem::RequestSet::GemDependencyAPI.new self, path gf = Gem::RequestSet::GemDependencyAPI.new self, path
gf.installing = installing
gf.without_groups = without_groups if without_groups gf.without_groups = without_groups if without_groups
gf.load gf.load
end end
@ -243,15 +295,29 @@ class Gem::RequestSet
set = Gem::Resolver.compose_sets(*@sets) set = Gem::Resolver.compose_sets(*@sets)
set.remote = @remote set.remote = @remote
set.prerelease = @prerelease
resolver = Gem::Resolver.new @dependencies, set resolver = Gem::Resolver.new @dependencies, set
resolver.development = @development resolver.development = @development
resolver.development_shallow = @development_shallow
resolver.ignore_dependencies = @ignore_dependencies resolver.ignore_dependencies = @ignore_dependencies
resolver.soft_missing = @soft_missing 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 @resolver = resolver
@requests = resolver.resolve @requests = resolver.resolve
@errors = set.errors
@requests
end end
## ##
@ -284,16 +350,20 @@ class Gem::RequestSet
node.spec.dependencies.each do |dep| node.spec.dependencies.each do |dep|
next if dep.type == :development and not @development next if dep.type == :development and not @development
match = @requests.find { |r| dep.match? r.spec.name, r.spec.version } match = @requests.find { |r|
if match dep.match? r.spec.name, r.spec.version, @prerelease
begin }
yield match
rescue TSort::Cyclic unless match then
end next if dep.type == :development and @development_shallow
else next if @soft_missing
unless @soft_missing raise Gem::DependencyError,
raise Gem::DependencyError, "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})" "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
end end
begin
yield match
rescue TSort::Cyclic
end end
end end
end end

Просмотреть файл

@ -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 class Gem::RequestSet::GemDependencyAPI
@ -21,6 +49,8 @@ class Gem::RequestSet::GemDependencyAPI
:ruby_21 => %w[ruby rbx maglev], :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' x86_mingw = Gem::Platform.new 'x86-mingw32'
x64_mingw = Gem::Platform.new 'x64-mingw32' x64_mingw = Gem::Platform.new 'x64-mingw32'
@ -39,7 +69,15 @@ class Gem::RequestSet::GemDependencyAPI
:mri_19 => Gem::Platform::RUBY, :mri_19 => Gem::Platform::RUBY,
:mri_20 => Gem::Platform::RUBY, :mri_20 => Gem::Platform::RUBY,
:mri_21 => 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, :rbx => Gem::Platform::RUBY,
:ruby => Gem::Platform::RUBY, :ruby => Gem::Platform::RUBY,
:ruby_18 => Gem::Platform::RUBY, :ruby_18 => Gem::Platform::RUBY,
@ -73,6 +111,14 @@ class Gem::RequestSet::GemDependencyAPI
:mri_20 => tilde_gt_2_0_0, :mri_20 => tilde_gt_2_0_0,
:mri_21 => tilde_gt_2_1_0, :mri_21 => tilde_gt_2_1_0,
:mswin => gt_eq_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, :rbx => gt_eq_0,
:ruby => gt_eq_0, :ruby => gt_eq_0,
:ruby_18 => tilde_gt_1_8_0, :ruby_18 => tilde_gt_1_8_0,
@ -96,6 +142,14 @@ class Gem::RequestSet::GemDependencyAPI
:mri_20 => :never, :mri_20 => :never,
:mri_21 => :never, :mri_21 => :never,
:mswin => :only, :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, :rbx => :never,
:ruby => :never, :ruby => :never,
:ruby_18 => :never, :ruby_18 => :never,
@ -107,6 +161,11 @@ class Gem::RequestSet::GemDependencyAPI
:x64_mingw_21 => :only, :x64_mingw_21 => :only,
} }
##
# 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 # A set of gems that are loaded via the +:git+ option to #gem
@ -136,14 +195,31 @@ class Gem::RequestSet::GemDependencyAPI
@path = path @path = path
@current_groups = nil @current_groups = nil
@current_platform = nil @current_platforms = nil
@current_repository = nil @current_repository = nil
@dependencies = {}
@default_sources = true @default_sources = true
@git_set = @set.git_set @git_set = @set.git_set
@git_sources = {}
@installing = false
@requires = Hash.new { |h, name| h[name] = [] } @requires = Hash.new { |h, name| h[name] = [] }
@vendor_set = @set.vendor_set @vendor_set = @set.vendor_set
@gem_sources = {} @gem_sources = {}
@without_groups = [] @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 end
## ##
@ -187,14 +263,26 @@ class Gem::RequestSet::GemDependencyAPI
end 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 def load
instance_eval File.read(@path).untaint, @path, 1 instance_eval File.read(@path).untaint, @path, 1
self
end end
## ##
# :category: Gem Dependencies DSL # :category: Gem Dependencies DSL
#
# :call-seq: # :call-seq:
# gem(name) # gem(name)
# gem(name, *requirements) # gem(name, *requirements)
@ -202,6 +290,66 @@ class Gem::RequestSet::GemDependencyAPI
# #
# Specifies a gem dependency with the given +name+ and +requirements+. You # Specifies a gem dependency with the given +name+ and +requirements+. You
# may also supply +options+ following the +requirements+ # 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 def gem name, *requirements
options = requirements.pop if requirements.last.kind_of?(Hash) options = requirements.pop if requirements.last.kind_of?(Hash)
@ -211,9 +359,20 @@ class Gem::RequestSet::GemDependencyAPI
source_set = false source_set = false
source_set ||= gem_path name, options source_set ||= gem_path name, options
source_set ||= gem_git name, options source_set ||= gem_git name, options
source_set ||= gem_github 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 return unless gem_platforms options
@ -225,6 +384,12 @@ class Gem::RequestSet::GemDependencyAPI
gem_requires name, options gem_requires name, options
if duplicate then
warn <<-WARNING
Gem dependencies file #{@path} requires #{name} more than once.
WARNING
end
@set.gem name, *requirements @set.gem name, *requirements
end end
@ -258,21 +423,27 @@ class Gem::RequestSet::GemDependencyAPI
private :gem_git 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: def gem_git_source name, options # :nodoc:
return unless path = options.delete(:github) 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 gem_git name, options
true true
end end
private :gem_github private :gem_git_source
## ##
# Handles the :group and :groups +options+ for the gem with the given # 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. # platform matches the current platform.
def gem_platforms options # :nodoc: def gem_platforms options # :nodoc:
platform_names = Array(options.delete :platforms) platform_names = Array(options.delete :platform)
platform_names << @current_platform if @current_platform platform_names.concat Array(options.delete :platforms)
platform_names.concat @current_platforms if @current_platforms
return true if platform_names.empty? return true if platform_names.empty?
@ -343,7 +515,7 @@ class Gem::RequestSet::GemDependencyAPI
private :gem_platforms 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+. # default file to the require list for +name+.
def gem_requires name, options # :nodoc: def gem_requires name, options # :nodoc:
@ -362,6 +534,11 @@ class Gem::RequestSet::GemDependencyAPI
# :category: Gem Dependencies DSL # :category: Gem Dependencies DSL
# #
# Block form for specifying gems from a git +repository+. # 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 def git repository
@current_repository = repository @current_repository = repository
@ -372,6 +549,15 @@ class Gem::RequestSet::GemDependencyAPI
@current_repository = nil @current_repository = nil
end 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 # Returns the basename of the file the dependencies were loaded from
@ -383,6 +569,23 @@ class Gem::RequestSet::GemDependencyAPI
# :category: Gem Dependencies DSL # :category: Gem Dependencies DSL
# #
# Loads dependencies from a gemspec file. # 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 = {} def gemspec options = {}
name = options.delete(:name) || '{,*}' name = options.delete(:name) || '{,*}'
@ -404,7 +607,20 @@ class Gem::RequestSet::GemDependencyAPI
## ##
# :category: Gem Dependencies DSL # :category: Gem Dependencies DSL
#
# Block form for placing a dependency in the given +groups+. # 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 def group *groups
@current_groups = groups @current_groups = groups
@ -440,28 +656,72 @@ class Gem::RequestSet::GemDependencyAPI
## ##
# :category: Gem Dependencies DSL # :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 def platform *platforms
@current_platform = what @current_platforms = platforms
yield yield
ensure ensure
@current_platform = nil @current_platforms = nil
end end
## ##
# :category: Gem Dependencies DSL # :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 alias :platforms :platform
## ##
# :category: Gem Dependencies DSL # :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 = {} def ruby version, options = {}
engine = options[:engine] engine = options[:engine]
@ -471,6 +731,8 @@ class Gem::RequestSet::GemDependencyAPI
'you must specify engine_version along with the ruby engine' if 'you must specify engine_version along with the ruby engine' if
engine and not engine_version engine and not engine_version
return true if @installing
unless RUBY_VERSION == version then unless RUBY_VERSION == version then
message = "Your Ruby version is #{RUBY_VERSION}, " + message = "Your Ruby version is #{RUBY_VERSION}, " +
"but your #{gem_deps_file} requires #{version}" "but your #{gem_deps_file} requires #{version}"
@ -503,7 +765,16 @@ class Gem::RequestSet::GemDependencyAPI
## ##
# :category: Gem Dependencies DSL # :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 def source url
Gem.sources.clear if @default_sources Gem.sources.clear if @default_sources

Просмотреть файл

@ -49,11 +49,14 @@ class Gem::RequestSet::Lockfile
# Creates a new Lockfile for the given +request_set+ and +gem_deps_file+ # Creates a new Lockfile for the given +request_set+ and +gem_deps_file+
# location. # location.
def initialize request_set, gem_deps_file def initialize request_set, gem_deps_file, dependencies = nil
@set = request_set @set = request_set
@dependencies = dependencies
@gem_deps_file = File.expand_path(gem_deps_file) @gem_deps_file = File.expand_path(gem_deps_file)
@gem_deps_dir = File.dirname(@gem_deps_file) @gem_deps_dir = File.dirname(@gem_deps_file)
@gem_deps_file.untaint unless gem_deps_file.tainted?
@current_token = nil @current_token = nil
@line = 0 @line = 0
@line_pos = 0 @line_pos = 0
@ -64,19 +67,42 @@ class Gem::RequestSet::Lockfile
def add_DEPENDENCIES out # :nodoc: def add_DEPENDENCIES out # :nodoc:
out << "DEPENDENCIES" out << "DEPENDENCIES"
@requests.sort_by { |r| r.name }.each do |request| dependencies =
spec = request.spec 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
if [Gem::Resolver::VendorSpecification, [name, requirement_string]
Gem::Resolver::GitSpecification].include? spec.class then end
out << " #{request.name}!"
else else
requirement = request.request.dependency.requirement @requests.sort_by { |r| r.name }.map do |request|
spec = request.spec
name = request.name
requirement = request.request.dependency.requirement
out << " #{request.name}#{requirement.for_lockfile}" requirement_string =
if [Gem::Resolver::VendorSpecification,
Gem::Resolver::GitSpecification].include? spec.class then
"!"
else
requirement.for_lockfile
end
[name, requirement_string]
end
end end
dependencies = dependencies.map do |name, requirement_string|
" #{name}#{requirement_string}"
end end
out.concat dependencies
out << nil out << nil
end end
@ -93,12 +119,15 @@ class Gem::RequestSet::Lockfile
out << " specs:" out << " specs:"
requests.sort_by { |request| request.name }.each do |request| requests.sort_by { |request| request.name }.each do |request|
next if request.spec.name == 'bundler'
platform = "-#{request.spec.platform}" unless platform = "-#{request.spec.platform}" unless
Gem::Platform::RUBY == request.spec.platform Gem::Platform::RUBY == request.spec.platform
out << " #{request.name} (#{request.version}#{platform})" out << " #{request.name} (#{request.version}#{platform})"
request.full_spec.dependencies.sort.each do |dependency| request.full_spec.dependencies.sort.each do |dependency|
next if dependency.type == :development
requirement = dependency.requirement requirement = dependency.requirement
out << " #{dependency.name}#{requirement.for_lockfile}" out << " #{dependency.name}#{requirement.for_lockfile}"
end end
@ -166,9 +195,8 @@ class Gem::RequestSet::Lockfile
out << "PLATFORMS" out << "PLATFORMS"
platforms = @requests.map { |request| request.spec.platform }.uniq 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}" out << " #{platform}"
end end
@ -250,7 +278,7 @@ class Gem::RequestSet::Lockfile
Gem::Resolver::VendorSet === set Gem::Resolver::VendorSet === set
}.map { |set| }.map { |set|
set.specs[name] set.specs[name]
}.first }.compact.first
requirements << spec.version requirements << spec.version
when :l_paren then when :l_paren then
@ -277,26 +305,33 @@ class Gem::RequestSet::Lockfile
end end
def parse_GEM # :nodoc: def parse_GEM # :nodoc:
get :entry, 'remote' sources = []
_, data, = get :text
source = Gem::Source.new data while [:entry, 'remote'] == peek.first(2) do
get :entry, 'remote'
_, data, = get :text
skip :newline
skip :newline sources << Gem::Source.new(data)
end
sources << Gem::Source.new(Gem::DEFAULT_HOST) if sources.empty?
get :entry, 'specs' get :entry, 'specs'
skip :newline skip :newline
set = Gem::Resolver::LockSet.new source set = Gem::Resolver::LockSet.new sources
last_spec = nil last_specs = nil
while not @tokens.empty? and :text == peek.first do while not @tokens.empty? and :text == peek.first do
_, name, column, = get :text _, name, column, = get :text
case peek[0] case peek[0]
when :newline then 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 when :l_paren then
get :l_paren get :l_paren
@ -308,11 +343,13 @@ class Gem::RequestSet::Lockfile
platform = platform =
platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY
last_spec = set.add name, version, platform last_specs = set.add name, version, platform
else else
dependency = parse_dependency name, data dependency = parse_dependency name, data
last_spec.add_dependency dependency last_specs.each do |spec|
spec.add_dependency dependency
end
end end
get :r_paren get :r_paren
@ -337,11 +374,21 @@ class Gem::RequestSet::Lockfile
skip :newline 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' get :entry, 'specs'
skip :newline skip :newline
set = Gem::Resolver::GitSet.new set = Gem::Resolver::GitSet.new
set.root_dir = @set.install_dir
last_spec = nil last_spec = nil
while not @tokens.empty? and :text == peek.first do while not @tokens.empty? and :text == peek.first do
@ -360,7 +407,7 @@ class Gem::RequestSet::Lockfile
else else
dependency = parse_dependency name, data dependency = parse_dependency name, data
last_spec.spec.dependencies << dependency last_spec.add_dependency dependency
end end
get :r_paren get :r_paren
@ -403,7 +450,7 @@ class Gem::RequestSet::Lockfile
else else
dependency = parse_dependency name, data dependency = parse_dependency name, data
last_spec.spec.dependencies << dependency last_spec.dependencies << dependency
end end
get :r_paren get :r_paren
@ -432,7 +479,7 @@ class Gem::RequestSet::Lockfile
# the first token of the requirements and returns a Gem::Dependency object. # the first token of the requirements and returns a Gem::Dependency object.
def parse_dependency name, op # :nodoc: 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 _, version, = get :text
@ -575,8 +622,10 @@ class Gem::RequestSet::Lockfile
# Writes the lock file alongside the gem dependencies file # Writes the lock file alongside the gem dependencies file
def write def write
content = to_s
open "#{@gem_deps_file}.lock", 'w' do |io| open "#{@gem_deps_file}.lock", 'w' do |io|
io.write to_s io.write content
end end
end end

Просмотреть файл

@ -8,6 +8,9 @@ Gem.load_yaml if defined? ::YAML
## ##
# A Requirement is a set of one or more version restrictions. It supports a # A Requirement is a set of one or more version restrictions. It supports a
# few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators. # few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators.
#
# See Gem::Version for a description on how versions and requirements work
# together in RubyGems.
class Gem::Requirement class Gem::Requirement
OPS = { #:nodoc: OPS = { #:nodoc:

Просмотреть файл

@ -1,4 +1,3 @@
require 'rubygems'
require 'rubygems/dependency' require 'rubygems/dependency'
require 'rubygems/exceptions' require 'rubygems/exceptions'
require 'rubygems/util/list' require 'rubygems/util/list'
@ -21,16 +20,23 @@ class Gem::Resolver
DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil? DEBUG_RESOLVER = !ENV['DEBUG_RESOLVER'].nil?
require 'pp' if DEBUG_RESOLVER
## ##
# Contains all the conflicts encountered while doing resolution # Contains all the conflicts encountered while doing resolution
attr_reader :conflicts 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 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. # When true, no dependencies are looked up for requested gems.
@ -43,6 +49,12 @@ class Gem::Resolver
attr_reader :stats 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 # When a missing dependency, don't stop. Just go on and record what was
# missing. # missing.
@ -100,26 +112,27 @@ class Gem::Resolver
@conflicts = [] @conflicts = []
@development = false @development = false
@development_shallow = false
@ignore_dependencies = false @ignore_dependencies = false
@missing = [] @missing = []
@skip_gems = {}
@soft_missing = false @soft_missing = false
@stats = Gem::Resolver::Stats.new @stats = Gem::Resolver::Stats.new
end end
def explain stage, *data # :nodoc: def explain stage, *data # :nodoc:
if DEBUG_RESOLVER return unless DEBUG_RESOLVER
d = data.map { |x| x.inspect }.join(", ")
STDOUT.printf "%20s %s\n", stage.to_s.upcase, d d = data.map { |x| x.pretty_inspect }.join(", ")
end $stderr.printf "%10s %s\n", stage.to_s.upcase, d
end end
def explain_list stage, data # :nodoc: def explain_list stage # :nodoc:
if DEBUG_RESOLVER return unless DEBUG_RESOLVER
STDOUT.printf "%20s (%d entries)\n", stage.to_s.upcase, data.size
data.each do |d| data = yield
STDOUT.printf "%20s %s\n", "", d $stderr.printf "%10s (%d entries)\n", stage.to_s.upcase, data.size
end PP.pp data, $stderr unless data.empty?
end
end end
## ##
@ -132,6 +145,7 @@ class Gem::Resolver
spec = possible.pop spec = possible.pop
explain :activate, [spec.full_name, possible.size] explain :activate, [spec.full_name, possible.size]
explain :possible, possible
activation_request = activation_request =
Gem::Resolver::ActivationRequest.new spec, dep, possible Gem::Resolver::ActivationRequest.new spec, dep, possible
@ -142,8 +156,15 @@ class Gem::Resolver
def requests s, act, reqs=nil # :nodoc: def requests s, act, reqs=nil # :nodoc:
return reqs if @ignore_dependencies return reqs if @ignore_dependencies
s.fetch_development_dependencies if @development
s.dependencies.reverse_each do |d| s.dependencies.reverse_each do |d|
next if d.type == :development and not @development 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) reqs.add Gem::Resolver::DependencyRequest.new(d, act)
@stats.requirement! @stats.requirement!
end end
@ -186,6 +207,15 @@ class Gem::Resolver
def find_possible dependency # :nodoc: def find_possible dependency # :nodoc:
all = @set.find_all dependency 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 matching_platform = select_local_platforms all
return matching_platform, all return matching_platform, all
@ -270,8 +300,8 @@ class Gem::Resolver
dep = needed.remove dep = needed.remove
explain :try, [dep, dep.requester ? dep.requester.request : :toplevel] explain :try, [dep, dep.requester ? dep.requester.request : :toplevel]
explain_list :next5, needed.next5 explain_list(:next5) { needed.next5 }
explain_list :specs, Array(specs).map { |x| x.full_name }.sort explain_list(:specs) { Array(specs).map { |x| x.full_name }.sort }
# If there is already a spec activated for the requested name... # If there is already a spec activated for the requested name...
if specs && existing = specs.find { |s| dep.name == s.name } if specs && existing = specs.find { |s| dep.name == s.name }
@ -403,7 +433,10 @@ class Gem::Resolver
@missing << dep @missing << dep
unless @soft_missing 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
end end

Просмотреть файл

@ -38,6 +38,13 @@ class Gem::Resolver::ActivationRequest
end end
end end
##
# Is this activation request for a development dependency?
def development?
@request.development?
end
## ##
# Downloads a gem at +path+ and returns the file path. # Downloads a gem at +path+ and returns the file path.

Просмотреть файл

@ -34,6 +34,8 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
@data = Hash.new { |h,k| h[k] = [] } @data = Hash.new { |h,k| h[k] = [] }
@source = Gem::Source.new @uri @source = Gem::Source.new @uri
@to_fetch = []
end end
## ##
@ -45,6 +47,10 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
return res unless @remote return res unless @remote
if @to_fetch.include?(req.name)
prefetch_now
end
versions(req.name).each do |ver| versions(req.name).each do |ver|
if req.dependency.match? req.name, ver[:number] if req.dependency.match? req.name, ver[:number]
res << Gem::Resolver::APISpecification.new(self, ver) res << Gem::Resolver::APISpecification.new(self, ver)
@ -61,9 +67,13 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
def prefetch reqs def prefetch reqs
return unless @remote return unless @remote
names = reqs.map { |r| r.dependency.name } names = reqs.map { |r| r.dependency.name }
needed = names - @data.keys needed = names - @data.keys - @to_fetch
return if needed.empty? @to_fetch += needed
end
def prefetch_now
needed, @to_fetch = @to_fetch, []
uri = @dep_uri + "?gems=#{needed.sort.join ','}" uri = @dep_uri + "?gems=#{needed.sort.join ','}"
str = Gem::RemoteFetcher.fetcher.fetch_path uri str = Gem::RemoteFetcher.fetcher.fetch_path uri

Просмотреть файл

@ -34,6 +34,12 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@dependencies == other.dependencies @dependencies == other.dependencies
end end
def fetch_development_dependencies # :nodoc:
spec = source.fetch_spec Gem::NameTuple.new @name, @version, @platform
@dependencies = spec.dependencies
end
def installable_platform? # :nodoc: def installable_platform? # :nodoc:
Gem::Platform.match @platform Gem::Platform.match @platform
end end

Просмотреть файл

@ -28,6 +28,10 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
pick_sets if @remote and @sets.empty? pick_sets if @remote and @sets.empty?
super super
rescue Gem::RemoteFetcher::FetchError => e
replace_failed_api_set e
retry
end end
def prefetch reqs # :nodoc: def prefetch reqs # :nodoc:
@ -46,5 +50,29 @@ class Gem::Resolver::BestSet < Gem::Resolver::ComposedSet
end end
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 end

Просмотреть файл

@ -21,6 +21,18 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
@sets = sets @sets = sets
end 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. # Sets the remote network access for all composed sets.
@ -30,6 +42,10 @@ class Gem::Resolver::ComposedSet < Gem::Resolver::Set
@sets.each { |set| set.remote = remote } @sets.each { |set| set.remote = remote }
end end
def errors
@errors + @sets.map { |set| set.errors }.flatten
end
## ##
# Finds all specs matching +req+ in all sets. # Finds all specs matching +req+ in all sets.

Просмотреть файл

@ -52,11 +52,40 @@ class Gem::Resolver::Conflict
def explanation def explanation
activated = @activated.spec.full_name 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" % [ unless alternates.empty? then
activated, request_path(@activated).join(', '), matching = <<-MATCHING.chomp
requirement, request_path(requester).join(', '),
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 end
@ -95,10 +124,19 @@ class Gem::Resolver::Conflict
path = [] path = []
while current do while current do
requirement = current.request.dependency.requirement case current
path << "#{current.spec.full_name} (#{requirement})" when Gem::Resolver::ActivationRequest then
path <<
"#{current.request.dependency}, #{current.spec.version} activated"
current = current.parent current = current.parent
when Gem::Resolver::DependencyRequest then
path << "#{current.dependency}"
current = current.requester
else
raise Gem::Exception, "[BUG] unknown request class #{current.class}"
end
end end
path = ['user request (gem command or Gemfile)'] if path.empty? path = ['user request (gem command or Gemfile)'] if path.empty?

Просмотреть файл

@ -35,7 +35,26 @@ class Gem::Resolver::DependencyRequest
end 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) def matches_spec?(spec)
@dependency.matches_spec? spec @dependency.matches_spec? spec

Просмотреть файл

@ -80,7 +80,7 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
prefetch nil prefetch nil
specs.values.select do |spec| specs.values.select do |spec|
req.matches_spec? spec req.match? spec
end end
end end

Просмотреть файл

@ -12,11 +12,15 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
@source == other.source @source == other.source
end end
def add_dependency dependency # :nodoc:
spec.dependencies << dependency
end
## ##
# Installing a git gem only involves building the extensions and generating # Installing a git gem only involves building the extensions and generating
# the executables. # the executables.
def install options def install options = {}
require 'rubygems/installer' require 'rubygems/installer'
installer = Gem::Installer.new '', options installer = Gem::Installer.new '', options
@ -31,5 +35,25 @@ class Gem::Resolver::GitSpecification < Gem::Resolver::SpecSpecification
installer.run_post_install_hooks installer.run_post_install_hooks
end 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 end

Просмотреть файл

@ -18,7 +18,9 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
@all = Hash.new { |h,k| h[k] = [] } @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| list.each do |uri, specs|
specs.each do |n| specs.each do |n|
@ -41,7 +43,7 @@ class Gem::Resolver::IndexSet < Gem::Resolver::Set
name = req.dependency.name name = req.dependency.name
@all[name].each do |uri, n| @all[name].each do |uri, n|
if req.dependency.match? n then if req.match? n, @prerelease then
res << Gem::Resolver::IndexSpecification.new( res << Gem::Resolver::IndexSpecification.new(
self, n.name, n.version, uri, n.platform) self, n.name, n.version, uri, n.platform)
end end

Просмотреть файл

@ -14,7 +14,7 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
# This is a null install as this specification is already installed. # This is a null install as this specification is already installed.
# +options+ are ignored. # +options+ are ignored.
def install options def install options = {}
yield nil yield nil
end end
@ -29,6 +29,24 @@ class Gem::Resolver::InstalledSpecification < Gem::Resolver::SpecSpecification
super super
end 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 # The source for this specification

Просмотреть файл

@ -20,6 +20,11 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
attr_accessor :ignore_installed # :nodoc: 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+. # Creates a new InstallerSet that will look for gems in +domain+.
@ -34,10 +39,52 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
@always_install = [] @always_install = []
@ignore_dependencies = false @ignore_dependencies = false
@ignore_installed = false @ignore_installed = false
@local = {}
@remote_set = Gem::Resolver::BestSet.new @remote_set = Gem::Resolver::BestSet.new
@specs = {} @specs = {}
end 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? # Should local gems should be considered?
@ -52,6 +99,13 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
@domain == :both or @domain == :remote @domain == :both or @domain == :remote
end end
##
# Errors encountered while resolving gems
def errors
@errors + @remote_set.errors
end
## ##
# Returns an array of IndexSpecification objects matching DependencyRequest # Returns an array of IndexSpecification objects matching DependencyRequest
# +req+. # +req+.
@ -62,30 +116,53 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
dep = req.dependency dep = req.dependency
return res if @ignore_dependencies and return res if @ignore_dependencies and
@always_install.none? { |spec| dep.matches_spec? spec } @always_install.none? { |spec| dep.match? spec }
name = dep.name name = dep.name
dep.matching_specs.each do |gemspec| 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) res << Gem::Resolver::InstalledSpecification.new(self, gemspec)
end unless @ignore_installed end unless @ignore_installed
if consider_local? then 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 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( 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
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.concat @remote_set.find_all req if consider_remote?
res res
end end
def prefetch(reqs)
@remote_set.prefetch(reqs)
end
def prerelease= allow_prerelease
super
@remote_set.prerelease = allow_prerelease
end
def inspect # :nodoc: def inspect # :nodoc:
always_install = @always_install.map { |s| s.full_name } always_install = @always_install.map { |s| s.full_name }
@ -108,6 +185,15 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end end
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: def pretty_print q # :nodoc:
q.group 2, '[InstallerSet', ']' do q.group 2, '[InstallerSet', ']' do
q.breakable q.breakable

Просмотреть файл

@ -12,5 +12,30 @@ class Gem::Resolver::LocalSpecification < Gem::Resolver::SpecSpecification
super super
end 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 end

Просмотреть файл

@ -6,13 +6,16 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
attr_reader :specs # :nodoc: 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() super()
@source = Gem::Source::Lock.new source @sources = sources.map do |source|
@specs = [] Gem::Source::Lock.new source
end
@specs = []
end end
## ##
@ -25,13 +28,14 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
def add name, version, platform # :nodoc: def add name, version, platform # :nodoc:
version = Gem::Version.new version version = Gem::Version.new version
spec = specs = @sources.map do |source|
Gem::Resolver::LockSpecification.new self, name, version, @source, Gem::Resolver::LockSpecification.new self, name, version, source,
platform platform
end
@specs << spec @specs.concat specs
spec specs
end end
## ##
@ -40,7 +44,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
def find_all req def find_all req
@specs.select do |spec| @specs.select do |spec|
req.matches_spec? spec req.match? spec
end end
end end

Просмотреть файл

@ -23,7 +23,7 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
# This is a null install as a locked specification is considered installed. # This is a null install as a locked specification is considered installed.
# +options+ are ignored. # +options+ are ignored.
def install options def install options = {}
destination = options[:install_dir] || Gem.dir destination = options[:install_dir] || Gem.dir
if File.exist? File.join(destination, 'specifications', spec.spec_name) then if File.exist? File.join(destination, 'specifications', spec.spec_name) then
@ -41,10 +41,36 @@ class Gem::Resolver::LockSpecification < Gem::Resolver::Specification
@dependencies << dependency @dependencies << dependency
end 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 # A specification constructed from the lockfile is returned
def spec def spec
@spec ||= Gem::Specification.find { |spec|
spec.name == @name and spec.version == @version
}
@spec ||= Gem::Specification.new do |s| @spec ||= Gem::Specification.new do |s|
s.name = @name s.name = @name
s.version = @version s.version = @version

Просмотреть файл

@ -9,8 +9,20 @@ class Gem::Resolver::Set
attr_accessor :remote 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: def initialize # :nodoc:
@remote = true @prerelease = false
@remote = true
@errors = []
end end
## ##

Просмотреть файл

@ -4,8 +4,6 @@
class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification class Gem::Resolver::SpecSpecification < Gem::Resolver::Specification
attr_reader :spec # :nodoc:
## ##
# A SpecSpecification is created for a +set+ for a Gem::Specification in # 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 # +spec+. The +source+ is either where the +spec+ came from, or should be

Просмотреть файл

@ -30,6 +30,14 @@ class Gem::Resolver::Specification
attr_reader :source 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. # The version of the gem for this specification.
@ -47,6 +55,13 @@ class Gem::Resolver::Specification
@version = nil @version = nil
end 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. # The name and version of the specification.
# #
@ -61,8 +76,11 @@ class Gem::Resolver::Specification
# install method yields a Gem::Installer instance, which indicates the # install method yields a Gem::Installer instance, which indicates the
# gem will be installed, or +nil+, which indicates the gem is already # gem will be installed, or +nil+, which indicates the gem is already
# installed. # installed.
#
# After installation #spec is updated to point to the just-installed
# specification.
def install options def install options = {}
require 'rubygems/installer' require 'rubygems/installer'
destination = options[:install_dir] || Gem.dir destination = options[:install_dir] || Gem.dir
@ -75,7 +93,7 @@ class Gem::Resolver::Specification
yield installer if block_given? yield installer if block_given?
installer.install @spec = installer.install
end end
## ##
@ -85,5 +103,8 @@ class Gem::Resolver::Specification
Gem::Platform.match spec.platform Gem::Platform.match spec.platform
end end
def local? # :nodoc:
false
end
end end

Просмотреть файл

@ -43,6 +43,8 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
@specs[spec.name] = spec @specs[spec.name] = spec
@directories[spec] = directory @directories[spec] = directory
spec
end end
## ##
@ -51,7 +53,7 @@ class Gem::Resolver::VendorSet < Gem::Resolver::Set
def find_all req def find_all req
@specs.values.select do |spec| @specs.values.select do |spec|
req.matches_spec? spec req.match? spec
end.map do |spec| end.map do |spec|
source = Gem::Source::Vendor.new @directories[spec] source = Gem::Source::Vendor.new @directories[spec]
Gem::Resolver::VendorSpecification.new self, spec, source Gem::Resolver::VendorSpecification.new self, spec, source

Просмотреть файл

@ -16,7 +16,7 @@ class Gem::Resolver::VendorSpecification < Gem::Resolver::SpecSpecification
# This is a null install as this gem was unpacked into a directory. # This is a null install as this gem was unpacked into a directory.
# +options+ are ignored. # +options+ are ignored.
def install options def install options = {}
yield nil yield nil
end end

Просмотреть файл

@ -218,6 +218,7 @@ class Gem::Security::Policy
# against # against
else else
alert_warning "#{full_name} is not signed" alert_warning "#{full_name} is not signed"
return
end end
end end

Просмотреть файл

@ -530,6 +530,36 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end end
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) def quick(req, res)
reset_gems reset_gems
@ -537,7 +567,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
add_date res add_date res
case req.request_uri.path 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 marshal_format, name, version, platform = $1, $2, $3, $4
specs = Gem::Specification.find_all_by_name name, version 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", @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
method(:latest_specs) 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 "/quick/", method(:quick)
@server.mount_proc("/gem-server-rdoc-style.css") do |req, res| @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|

Просмотреть файл

@ -78,15 +78,21 @@ class Gem::Source
# Returns a Set that can fetch specifications from this source. # Returns a Set that can fetch specifications from this source.
def dependency_resolver_set # :nodoc: def dependency_resolver_set # :nodoc:
return Gem::Resolver::IndexSet.new self if 'file' == api_uri.scheme
bundler_api_uri = api_uri + './api/v1/dependencies' bundler_api_uri = api_uri + './api/v1/dependencies'
begin begin
fetcher = Gem::RemoteFetcher.fetcher 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 rescue Gem::RemoteFetcher::FetchError
Gem::Resolver::IndexSet.new self Gem::Resolver::IndexSet.new self
else 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
end end
@ -100,6 +106,8 @@ class Gem::Source
def cache_dir(uri) def cache_dir(uri)
# Correct for windows paths # Correct for windows paths
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/') 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) File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end end

Просмотреть файл

@ -51,7 +51,7 @@ class Gem::Source::Git < Gem::Source
# will be checked out when the gem is installed. # will be checked out when the gem is installed.
def initialize name, repository, reference, submodules = false def initialize name, repository, reference, submodules = false
super(nil) super repository
@name = name @name = name
@repository = repository @repository = repository
@ -67,7 +67,7 @@ class Gem::Source::Git < Gem::Source
case other case other
when Gem::Source::Git then when Gem::Source::Git then
0 0
when Gem::Source::Installed, when Gem::Source::Vendor,
Gem::Source::Lock then Gem::Source::Lock then
-1 -1
when Gem::Source then when Gem::Source then
@ -101,7 +101,7 @@ class Gem::Source::Git < Gem::Source
Dir.chdir install_dir do Dir.chdir install_dir do
system @git, 'fetch', '--quiet', '--force', '--tags', install_dir system @git, 'fetch', '--quiet', '--force', '--tags', install_dir
success = system @git, 'reset', '--quiet', '--hard', @reference success = system @git, 'reset', '--quiet', '--hard', rev_parse
success &&= success &&=
Gem::Util.silent_system @git, 'submodule', 'update', Gem::Util.silent_system @git, 'submodule', 'update',

Просмотреть файл

@ -12,7 +12,8 @@ class Gem::Source::Installed < Gem::Source
def <=> other def <=> other
case other case other
when Gem::Source::Lock, when Gem::Source::Git,
Gem::Source::Lock,
Gem::Source::Vendor then Gem::Source::Vendor then
-1 -1
when Gem::Source::Installed then when Gem::Source::Installed then
@ -31,5 +32,9 @@ class Gem::Source::Installed < Gem::Source
nil nil
end end
def pretty_print q # :nodoc:
q.text '[Installed]'
end
end end

Просмотреть файл

@ -4,6 +4,11 @@
class Gem::Source::SpecificFile < Gem::Source 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+ # Creates a new SpecificFile for the gem in +file+
@ -37,7 +42,7 @@ class Gem::Source::SpecificFile < Gem::Source
end end
def pretty_print q # :nodoc: def pretty_print q # :nodoc:
q.group 2, '[Local:', ']' do q.group 2, '[SpecificFile:', ']' do
q.breakable q.breakable
q.text @path q.text @path
end end

Просмотреть файл

@ -186,7 +186,7 @@ class Gem::SpecFetcher
def suggest_gems_from_name gem_name def suggest_gems_from_name gem_name
gem_name = gem_name.downcase.tr('_-', '') gem_name = gem_name.downcase.tr('_-', '')
max = gem_name.size / 2 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| matches = names.map { |n|
next unless n.match_platform? next unless n.match_platform?
@ -258,18 +258,11 @@ class Gem::SpecFetcher
# etc.). If +gracefully_ignore+ is true, errors are ignored. # etc.). If +gracefully_ignore+ is true, errors are ignored.
def tuples_for(source, type, gracefully_ignore=false) # :nodoc: def tuples_for(source, type, gracefully_ignore=false) # :nodoc:
cache = @caches[type] @caches[type][source.uri] ||=
source.load_specs(type).sort_by { |tup| tup.name }
tuples = rescue Gem::RemoteFetcher::FetchError
begin raise unless gracefully_ignore
cache[source.uri] ||= []
source.load_specs(type).sort_by { |tup| tup.name }
rescue Gem::RemoteFetcher::FetchError
raise unless gracefully_ignore
[]
end
tuples
end end
end end

Просмотреть файл

@ -14,12 +14,6 @@ require 'rubygems/basic_specification'
require 'rubygems/stub_specification' require 'rubygems/stub_specification'
require 'rubygems/util/stringio' 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 # The Specification class contains the information for a Gem. Typically
# defined in a .gemspec file or a Rakefile, and looks like this: # defined in a .gemspec file or a Rakefile, and looks like this:
@ -218,9 +212,11 @@ class Gem::Specification < Gem::BasicSpecification
# Usage: # Usage:
# #
# # If all library files are in the root directory... # # 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. # The version of RubyGems used to create this gem.
@ -373,7 +369,9 @@ class Gem::Specification < Gem::BasicSpecification
## ##
# A long description of this gem # 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: # Usage:
# #
@ -412,6 +410,16 @@ class Gem::Specification < Gem::BasicSpecification
attr_accessor :post_install_message 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. # The key used to sign this gem. See Gem::Security for details.
@ -472,6 +480,9 @@ class Gem::Specification < Gem::BasicSpecification
# found in bindir. These files must be executable Ruby files. Files that # found in bindir. These files must be executable Ruby files. Files that
# use bash or other interpreters will not work. # use bash or other interpreters will not work.
# #
# Executables included may only be ruby scripts, not scripts for other
# languages or compiled binaries.
#
# Usage: # Usage:
# #
# spec.executables << 'rake' # spec.executables << 'rake'
@ -532,20 +543,26 @@ class Gem::Specification < Gem::BasicSpecification
## ##
# :category: Recommended gemspec attributes # :category: Recommended gemspec attributes
#
# The license for this gem. # 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 # This should just be the name of your license. The full text of the license
# text of the license should be inside of the gem when you build it. # should be inside of the gem (at the top level) when you build it.
# #
# See http://opensource.org/licenses/alphabetical for a list of licenses and # The simplest way, is to specify the standard SPDX ID
# their abbreviations (or short names). GitHub also provides a # https://spdx.org/licenses/ for the license.
# license picker at http://choosealicense.com/ # Ideally you should pick one that is OSI (Open Source Initiative)
# http://opensource.org/licenses/alphabetical approved.
# #
# According to copyright law, not having an OSI-approved open source license # The most commonly used OSI approved licenses are BSD-3-Clause and MIT.
# means you have no rights to use the code for any purpose-- in other words, # GitHub also provides a license picker at http://choosealicense.com/.
# "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= # You can set multiple licenses with #licenses=
# #
@ -607,6 +624,13 @@ class Gem::Specification < Gem::BasicSpecification
@required_ruby_version = Gem::Requirement.create req @required_ruby_version = Gem::Requirement.create req
end 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 # Lists the external (to RubyGems) requirements that must be met for this gem
# to work. It's simply information for the user. # 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 = Dir.glob('test/tc_*.rb')
# spec.test_files = ['tests/test-suite.rb'] # spec.test_files = ['tests/test-suite.rb']
def test_files= files def test_files= files # :nodoc:
@test_files = Array files @test_files = Array files
end end
@ -661,16 +685,6 @@ class Gem::Specification < Gem::BasicSpecification
attr_writer :original_platform # :nodoc: 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' # The rubyforge project this gem lives under. i.e. RubyGems'
# rubyforge_project is "rubygems". # rubyforge_project is "rubygems".
@ -1259,9 +1273,13 @@ class Gem::Specification < Gem::BasicSpecification
# there are conflicts upon activation. # there are conflicts upon activation.
def activate 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 activate_dependencies
add_self_to_load_path add_self_to_load_path
@ -1403,7 +1421,10 @@ class Gem::Specification < Gem::BasicSpecification
def build_args def build_args
if File.exist? build_info_file 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 else
[] []
end end
@ -1418,8 +1439,8 @@ class Gem::Specification < Gem::BasicSpecification
return if extensions.empty? return if extensions.empty?
return if installed_by_version < Gem::Version.new('2.2.0.preview.2') return if installed_by_version < Gem::Version.new('2.2.0.preview.2')
return if File.exist? gem_build_complete_path return if File.exist? gem_build_complete_path
return if !File.writable?(base_dir) && return if !File.writable?(base_dir)
!File.exist?(File.join(base_dir, 'extensions')) return if !File.exist?(File.join(base_dir, 'extensions'))
begin begin
# We need to require things in $LOAD_PATH without looking for the # We need to require things in $LOAD_PATH without looking for the
@ -1477,13 +1498,12 @@ class Gem::Specification < Gem::BasicSpecification
def conflicts def conflicts
conflicts = {} conflicts = {}
Gem.loaded_specs.values.each do |spec| self.runtime_dependencies.each { |dep|
bad = self.runtime_dependencies.find_all { |dep| spec = Gem.loaded_specs[dep.name]
spec.name == dep.name and not spec.satisfies_requirement? dep if spec and not spec.satisfies_requirement? dep
} (conflicts[spec] ||= []) << dep
end
conflicts[spec] = bad unless bad.empty? }
end
conflicts conflicts
end end
@ -1496,6 +1516,11 @@ class Gem::Specification < Gem::BasicSpecification
@date ||= TODAY @date ||= TODAY
end end
DateLike = Object.new # :nodoc:
def DateLike.===(obj) # :nodoc:
defined?(::Date) and Date === obj
end
DateTimeFormat = # :nodoc: DateTimeFormat = # :nodoc:
/\A /\A
(\d{4})-(\d{2})-(\d{2}) (\d{4})-(\d{2})-(\d{2})
@ -1525,7 +1550,7 @@ class Gem::Specification < Gem::BasicSpecification
raise(Gem::InvalidSpecificationException, raise(Gem::InvalidSpecificationException,
"invalid date format in specification: #{date.inspect}") "invalid date format in specification: #{date.inspect}")
end end
when Time, Date then when Time, DateLike then
Time.utc(date.year, date.month, date.day) Time.utc(date.year, date.month, date.day)
else else
TODAY TODAY
@ -1779,7 +1804,7 @@ class Gem::Specification < Gem::BasicSpecification
## ##
# True if this gem has files in test_files # True if this gem has files in test_files
def has_unit_tests? def has_unit_tests? # :nodoc:
not test_files.empty? not test_files.empty?
end end
@ -1952,6 +1977,19 @@ class Gem::Specification < Gem::BasicSpecification
end end
end 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: # Normalize the list of files so that:
# * All file lists have redundancies removed. # * All file lists have redundancies removed.
@ -2035,35 +2073,35 @@ class Gem::Specification < Gem::BasicSpecification
end end
end end
##
# Raise an exception if the version of this spec conflicts with the one
# that is already loaded (+other+)
def check_version_conflict other # :nodoc:
return if self.version == other.version
# 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}"
e = Gem::LoadError.new msg
e.name = self.name
# TODO: e.requirement = dep.requirement
raise e
end
private :check_version_conflict
## ##
# Check the spec for possible conflicts and freak out if there are any. # Check the spec for possible conflicts and freak out if there are any.
def raise_if_conflicts def raise_if_conflicts # :nodoc:
other = Gem.loaded_specs[self.name]
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.
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
raise e
end
conf = self.conflicts conf = self.conflicts
unless conf.empty? then unless conf.empty? then
y = conf.map { |act,con| raise Gem::ConflictError.new self, conf
"#{act.full_name} conflicts with #{con.join(", ")}"
}.join ", "
# TODO: improve message by saying who activated `con`
raise Gem::LoadError, "Unable to activate #{self.full_name}, because #{y}"
end end
end end
@ -2087,14 +2125,7 @@ class Gem::Specification < Gem::BasicSpecification
# Singular accessor for #require_paths # Singular accessor for #require_paths
def require_path= path def require_path= path
self.require_paths = [path] self.require_paths = Array(path)
end
##
# The RubyGems version required by this gem
def required_rubygems_version= req
@required_rubygems_version = Gem::Requirement.create req
end 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 = obj.keys.sort.map { |k| "#{k.to_s.dump} => #{obj[k].to_s.dump}" }
"{ #{seg.join(', ')} }" "{ #{seg.join(', ')} }"
when Gem::Version then obj.to_s.dump 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 Time then obj.strftime('%Y-%m-%d').dump
when Numeric then obj.inspect when Numeric then obj.inspect
when true, false, nil then obj.inspect when true, false, nil then obj.inspect
@ -2217,14 +2248,14 @@ class Gem::Specification < Gem::BasicSpecification
## ##
# Singular accessor for #test_files # Singular accessor for #test_files
def test_file def test_file # :nodoc:
val = test_files and val.first val = test_files and val.first
end end
## ##
# Singular mutator for #test_files # Singular mutator for #test_files
def test_file= file def test_file= file # :nodoc:
self.test_files = [file] self.test_files = [file]
end end
@ -2232,7 +2263,7 @@ class Gem::Specification < Gem::BasicSpecification
# Test files included in this gem. You cannot append to this accessor, you # Test files included in this gem. You cannot append to this accessor, you
# must assign to it. # must assign to it.
def test_files def test_files # :nodoc:
# Handle the possibility that we have @test_suite_file but not # Handle the possibility that we have @test_suite_file but not
# @test_files. This will happen when an old gem is loaded via # @test_files. This will happen when an old gem is loaded via
# YAML. # YAML.
@ -2256,7 +2287,7 @@ class Gem::Specification < Gem::BasicSpecification
mark_version mark_version
result = [] result = []
result << "# -*- encoding: utf-8 -*-" 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 result << "#{Gem::StubSpecification::PREFIX}#{extensions.join "\0"}" unless
extensions.empty? extensions.empty?
result << nil result << nil
@ -2273,7 +2304,7 @@ class Gem::Specification < Gem::BasicSpecification
if metadata and !metadata.empty? if metadata and !metadata.empty?
result << " s.metadata = #{ruby_code metadata} if s.respond_to? :metadata=" result << " s.metadata = #{ruby_code metadata} if s.respond_to? :metadata="
end end
result << " s.require_paths = #{ruby_code @require_paths}" result << " s.require_paths = #{ruby_code raw_require_paths}"
handled = [ handled = [
:dependencies, :dependencies,
@ -2443,7 +2474,7 @@ class Gem::Specification < Gem::BasicSpecification
"invalid value for attribute name: \"#{name.inspect}\"" "invalid value for attribute name: \"#{name.inspect}\""
end end
if @require_paths.empty? then if raw_require_paths.empty? then
raise Gem::InvalidSpecificationException, raise Gem::InvalidSpecificationException,
'specification must have at least one require_path' 'specification must have at least one require_path'
end end
@ -2627,7 +2658,8 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
dep.requirement.requirements.any? do |op, version| dep.requirement.requirements.any? do |op, version|
op == '~>' and op == '~>' and
not version.prerelease? and not version.prerelease? and
version.segments.length > 2 version.segments.length > 2 and
version.segments.first != 0
end end
if overly_strict then if overly_strict then

Просмотреть файл

@ -120,6 +120,13 @@ class Gem::StubSpecification < Gem::BasicSpecification
super super
end end
def missing_extensions?
return false if default_gem?
return false if extensions.empty?
to_spec.missing_extensions?
end
## ##
# Name of the gem # 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 # The full Gem::Specification for this gem, loaded from evalling its gemspec
def to_spec def to_spec
@spec ||= Gem.loaded_specs.values.find { |spec|
spec.name == @name and spec.version == @version
}
@spec ||= Gem::Specification.load(loaded_from) @spec ||= Gem::Specification.load(loaded_from)
@spec.ignored = @ignored if instance_variable_defined? :@ignored
@spec
end end
## ##

Просмотреть файл

@ -218,8 +218,11 @@ class Gem::TestCase < MiniTest::Unit::TestCase
def setup def setup
super super
@orig_gem_home = ENV['GEM_HOME'] @orig_gem_home = ENV['GEM_HOME']
@orig_gem_path = ENV['GEM_PATH'] @orig_gem_path = ENV['GEM_PATH']
@orig_gem_vendor = ENV['GEM_VENDOR']
ENV['GEM_VENDOR'] = nil
@current_dir = Dir.pwd @current_dir = Dir.pwd
@fetcher = nil @fetcher = nil
@ -347,8 +350,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase
FileUtils.rm_rf @tempdir unless ENV['KEEP_FILES'] FileUtils.rm_rf @tempdir unless ENV['KEEP_FILES']
ENV['GEM_HOME'] = @orig_gem_home ENV['GEM_HOME'] = @orig_gem_home
ENV['GEM_PATH'] = @orig_gem_path ENV['GEM_PATH'] = @orig_gem_path
ENV['GEM_VENDOR'] = @orig_gem_vendor
Gem.ruby = @orig_ruby if @orig_ruby 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. # The StaticSet is a static set of gem specifications used for testing only.
# It is available by requiring Gem::TestCase. # 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. # 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+ # Creates a new StaticSet for the given +specs+
def initialize(specs) def initialize(specs)
super()
@specs = specs @specs = specs
@remote = true @remote = true
@ -1299,7 +1305,7 @@ Also, a list:
# Finds all gems matching +dep+ in this set. # Finds all gems matching +dep+ in this set.
def find_all(dep) def find_all(dep)
@specs.find_all { |s| dep.matches_spec? s } @specs.find_all { |s| dep.match? s, @prerelease }
end end
## ##

Просмотреть файл

@ -38,6 +38,8 @@ class Gem::FakeFetcher
end end
def find_data(path) 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 if URI === path and "URI::#{path.scheme.upcase}" != path.class.name then
raise ArgumentError, raise ArgumentError,
"mismatch for scheme #{path.scheme} and class #{path.class}" "mismatch for scheme #{path.scheme} and class #{path.class}"

Просмотреть файл

@ -26,6 +26,16 @@ module Gem::Text
result.join("\n").gsub(/^/, " " * indent) result.join("\n").gsub(/^/, " " * indent)
end 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 # This code is based directly on the Text gem implementation
# Returns a value representing the "cost" of transforming str1 into str2 # Returns a value representing the "cost" of transforming str1 into str2
def levenshtein_distance str1, str2 def levenshtein_distance str1, str2
@ -42,16 +52,16 @@ module Gem::Text
d = (0..m).to_a d = (0..m).to_a
x = nil x = nil
n.times do |i| str1.each_char.each_with_index do |char1,i|
e = i+1 e = i+1
m.times do |j| str2.each_char.each_with_index do |char2,j|
cost = (s[i] == t[j]) ? 0 : 1 cost = (char1 == char2) ? 0 : 1
x = [ x = min3(
d[j+1] + 1, # insertion d[j+1] + 1, # insertion
e + 1, # deletion e + 1, # deletion
d[j] + cost # substitution d[j] + cost # substitution
].min )
d[j] = e d[j] = e
e = x e = x
end end

Просмотреть файл

@ -96,6 +96,8 @@ class Gem::Uninstaller
(@user_install and spec.base_dir == Gem.user_dir) (@user_install and spec.base_dir == Gem.user_dir)
end end
list.sort!
if list.empty? then if list.empty? then
if other_repo_specs.empty? if other_repo_specs.empty?
if default_specs.empty? if default_specs.empty?
@ -120,7 +122,8 @@ class Gem::Uninstaller
remove_all list remove_all list
elsif list.size > 1 then 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 say
_, index = choose_from_list "Select gem to uninstall:", gem_names _, index = choose_from_list "Select gem to uninstall:", gem_names

Просмотреть файл

@ -157,6 +157,14 @@ module Gem::UserInteraction
def terminate_interaction exit_code = 0 def terminate_interaction exit_code = 0
ui.terminate_interaction exit_code ui.terminate_interaction exit_code
end 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 end
## ##

Просмотреть файл

@ -143,6 +143,10 @@
# "~> 3.0.0" 3.0.0 ... 3.1 # "~> 3.0.0" 3.0.0 ... 3.1
# "~> 3.5" 3.5 ... 4.0 # "~> 3.5" 3.5 ... 4.0
# "~> 3.5.0" 3.5.0 ... 3.6 # "~> 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 class Gem::Version
autoload :Requirement, 'rubygems/requirement' autoload :Requirement, 'rubygems/requirement'
@ -189,7 +193,7 @@ class Gem::Version
@@all = {} @@all = {}
def self.new version # :nodoc: def self.new version # :nodoc:
return super unless Gem::VERSION == self.class return super unless Gem::Version == self
@@all[version] ||= super @@all[version] ||= super
end end

Просмотреть файл

@ -75,6 +75,21 @@ class TestGem < Gem::TestCase
end end
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 def test_require_missing
save_loaded_features do save_loaded_features do
assert_raises ::LoadError do assert_raises ::LoadError do
@ -216,6 +231,58 @@ class TestGem < Gem::TestCase
end end
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 def test_self_default_sources
assert_equal %w[https://rubygems.org/], Gem.default_sources assert_equal %w[https://rubygems.org/], Gem.default_sources
end end
@ -816,6 +883,23 @@ class TestGem < Gem::TestCase
assert_match %r%Could not find 'b' %, e.message assert_match %r%Could not find 'b' %, e.message
end 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 def test_self_use_paths
util_ensure_gem_dirs util_ensure_gem_dirs
@ -951,6 +1035,30 @@ class TestGem < Gem::TestCase
end end
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 def test_load_plugins
skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7" skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
plugin_path = File.join "lib", "rubygems_plugin.rb" plugin_path = File.join "lib", "rubygems_plugin.rb"
@ -1251,13 +1359,28 @@ class TestGem < Gem::TestCase
end end
def test_use_gemdeps 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 rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
spec = util_spec 'a', 1 spec = util_spec 'a', 1
refute spec.activated? refute spec.activated?
open 'Gemfile', 'w' do |io| open 'gem.deps.rb', 'w' do |io|
io.write 'gem "a"' io.write 'gem "a"'
end end
@ -1268,6 +1391,29 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end 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 def test_use_gemdeps_automatic
skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7" skip 'Insecure operation - chdir' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-' rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '-'
@ -1287,6 +1433,17 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end 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 def test_use_gemdeps_disabled
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], '' rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], ''
@ -1294,7 +1451,7 @@ class TestGem < Gem::TestCase
refute spec.activated? refute spec.activated?
open 'Gemfile', 'w' do |io| open 'gem.deps.rb', 'w' do |io|
io.write 'gem "a"' io.write 'gem "a"'
end end
@ -1305,6 +1462,27 @@ class TestGem < Gem::TestCase
ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps ENV['RUBYGEMS_GEMDEPS'] = rubygems_gemdeps
end 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 def test_use_gemdeps_specific
skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7" skip 'Insecure operation - read' if RUBY_VERSION <= "1.8.7"
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x' rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], 'x'

Просмотреть файл

@ -3,6 +3,7 @@ require 'rubygems/available_set'
require 'rubygems/security' require 'rubygems/security'
class TestGemAvailableSet < Gem::TestCase class TestGemAvailableSet < Gem::TestCase
def setup def setup
super super
@ -23,22 +24,24 @@ class TestGemAvailableSet < Gem::TestCase
end end
def test_find_all 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 = 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 dep = Gem::Resolver::DependencyRequest.new dep('a'), nil
specs = set.find_all dep assert_equal %w[a-1], set.find_all(dep).map { |spec| spec.full_name }
spec = specs.first dep = Gem::Resolver::DependencyRequest.new dep('a', '>= 0.a'), nil
assert_kind_of Gem::Resolver::LocalSpecification, spec assert_equal %w[a-1 a-1.a],
set.find_all(dep).map { |spec| spec.full_name }.sort
assert_equal 'a-1', spec.full_name
end end
def test_match_platform def test_match_platform

Просмотреть файл

@ -184,5 +184,60 @@ class TestGemCommand < Gem::TestCase
assert_equal ['-h', 'command'], args assert_equal ['-h', 'command'], args
end 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 end

Просмотреть файл

@ -179,6 +179,7 @@ Added '/CN=alternate/DC=example'
assert_empty @ui.error assert_empty @ui.error
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem') assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
end end
def test_execute_build_encrypted_key def test_execute_build_encrypted_key

Просмотреть файл

@ -9,8 +9,8 @@ class TestGemCommandsContentsCommand < Gem::TestCase
@cmd = Gem::Commands::ContentsCommand.new @cmd = Gem::Commands::ContentsCommand.new
end end
def gem name def gem name, version = 2
spec = quick_gem name do |gem| spec = quick_gem name, version do |gem|
gem.files = %W[lib/#{name}.rb Rakefile] gem.files = %W[lib/#{name}.rb Rakefile]
end end
write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb]) write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
@ -135,6 +135,40 @@ class TestGemCommandsContentsCommand < Gem::TestCase
assert_equal "", @ui.error assert_equal "", @ui.error
end 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 def test_execute_no_prefix
@cmd.options[:args] = %w[foo] @cmd.options[:args] = %w[foo]
@cmd.options[:prefix] = false @cmd.options[:prefix] = false
@ -183,13 +217,22 @@ lib/foo.rb
assert @cmd.options[:prefix] assert @cmd.options[:prefix]
assert_empty @cmd.options[:specdirs] assert_empty @cmd.options[:specdirs]
assert_nil @cmd.options[:version] 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] assert @cmd.options[:lib_only]
refute @cmd.options[:prefix] refute @cmd.options[:prefix]
assert_equal %w[foo], @cmd.options[:specdirs] assert_equal %w[foo], @cmd.options[:specdirs]
assert_equal Gem::Requirement.new('0.0.2'), @cmd.options[:version] assert_equal Gem::Requirement.new('0.0.2'), @cmd.options[:version]
assert @cmd.options[:show_install_dir]
end end
end end

Просмотреть файл

@ -28,6 +28,7 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
assert_match %r|RUBYGEMS PREFIX: |, @ui.output assert_match %r|RUBYGEMS PREFIX: |, @ui.output
assert_match %r|RUBY EXECUTABLE:.*#{RbConfig::CONFIG['ruby_install_name']}|, assert_match %r|RUBY EXECUTABLE:.*#{RbConfig::CONFIG['ruby_install_name']}|,
@ui.output @ui.output
assert_match %r|SYSTEM CONFIGURATION DIRECTORY:|, @ui.output
assert_match %r|EXECUTABLE DIRECTORY:|, @ui.output assert_match %r|EXECUTABLE DIRECTORY:|, @ui.output
assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output
assert_match %r|- #{Gem::Platform.local}|, @ui.output assert_match %r|- #{Gem::Platform.local}|, @ui.output

Просмотреть файл

@ -22,6 +22,13 @@ class TestGemCommandsHelpCommand < Gem::TestCase
end end
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 def test_gem_help_platforms
util_gem 'platforms' do |out, err| util_gem 'platforms' do |out, err|
assert_match(/x86-freebsd/, out) assert_match(/x86-freebsd/, out)

Просмотреть файл

@ -24,6 +24,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
Gem::Command.build_args = @orig_args Gem::Command.build_args = @orig_args
File.unlink @gemdeps if File.file? @gemdeps File.unlink @gemdeps if File.file? @gemdeps
File.unlink "#{@gemdeps}.lock" if File.file? "#{@gemdeps}.lock"
end end
def test_execute_exclude_prerelease 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) assert_match(%r!Unable to download data from http://not-there.nothing!, errs.shift)
end 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 def test_execute_nonexistent_with_hint
misspelled = "nonexistent_with_hint" misspelled = "nonexistent_with_hint"
correctly_spelled = "non_existent_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 assert_equal 2, e.exit_code
end 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" output = @ui.error.split "\n"
@ -535,6 +565,11 @@ ERROR: Possible alternatives: non_existent_with_hint
end end
def test_install_gem_ignore_dependencies_both def test_install_gem_ignore_dependencies_both
done_installing = false
Gem.done_installing do
done_installing = true
end
spec = quick_spec 'a', 2 spec = quick_spec 'a', 2
util_build_gem spec util_build_gem spec
@ -546,6 +581,8 @@ ERROR: Possible alternatives: non_existent_with_hint
@cmd.install_gem 'a', '>= 0' @cmd.install_gem 'a', '>= 0'
assert_equal %w[a-2], @cmd.installed_specs.map { |s| s.full_name } assert_equal %w[a-2], @cmd.installed_specs.map { |s| s.full_name }
assert done_installing, 'documentation was not generated'
end end
def test_install_gem_ignore_dependencies_remote def test_install_gem_ignore_dependencies_remote
@ -622,8 +659,8 @@ ERROR: Possible alternatives: non_existent_with_hint
end end
assert_equal 2, e.exit_code 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 end
def test_show_source_problems_even_on_success def test_show_source_problems_even_on_success
@ -648,7 +685,7 @@ ERROR: Possible alternatives: non_existent_with_hint
e = @ui.error 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 assert_equal x, e
end end
@ -672,6 +709,56 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name } assert_equal %w[], @cmd.installed_specs.map { |spec| spec.full_name }
assert_match "Using a (2)", @ui.output 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 end
def test_execute_installs_from_a_gemdeps 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] assert_equal 'gem.deps.rb', @cmd.options[:gemdeps]
end 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 def test_handle_options_without
@cmd.handle_options %w[--without test] @cmd.handle_options %w[--without test]

Просмотреть файл

@ -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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше