Tag unstable CGI specs.
[rbx.git] / lib / rubygems / command.rb
blob860764e6d5415a71e17ab881d5c3f2e463678559
1 #--
2 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
3 # All rights reserved.
4 # See LICENSE.txt for permissions.
5 #++
7 require 'optparse'
9 require 'rubygems/user_interaction'
11 module Gem
13   # Base class for all Gem commands.  When creating a new gem command, define
14   # #arguments, #defaults_str, #description and #usage (as appropriate).
15   class Command
17     include UserInteraction
19     # The name of the command.
20     attr_reader :command
22     # The options for the command.
23     attr_reader :options
25     # The default options for the command.
26     attr_accessor :defaults
28     # The name of the command for command-line invocation.
29     attr_accessor :program_name
31     # A short description of the command.
32     attr_accessor :summary
34     # Initializes a generic gem command named +command+.  +summary+ is a short
35     # description displayed in `gem help commands`.  +defaults+ are the
36     # default options.  Defaults should be mirrored in #defaults_str, unless
37     # there are none.
38     #
39     # Use add_option to add command-line switches.
40     def initialize(command, summary=nil, defaults={})
41       @command = command
42       @summary = summary
43       @program_name = "gem #{command}"
44       @defaults = defaults
45       @options = defaults.dup
46       @option_groups = Hash.new { |h,k| h[k] = [] }
47       @parser = nil
48       @when_invoked = nil
49     end
51     # True if +long+ begins with the characters from +short+.
52     def begins?(long, short)
53       return false if short.nil?
54       long[0, short.length] == short
55     end
57     # Override to provide command handling.
58     def execute
59       fail "Generic command has no actions"
60     end
62     # Get all gem names from the command line.
63     def get_all_gem_names
64       args = options[:args]
66       if args.nil? or args.empty? then
67         raise Gem::CommandLineError,
68               "Please specify at least one gem name (e.g. gem build GEMNAME)"
69       end
71       gem_names = args.select { |arg| arg !~ /^-/ }
72     end
74     # Get the single gem name from the command line.  Fail if there is no gem
75     # name or if there is more than one gem name given.
76     def get_one_gem_name
77       args = options[:args]
79       if args.nil? or args.empty? then
80         raise Gem::CommandLineError,
81              "Please specify a gem name on the command line (e.g. gem build GEMNAME)"
82       end
84       if args.size > 1 then
85         raise Gem::CommandLineError,
86               "Too many gem names (#{args.join(', ')}); please specify only one"
87       end
89       args.first
90     end
92     # Get a single optional argument from the command line.  If more than one
93     # argument is given, return only the first. Return nil if none are given.
94     def get_one_optional_argument
95       args = options[:args] || []
96       args.first
97     end
99     # Override to provide details of the arguments a command takes.
100     # It should return a left-justified string, one argument per line.
101     def arguments
102       ""
103     end
105     # Override to display the default values of the command
106     # options. (similar to +arguments+, but displays the default
107     # values).
108     def defaults_str
109       ""
110     end
112     # Override to display a longer description of what this command does.
113     def description
114       nil
115     end
117     # Override to display the usage for an individual gem command.
118     def usage
119       program_name
120     end
122     # Display the help message for the command.
123     def show_help
124       parser.program_name = usage
125       say parser
126     end
128     # Invoke the command with the given list of arguments.
129     def invoke(*args)
130       handle_options(args)
131       if options[:help]
132         show_help
133       elsif @when_invoked
134         @when_invoked.call(options)
135       else
136         execute
137       end
138     end
140     # Call the given block when invoked.
141     #
142     # Normal command invocations just executes the +execute+ method of
143     # the command.  Specifying an invocation block allows the test
144     # methods to override the normal action of a command to determine
145     # that it has been invoked correctly.
146     def when_invoked(&block)
147       @when_invoked = block
148     end
150     # Add a command-line option and handler to the command.
151     #
152     # See OptionParser#make_switch for an explanation of +opts+.
153     #
154     # +handler+ will be called with two values, the value of the argument and
155     # the options hash.
156     def add_option(*opts, &handler) # :yields: value, options
157       group_name = Symbol === opts.first ? opts.shift : :options
159       @option_groups[group_name] << [opts, handler]
160     end
162     # Remove previously defined command-line argument +name+.
163     def remove_option(name)
164       @option_groups.each do |_, option_list|
165         option_list.reject! { |args, _| args.any? { |x| x =~ /^#{name}/ } }
166       end
167     end
169     # Merge a set of command options with the set of default options
170     # (without modifying the default option hash).
171     def merge_options(new_options)
172       @options = @defaults.clone
173       new_options.each do |k,v| @options[k] = v end
174     end
176     # True if the command handles the given argument list.
177     def handles?(args)
178       begin
179         parser.parse!(args.dup)
180         return true
181       rescue
182         return false
183       end
184     end
186     # Handle the given list of arguments by parsing them and recording
187     # the results.
188     def handle_options(args)
189       args = add_extra_args(args)
190       @options = @defaults.clone
191       parser.parse!(args)
192       @options[:args] = args
193     end
195     def add_extra_args(args)
196       result = []
197       s_extra = Command.specific_extra_args(@command)
198       extra = Command.extra_args + s_extra
199       while ! extra.empty?
200         ex = []
201         ex << extra.shift
202         ex << extra.shift if extra.first.to_s =~ /^[^-]/
203         result << ex if handles?(ex)
204       end
205       result.flatten!
206       result.concat(args)
207       result
208     end
210     private
212     # Create on demand parser.
213     def parser
214       create_option_parser if @parser.nil?
215       @parser
216     end
218     def create_option_parser
219       @parser = OptionParser.new
221       @parser.separator("")
222       regular_options = @option_groups.delete :options
224       configure_options "", regular_options
226       @option_groups.sort_by { |n,_| n.to_s }.each do |group_name, option_list|
227         configure_options group_name, option_list
228       end
230       configure_options "Common", Command.common_options
232       @parser.separator("")
233       unless arguments.empty?
234         @parser.separator("  Arguments:")
235         arguments.split(/\n/).each do |arg_desc|
236           @parser.separator("    #{arg_desc}")
237         end
238         @parser.separator("")
239       end
241       @parser.separator("  Summary:")
242       wrap(@summary, 80 - 4).split("\n").each do |line|
243         @parser.separator("    #{line.strip}")
244       end
246       if description then
247         formatted = description.split("\n\n").map do |chunk|
248           wrap(chunk, 80 - 4)
249         end.join("\n")
251         @parser.separator ""
252         @parser.separator "  Description:"
253         formatted.split("\n").each do |line|
254           @parser.separator "    #{line.rstrip}"
255         end
256       end
258       unless defaults_str.empty?
259         @parser.separator("")
260         @parser.separator("  Defaults:")
261         defaults_str.split(/\n/).each do |line|
262           @parser.separator("    #{line}")
263         end
264       end
265     end
267     def configure_options(header, option_list)
268       return if option_list.nil? or option_list.empty?
270       header = header.to_s.empty? ? '' : "#{header} "
271       @parser.separator "  #{header}Options:"
273       option_list.each do |args, handler|
274         dashes = args.select { |arg| arg =~ /^-/ }
275         @parser.on(*args) do |value|
276           handler.call(value, @options)
277         end
278       end
280       @parser.separator ''
281     end
283     # Wraps +text+ to +width+
284     def wrap(text, width)
285       text.gsub(/(.{1,#{width}})( +|$\n?)|(.{1,#{width}})/, "\\1\\3\n")
286     end
288     ##################################################################
289     # Class methods for Command.
290     class << self
291       def common_options
292         @common_options ||= []
293       end
295       def add_common_option(*args, &handler)
296         Gem::Command.common_options << [args, handler]
297       end
299       def extra_args
300         @extra_args ||= []
301       end
303       def extra_args=(value)
304         case value
305         when Array
306           @extra_args = value
307         when String
308           @extra_args = value.split
309         end
310       end
312       # Return an array of extra arguments for the command.  The extra
313       # arguments come from the gem configuration file read at program
314       # startup.
315       def specific_extra_args(cmd)
316         specific_extra_args_hash[cmd]
317       end
319       # Add a list of extra arguments for the given command.  +args+
320       # may be an array or a string to be split on white space.
321       def add_specific_extra_args(cmd,args)
322         args = args.split(/\s+/) if args.kind_of? String
323         specific_extra_args_hash[cmd] = args
324       end
326       # Accessor for the specific extra args hash (self initializing).
327       def specific_extra_args_hash
328         @specific_extra_args_hash ||= Hash.new do |h,k|
329           h[k] = Array.new
330         end
331       end
332     end
334     # ----------------------------------------------------------------
335     # Add the options common to all commands.
337     add_common_option('-h', '--help',
338       'Get help on this command') do
339       |value, options|
340       options[:help] = true
341     end
343     add_common_option('-V', '--[no-]verbose',
344                       'Set the verbose level of output') do |value, options|
345       # Set us to "really verbose" so the progress meter works
346       if Gem.configuration.verbose and value then
347         Gem.configuration.verbose = 1
348       else
349         Gem.configuration.verbose = value
350       end
351     end
353     add_common_option('-q', '--quiet', 'Silence commands') do |value, options|
354       Gem.configuration.verbose = false
355     end
357     # Backtrace and config-file are added so they show up in the help
358     # commands.  Both options are actually handled before the other
359     # options get parsed.
361     add_common_option('--config-file FILE',
362       "Use this config file instead of default") do
363     end
365     add_common_option('--backtrace',
366       'Show stack backtrace on errors') do
367     end
369     add_common_option('--debug',
370       'Turn on Ruby debugging') do
371     end
373     # :stopdoc:
374     HELP = %{
375       RubyGems is a sophisticated package manager for Ruby.  This is a
376       basic help message containing pointers to more information.
378         Usage:
379           gem -h/--help
380           gem -v/--version
381           gem command [arguments...] [options...]
383         Examples:
384           gem install rake
385           gem list --local
386           gem build package.gemspec
387           gem help install
389         Further help:
390           gem help commands            list all 'gem' commands
391           gem help examples            show some examples of usage
392           gem help platforms           show information about platforms
393           gem help <COMMAND>           show help on COMMAND
394                                          (e.g. 'gem help install')
395         Further information:
396           http://rubygems.rubyforge.org
397     }.gsub(/^    /, "")
399     # :startdoc:
401   end # class
403   # This is where Commands will be placed in the namespace
404   module Commands; end