summaryrefslogtreecommitdiff
path: root/lib/rubygems/commands
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-11-10 07:48:56 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2007-11-10 07:48:56 +0000
commitfbf59bdbea63efd34ccc144e648467d2f52e7345 (patch)
tree244f0e7ae112cc7dd135e5d1ac24e6c70ba71b4a /lib/rubygems/commands
parent7a4aad75356496559460041a6c063bdb736c7236 (diff)
Import RubyGems trunk revision 1493.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@13862 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rubygems/commands')
-rw-r--r--lib/rubygems/commands/build_command.rb53
-rw-r--r--lib/rubygems/commands/cert_command.rb86
-rw-r--r--lib/rubygems/commands/check_command.rb74
-rw-r--r--lib/rubygems/commands/cleanup_command.rb93
-rw-r--r--lib/rubygems/commands/contents_command.rb74
-rw-r--r--lib/rubygems/commands/dependency_command.rb150
-rw-r--r--lib/rubygems/commands/environment_command.rb80
-rw-r--r--lib/rubygems/commands/fetch_command.rb62
-rw-r--r--lib/rubygems/commands/generate_index_command.rb57
-rw-r--r--lib/rubygems/commands/help_command.rb172
-rw-r--r--lib/rubygems/commands/install_command.rb125
-rw-r--r--lib/rubygems/commands/list_command.rb35
-rw-r--r--lib/rubygems/commands/lock_command.rb101
-rw-r--r--lib/rubygems/commands/mirror_command.rb105
-rw-r--r--lib/rubygems/commands/outdated_command.rb30
-rw-r--r--lib/rubygems/commands/pristine_command.rb133
-rw-r--r--lib/rubygems/commands/query_command.rb118
-rw-r--r--lib/rubygems/commands/rdoc_command.rb78
-rw-r--r--lib/rubygems/commands/search_command.rb37
-rw-r--r--lib/rubygems/commands/server_command.rb48
-rw-r--r--lib/rubygems/commands/sources_command.rb115
-rw-r--r--lib/rubygems/commands/specification_command.rb72
-rw-r--r--lib/rubygems/commands/uninstall_command.rb56
-rw-r--r--lib/rubygems/commands/unpack_command.rb76
-rw-r--r--lib/rubygems/commands/update_command.rb149
-rw-r--r--lib/rubygems/commands/which_command.rb86
26 files changed, 2265 insertions, 0 deletions
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
new file mode 100644
index 00000000000..c2e1abc92f8
--- /dev/null
+++ b/lib/rubygems/commands/build_command.rb
@@ -0,0 +1,53 @@
+require 'rubygems/command'
+require 'rubygems/builder'
+
+class Gem::Commands::BuildCommand < Gem::Command
+
+ def initialize
+ super('build', 'Build a gem from a gemspec')
+ end
+
+ def arguments # :nodoc:
+ "GEMSPEC_FILE gemspec file name to build a gem for"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMSPEC_FILE"
+ end
+
+ def execute
+ gemspec = get_one_gem_name
+ if File.exist?(gemspec)
+ specs = load_gemspecs(gemspec)
+ specs.each do |spec|
+ Gem::Builder.new(spec).build
+ end
+ else
+ alert_error "Gemspec file not found: #{gemspec}"
+ end
+ end
+
+ def load_gemspecs(filename)
+ if yaml?(filename)
+ result = []
+ open(filename) do |f|
+ begin
+ while not f.eof? and spec = Gem::Specification.from_yaml(f)
+ result << spec
+ end
+ rescue Gem::EndOfYAMLException => e
+ # OK
+ end
+ end
+ else
+ result = [Gem::Specification.load(filename)]
+ end
+ result
+ end
+
+ def yaml?(filename)
+ line = open(filename) { |f| line = f.gets }
+ result = line =~ %r{^--- *!ruby/object:Gem::Specification}
+ result
+ end
+end
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
new file mode 100644
index 00000000000..2c320992548
--- /dev/null
+++ b/lib/rubygems/commands/cert_command.rb
@@ -0,0 +1,86 @@
+require 'rubygems/command'
+require 'rubygems/security'
+
+class Gem::Commands::CertCommand < Gem::Command
+
+ def initialize
+ super 'cert', 'Manage RubyGems certificates and signing settings'
+
+ add_option('-a', '--add CERT',
+ 'Add a trusted certificate.') do |value, options|
+ cert = OpenSSL::X509::Certificate.new(File.read(value))
+ Gem::Security.add_trusted_cert(cert)
+ say "Added '#{cert.subject.to_s}'"
+ end
+
+ add_option('-l', '--list',
+ 'List trusted certificates.') do |value, options|
+ glob_str = File::join(Gem::Security::OPT[:trust_dir], '*.pem')
+ Dir::glob(glob_str) do |path|
+ begin
+ cert = OpenSSL::X509::Certificate.new(File.read(path))
+ # this could proably be formatted more gracefully
+ say cert.subject.to_s
+ rescue OpenSSL::X509::CertificateError
+ next
+ end
+ end
+ end
+
+ add_option('-r', '--remove STRING',
+ 'Remove trusted certificates containing',
+ 'STRING.') do |value, options|
+ trust_dir = Gem::Security::OPT[:trust_dir]
+ glob_str = File::join(trust_dir, '*.pem')
+
+ Dir::glob(glob_str) do |path|
+ begin
+ cert = OpenSSL::X509::Certificate.new(File.read(path))
+ if cert.subject.to_s.downcase.index(value)
+ say "Removed '#{cert.subject.to_s}'"
+ File.unlink(path)
+ end
+ rescue OpenSSL::X509::CertificateError
+ next
+ end
+ end
+ end
+
+ add_option('-b', '--build EMAIL_ADDR',
+ 'Build private key and self-signed',
+ 'certificate for EMAIL_ADDR.') do |value, options|
+ vals = Gem::Security.build_self_signed_cert(value)
+ File.chmod 0600, vals[:key_path]
+ say "Public Cert: #{vals[:cert_path]}"
+ say "Private Key: #{vals[:key_path]}"
+ say "Don't forget to move the key file to somewhere private..."
+ end
+
+ add_option('-C', '--certificate CERT',
+ 'Certificate for --sign command.') do |value, options|
+ cert = OpenSSL::X509::Certificate.new(File.read(value))
+ Gem::Security::OPT[:issuer_cert] = cert
+ end
+
+ add_option('-K', '--private-key KEY',
+ 'Private key for --sign command.') do |value, options|
+ key = OpenSSL::PKey::RSA.new(File.read(value))
+ Gem::Security::OPT[:issuer_key] = key
+ end
+
+ add_option('-s', '--sign NEWCERT',
+ 'Sign a certificate with my key and',
+ 'certificate.') do |value, options|
+ cert = OpenSSL::X509::Certificate.new(File.read(value))
+ my_cert = Gem::Security::OPT[:issuer_cert]
+ my_key = Gem::Security::OPT[:issuer_key]
+ cert = Gem::Security.sign_cert(cert, my_key, my_cert)
+ File.open(value, 'wb') { |file| file.write(cert.to_pem) }
+ end
+ end
+
+ def execute
+ end
+
+end
+
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
new file mode 100644
index 00000000000..ca5e14b12d8
--- /dev/null
+++ b/lib/rubygems/commands/check_command.rb
@@ -0,0 +1,74 @@
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/validator'
+
+class Gem::Commands::CheckCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'check', 'Check installed gems',
+ :verify => false, :alien => false
+
+ add_option( '--verify FILE',
+ 'Verify gem file against its internal',
+ 'checksum') do |value, options|
+ options[:verify] = value
+ end
+
+ add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the",
+ "gem repository") do |value, options|
+ options[:alien] = true
+ end
+
+ add_option('-t', '--test', "Run unit tests for gem") do |value, options|
+ options[:test] = true
+ end
+
+ add_version_option 'run tests for'
+ end
+
+ def execute
+ if options[:test]
+ version = options[:version] || Gem::Requirement.default
+ gem_spec = Gem::SourceIndex.from_installed_gems.search(get_one_gem_name, version).first
+ Gem::Validator.new.unit_test(gem_spec)
+ end
+
+ if options[:alien]
+ say "Performing the 'alien' operation"
+ Gem::Validator.new.alien.each do |key, val|
+ if(val.size > 0)
+ say "#{key} has #{val.size} problems"
+ val.each do |error_entry|
+ say "\t#{error_entry.path}:"
+ say "\t#{error_entry.problem}"
+ say
+ end
+ else
+ say "#{key} is error-free"
+ end
+ say
+ end
+ end
+
+ if options[:verify]
+ gem_name = options[:verify]
+ unless gem_name
+ alert_error "Must specify a .gem file with --verify NAME"
+ return
+ end
+ unless File.exist?(gem_name)
+ alert_error "Unknown file: #{gem_name}."
+ return
+ end
+ say "Verifying gem: '#{gem_name}'"
+ begin
+ Gem::Validator.new.verify_gem_file(gem_name)
+ rescue Exception => e
+ alert_error "#{gem_name} is invalid."
+ end
+ end
+ end
+
+end
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
new file mode 100644
index 00000000000..f6deac98297
--- /dev/null
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -0,0 +1,93 @@
+require 'rubygems/command'
+require 'rubygems/source_index'
+require 'rubygems/dependency_list'
+
+module Gem
+ module Commands
+ class CleanupCommand < Command
+ def initialize
+ super(
+ 'cleanup',
+ 'Clean up old versions of installed gems in the local repository',
+ {
+ :force => false,
+ :test => false,
+ :install_dir => Gem.dir
+ })
+ add_option('-d', '--dryrun', "") do |value, options|
+ options[:dryrun] = true
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to cleanup"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-dryrun"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [GEMNAME ...]"
+ end
+
+ def execute
+ say "Cleaning up installed gems..."
+ srcindex = Gem::SourceIndex.from_installed_gems
+ primary_gems = {}
+
+ srcindex.each do |name, spec|
+ if primary_gems[spec.name].nil? or primary_gems[spec.name].version < spec.version
+ primary_gems[spec.name] = spec
+ end
+ end
+
+ gems_to_cleanup = []
+
+ unless options[:args].empty? then
+ options[:args].each do |gem_name|
+ specs = Gem.cache.search(/^#{gem_name}$/i)
+ specs.each do |spec|
+ gems_to_cleanup << spec
+ end
+ end
+ else
+ srcindex.each do |name, spec|
+ gems_to_cleanup << spec
+ end
+ end
+
+ gems_to_cleanup = gems_to_cleanup.select { |spec|
+ primary_gems[spec.name].version != spec.version
+ }
+
+ uninstall_command = Gem::CommandManager.instance['uninstall']
+ deplist = DependencyList.new
+ gems_to_cleanup.uniq.each do |spec| deplist.add(spec) end
+
+ deplist.dependency_order.each do |spec|
+ if options[:dryrun] then
+ say "Dry Run Mode: Would uninstall #{spec.full_name}"
+ else
+ say "Attempting uninstall on #{spec.full_name}"
+
+ options[:args] = [spec.name]
+ options[:version] = "= #{spec.version}"
+ options[:executables] = true
+
+ uninstall_command.merge_options(options)
+
+ begin
+ uninstall_command.execute
+ rescue Gem::DependencyRemovalException => ex
+ say "Unable to uninstall #{spec.full_name} ... continuing with remaining gems"
+ end
+ end
+ end
+
+ say "Clean Up Complete"
+ end
+ end
+
+ end
+end
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
new file mode 100644
index 00000000000..5060403fd82
--- /dev/null
+++ b/lib/rubygems/commands/contents_command.rb
@@ -0,0 +1,74 @@
+require 'rubygems/command'
+require 'rubygems/version_option'
+
+class Gem::Commands::ContentsCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'contents', 'Display the contents of the installed gems',
+ :specdirs => [], :lib_only => false
+
+ add_version_option
+
+ add_option('-s', '--spec-dir a,b,c', Array,
+ "Search for gems under specific paths") do |spec_dirs, options|
+ options[:specdirs] = spec_dirs
+ end
+
+ add_option('-l', '--[no-]lib-only',
+ "Only return files in the Gem's lib_dirs") do |lib_only, options|
+ options[:lib_only] = lib_only
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to list contents for"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-lib-only"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME"
+ end
+
+ def execute
+ version = options[:version] || Gem::Requirement.default
+ gem = get_one_gem_name
+
+ s = options[:specdirs].map do |i|
+ [i, File.join(i, "specifications")]
+ end.flatten
+
+ path_kind = if s.empty? then
+ s = Gem::SourceIndex.installed_spec_directories
+ "default gem paths"
+ else
+ "specified path"
+ end
+
+ si = Gem::SourceIndex.from_gems_in(*s)
+
+ gem_spec = si.search(/\A#{gem}\z/, version).last
+
+ unless gem_spec then
+ say "Unable to find gem '#{gem}' in #{path_kind}"
+
+ if Gem.configuration.verbose then
+ say "\nDirectories searched:"
+ s.each { |dir| say dir }
+ end
+
+ terminate_interaction
+ end
+
+ files = options[:lib_only] ? gem_spec.lib_files : gem_spec.files
+ files.each do |f|
+ say File.join(gem_spec.full_gem_path, f)
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
new file mode 100644
index 00000000000..1a43505d7c3
--- /dev/null
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -0,0 +1,150 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/version_option'
+require 'rubygems/source_info_cache'
+
+class Gem::Commands::DependencyCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super 'dependency',
+ 'Show the dependencies of an installed gem',
+ :version => Gem::Requirement.default, :domain => :local
+
+ add_version_option
+ add_platform_option
+
+ add_option('-R', '--[no-]reverse-dependencies',
+ 'Include reverse dependencies in the output') do
+ |value, options|
+ options[:reverse_dependencies] = value
+ end
+
+ add_option('-p', '--pipe',
+ "Pipe Format (name --version ver)") do |value, options|
+ options[:pipe_format] = value
+ end
+
+ add_local_remote_options
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to show dependencies for"
+ end
+
+ def defaults_str # :nodoc:
+ "--local --version '#{Gem::Requirement.default}' --no-reverse-dependencies"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME"
+ end
+
+ def execute
+ options[:args] << '.' if options[:args].empty?
+ specs = {}
+
+ source_indexes = []
+
+ if local? then
+ source_indexes << Gem::SourceIndex.from_installed_gems
+ end
+
+ if remote? then
+ Gem::SourceInfoCache.cache_data.map do |_, sice|
+ source_indexes << sice.source_index
+ end
+ end
+
+ options[:args].each do |name|
+ new_specs = nil
+ source_indexes.each do |source_index|
+ new_specs = find_gems(name, source_index)
+ end
+
+ say "No match found for #{name} (#{options[:version]})" if
+ new_specs.empty?
+
+ specs = specs.merge new_specs
+ end
+
+ terminate_interaction 1 if specs.empty?
+
+ reverse = Hash.new { |h, k| h[k] = [] }
+
+ if options[:reverse_dependencies] then
+ specs.values.each do |source_index, spec|
+ reverse[spec.full_name] = find_reverse_dependencies spec, source_index
+ end
+ end
+
+ if options[:pipe_format] then
+ specs.values.sort_by { |_, spec| spec }.each do |_, spec|
+ unless spec.dependencies.empty?
+ spec.dependencies.each do |dep|
+ say "#{dep.name} --version '#{dep.version_requirements}'"
+ end
+ end
+ end
+ else
+ response = ''
+
+ specs.values.sort_by { |_, spec| spec }.each do |_, spec|
+ response << print_dependencies(spec)
+ unless reverse[spec.full_name].empty? then
+ response << " Used by\n"
+ reverse[spec.full_name].each do |sp, dep|
+ response << " #{sp} (#{dep})\n"
+ end
+ end
+ response << "\n"
+ end
+
+ say response
+ end
+ end
+
+ def print_dependencies(spec, level = 0)
+ response = ''
+ response << ' ' * level + "Gem #{spec.full_name}\n"
+ unless spec.dependencies.empty? then
+ spec.dependencies.each do |dep|
+ response << ' ' * level + " #{dep}\n"
+ end
+ end
+ response
+ end
+
+ # Retuns list of [specification, dep] that are satisfied by spec.
+ def find_reverse_dependencies(spec, source_index)
+ result = []
+
+ source_index.each do |name, sp|
+ sp.dependencies.each do |dep|
+ dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
+
+ if spec.name == dep.name and
+ dep.version_requirements.satisfied_by?(spec.version) then
+ result << [sp.full_name, dep]
+ end
+ end
+ end
+
+ result
+ end
+
+ def find_gems(name, source_index)
+ specs = {}
+
+ spec_list = source_index.search name, options[:version]
+
+ spec_list.each do |spec|
+ specs[spec.full_name] = [source_index, spec]
+ end
+
+ specs
+ end
+end
+
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
new file mode 100644
index 00000000000..337d74893b1
--- /dev/null
+++ b/lib/rubygems/commands/environment_command.rb
@@ -0,0 +1,80 @@
+require 'rubygems/command'
+
+class Gem::Commands::EnvironmentCommand < Gem::Command
+
+ def initialize
+ super 'environment', 'Display information about the RubyGems environment'
+ end
+
+ def arguments # :nodoc:
+ args = <<-EOF
+ packageversion display the package version
+ gemdir display the path where gems are installed
+ gempath display path used to search for gems
+ version display the gem format version
+ remotesources display the remote gem servers
+ <omitted> display everything
+ EOF
+ return args.gsub(/^\s+/, '')
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [arg]"
+ end
+
+ def execute
+ out = ''
+ arg = options[:args][0]
+ if begins?("packageversion", arg) then
+ out << Gem::RubyGemsPackageVersion
+ elsif begins?("version", arg) then
+ out << Gem::RubyGemsVersion
+ elsif begins?("gemdir", arg) then
+ out << Gem.dir
+ elsif begins?("gempath", arg) then
+ out << Gem.path.join("\n")
+ elsif begins?("remotesources", arg) then
+ out << Gem.sources.join("\n")
+ elsif arg then
+ fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
+ else
+ out = "RubyGems Environment:\n"
+
+ out << " - RUBYGEMS VERSION: #{Gem::RubyGemsVersion} (#{Gem::RubyGemsPackageVersion})\n"
+
+ out << " - RUBY VERSION: #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
+ out << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
+ out << ") [#{RUBY_PLATFORM}]\n"
+
+ out << " - INSTALLATION DIRECTORY: #{Gem.dir}\n"
+
+ out << " - RUBYGEMS PREFIX: #{Gem.prefix}\n" unless Gem.prefix.nil?
+
+ out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
+
+ out << " - RUBYGEMS PLATFORMS:\n"
+ Gem.platforms.each do |platform|
+ out << " - #{platform}\n"
+ end
+
+ out << " - GEM PATHS:\n"
+ Gem.path.each do |p|
+ out << " - #{p}\n"
+ end
+
+ out << " - GEM CONFIGURATION:\n"
+ Gem.configuration.each do |name, value|
+ out << " - #{name.inspect} => #{value.inspect}\n"
+ end
+
+ out << " - REMOTE SOURCES:\n"
+ Gem.sources.each do |s|
+ out << " - #{s}\n"
+ end
+ end
+ say out
+ true
+ end
+
+end
+
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
new file mode 100644
index 00000000000..7db365eba01
--- /dev/null
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -0,0 +1,62 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/version_option'
+require 'rubygems/source_info_cache'
+
+class Gem::Commands::FetchCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super 'fetch', 'Download a gem and place it in the current directory'
+
+ add_bulk_threshold_option
+ add_proxy_option
+ add_source_option
+
+ add_version_option
+ add_platform_option
+ end
+
+ def arguments # :nodoc:
+ 'GEMNAME name of gem to download'
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}'"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...]"
+ end
+
+ def execute
+ version = options[:version] || Gem::Requirement.default
+
+ gem_names = get_all_gem_names
+
+ gem_names.each do |gem_name|
+ dep = Gem::Dependency.new gem_name, version
+ specs_and_sources = Gem::SourceInfoCache.search_with_source dep, true
+
+ specs_and_sources.sort_by { |spec,| spec.version }
+
+ spec, source_uri = specs_and_sources.last
+
+ gem_file = "#{spec.full_name}.gem"
+
+ gem_path = File.join source_uri, 'gems', gem_file
+
+ gem = Gem::RemoteFetcher.fetcher.fetch_path gem_path
+
+ File.open gem_file, 'wb' do |fp|
+ fp.write gem
+ end
+
+ say "Downloaded #{gem_file}"
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb
new file mode 100644
index 00000000000..1bd87569ed8
--- /dev/null
+++ b/lib/rubygems/commands/generate_index_command.rb
@@ -0,0 +1,57 @@
+require 'rubygems/command'
+require 'rubygems/indexer'
+
+class Gem::Commands::GenerateIndexCommand < Gem::Command
+
+ def initialize
+ super 'generate_index',
+ 'Generates the index files for a gem server directory',
+ :directory => '.'
+
+ add_option '-d', '--directory=DIRNAME',
+ 'repository base dir containing gems subdir' do |dir, options|
+ options[:directory] = File.expand_path dir
+ end
+ end
+
+ def defaults_str # :nodoc:
+ "--directory ."
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The generate_index command creates a set of indexes for serving gems
+statically. The command expects a 'gems' directory under the path given to
+the --directory option. When done, it will generate a set of files like this:
+
+ gems/ # .gem files you want to index
+ quick/index
+ quick/index.rz # quick index manifest
+ quick/<gemname>.gemspec.rz # legacy YAML quick index file
+ quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file
+ Marshal.<version>
+ Marshal.<version>.Z # Marshal full index
+ yaml
+ yaml.Z # legacy YAML full index
+
+The .Z and .rz extension files are compressed with the inflate algorithm. The
+Marshal version number comes from ruby's Marshal::MAJOR_VERSION and
+Marshal::MINOR_VERSION constants. It is used to ensure compatibility. The
+yaml indexes exist for legacy RubyGems clients and fallback in case of Marshal
+version changes.
+ EOF
+ end
+
+ def execute
+ if not File.exist?(options[:directory]) or
+ not File.directory?(options[:directory]) then
+ alert_error "unknown directory name #{directory}."
+ terminate_interaction 1
+ else
+ indexer = Gem::Indexer.new options[:directory]
+ indexer.generate_index
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
new file mode 100644
index 00000000000..05ea3f7a717
--- /dev/null
+++ b/lib/rubygems/commands/help_command.rb
@@ -0,0 +1,172 @@
+require 'rubygems/command'
+
+class Gem::Commands::HelpCommand < Gem::Command
+
+ # :stopdoc:
+ EXAMPLES = <<-EOF
+Some examples of 'gem' usage.
+
+* Install 'rake', either from local directory or remote server:
+
+ gem install rake
+
+* Install 'rake', only from remote server:
+
+ gem install rake --remote
+
+* Install 'rake' from remote server, and run unit tests,
+ and generate RDocs:
+
+ gem install --remote rake --test --rdoc --ri
+
+* Install 'rake', but only version 0.3.1, even if dependencies
+ are not met, and into a specific directory:
+
+ gem install rake --version 0.3.1 --force --install-dir $HOME/.gems
+
+* List local gems whose name begins with 'D':
+
+ gem list D
+
+* List local and remote gems whose name contains 'log':
+
+ gem search log --both
+
+* List only remote gems whose name contains 'log':
+
+ gem search log --remote
+
+* Uninstall 'rake':
+
+ gem uninstall rake
+
+* Create a gem:
+
+ See http://rubygems.rubyforge.org/wiki/wiki.pl?CreateAGemInTenMinutes
+
+* See information about RubyGems:
+
+ gem environment
+
+* Update all gems on your system:
+
+ gem update
+ EOF
+
+ PLATFORMS = <<-'EOF'
+RubyGems platforms are composed of three parts, a CPU, an OS, and a
+version. These values are taken from values in rbconfig.rb. You can view
+your current platform by running `gem environment`.
+
+RubyGems matches platforms as follows:
+
+ * The CPU must match exactly, unless one of the platforms has
+ "universal" as the CPU.
+ * The OS must match exactly.
+ * The versions must match exactly unless one of the versions is nil.
+
+For commands that install, uninstall and list gems, you can override what
+RubyGems thinks your platform is with the --platform option. The platform
+you pass must match "#{cpu}-#{os}" or "#{cpu}-#{os}-#{version}". On mswin
+platforms, the version is the compiler version, not the OS version. (Ruby
+compiled with VC6 uses "60" as the compiler version, VC8 uses "80".)
+
+Example platforms:
+
+ x86-freebsd # Any FreeBSD version on an x86 CPU
+ universal-darwin-8 # Darwin 8 only gems that run on any CPU
+ x86-mswin32-80 # Windows gems compiled with VC8
+
+When building platform gems, set the platform in the gem specification to
+Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's
+platform.
+ EOF
+ # :startdoc:
+
+ def initialize
+ super 'help', "Provide help on the 'gem' command"
+ end
+
+ def arguments # :nodoc:
+ args = <<-EOF
+ commands List all 'gem' commands
+ examples Show examples of 'gem' usage
+ <command> Show specific help for <command>
+ EOF
+ return args.gsub(/^\s+/, '')
+ end
+
+ def usage # :nodoc:
+ "#{program_name} ARGUMENT"
+ end
+
+ def execute
+ command_manager = Gem::CommandManager.instance
+ arg = options[:args][0]
+
+ if begins? "commands", arg then
+ out = []
+ out << "GEM commands are:"
+ out << nil
+
+ margin_width = 4
+
+ desc_width = command_manager.command_names.map { |n| n.size }.max + 4
+
+ summary_width = 80 - margin_width - desc_width
+ wrap_indent = ' ' * (margin_width + desc_width)
+ format = "#{' ' * margin_width}%-#{desc_width}s%s"
+
+ command_manager.command_names.each do |cmd_name|
+ summary = command_manager[cmd_name].summary
+ summary = wrap(summary, summary_width).split "\n"
+ out << sprintf(format, cmd_name, summary.shift)
+ until summary.empty? do
+ out << "#{wrap_indent}#{summary.shift}"
+ end
+ end
+
+ out << nil
+ out << "For help on a particular command, use 'gem help COMMAND'."
+ out << nil
+ out << "Commands may be abbreviated, so long as they are unambiguous."
+ out << "e.g. 'gem i rake' is short for 'gem install rake'."
+
+ say out.join("\n")
+
+ elsif begins? "options", arg then
+ say Gem::Command::HELP
+
+ elsif begins? "examples", arg then
+ say EXAMPLES
+
+ elsif begins? "platforms", arg then
+ say PLATFORMS
+
+ elsif options[:help] then
+ command = command_manager[options[:help]]
+ if command
+ # help with provided command
+ command.invoke("--help")
+ else
+ alert_error "Unknown command #{options[:help]}. Try 'gem help commands'"
+ end
+
+ elsif arg then
+ possibilities = command_manager.find_command_possibilities(arg.downcase)
+ if possibilities.size == 1
+ command = command_manager[possibilities.first]
+ command.invoke("--help")
+ elsif possibilities.size > 1
+ alert_warning "Ambiguous command #{arg} (#{possibilities.join(', ')})"
+ else
+ alert_warning "Unknown command #{arg}. Try gem help commands"
+ end
+
+ else
+ say Gem::Command::HELP
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
new file mode 100644
index 00000000000..4c67c0487b4
--- /dev/null
+++ b/lib/rubygems/commands/install_command.rb
@@ -0,0 +1,125 @@
+require 'rubygems/command'
+require 'rubygems/doc_manager'
+require 'rubygems/install_update_options'
+require 'rubygems/dependency_installer'
+require 'rubygems/local_remote_options'
+require 'rubygems/validator'
+require 'rubygems/version_option'
+
+class Gem::Commands::InstallCommand < Gem::Command
+
+ include Gem::VersionOption
+ include Gem::LocalRemoteOptions
+ include Gem::InstallUpdateOptions
+
+ def initialize
+ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
+ :generate_rdoc => true,
+ :generate_ri => true,
+ :install_dir => Gem.dir,
+ :test => false,
+ :version => Gem::Requirement.default,
+ })
+
+ super 'install', 'Install a gem into the local repository', defaults
+
+ add_install_update_options
+ add_local_remote_options
+ add_platform_option
+ add_version_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to install"
+ end
+
+ def defaults_str # :nodoc:
+ "--both --version '#{Gem::Requirement.default}' --rdoc --ri --no-force\n" \
+ "--no-test --install-dir #{Gem.dir}"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags"
+ end
+
+ def execute
+ if options[:include_dependencies] then
+ alert "`gem install -y` is now default and will be removed"
+ alert "use --ignore-dependencies to install only the gems you list"
+ end
+
+ installed_gems = []
+
+ ENV['GEM_PATH'] = options[:install_dir] # HACK what does this do?
+
+ install_options = {
+ :env_shebang => options[:env_shebang],
+ :domain => options[:domain],
+ :force => options[:force],
+ :ignore_dependencies => options[:ignore_dependencies],
+ :install_dir => options[:install_dir],
+ :security_policy => options[:security_policy],
+ :wrappers => options[:wrappers],
+ }
+
+ get_all_gem_names.each do |gem_name|
+ begin
+ inst = Gem::DependencyInstaller.new gem_name, options[:version],
+ install_options
+ inst.install
+
+ inst.installed_gems.each do |spec|
+ say "Successfully installed #{spec.full_name}"
+ end
+
+ installed_gems.push(*inst.installed_gems)
+ rescue Gem::InstallError => e
+ alert_error "Error installing #{gem_name}:\n\t#{e.message}"
+ rescue Gem::GemNotFoundException => e
+ alert_error e.message
+# rescue => e
+# # TODO: Fix this handle to allow the error to propagate to
+# # the top level handler. Examine the other errors as
+# # well. This implementation here looks suspicious to me --
+# # JimWeirich (4/Jan/05)
+# alert_error "Error installing gem #{gem_name}: #{e.message}"
+# return
+ end
+ end
+
+ unless installed_gems.empty? then
+ gems = installed_gems.length == 1 ? 'gem' : 'gems'
+ say "#{installed_gems.length} #{gems} installed"
+ end
+
+ # NOTE: *All* of the RI documents must be generated first.
+ # For some reason, RI docs cannot be generated after any RDoc
+ # documents are generated.
+
+ if options[:generate_ri] then
+ installed_gems.each do |gem|
+ Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri
+ end
+ end
+
+ if options[:generate_rdoc] then
+ installed_gems.each do |gem|
+ Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc
+ end
+ end
+
+ if options[:test] then
+ installed_gems.each do |spec|
+ gem_spec = Gem::SourceIndex.from_installed_gems.search(spec.name, spec.version.version).first
+ result = Gem::Validator.new.unit_test(gem_spec)
+ if result and not result.passed?
+ unless ask_yes_no("...keep Gem?", true) then
+ Gem::Uninstaller.new(spec.name, :version => spec.version.version).uninstall
+ end
+ end
+ end
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
new file mode 100644
index 00000000000..e179ff57ee8
--- /dev/null
+++ b/lib/rubygems/commands/list_command.rb
@@ -0,0 +1,35 @@
+require 'rubygems/command'
+require 'rubygems/commands/query_command'
+
+module Gem
+ module Commands
+ class ListCommand < QueryCommand
+
+ def initialize
+ super(
+ 'list',
+ 'Display all gems whose name starts with STRING'
+ )
+ remove_option('--name-matches')
+ end
+
+ def arguments # :nodoc:
+ "STRING start of gem name to look for"
+ end
+
+ def defaults_str # :nodoc:
+ "--local --no-details"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [STRING]"
+ end
+
+ def execute
+ string = get_one_optional_argument || ''
+ options[:name] = /^#{string}/i
+ super
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
new file mode 100644
index 00000000000..3a3dcc0c6b5
--- /dev/null
+++ b/lib/rubygems/commands/lock_command.rb
@@ -0,0 +1,101 @@
+require 'rubygems/command'
+
+class Gem::Commands::LockCommand < Gem::Command
+
+ def initialize
+ super 'lock', 'Generate a lockdown list of gems',
+ :strict => false
+
+ add_option '-s', '--[no-]strict',
+ 'fail if unable to satisfy a dependency' do |strict, options|
+ options[:strict] = strict
+ end
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to lock\nVERSION version of gem to lock"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-strict"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The lock command will generate a list of +gem+ statements that will lock down
+the versions for the gem given in the command line. It will specify exact
+versions in the requirements list to ensure that the gems loaded will always
+be consistent. A full recursive search of all effected gems will be
+generated.
+
+Example:
+
+ gemlock rails-1.0.0 > lockdown.rb
+
+will produce in lockdown.rb:
+
+ require "rubygems"
+ gem 'rails', '= 1.0.0'
+ gem 'rake', '= 0.7.0.1'
+ gem 'activesupport', '= 1.2.5'
+ gem 'activerecord', '= 1.13.2'
+ gem 'actionpack', '= 1.11.2'
+ gem 'actionmailer', '= 1.1.5'
+ gem 'actionwebservice', '= 1.0.0'
+
+Just load lockdown.rb from your application to ensure that the current
+versions are loaded. Make sure that lockdown.rb is loaded *before* any
+other require statements.
+
+Notice that rails 1.0.0 only requires that rake 0.6.2 or better be used.
+Rake-0.7.0.1 is the most recent version installed that satisfies that, so we
+lock it down to the exact version.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME-VERSION [GEMNAME-VERSION ...]"
+ end
+
+ def complain(message)
+ if options.strict then
+ raise message
+ else
+ say "# #{message}"
+ end
+ end
+
+ def execute
+ say 'require "rubygems"'
+
+ locked = {}
+
+ pending = options[:args]
+
+ until pending.empty? do
+ full_name = pending.shift
+
+ spec = Gem::SourceIndex.load_specification spec_path(full_name)
+
+ say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name]
+ locked[spec.name] = true
+
+ spec.dependencies.each do |dep|
+ next if locked[dep.name]
+ candidates = Gem.source_index.search dep.name, dep.requirement_list
+
+ if candidates.empty? then
+ complain "Unable to satisfy '#{dep}' from currently installed gems."
+ else
+ pending << candidates.last.full_name
+ end
+ end
+ end
+ end
+
+ def spec_path(gem_full_name)
+ File.join Gem.path, "specifications", "#{gem_full_name }.gemspec"
+ end
+
+end
+
diff --git a/lib/rubygems/commands/mirror_command.rb b/lib/rubygems/commands/mirror_command.rb
new file mode 100644
index 00000000000..74f6970e9e1
--- /dev/null
+++ b/lib/rubygems/commands/mirror_command.rb
@@ -0,0 +1,105 @@
+require 'yaml'
+require 'zlib'
+
+require 'rubygems/command'
+require 'rubygems/gem_open_uri'
+
+class Gem::Commands::MirrorCommand < Gem::Command
+
+ def initialize
+ super 'mirror', 'Mirror a gem repository'
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The mirror command uses the ~/.gemmirrorrc config file to mirror remote gem
+repositories to a local path. The config file is a YAML document that looks
+like this:
+
+ ---
+ - from: http://gems.example.com # source repository URI
+ to: /path/to/mirror # destination directory
+
+Multiple sources and destinations may be specified.
+ EOF
+ end
+
+ def execute
+ config_file = File.join Gem.user_home, '.gemmirrorrc'
+
+ raise "Config file #{config_file} not found" unless File.exist? config_file
+
+ mirrors = YAML.load_file config_file
+
+ raise "Invalid config file #{config_file}" unless mirrors.respond_to? :each
+
+ mirrors.each do |mir|
+ raise "mirror missing 'from' field" unless mir.has_key? 'from'
+ raise "mirror missing 'to' field" unless mir.has_key? 'to'
+
+ get_from = mir['from']
+ save_to = File.expand_path mir['to']
+
+ raise "Directory not found: #{save_to}" unless File.exist? save_to
+ raise "Not a directory: #{save_to}" unless File.directory? save_to
+
+ gems_dir = File.join save_to, "gems"
+
+ if File.exist? gems_dir then
+ raise "Not a directory: #{gems_dir}" unless File.directory? gems_dir
+ else
+ Dir.mkdir gems_dir
+ end
+
+ sourceindex_data = ''
+
+ say "fetching: #{get_from}/Marshal.#{Gem.marshal_version}.Z"
+
+ get_from = URI.parse get_from
+
+ if get_from.scheme.nil? then
+ get_from = get_from.to_s
+ elsif get_from.scheme == 'file' then
+ get_from = get_from.to_s[5..-1]
+ end
+
+ open File.join(get_from, "Marshal.#{Gem.marshal_version}.Z"), "rb" do |y|
+ sourceindex_data = Zlib::Inflate.inflate y.read
+ open File.join(save_to, "Marshal.#{Gem.marshal_version}"), "wb" do |out|
+ out.write sourceindex_data
+ end
+ end
+
+ sourceindex = Marshal.load(sourceindex_data)
+
+ progress = ui.progress_reporter sourceindex.size,
+ "Fetching #{sourceindex.size} gems"
+ sourceindex.each do |fullname, gem|
+ gem_file = "#{fullname}.gem"
+ gem_dest = File.join gems_dir, gem_file
+
+ unless File.exist? gem_dest then
+ begin
+ open "#{get_from}/gems/#{gem_file}", "rb" do |g|
+ contents = g.read
+ open gem_dest, "wb" do |out|
+ out.write contents
+ end
+ end
+ rescue
+ old_gf = gem_file
+ gem_file = gem_file.downcase
+ retry if old_gf != gem_file
+ alert_error $!
+ end
+ end
+
+ progress.updated gem_file
+ end
+
+ progress.done
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb
new file mode 100644
index 00000000000..9c0062019b7
--- /dev/null
+++ b/lib/rubygems/commands/outdated_command.rb
@@ -0,0 +1,30 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/source_info_cache'
+require 'rubygems/version_option'
+
+class Gem::Commands::OutdatedCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super 'outdated', 'Display all gems that need updates'
+
+ add_local_remote_options
+ add_platform_option
+ end
+
+ def execute
+ locals = Gem::SourceIndex.from_installed_gems
+
+ locals.outdated.sort.each do |name|
+ local = locals.search(/^#{name}$/).last
+ remotes = Gem::SourceInfoCache.search_with_source(/^#{name}$/, true)
+ remote = remotes.last.first
+ say "#{local.name} (#{local.version} < #{remote.version})"
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
new file mode 100644
index 00000000000..2900e7e739d
--- /dev/null
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -0,0 +1,133 @@
+require 'fileutils'
+require 'rubygems/command'
+require 'rubygems/format'
+require 'rubygems/installer'
+require 'rubygems/version_option'
+
+class Gem::Commands::PristineCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'pristine',
+ 'Restores installed gems to pristine condition from files located in the gem cache',
+ :version => Gem::Requirement.default
+
+ add_option('--all',
+ 'Restore all installed gems to pristine',
+ 'condition') do |value, options|
+ options[:all] = value
+ end
+
+ add_version_option('restore to', 'pristine condition')
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME gem to restore to pristine condition (unless --all)"
+ end
+
+ def defaults_str # :nodoc:
+ "--all"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The pristine command compares the installed gems with the contents of the
+cached gem and restores any files that don't match the cached gem's copy.
+
+If you have made modifications to your installed gems, the pristine command
+will revert them. After all the gem's files have been checked all bin stubs
+for the gem are regenerated.
+
+If the cached gem cannot be found, you will need to use `gem install` to
+revert the gem.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [args]"
+ end
+
+ def execute
+ gem_name = nil
+
+ specs = if options[:all] then
+ Gem::SourceIndex.from_installed_gems.map do |name, spec|
+ spec
+ end
+ else
+ gem_name = get_one_gem_name
+ Gem::SourceIndex.from_installed_gems.search(gem_name,
+ options[:version])
+ end
+
+ if specs.empty? then
+ raise Gem::Exception,
+ "Failed to find gem #{gem_name} #{options[:version]}"
+ end
+
+ install_dir = Gem.dir # TODO use installer option
+
+ raise Gem::FilePermissionError.new(install_dir) unless
+ File.writable?(install_dir)
+
+ say "Restoring gem(s) to pristine condition..."
+
+ specs.each do |spec|
+ gem = Dir[File.join(Gem.dir, 'cache', "#{spec.full_name}.gem")].first
+
+ if gem.nil? then
+ alert_error "Cached gem for #{spec.full_name} not found, use `gem install` to restore"
+ next
+ end
+
+ # TODO use installer options
+ installer = Gem::Installer.new gem, :wrappers => true
+
+ gem_file = File.join install_dir, "cache", "#{spec.full_name}.gem"
+
+ security_policy = nil # TODO use installer option
+
+ format = Gem::Format.from_file_by_path gem_file, security_policy
+
+ target_directory = File.join(install_dir, "gems", format.spec.full_name)
+ target_directory.untaint
+
+ pristine_files = format.file_entries.collect { |data| data[0]["path"] }
+ file_map = {}
+
+ format.file_entries.each do |entry, file_data|
+ file_map[entry["path"]] = file_data
+ end
+
+ Dir.chdir target_directory do
+ deployed_files = Dir.glob(File.join("**", "*")) +
+ Dir.glob(File.join("**", ".*"))
+
+ pristine_files = pristine_files.map { |f| File.expand_path f }
+ deployed_files = deployed_files.map { |f| File.expand_path f }
+
+ to_redeploy = (pristine_files - deployed_files)
+ to_redeploy = to_redeploy.map { |path| path.untaint}
+
+ if to_redeploy.length > 0 then
+ say "Restoring #{to_redeploy.length} file#{to_redeploy.length == 1 ? "" : "s"} to #{spec.full_name}..."
+
+ to_redeploy.each do |path|
+ say " #{path}"
+ FileUtils.mkdir_p File.dirname(path)
+ File.open(path, "wb") do |out|
+ out.write file_map[path]
+ end
+ end
+ else
+ say "#{spec.full_name} is in pristine condition"
+ end
+ end
+
+ installer.generate_bin
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
new file mode 100644
index 00000000000..581d4bb734b
--- /dev/null
+++ b/lib/rubygems/commands/query_command.rb
@@ -0,0 +1,118 @@
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/source_info_cache'
+
+class Gem::Commands::QueryCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+
+ def initialize(name = 'query',
+ summary = 'Query gem information in local or remote repositories')
+ super name, summary,
+ :name => /.*/, :domain => :local, :details => false, :versions => true
+
+ add_option('-n', '--name-matches REGEXP',
+ 'Name of gem(s) to query on matches the',
+ 'provided REGEXP') do |value, options|
+ options[:name] = /#{value}/i
+ end
+
+ add_option('-d', '--[no-]details',
+ 'Display detailed information of gem(s)') do |value, options|
+ options[:details] = value
+ end
+
+ add_option( '--[no-]versions',
+ 'Display only gem names') do |value, options|
+ options[:versions] = value
+ options[:details] = false unless value
+ end
+
+ add_local_remote_options
+ end
+
+ def defaults_str # :nodoc:
+ "--local --name-matches '.*' --no-details --versions"
+ end
+
+ def execute
+ name = options[:name]
+
+ if local? then
+ say
+ say "*** LOCAL GEMS ***"
+ say
+ output_query_results Gem.cache.search(name)
+ end
+
+ if remote? then
+ say
+ say "*** REMOTE GEMS ***"
+ say
+ output_query_results Gem::SourceInfoCache.search(name)
+ end
+ end
+
+ private
+
+ def output_query_results(gemspecs)
+ output = []
+ gem_list_with_version = {}
+
+ gemspecs.flatten.each do |gemspec|
+ gem_list_with_version[gemspec.name] ||= []
+ gem_list_with_version[gemspec.name] << gemspec
+ end
+
+ gem_list_with_version = gem_list_with_version.sort_by do |name, spec|
+ name.downcase
+ end
+
+ gem_list_with_version.each do |gem_name, list_of_matching|
+ list_of_matching = list_of_matching.sort_by { |x| x.version.to_ints }.reverse
+ seen_versions = {}
+
+ list_of_matching.delete_if do |item|
+ if seen_versions[item.version] then
+ true
+ else
+ seen_versions[item.version] = true
+ false
+ end
+ end
+
+ entry = gem_name.dup
+ if options[:versions] then
+ entry << " (#{list_of_matching.map{|gem| gem.version.to_s}.join(", ")})"
+ end
+
+ entry << "\n" << format_text(list_of_matching[0].summary, 68, 4) if
+ options[:details]
+ output << entry
+ end
+
+ say output.join(options[:details] ? "\n\n" : "\n")
+ end
+
+ ##
+ # Used for wrapping and indenting text
+ #
+ def format_text(text, wrap, indent=0)
+ result = []
+ work = text.dup
+
+ while work.length > wrap
+ if work =~ /^(.{0,#{wrap}})[ \n]/o then
+ result << $1
+ work.slice!(0, $&.length)
+ else
+ result << work.slice!(0, wrap)
+ end
+ end
+
+ result << work if work.length.nonzero?
+ result.join("\n").gsub(/^/, " " * indent)
+ end
+
+end
+
diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb
new file mode 100644
index 00000000000..f2e677c1159
--- /dev/null
+++ b/lib/rubygems/commands/rdoc_command.rb
@@ -0,0 +1,78 @@
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/doc_manager'
+
+module Gem
+ module Commands
+ class RdocCommand < Command
+ include VersionOption
+
+ def initialize
+ super('rdoc',
+ 'Generates RDoc for pre-installed gems',
+ {
+ :version => Gem::Requirement.default,
+ :include_rdoc => true,
+ :include_ri => true,
+ })
+ add_option('--all',
+ 'Generate RDoc/RI documentation for all',
+ 'installed gems') do |value, options|
+ options[:all] = value
+ end
+ add_option('--[no-]rdoc',
+ 'Include RDoc generated documents') do
+ |value, options|
+ options[:include_rdoc] = value
+ end
+ add_option('--[no-]ri',
+ 'Include RI generated documents'
+ ) do |value, options|
+ options[:include_ri] = value
+ end
+ add_version_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME gem to generate documentation for (unless --all)"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}' --rdoc --ri"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [args]"
+ end
+
+ def execute
+ if options[:all]
+ specs = Gem::SourceIndex.from_installed_gems.collect { |name, spec|
+ spec
+ }
+ else
+ gem_name = get_one_gem_name
+ specs = Gem::SourceIndex.from_installed_gems.search(
+ gem_name, options[:version])
+ end
+
+ if specs.empty?
+ fail "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}"
+ end
+ if options[:include_ri]
+ specs.each do |spec|
+ Gem::DocManager.new(spec).generate_ri
+ end
+ end
+ if options[:include_rdoc]
+ specs.each do |spec|
+ Gem::DocManager.new(spec).generate_rdoc
+ end
+ end
+
+ true
+ end
+ end
+
+ end
+end
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
new file mode 100644
index 00000000000..96da19c0f7d
--- /dev/null
+++ b/lib/rubygems/commands/search_command.rb
@@ -0,0 +1,37 @@
+require 'rubygems/command'
+require 'rubygems/commands/query_command'
+
+module Gem
+ module Commands
+
+ class SearchCommand < QueryCommand
+
+ def initialize
+ super(
+ 'search',
+ 'Display all gems whose name contains STRING'
+ )
+ remove_option('--name-matches')
+ end
+
+ def arguments # :nodoc:
+ "STRING fragment of gem name to search for"
+ end
+
+ def defaults_str # :nodoc:
+ "--local --no-details"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [STRING]"
+ end
+
+ def execute
+ string = get_one_optional_argument
+ options[:name] = /#{string}/i
+ super
+ end
+ end
+
+ end
+end
diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb
new file mode 100644
index 00000000000..34e5e46feca
--- /dev/null
+++ b/lib/rubygems/commands/server_command.rb
@@ -0,0 +1,48 @@
+require 'rubygems/command'
+require 'rubygems/server'
+
+class Gem::Commands::ServerCommand < Gem::Command
+
+ def initialize
+ super 'server', 'Documentation and gem repository HTTP server',
+ :port => 8808, :gemdir => Gem.dir, :daemon => false
+
+ add_option '-p', '--port=PORT',
+ 'port to listen on' do |port, options|
+ options[:port] = port
+ end
+
+ add_option '-d', '--dir=GEMDIR',
+ 'directory from which to serve gems' do |gemdir, options|
+ options[:gemdir] = gemdir
+ end
+
+ add_option '--[no]-daemon', 'run as a daemon' do |daemon, options|
+ options[:daemon] = daemon
+ end
+ end
+
+ def defaults_str # :nodoc:
+ "--port 8808 --dir #{Gem.dir} --no-daemon"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The server command starts up a web server that hosts the RDoc for your
+installed gems and can operate as a server for installation of gems on other
+machines.
+
+The cache files for installed gems must exist to use the server as a source
+for gem installation.
+
+To install gems from a running server, use `gem install GEMNAME --source
+http://gem_server_host:8808`
+ EOF
+ end
+
+ def execute
+ Gem::Server.run options
+ end
+
+end
+
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
new file mode 100644
index 00000000000..def9c01a3f2
--- /dev/null
+++ b/lib/rubygems/commands/sources_command.rb
@@ -0,0 +1,115 @@
+require 'rubygems/command'
+require 'rubygems/remote_fetcher'
+require 'rubygems/source_info_cache'
+require 'rubygems/source_info_cache_entry'
+
+class Gem::Commands::SourcesCommand < Gem::Command
+
+ def initialize
+ super 'sources',
+ 'Manage the sources and cache file RubyGems uses to search for gems'
+
+ add_option '-a', '--add SOURCE_URI', 'Add source' do |value, options|
+ options[:add] = value
+ end
+
+ add_option '-l', '--list', 'List sources' do |value, options|
+ options[:list] = value
+ end
+
+ add_option '-r', '--remove SOURCE_URI', 'Remove source' do |value, options|
+ options[:remove] = value
+ end
+
+ add_option '-u', '--update', 'Update source cache' do |value, options|
+ options[:update] = value
+ end
+
+ add_option '-c', '--clear-all',
+ 'Remove all sources (clear the cache)' do |value, options|
+ options[:clear_all] = value
+ end
+ end
+
+ def defaults_str
+ '--list'
+ end
+
+ def execute
+ options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update])
+
+ if options[:clear_all] then
+ remove_cache_file("user", Gem::SourceInfoCache.user_cache_file)
+ remove_cache_file("system", Gem::SourceInfoCache.system_cache_file)
+ end
+
+ if options[:add] then
+ source_uri = options[:add]
+
+ sice = Gem::SourceInfoCacheEntry.new nil, nil
+ begin
+ sice.refresh source_uri
+
+ Gem::SourceInfoCache.cache_data[source_uri] = sice
+ Gem::SourceInfoCache.cache.update
+ Gem::SourceInfoCache.cache.flush
+
+ Gem.sources << source_uri
+ Gem.configuration.write
+
+ say "#{source_uri} added to sources"
+ rescue URI::Error, ArgumentError
+ say "#{source_uri} is not a URI"
+ rescue Gem::RemoteFetcher::FetchError => e
+ say "Error fetching #{source_uri}:\n\t#{e.message}"
+ end
+ end
+
+ if options[:update] then
+ Gem::SourceInfoCache.cache.refresh
+ Gem::SourceInfoCache.cache.flush
+
+ say "source cache successfully updated"
+ end
+
+ if options[:remove] then
+ source_uri = options[:remove]
+
+ unless Gem.sources.include? source_uri then
+ say "source #{source_uri} not present in cache"
+ else
+ Gem::SourceInfoCache.cache_data.delete source_uri
+ Gem::SourceInfoCache.cache.update
+ Gem::SourceInfoCache.cache.flush
+ Gem.sources.delete source_uri
+ Gem.configuration.write
+
+ say "#{source_uri} removed from sources"
+ end
+ end
+
+ if options[:list] then
+ say "*** CURRENT SOURCES ***"
+ say
+
+ Gem.sources.each do |source_uri|
+ say source_uri
+ end
+ end
+ end
+
+ private
+
+ def remove_cache_file(desc, fn)
+ FileUtils.rm_rf fn rescue nil
+ if ! File.exist?(fn)
+ say "*** Removed #{desc} source cache ***"
+ elsif ! File.writable?(fn)
+ say "*** Unable to remove #{desc} source cache (write protected) ***"
+ else
+ say "*** Unable to remove #{desc} source cache ***"
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
new file mode 100644
index 00000000000..954b38ac377
--- /dev/null
+++ b/lib/rubygems/commands/specification_command.rb
@@ -0,0 +1,72 @@
+require 'yaml'
+require 'rubygems/command'
+require 'rubygems/local_remote_options'
+require 'rubygems/version_option'
+require 'rubygems/source_info_cache'
+
+class Gem::Commands::SpecificationCommand < Gem::Command
+
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super 'specification', 'Display gem specification (in yaml)',
+ :domain => :local, :version => Gem::Requirement.default
+
+ add_version_option('examine')
+ add_platform_option
+
+ add_option('--all', 'Output specifications for all versions of',
+ 'the gem') do |value, options|
+ options[:all] = true
+ end
+
+ add_local_remote_options
+ end
+
+ def arguments # :nodoc:
+ "GEMFILE name of gem to show the gemspec for"
+ end
+
+ def defaults_str # :nodoc:
+ "--local --version '#{Gem::Requirement.default}'"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [GEMFILE]"
+ end
+
+ def execute
+ specs = []
+ gem = get_one_gem_name
+
+ if local? then
+ source_index = Gem::SourceIndex.from_installed_gems
+ specs.push(*source_index.search(/\A#{gem}\z/, options[:version]))
+ end
+
+ if remote? then
+ alert_warning "Remote information is not complete\n\n"
+
+ Gem::SourceInfoCache.cache_data.each do |_,sice|
+ specs.push(*sice.source_index.search(gem, options[:version]))
+ end
+ end
+
+ if specs.empty? then
+ alert_error "Unknown gem '#{gem}'"
+ terminate_interaction 1
+ end
+
+ output = lambda { |spec| say spec.to_yaml; say "\n" }
+
+ if options[:all] then
+ specs.each(&output)
+ else
+ spec = specs.sort_by { |spec| spec.version }.last
+ output[spec]
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
new file mode 100644
index 00000000000..7d2908836c9
--- /dev/null
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -0,0 +1,56 @@
+require 'rubygems/command'
+require 'rubygems/version_option'
+require 'rubygems/uninstaller'
+
+module Gem
+ module Commands
+ class UninstallCommand < Command
+
+ include VersionOption
+
+ def initialize
+ super 'uninstall', 'Uninstall gems from the local repository',
+ :version => Gem::Requirement.default
+
+ add_option('-a', '--[no-]all',
+ 'Uninstall all matching versions'
+ ) do |value, options|
+ options[:all] = value
+ end
+
+ add_option('-i', '--[no-]ignore-dependencies',
+ 'Ignore dependency requirements while',
+ 'uninstalling') do |value, options|
+ options[:ignore] = value
+ end
+
+ add_option('-x', '--[no-]executables',
+ 'Uninstall applicable executables without',
+ 'confirmation') do |value, options|
+ options[:executables] = value
+ end
+
+ add_version_option
+ add_platform_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to uninstall"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}' --no-force"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...]"
+ end
+
+ def execute
+ get_all_gem_names.each do |gem_name|
+ Gem::Uninstaller.new(gem_name, options).uninstall
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
new file mode 100644
index 00000000000..ece24745a29
--- /dev/null
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -0,0 +1,76 @@
+require 'fileutils'
+require 'rubygems/command'
+require 'rubygems/installer'
+require 'rubygems/version_option'
+
+class Gem::Commands::UnpackCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'unpack', 'Unpack an installed gem to the current directory',
+ :version => Gem::Requirement.default
+ add_version_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to unpack"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}'"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME"
+ end
+
+ #--
+ # TODO: allow, e.g., 'gem unpack rake-0.3.1'. Find a general solution for
+ # this, so that it works for uninstall as well. (And check other commands
+ # at the same time.)
+ def execute
+ gemname = get_one_gem_name
+ path = get_path(gemname, options[:version])
+ if path
+ target_dir = File.basename(path).sub(/\.gem$/, '')
+ FileUtils.mkdir_p target_dir
+ Gem::Installer.new(path).unpack(File.expand_path(target_dir))
+ say "Unpacked gem: '#{target_dir}'"
+ else
+ alert_error "Gem '#{gemname}' not installed."
+ end
+ end
+
+ # Return the full path to the cached gem file matching the given
+ # name and version requirement. Returns 'nil' if no match.
+ #
+ # Example:
+ #
+ # get_path('rake', '> 0.4') # -> '/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem'
+ # get_path('rake', '< 0.1') # -> nil
+ # get_path('rak') # -> nil (exact name required)
+ #--
+ # TODO: This should be refactored so that it's a general service. I don't
+ # think any of our existing classes are the right place though. Just maybe
+ # 'Cache'?
+ #
+ # TODO: It just uses Gem.dir for now. What's an easy way to get the list of
+ # source directories?
+ def get_path(gemname, version_req)
+ return gemname if gemname =~ /\.gem$/i
+ specs = Gem::SourceIndex.from_installed_gems.search(/\A#{gemname}\z/, version_req)
+ selected = specs.sort_by { |s| s.version }.last
+ return nil if selected.nil?
+ # We expect to find (basename).gem in the 'cache' directory.
+ # Furthermore, the name match must be exact (ignoring case).
+ if gemname =~ /^#{selected.name}$/i
+ filename = selected.full_name + '.gem'
+ return File.join(Gem.dir, 'cache', filename)
+ else
+ return nil
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
new file mode 100644
index 00000000000..e17ba2516a7
--- /dev/null
+++ b/lib/rubygems/commands/update_command.rb
@@ -0,0 +1,149 @@
+require 'rubygems/command'
+require 'rubygems/install_update_options'
+require 'rubygems/local_remote_options'
+require 'rubygems/source_info_cache'
+require 'rubygems/version_option'
+
+module Gem
+ module Commands
+ class UpdateCommand < Command
+
+ include Gem::InstallUpdateOptions
+ include Gem::LocalRemoteOptions
+ include Gem::VersionOption
+
+ def initialize
+ super(
+ 'update',
+ 'Update the named gems (or all installed gems) in the local repository',
+ {
+ :generate_rdoc => true,
+ :generate_ri => true,
+ :force => false,
+ :test => false,
+ :install_dir => Gem.dir
+ })
+
+ add_install_update_options
+
+ add_option('--system',
+ 'Update the RubyGems system software') do |value, options|
+ options[:system] = value
+ end
+
+ add_local_remote_options
+
+ add_platform_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to update"
+ end
+
+ def defaults_str # :nodoc:
+ "--rdoc --ri --no-force --no-test\n" +
+ "--install-dir #{Gem.dir}"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME [GEMNAME ...]"
+ end
+
+ def execute
+ if options[:system] then
+ say "Updating RubyGems..."
+
+ unless options[:args].empty? then
+ fail "No gem names are allowed with the --system option"
+ end
+
+ options[:args] = ["rubygems-update"]
+ else
+ say "Updating installed gems..."
+ end
+
+ hig = highest_installed_gems = {}
+
+ Gem::SourceIndex.from_installed_gems.each do |name, spec|
+ if hig[spec.name].nil? or hig[spec.name].version < spec.version
+ hig[spec.name] = spec
+ end
+ end
+
+ remote_gemspecs = Gem::SourceInfoCache.search(//)
+
+ gems_to_update = if options[:args].empty? then
+ which_to_update(highest_installed_gems, remote_gemspecs)
+ else
+ options[:args]
+ end
+
+ options[:domain] = :remote # install from remote source
+
+ # HACK use the real API
+ install_command = Gem::CommandManager.instance['install']
+
+ gems_to_update.uniq.sort.each do |name|
+ say "Attempting remote update of #{name}"
+ options[:args] = [name]
+ options[:ignore_dependencies] = true # HACK skip seen gems instead
+ install_command.merge_options(options)
+ install_command.execute
+ end
+
+ if gems_to_update.include?("rubygems-update") then
+ latest_ruby_gem = remote_gemspecs.select { |s|
+ s.name == 'rubygems-update'
+ }.sort_by { |s|
+ s.version
+ }.last
+
+ say "Updating version of RubyGems to #{latest_ruby_gem.version}"
+ installed = do_rubygems_update(latest_ruby_gem.version.to_s)
+
+ say "RubyGems system software updated" if installed
+ else
+ say "Gems: [#{gems_to_update.uniq.sort.collect{|g| g.to_s}.join(', ')}] updated"
+ end
+ end
+
+ def do_rubygems_update(version_string)
+ args = []
+ args.push '--prefix', Gem.prefix unless Gem.prefix.nil?
+ args << '--no-rdoc' unless options[:generate_rdoc]
+ args << '--no-ri' unless options[:generate_ri]
+
+ update_dir = File.join(Gem.dir, 'gems',
+ "rubygems-update-#{version_string}")
+
+ success = false
+
+ Dir.chdir update_dir do
+ say "Installing RubyGems #{version_string}"
+ setup_cmd = "#{Gem.ruby} setup.rb #{args.join ' '}"
+
+ # Make sure old rubygems isn't loaded
+ if Gem.win_platform? then
+ system "set RUBYOPT= & #{setup_cmd}"
+ else
+ system "RUBYOPT=\"\" #{setup_cmd}"
+ end
+ end
+ end
+
+ def which_to_update(highest_installed_gems, remote_gemspecs)
+ result = []
+ highest_installed_gems.each do |l_name, l_spec|
+ highest_remote_gem =
+ remote_gemspecs.select { |spec| spec.name == l_name }.
+ sort_by { |spec| spec.version }.
+ last
+ if highest_remote_gem and l_spec.version < highest_remote_gem.version
+ result << l_name
+ end
+ end
+ result
+ end
+ end
+ end
+end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
new file mode 100644
index 00000000000..b42244ce7d3
--- /dev/null
+++ b/lib/rubygems/commands/which_command.rb
@@ -0,0 +1,86 @@
+require 'rubygems/command'
+require 'rubygems/gem_path_searcher'
+
+class Gem::Commands::WhichCommand < Gem::Command
+
+ EXT = %w[.rb .rbw .so .dll] # HACK
+
+ def initialize
+ super 'which', 'Find the location of a library',
+ :search_gems_first => false, :show_all => false
+
+ add_option '-a', '--[no-]all', 'show all matching files' do |show_all, options|
+ options[:show_all] = show_all
+ end
+
+ add_option '-g', '--[no-]gems-first',
+ 'search gems before non-gems' do |gems_first, options|
+ options[:search_gems_first] = gems_first
+ end
+ end
+
+ def arguments # :nodoc:
+ "FILE name of file to find"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-gems-first --no-all"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} FILE [FILE ...]"
+ end
+
+ def execute
+ searcher = Gem::GemPathSearcher.new
+
+ options[:args].each do |arg|
+ dirs = $LOAD_PATH
+ spec = searcher.find arg
+
+ if spec then
+ if options[:search_gems_first] then
+ dirs = gem_paths(spec) + $LOAD_PATH
+ else
+ dirs = $LOAD_PATH + gem_paths(spec)
+ end
+
+ say "(checking gem #{spec.full_name} for #{arg})" if
+ Gem.configuration.verbose
+ end
+
+ paths = find_paths arg, dirs
+
+ if paths.empty? then
+ say "Can't find #{arg}"
+ else
+ say paths
+ end
+ end
+ end
+
+ def find_paths(package_name, dirs)
+ result = []
+
+ dirs.each do |dir|
+ EXT.each do |ext|
+ full_path = File.join dir, "#{package_name}#{ext}"
+ if File.exist? full_path then
+ result << full_path
+ return result unless options[:show_all]
+ end
+ end
+ end
+
+ result
+ end
+
+ def gem_paths(spec)
+ spec.require_paths.collect { |d| File.join spec.full_gem_path, d }
+ end
+
+ def usage # :nodoc:
+ "#{program_name} FILE [...]"
+ end
+
+end