summaryrefslogtreecommitdiff
path: root/lib/rubygems/command.rb
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/command.rb
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/command.rb')
-rw-r--r--lib/rubygems/command.rb406
1 files changed, 406 insertions, 0 deletions
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
new file mode 100644
index 0000000..66855c7
--- /dev/null
+++ b/lib/rubygems/command.rb
@@ -0,0 +1,406 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'optparse'
+
+require 'rubygems/user_interaction'
+
+module Gem
+
+ # Base class for all Gem commands. When creating a new gem command, define
+ # #arguments, #defaults_str, #description and #usage (as appropriate).
+ class Command
+
+ include UserInteraction
+
+ # The name of the command.
+ attr_reader :command
+
+ # The options for the command.
+ attr_reader :options
+
+ # The default options for the command.
+ attr_accessor :defaults
+
+ # The name of the command for command-line invocation.
+ attr_accessor :program_name
+
+ # A short description of the command.
+ attr_accessor :summary
+
+ # Initializes a generic gem command named +command+. +summary+ is a short
+ # description displayed in `gem help commands`. +defaults+ are the
+ # default options. Defaults should be mirrored in #defaults_str, unless
+ # there are none.
+ #
+ # Use add_option to add command-line switches.
+ def initialize(command, summary=nil, defaults={})
+ @command = command
+ @summary = summary
+ @program_name = "gem #{command}"
+ @defaults = defaults
+ @options = defaults.dup
+ @option_groups = Hash.new { |h,k| h[k] = [] }
+ @parser = nil
+ @when_invoked = nil
+ 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
+
+ # Override to provide command handling.
+ def execute
+ fail "Generic command has no actions"
+ end
+
+ # Get all gem names from the command line.
+ def get_all_gem_names
+ args = options[:args]
+
+ if args.nil? or args.empty? then
+ raise Gem::CommandLineError,
+ "Please specify at least one gem name (e.g. gem build GEMNAME)"
+ end
+
+ gem_names = args.select { |arg| arg !~ /^-/ }
+ end
+
+ # 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? then
+ raise Gem::CommandLineError,
+ "Please specify a gem name on the command line (e.g. gem build GEMNAME)"
+ end
+
+ if args.size > 1 then
+ raise 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
+
+ # Override to provide details of the arguments a command takes.
+ # It should return a left-justified string, one argument per line.
+ def arguments
+ ""
+ end
+
+ # Override to display the default values of the command
+ # options. (similar to +arguments+, but displays the default
+ # values).
+ def defaults_str
+ ""
+ end
+
+ # Override to display a longer description of what this command does.
+ def description
+ nil
+ end
+
+ # Override to display the usage for an individual gem command.
+ def usage
+ program_name
+ end
+
+ # Display the help message for the command.
+ def show_help
+ parser.program_name = usage
+ say parser
+ end
+
+ # Invoke the command with the given list of arguments.
+ def invoke(*args)
+ handle_options(args)
+ if options[:help]
+ show_help
+ elsif @when_invoked
+ @when_invoked.call(options)
+ else
+ execute
+ end
+ end
+
+ # Call the given block when invoked.
+ #
+ # Normal command invocations just executes the +execute+ method of
+ # the command. Specifying an invocation block allows the test
+ # methods to override the normal action of a command to determine
+ # that it has been invoked correctly.
+ def when_invoked(&block)
+ @when_invoked = block
+ end
+
+ # Add a command-line option and handler to the command.
+ #
+ # See OptionParser#make_switch for an explanation of +opts+.
+ #
+ # +handler+ will be called with two values, the value of the argument and
+ # the options hash.
+ def add_option(*opts, &handler) # :yields: value, options
+ group_name = Symbol === opts.first ? opts.shift : :options
+
+ @option_groups[group_name] << [opts, handler]
+ end
+
+ # Remove previously defined command-line argument +name+.
+ def remove_option(name)
+ @option_groups.each do |_, option_list|
+ option_list.reject! { |args, _| args.any? { |x| x =~ /^#{name}/ } }
+ end
+ end
+
+ # Merge a set of command options with the set of default options
+ # (without modifying the default option hash).
+ def merge_options(new_options)
+ @options = @defaults.clone
+ new_options.each do |k,v| @options[k] = v end
+ end
+
+ # True if the command handles the given argument list.
+ def handles?(args)
+ begin
+ parser.parse!(args.dup)
+ return true
+ rescue
+ return false
+ end
+ end
+
+ # Handle the given list of arguments by parsing them and recording
+ # the results.
+ def handle_options(args)
+ args = add_extra_args(args)
+ @options = @defaults.clone
+ parser.parse!(args)
+ @options[:args] = args
+ end
+
+ def add_extra_args(args)
+ result = []
+ s_extra = Command.specific_extra_args(@command)
+ extra = Command.extra_args + s_extra
+ while ! extra.empty?
+ ex = []
+ ex << extra.shift
+ ex << extra.shift if extra.first.to_s =~ /^[^-]/
+ result << ex if handles?(ex)
+ end
+ result.flatten!
+ result.concat(args)
+ result
+ end
+
+ private
+
+ # Create on demand parser.
+ def parser
+ create_option_parser if @parser.nil?
+ @parser
+ end
+
+ def create_option_parser
+ @parser = OptionParser.new
+
+ @parser.separator("")
+ regular_options = @option_groups.delete :options
+
+ configure_options "", regular_options
+
+ @option_groups.sort_by { |n,_| n.to_s }.each do |group_name, option_list|
+ configure_options group_name, option_list
+ end
+
+ configure_options "Common", Command.common_options
+
+ @parser.separator("")
+ unless arguments.empty?
+ @parser.separator(" Arguments:")
+ arguments.split(/\n/).each do |arg_desc|
+ @parser.separator(" #{arg_desc}")
+ end
+ @parser.separator("")
+ end
+
+ @parser.separator(" Summary:")
+ wrap(@summary, 80 - 4).split("\n").each do |line|
+ @parser.separator(" #{line.strip}")
+ end
+
+ if description then
+ formatted = description.split("\n\n").map do |chunk|
+ wrap(chunk, 80 - 4)
+ end.join("\n")
+
+ @parser.separator ""
+ @parser.separator " Description:"
+ formatted.split("\n").each do |line|
+ @parser.separator " #{line.rstrip}"
+ end
+ end
+
+ unless defaults_str.empty?
+ @parser.separator("")
+ @parser.separator(" Defaults:")
+ defaults_str.split(/\n/).each do |line|
+ @parser.separator(" #{line}")
+ end
+ end
+ end
+
+ def configure_options(header, option_list)
+ return if option_list.nil? or option_list.empty?
+
+ header = header.to_s.empty? ? '' : "#{header} "
+ @parser.separator " #{header}Options:"
+
+ option_list.each do |args, handler|
+ dashes = args.select { |arg| arg =~ /^-/ }
+ @parser.on(*args) do |value|
+ handler.call(value, @options)
+ end
+ end
+
+ @parser.separator ''
+ end
+
+ # Wraps +text+ to +width+
+ def wrap(text, width)
+ text.gsub(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n")
+ end
+
+ ##################################################################
+ # Class methods for Command.
+ class << self
+ def common_options
+ @common_options ||= []
+ end
+
+ def add_common_option(*args, &handler)
+ Gem::Command.common_options << [args, handler]
+ end
+
+ def extra_args
+ @extra_args ||= []
+ end
+
+ def extra_args=(value)
+ case value
+ when Array
+ @extra_args = value
+ when String
+ @extra_args = value.split
+ end
+ end
+
+ # Return an array of extra arguments for the command. The extra
+ # arguments come from the gem configuration file read at program
+ # startup.
+ def specific_extra_args(cmd)
+ specific_extra_args_hash[cmd]
+ end
+
+ # Add a list of extra arguments for the given command. +args+
+ # may be an array or a string to be split on white space.
+ def add_specific_extra_args(cmd,args)
+ args = args.split(/\s+/) if args.kind_of? String
+ specific_extra_args_hash[cmd] = args
+ end
+
+ # Accessor for the specific extra args hash (self initializing).
+ def specific_extra_args_hash
+ @specific_extra_args_hash ||= Hash.new do |h,k|
+ h[k] = Array.new
+ end
+ end
+ end
+
+ # ----------------------------------------------------------------
+ # Add the options common to all commands.
+
+ add_common_option('-h', '--help',
+ 'Get help on this command') do
+ |value, options|
+ options[:help] = true
+ end
+
+ add_common_option('-V', '--[no-]verbose',
+ 'Set the verbose level of output') do |value, options|
+ # Set us to "really verbose" so the progess meter works
+ if Gem.configuration.verbose and value then
+ Gem.configuration.verbose = 1
+ else
+ Gem.configuration.verbose = value
+ end
+ end
+
+ add_common_option('-q', '--quiet', 'Silence commands') do |value, options|
+ Gem.configuration.verbose = false
+ end
+
+ # Backtrace and config-file are added so they show up in the help
+ # commands. Both options are actually handled before the other
+ # options get parsed.
+
+ add_common_option('--config-file FILE',
+ "Use this config file instead of default") do
+ end
+
+ add_common_option('--backtrace',
+ 'Show stack backtrace on errors') do
+ end
+
+ add_common_option('--debug',
+ 'Turn on Ruby debugging') do
+ end
+
+ # :stopdoc:
+ 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 platforms show information about platforms
+ gem help <COMMAND> show help on COMMAND
+ (e.g. 'gem help install')
+ Further information:
+ http://rubygems.rubyforge.org
+ }.gsub(/^ /, "")
+
+ # :startdoc:
+
+ end # class
+
+ # This is where Commands will be placed in the namespace
+ module Commands; end
+
+end