You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
amarok/amarok/src/mediadevice/daap/mongrel/lib/rubygems/gem_commands.rb

1442 lines
40 KiB

#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++
module Gem
class CommandLineError < Gem::Exception; end
####################################################################
# The following mixin methods aid in the retrieving of information
# from the command line.
#
module CommandAids
# Get the single gem name from the command line. Fail if there is
# no gem name or if there is more than one gem name given.
def get_one_gem_name
args = options[:args]
if args.nil? or args.empty?
fail Gem::CommandLineError,
"Please specify a gem name on the command line (e.g. gem build GEMNAME)"
end
if args.size > 1
fail Gem::CommandLineError,
"Too many gem names (#{args.join(', ')}); please specify only one"
end
args.first
end
# Get a single optional argument from the command line. If more
# than one argument is given, return only the first. Return nil if
# none are given.
def get_one_optional_argument
args = options[:args] || []
args.first
end
# True if +long+ begins with the characters from +short+.
def begins?(long, short)
return false if short.nil?
long[0, short.length] == short
end
end
####################################################################
# Mixin methods for handling the local/remote command line options.
#
module LocalRemoteOptions
# Add the local/remote options to the command line parser.
def add_local_remote_options
add_option('-l', '--local',
'Restrict operations to the LOCAL domain (default)') do
|value, options|
options[:domain] = :local
end
add_option('-r', '--remote',
'Restrict operations to the REMOTE domain') do
|value, options|
options[:domain] = :remote
end
add_option('-b', '--both',
'Allow LOCAL and REMOTE operations') do
|value, options|
options[:domain] = :both
end
end
# Is local fetching enabled?
def local?
options[:domain] == :local || options[:domain] == :both
end
# Is remote fetching enabled?
def remote?
options[:domain] == :remote || options[:domain] == :both
end
end
####################################################################
# Mixin methods and OptionParser options specific to the gem install
# command.
#
module InstallUpdateOptions
# Add the install/update options to the option parser.
def add_install_update_options
add_option('-i', '--install-dir DIR',
'Gem repository directory to get installed gems.') do
|value, options|
options[:install_dir] = File.expand_path(value)
end
add_option('-d', '--[no-]rdoc',
'Generate RDoc documentation for the gem on install') do
|value, options|
options[:generate_rdoc] = value
end
add_option('--[no-]ri',
'Generate RI documentation for the gem on install') do
|value, options|
options[:generate_ri] = value
end
add_option('-f', '--[no-]force',
'Force gem to install, bypassing dependency checks') do
|value, options|
options[:force] = value
end
add_option('-t', '--[no-]test',
'Run unit tests prior to installation') do
|value, options|
options[:test] = value
end
add_option('-w', '--[no-]wrappers',
'Use bin wrappers for executables',
'Not available on dosish platforms') do
|value, options|
options[:wrappers] = value
end
add_option('-P', '--trust-policy POLICY',
'Specify gem trust policy.') do
|value, options|
options[:security_policy] = value
end
add_option('--ignore-dependencies',
'Do not install any required dependent gems') do
|value, options|
options[:ignore_dependencies] = value
end
add_option('-y', '--include-dependencies',
'Unconditionally install the required dependent gems') do
|value, options|
options[:include_dependencies] = value
end
end
# Default options for the gem install command.
def install_update_defaults_str
'--rdoc --no-force --no-test --wrappers'
end
end
####################################################################
# Mixin methods for the version command.
#
module VersionOption
# Add the options to the option parser.
def add_version_option(taskname)
add_option('-v', '--version VERSION',
"Specify version of gem to #{taskname}") do
|value, options|
options[:version] = value
end
end
end
####################################################################
# Gem install command.
#
class InstallCommand < Command
include CommandAids
include VersionOption
include LocalRemoteOptions
include InstallUpdateOptions
def initialize
super(
'install',
'Install a gem into the local repository',
{
:domain => :both,
:generate_rdoc => true,
:generate_ri => true,
:force => false,
:test => false,
:wrappers => true,
:version => "> 0",
:install_dir => Gem.dir,
:security_policy => nil,
})
add_version_option('install')
add_local_remote_options
add_install_update_options
end
def usage
"#{program_name} GEMNAME [options]
or: #{program_name} GEMNAME [options] -- --build-flags"
end
def arguments
"GEMNAME name of gem to install"
end
def defaults_str
"--both --version '> 0' --rdoc --ri --no-force --no-test\n" +
"--install-dir #{Gem.dir}"
end
def execute
ENV['GEM_PATH'] = options[:install_dir]
if(options[:args].empty?)
fail Gem::CommandLineError,
"Please specify a gem name on the command line (e.g. gem build GEMNAME)"
end
options[:args].each do |gem_name|
if local?
begin
entries = []
if(File.exist?(gem_name) && !File.directory?(gem_name))
entries << gem_name
else
filepattern = gem_name + "*.gem"
entries = Dir[filepattern]
end
unless entries.size > 0
if options[:domain] == :local
alert_error "Local gem file not found: #{filepattern}"
end
else
result = Gem::Installer.new(entries.last, options).install(
options[:force],
options[:install_dir])
installed_gems = [result].flatten
say "Successfully installed #{installed_gems[0].name}, " +
"version #{installed_gems[0].version}" if installed_gems
end
rescue LocalInstallationError => e
say " -> Local installation can't proceed: #{e.message}"
rescue Gem::LoadError => e
say " -> Local installation can't proceed due to LoadError: #{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}[.gem]: #{e.message}"
return
end
end
if remote? && installed_gems.nil?
installer = Gem::RemoteInstaller.new(options)
installed_gems = installer.install(
gem_name,
options[:version],
options[:force],
options[:install_dir])
if installed_gems
installed_gems.compact!
installed_gems.each do |spec|
say "Successfully installed #{spec.full_name}"
end
end
end
unless installed_gems
alert_error "Could not install a local " +
"or remote copy of the gem: #{gem_name}"
terminate_interaction(1)
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]
installed_gems.each do |gem|
Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri
end
end
if options[:generate_rdoc]
installed_gems.each do |gem|
Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc
end
end
if options[:test]
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)
unless result.passed?
unless ask_yes_no("...keep Gem?", true) then
Gem::Uninstaller.new(spec.name, spec.version.version).uninstall
end
end
end
end
end
end
end
####################################################################
class UninstallCommand < Command
include VersionOption
include CommandAids
def initialize
super('uninstall', 'Uninstall a gem from the local repository', {:version=>"> 0"})
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('uninstall')
end
def defaults_str
"--version '> 0' --no-force"
end
def usage
"#{program_name} GEMNAME"
end
def arguments
"GEMNAME name of gem to uninstall"
end
def execute
gem_name = get_one_gem_name
Gem::Uninstaller.new(gem_name, options).uninstall
end
end
class CertCommand < Command
include CommandAids
def initialize
super(
'cert',
'Adjust RubyGems certificate 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)
puts "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|
cert = OpenSSL::X509::Certificate.new(File.read(path))
# this could proably be formatted more gracefully
puts cert.subject.to_s
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|
cert = OpenSSL::X509::Certificate.new(File.read(path))
if cert.subject.to_s.downcase.index(value)
puts "Removing '#{cert.subject.to_s}'"
File.unlink(path)
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])
puts "Public Cert: #{vals[:cert_path]}",
"Private Key: #{vals[:key_path]}",
"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
####################################################################
class DependencyCommand < Command
include VersionOption
include CommandAids
def initialize
super('dependency',
'Show the dependencies of an installed gem',
{:version=>"> 0"})
add_version_option('uninstall')
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
end
def defaults_str
"--version '> 0' --no-reverse"
end
def usage
"#{program_name} GEMNAME"
end
def arguments
"GEMNAME name of gems to show"
end
def execute
specs = {}
srcindex = SourceIndex.from_installed_gems
options[:args] << '.' if options[:args].empty?
options[:args].each do |name|
speclist = srcindex.search(name, options[:version])
if speclist.empty?
say "No match found for #{name} (#{options[:version]})"
else
speclist.each do |spec|
specs[spec.full_name] = spec
end
end
end
reverse = Hash.new { |h, k| h[k] = [] }
if options[:reverse_dependencies]
specs.values.each do |spec|
reverse[spec.full_name] = find_reverse_dependencies(spec, srcindex)
end
end
if options[:pipe_format]
specs.values.sort.each do |spec|
unless spec.dependencies.empty?
spec.dependencies.each do |dep|
puts "#{dep.name} --version '#{dep.version_requirements}'"
end
end
end
else
response = ''
specs.values.sort.each do |spec|
response << print_dependencies(spec)
unless reverse[spec.full_name].empty?
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?
# response << ' ' * level + " Requires\n"
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, srcindex)
result = []
srcindex.each do |name, sp|
sp.dependencies.each do |dep|
if spec.name == dep.name &&
dep.version_requirements.satisfied_by?(spec.version)
result << [sp.full_name, dep]
end
end
end
result
end
end
####################################################################
class CheckCommand < Command
include CommandAids
def initialize
super('check', 'Check installed gems', {:verify => false, :alien => false})
add_option('-v', '--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_option('-V', '--version', "Specify version for which to run unit tests") do |value, options|
options[:version] = value
end
end
def execute
if options[:test]
version = options[:version] || "> 0.0.0"
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 # class
####################################################################
class BuildCommand < Command
include CommandAids
def initialize
super('build', 'Build a gem from a gemspec')
end
def usage
"#{program_name} GEMSPEC_FILE"
end
def arguments
"GEMSPEC_FILE name of gemspec file used to build the gem"
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
return
else
alert_error "Gemspec file not found: #{gemspec}"
end
end
def load_gemspecs(filename)
if yaml?(filename)
require 'yaml'
result = []
open(filename) do |f|
begin
while spec = Gem::Specification.from_yaml(f)
result << spec
end
rescue 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
####################################################################
class QueryCommand < Command
include LocalRemoteOptions
def initialize(name='query', summary='Query gem information in local or remote repositories')
super(name,
summary,
{:name=>/.*/, :domain=>:local, :details=>false}
)
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_local_remote_options
end
def defaults_str
"--local --name-matches '.*' --no-details"
end
def execute
if local?
say
say "*** LOCAL GEMS ***"
output_query_results(Gem::cache.search(options[:name]))
end
if remote?
say
say "*** REMOTE GEMS ***"
output_query_results(Gem::RemoteInstaller.new(options).search(options[:name]))
end
end
private
def output_query_results(gemspecs)
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 do |first, second|
first[0].downcase <=> second[0].downcase
end
gem_list_with_version.each do |gem_name, list_of_matching|
say
list_of_matching = list_of_matching.sort_by { |x| x.version }.reverse
seen_versions = []
list_of_matching.delete_if do |item|
if(seen_versions.member?(item.version))
true
else
seen_versions << item.version
false
end
end
say "#{gem_name} (#{list_of_matching.map{|gem| gem.version.to_s}.join(", ")})"
say format_text(list_of_matching[0].summary, 68, 4)
end
end
##
# Used for wrapping and indenting text
#
def format_text(text, wrap, indent=0)
result = []
pattern = Regexp.new("^(.{0,#{wrap}})[ \n]")
work = text.dup
while work.length > wrap
if work =~ pattern
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
####################################################################
class ListCommand < QueryCommand
include CommandAids
def initialize
super(
'list',
'Display all gems whose name starts with STRING'
)
remove_option('--name-matches')
end
def defaults_str
"--local --no-details"
end
def usage
"#{program_name} [STRING]"
end
def arguments
"STRING start of gem name to look for"
end
def execute
string = get_one_optional_argument || ''
options[:name] = /^#{string}/i
super
end
end
####################################################################
class SearchCommand < QueryCommand
include CommandAids
def initialize
super(
'search',
'Display all gems whose name contains STRING'
)
remove_option('--name-matches')
end
def defaults_str
"--local --no-details"
end
def usage
"#{program_name} [STRING]"
end
def arguments
"STRING fragment of gem name to look for"
end
def execute
string = get_one_optional_argument
options[:name] = /#{string}/i
super
end
end
####################################################################
class UpdateCommand < Command
include InstallUpdateOptions
def initialize
super(
'update',
'Update the named gem (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
end
def defaults_str
"--rdoc --ri --no-force --no-test\n" +
"--install-dir #{Gem.dir}"
end
def arguments
"GEMNAME(s) name of gem(s) to update"
end
def execute
if options[:system]
say "Updating RubyGems..."
if ! options[:args].empty?
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::RemoteInstaller.new(options).search(//)
# For some reason, this is an array of arrays. The actual list
# of specifications is the first and only element. If there
# were more remote sources, perhaps there would be more.
remote_gemspecs = remote_gemspecs.flatten
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
install_command = command_manager['install']
gems_to_update.uniq.sort.each do |name|
say "Attempting remote update of #{name}"
options[:args] = [name]
install_command.merge_options(options)
install_command.execute
end
if gems_to_update.include?("rubygems-update")
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}"
do_rubygems_update(latest_ruby_gem.version.to_s)
end
if(options[:system]) then
say "RubyGems system software updated"
else
say "Gems: [#{gems_to_update.uniq.sort.collect{|g| g.to_s}.join(', ')}] updated"
end
end
def do_rubygems_update(version_string)
update_dir = File.join(Gem.dir, "gems", "rubygems-update-#{version_string}")
Dir.chdir(update_dir) do
puts "Installing RubyGems #{version_string}"
system "#{Gem.ruby} setup.rb"
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
####################################################################
class CleanupCommand < Command
def initialize
super(
'cleanup',
'Cleanup 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 defaults_str
"--no-dryrun"
end
def arguments
"GEMNAME(s) name of gem(s) to cleanup"
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 = []
if ! options[:args].empty?
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 = command_manager['uninstall']
deplist = DependencyList.new
gems_to_cleanup.uniq.each do |spec| deplist.add(spec) end
deplist.dependency_order.each do |spec|
if options[:dryrun]
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
####################################################################
class RDocCommand < Command
include VersionOption
include CommandAids
def initialize
super('rdoc',
'Generates RDoc for pre-installed gems',
{
:version => "> 0.0.0",
: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('rdoc')
end
def defaults_str
"--version '> 0.0.0' --rdoc --ri"
end
def usage
"#{program_name} [args]"
end
def arguments
"GEMNAME The gem to generate RDoc for (unless --all)"
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
####################################################################
class EnvironmentCommand < Command
include CommandAids
def initialize
super('environment', 'Display RubyGems environmental information')
end
def usage
"#{program_name} [args]"
end
def arguments
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 execute
out = ''
arg = options[:args][0]
if begins?("packageversion", arg)
out = Gem::RubyGemsPackageVersion.to_s
elsif begins?("version", arg)
out = Gem::RubyGemsVersion.to_s
elsif begins?("gemdir", arg)
out = Gem.dir
elsif begins?("gempath", arg)
Gem.path.collect { |p| out << "#{p}\n" }
elsif begins?("remotesources", arg)
Gem::RemoteInstaller.new.sources.collect do |s|
out << "#{s}\n"
end
elsif arg
fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
else
out = "Rubygems Environment:\n"
out << " - VERSION: #{Gem::RubyGemsVersion} (#{Gem::RubyGemsPackageVersion})\n"
out << " - INSTALLATION DIRECTORY: #{Gem.dir}\n"
out << " - GEM PATH:\n"
Gem.path.collect { |p| out << " - #{p}\n" }
out << " - REMOTE SOURCES:\n"
Gem::RemoteInstaller.new.sources.collect do |s|
out << " - #{s}\n"
end
end
say out
true
end
end
####################################################################
class SpecificationCommand < Command
include VersionOption
include LocalRemoteOptions
include CommandAids
def initialize
super('specification', 'Display gem specification (in yaml)', {:domain=>:local, :version=>"> 0.0.0"})
add_version_option('examine')
add_local_remote_options
add_option('--all', 'Output specifications for all versions of the gem') do
options[:all] = true
end
end
def defaults_str
"--local --version '(latest)'"
end
def usage
"#{program_name} GEMFILE"
end
def arguments
"GEMFILE Name of a .gem file to examine"
end
def execute
if local?
gem = get_one_gem_name
gem_specs = Gem::SourceIndex.from_installed_gems.search(gem, options[:version])
unless gem_specs.empty?
require 'yaml'
output = lambda { |spec| say spec.to_yaml; say "\n" }
if options[:all]
gem_specs.each(&output)
else
spec = gem_specs.sort_by { |spec| spec.version }.last
output[spec]
end
else
alert_error "Unknown gem #{gem}"
end
end
if remote?
say "(Remote 'info' operation is not yet implemented.)"
# NOTE: when we do implement remote info, make sure we don't
# duplicate huge swabs of local data. If it's the same, just
# say it's the same.
end
end
end
####################################################################
class UnpackCommand < Command
include VersionOption
include CommandAids
def initialize
super(
'unpack',
'Unpack an installed gem to the current directory',
{ :version => '> 0' }
)
add_version_option('unpack')
end
def defaults_str
"--version '> 0'"
end
def usage
"#{program_name} GEMNAME"
end
def arguments
"GEMNAME Name of the gem to unpack"
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
require 'fileutils'
target_dir = File.basename(path).sub(/\.gem$/, '')
FileUtils.mkdir_p target_dir
Installer.new(path).unpack(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 = SourceIndex.from_installed_gems.search(gemname, 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
####################################################################
class HelpCommand < Command
include CommandAids
def initialize
super('help', "Provide help on the 'gem' command")
end
def usage
"#{program_name} ARGUMENT"
end
def arguments
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 execute
arg = options[:args][0]
if begins?("commands", arg)
require 'stringio'
out = StringIO.new
out.puts "\nGEM commands are:\n\n"
desc_indent = command_manager.command_names.collect {|n| n.size}.max + 4
format = " %-#{desc_indent}s %s\n"
command_manager.command_names.each do |cmd_name|
out.printf format, "#{cmd_name}", command_manager[cmd_name].summary
end
out.puts "\nFor help on a particular command, use 'gem help COMMAND'."
out.puts "\nCommands may be abbreviated, so long as they are unambiguous."
out.puts "e.g. 'gem i rake' is short for 'gem install rake'."
say out.string
elsif begins?("options", arg)
say Gem::HELP
elsif begins?("examples", arg)
say Gem::EXAMPLES
elsif options[:help]
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
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::HELP
end
end
end
####################################################################
class ContentsCommand < Command
include CommandAids
def initialize
super('contents','Display the contents of the installed gems', {:list => true, :specdirs => [] })
add_option("-l","--list",'List the files inside a Gem') do |v,o|
o[:list] = true
end
add_option("-V","--version","Specify version for gem to view") do |v,o|
o[:version] = v
end
add_option('-s','--spec-dir a,b,c', Array, "Search for gems under specific paths") do |v,o|
o[:specdirs] = v
end
add_option('-v','--verbose','Be verbose when showing status') do |v,o|
o[:verbose] = v
end
end
def execute(io=STDOUT)
if options[:list]
version = options[:version] || "> 0.0.0"
gem = get_one_gem_name
s = options[:specdirs].map do |i|
[i, File.join(i,"specifications")]
end.flatten
if s.empty?
path_kind = "default gem paths"
system = true
else
path_kind = "specified path"
system = false
end
si = Gem::SourceIndex.from_gems_in(*s)
gem_spec = si.search(gem, version).first
unless gem_spec
io.puts "Unable to find gem '#{gem}' in #{path_kind}"
if options[:verbose]
io.puts "\nDirectories searched:"
if system
Gem.path.each do |p|
io.puts p
end
else
s.each do |p|
io.puts p
end
end
end
return
end
# show the list of files.
gem_spec.files.each do |f|
io.puts File.join(gem_spec.full_gem_path, f)
end
end
end
end
end # module
######################################################################
# Documentation Constants
#
module Gem
HELP = %{
RubyGems is a sophisticated package manager for Ruby. This is a
basic help message containing pointers to more information.
Usage:
gem -h/--help
gem -v/--version
gem command [arguments...] [options...]
Examples:
gem install rake
gem list --local
gem build package.gemspec
gem help install
Further help:
gem help commands list all 'gem' commands
gem help examples show some examples of usage
gem help <COMMAND> show help on COMMAND
(e.g. 'gem help install')
Further information:
http://rubygems.rubyforge.org
}.gsub(/^ /, "")
EXAMPLES = %{
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
}.gsub(/^ /, "")
end