| |   |
| 30 | 30 | require file |
| 31 | 31 | end |
| 32 | 32 | |
| 33 | require "piston/version" |
| 34 | require File.join(PISTON_ROOT, "transat", "parser") |
| 35 | require File.join(PISTON_ROOT, 'piston', 'command') |
| 36 | Dir[File.join(PISTON_ROOT, "piston", "commands", "*.rb")].each do |file| |
| 37 | require file.gsub(PISTON_ROOT, "")[1..-4] |
| 38 | end |
| 39 | |
| 33 | 40 | module Piston |
| 34 | 41 | ROOT = "piston:root" |
| 35 | 42 | UUID = "piston:uuid" |
| … | … | |
| 45 | 45 | LOCKED = "piston:locked" |
| 46 | 46 | end |
| 47 | 47 | |
| 48 | | require File.join(PISTON_ROOT, 'piston', 'command') |
| 49 | | require File.join(PISTON_ROOT, 'piston', 'command_error') |
| 50 | | require File.join(PISTON_ROOT, 'piston', 'ui', 'command_line') |
| 48 | PistonCommandLineProcessor = Transat::Parser.new do |
| 49 | program_name "Piston" |
| 50 | version [Piston::VERSION::STRING] |
| 51 | |
| 52 | option :verbose, :short => :v, :default => true, :message => "Show subversion commands and results as they are executed" |
| 53 | option :quiet, :short => :q, :default => false, :message => "Do not output any messages except errors" |
| 54 | option :revision, :short => :r, :param_name => "REVISION", :type => :int |
| 55 | option :show_updates, :short => :u, :message => "Query the remote repository for out of dateness information" |
| 56 | option :lock, :short => :l, :message => "Close down and lock the imported directory from further changes" |
| 57 | option :dry_run, :message => "Does not actually execute any commands" |
| 58 | option :force, :message => "Force the command to run, even if Piston thinks it would cause a problem" |
| 59 | |
| 60 | command :switch, Piston::Commands::Switch, :valid_options => %w(lock dry_run force revision quiet verbose) |
| 61 | command :update, Piston::Commands::Update, :valid_options => %w(lock dry_run force revision quiet verbose) |
| 62 | command :import, Piston::Commands::Import, :valid_options => %w(lock dry_run force revision quiet verbose) |
| 63 | command :convert, Piston::Commands::Convert, :valid_options => %w(lock verbose dry_run) |
| 64 | command :unlock, Piston::Commands::Unlock, :valid_options => %w(force dry_run verbose) |
| 65 | command :lock, Piston::Commands::Lock, :valid_options => %w(force dry_run revision verbose) |
| 66 | command :status, Piston::Commands::Status, :valid_options => %w(show_updates verbose) |
| 67 | end |
| toggle raw diff |
--- a/lib/piston.rb
+++ b/lib/piston.rb
@@ -30,6 +30,13 @@ Dir[File.join(PISTON_ROOT, 'core_ext', '*.rb')].each do |file|
require file
end
+require "piston/version"
+require File.join(PISTON_ROOT, "transat", "parser")
+require File.join(PISTON_ROOT, 'piston', 'command')
+Dir[File.join(PISTON_ROOT, "piston", "commands", "*.rb")].each do |file|
+ require file.gsub(PISTON_ROOT, "")[1..-4]
+end
+
module Piston
ROOT = "piston:root"
UUID = "piston:uuid"
@@ -38,6 +45,23 @@ module Piston
LOCKED = "piston:locked"
end
-require File.join(PISTON_ROOT, 'piston', 'command')
-require File.join(PISTON_ROOT, 'piston', 'command_error')
-require File.join(PISTON_ROOT, 'piston', 'ui', 'command_line')
+PistonCommandLineProcessor = Transat::Parser.new do
+ program_name "Piston"
+ version [Piston::VERSION::STRING]
+
+ option :verbose, :short => :v, :default => true, :message => "Show subversion commands and results as they are executed"
+ option :quiet, :short => :q, :default => false, :message => "Do not output any messages except errors"
+ option :revision, :short => :r, :param_name => "REVISION", :type => :int
+ option :show_updates, :short => :u, :message => "Query the remote repository for out of dateness information"
+ option :lock, :short => :l, :message => "Close down and lock the imported directory from further changes"
+ option :dry_run, :message => "Does not actually execute any commands"
+ option :force, :message => "Force the command to run, even if Piston thinks it would cause a problem"
+
+ command :switch, Piston::Commands::Switch, :valid_options => %w(lock dry_run force revision quiet verbose)
+ command :update, Piston::Commands::Update, :valid_options => %w(lock dry_run force revision quiet verbose)
+ command :import, Piston::Commands::Import, :valid_options => %w(lock dry_run force revision quiet verbose)
+ command :convert, Piston::Commands::Convert, :valid_options => %w(lock verbose dry_run)
+ command :unlock, Piston::Commands::Unlock, :valid_options => %w(force dry_run verbose)
+ command :lock, Piston::Commands::Lock, :valid_options => %w(force dry_run revision verbose)
+ command :status, Piston::Commands::Status, :valid_options => %w(show_updates verbose)
+end |
| |   |
| 2 | 2 | # The base class which all commands subclass to obtain services from. |
| 3 | 3 | class Command |
| 4 | 4 | attr_accessor :revision, :dry_run, :quiet, :verbose, :force, :lock, |
| 5 | | :recursive, :logging_stream, :show_updates |
| 5 | :recursive, :show_updates |
| 6 | attr_reader :args |
| 7 | attr_writer :logging_stream |
| 8 | |
| 9 | def initialize(non_options, options) |
| 10 | @args = non_options |
| 11 | options.each do |option, value| |
| 12 | self.send("#{option}=", value) |
| 13 | end |
| 14 | end |
| 6 | 15 | |
| 7 | 16 | # Execute this command. The arguments are pre-processed to expand any |
| 8 | 17 | # wildcards using Dir#[]. This is because the Windows shell does not |
| toggle raw diff |
--- a/lib/piston/command.rb
+++ b/lib/piston/command.rb
@@ -2,7 +2,16 @@ module Piston
# The base class which all commands subclass to obtain services from.
class Command
attr_accessor :revision, :dry_run, :quiet, :verbose, :force, :lock,
- :recursive, :logging_stream, :show_updates
+ :recursive, :show_updates
+ attr_reader :args
+ attr_writer :logging_stream
+
+ def initialize(non_options, options)
+ @args = non_options
+ options.each do |option, value|
+ self.send("#{option}=", value)
+ end
+ end
# Execute this command. The arguments are pre-processed to expand any
# wildcards using Dir#[]. This is because the Windows shell does not |
| |   |
| 0 | | module Piston |
| 1 | | module Commands |
| 2 | | class Help < Piston::Command |
| 3 | | def run(targets=nil) |
| 4 | | command = targets.shift |
| 5 | | |
| 6 | | return help_on_command(command) if command |
| 7 | | general_help |
| 8 | | end |
| 9 | | |
| 10 | | def help_on_command(command_name) |
| 11 | | begin |
| 12 | | require File.join(PISTON_ROOT, 'piston', 'commands', command_name) |
| 13 | | command = Piston::Commands.const_get(command_name.capitalize) |
| 14 | | command.detailed_help(logging_stream) |
| 15 | | rescue LoadError |
| 16 | | logging_stream.puts "No help available for '#{command_name}'" |
| 17 | | general_help |
| 18 | | end |
| 19 | | end |
| 20 | | |
| 21 | | def general_help |
| 22 | | logging_stream.puts "Available commands are:" |
| 23 | | commands = Array.new |
| 24 | | Dir[File.join(PISTON_ROOT, 'piston', 'commands', '*.rb')].each do |file| |
| 25 | | require file |
| 26 | | commands << Piston::Commands.const_get(File.basename(file).gsub(/\.rb$/, '').capitalize) |
| 27 | | end |
| 28 | | |
| 29 | | commands.each do |command| |
| 30 | | logging_stream.printf " %-12s %s\n", command.aliases.first, command.help |
| 31 | | end |
| 32 | | end |
| 33 | | |
| 34 | | def self.help |
| 35 | | "Returns detailed help on a specific command" |
| 36 | | end |
| 37 | | |
| 38 | | def self.aliases |
| 39 | | %w(help) |
| 40 | | end |
| 41 | | end |
| 42 | | end |
| 43 | | end |
| toggle raw diff |
--- a/lib/piston/commands/help.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module Piston
- module Commands
- class Help < Piston::Command
- def run(targets=nil)
- command = targets.shift
-
- return help_on_command(command) if command
- general_help
- end
-
- def help_on_command(command_name)
- begin
- require File.join(PISTON_ROOT, 'piston', 'commands', command_name)
- command = Piston::Commands.const_get(command_name.capitalize)
- command.detailed_help(logging_stream)
- rescue LoadError
- logging_stream.puts "No help available for '#{command_name}'"
- general_help
- end
- end
-
- def general_help
- logging_stream.puts "Available commands are:"
- commands = Array.new
- Dir[File.join(PISTON_ROOT, 'piston', 'commands', '*.rb')].each do |file|
- require file
- commands << Piston::Commands.const_get(File.basename(file).gsub(/\.rb$/, '').capitalize)
- end
-
- commands.each do |command|
- logging_stream.printf " %-12s %s\n", command.aliases.first, command.help
- end
- end
-
- def self.help
- "Returns detailed help on a specific command"
- end
-
- def self.aliases
- %w(help)
- end
- end
- end
-end |
| |   |
| 0 | | require 'getoptlong' |
| 1 | | |
| 2 | | module Piston |
| 3 | | module Ui |
| 4 | | module CommandLine |
| 5 | | def self.start |
| 6 | | opts = ::GetoptLong.new( |
| 7 | | [ '--revision', '-r', GetoptLong::REQUIRED_ARGUMENT ], |
| 8 | | [ '--help', '-h', GetoptLong::NO_ARGUMENT ], |
| 9 | | [ '--dry-run', GetoptLong::NO_ARGUMENT ], |
| 10 | | [ '--quiet', '-q', GetoptLong::NO_ARGUMENT ], |
| 11 | | [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ], |
| 12 | | [ '--show-updates', '-u', GetoptLong::NO_ARGUMENT ], |
| 13 | | [ '--lock', GetoptLong::NO_ARGUMENT ], |
| 14 | | [ '--force', '-f', GetoptLong::NO_ARGUMENT ], |
| 15 | | [ '--version', GetoptLong::NO_ARGUMENT ] |
| 16 | | ) |
| 17 | | |
| 18 | | options = Hash.new |
| 19 | | |
| 20 | | opts.each do |opt, arg| |
| 21 | | case opt |
| 22 | | when '--revision' |
| 23 | | options[:revision] = arg.to_i |
| 24 | | |
| 25 | | when '--help' |
| 26 | | return help |
| 27 | | |
| 28 | | when '--version' |
| 29 | | require 'piston/version' |
| 30 | | puts "Piston #{Piston::VERSION::STRING}" |
| 31 | | puts "Copyright 2006, Francois Beausoleil" |
| 32 | | puts "\nSee the LICENSE file for details" |
| 33 | | exit |
| 34 | | |
| 35 | | when /--([-\w]+)$/ |
| 36 | | options[$1.gsub('-', '_').to_sym] = true |
| 37 | | end |
| 38 | | end |
| 39 | | |
| 40 | | return help if ARGV.empty? |
| 41 | | |
| 42 | | command_name = ARGV.shift.downcase |
| 43 | | begin |
| 44 | | require File.join(PISTON_ROOT, 'piston', 'commands', command_name) |
| 45 | | rescue LoadError |
| 46 | | return help |
| 47 | | end |
| 48 | | |
| 49 | | command_class = Piston::Commands.const_get("#{command_name.capitalize}") |
| 50 | | command = command_class.new |
| 51 | | options.each do |key, value| |
| 52 | | command.send "#{key}=", value |
| 53 | | end |
| 54 | | |
| 55 | | begin |
| 56 | | command.execute(ARGV) |
| 57 | | rescue Piston::CommandError |
| 58 | | $stderr.puts "ERROR: #{$!.message}" |
| 59 | | exit 1 |
| 60 | | end |
| 61 | | end |
| 62 | | |
| 63 | | def self.help |
| 64 | | require File.join(PISTON_ROOT, 'piston', 'commands', 'help') |
| 65 | | Piston::Commands::Help.new.run(ARGV) |
| 66 | | end |
| 67 | | end |
| 68 | | end |
| 69 | | end |
| 70 | | |
| 71 | | Piston::Ui::CommandLine.start if $0 == __FILE__ |
| toggle raw diff |
--- a/lib/piston/ui/command_line.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-require 'getoptlong'
-
-module Piston
- module Ui
- module CommandLine
- def self.start
- opts = ::GetoptLong.new(
- [ '--revision', '-r', GetoptLong::REQUIRED_ARGUMENT ],
- [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
- [ '--dry-run', GetoptLong::NO_ARGUMENT ],
- [ '--quiet', '-q', GetoptLong::NO_ARGUMENT ],
- [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
- [ '--show-updates', '-u', GetoptLong::NO_ARGUMENT ],
- [ '--lock', GetoptLong::NO_ARGUMENT ],
- [ '--force', '-f', GetoptLong::NO_ARGUMENT ],
- [ '--version', GetoptLong::NO_ARGUMENT ]
- )
-
- options = Hash.new
-
- opts.each do |opt, arg|
- case opt
- when '--revision'
- options[:revision] = arg.to_i
-
- when '--help'
- return help
-
- when '--version'
- require 'piston/version'
- puts "Piston #{Piston::VERSION::STRING}"
- puts "Copyright 2006, Francois Beausoleil"
- puts "\nSee the LICENSE file for details"
- exit
-
- when /--([-\w]+)$/
- options[$1.gsub('-', '_').to_sym] = true
- end
- end
-
- return help if ARGV.empty?
-
- command_name = ARGV.shift.downcase
- begin
- require File.join(PISTON_ROOT, 'piston', 'commands', command_name)
- rescue LoadError
- return help
- end
-
- command_class = Piston::Commands.const_get("#{command_name.capitalize}")
- command = command_class.new
- options.each do |key, value|
- command.send "#{key}=", value
- end
-
- begin
- command.execute(ARGV)
- rescue Piston::CommandError
- $stderr.puts "ERROR: #{$!.message}"
- exit 1
- end
- end
-
- def self.help
- require File.join(PISTON_ROOT, 'piston', 'commands', 'help')
- Piston::Commands::Help.new.run(ARGV)
- end
- end
- end
-end
-
-Piston::Ui::CommandLine.start if $0 == __FILE__ |
| |   |
| 1 | require "optparse" |
| 2 | |
| 3 | module Transat |
| 4 | class VersionNeeded < StandardError; end |
| 5 | |
| 6 | class HelpNeeded < StandardError |
| 7 | attr_reader :command |
| 8 | |
| 9 | def initialize(command) |
| 10 | @command = command |
| 11 | end |
| 12 | end |
| 13 | |
| 14 | class NoCommandGiven < StandardError |
| 15 | def message |
| 16 | "No command given" |
| 17 | end |
| 18 | end |
| 19 | |
| 20 | class UnknownOptions < StandardError |
| 21 | attr_reader :command |
| 22 | |
| 23 | def initialize(command, unrecognized_options) |
| 24 | @command, @unrecognized_options = command, unrecognized_options |
| 25 | end |
| 26 | |
| 27 | def message |
| 28 | "Command #{@command} does not accept options #{@unrecognized_options.join(", ")}" |
| 29 | end |
| 30 | end |
| 31 | |
| 32 | class UnknownCommand < StandardError |
| 33 | def initialize(command, parser) |
| 34 | @command, @parser = command, parser |
| 35 | end |
| 36 | |
| 37 | def message |
| 38 | "Unknown command: #{@command.inspect}" |
| 39 | end |
| 40 | end |
| 41 | |
| 42 | class BaseCommand |
| 43 | attr_reader :non_options, :options |
| 44 | def initialize(non_options, options) |
| 45 | @non_options, @options = non_options, options |
| 46 | end |
| 47 | end |
| 48 | |
| 49 | class VersionCommand < BaseCommand |
| 50 | def run |
| 51 | raise VersionNeeded |
| 52 | end |
| 53 | end |
| 54 | |
| 55 | class HelpCommand < BaseCommand |
| 56 | def run |
| 57 | raise HelpNeeded.new(non_options.first) |
| 58 | end |
| 59 | end |
| 60 | |
| 61 | class Parser |
| 62 | def initialize(&block) |
| 63 | @valid_options, @received_options, @commands = [], {}, {} |
| 64 | @option_parser = OptionParser.new |
| 65 | |
| 66 | command(:help, Transat::HelpCommand) |
| 67 | command(:version, Transat::VersionCommand) |
| 68 | instance_eval(&block) if block_given? |
| 69 | end |
| 70 | |
| 71 | def option(name, options={}) |
| 72 | options[:long] = name.to_s.gsub("_", "-") unless options[:long] |
| 73 | @valid_options << name |
| 74 | @received_options[name] = nil |
| 75 | |
| 76 | opt_args = [] |
| 77 | opt_args << "-#{options[:short]}" if options.has_key?(:short) |
| 78 | opt_args << "--#{options[:long] || name}" |
| 79 | opt_args << "=#{options[:param_name]}" if options.has_key?(:param_name) |
| 80 | opt_args << options[:message] |
| 81 | case options[:type] |
| 82 | when :int, :integer |
| 83 | opt_args << Integer |
| 84 | when :float |
| 85 | opt_args << Float |
| 86 | when nil |
| 87 | # NOP |
| 88 | else |
| 89 | raise ArgumentError, "Option #{name} has a bad :type parameter: #{options[:type].inspect}" |
| 90 | end |
| 91 | |
| 92 | @option_parser.on(*opt_args.compact) do |value| |
| 93 | @received_options[name] = value |
| 94 | end |
| 95 | end |
| 96 | |
| 97 | def command(name, klass, options={}) |
| 98 | @commands[name.to_s] = options.merge(:class => klass) |
| 99 | end |
| 100 | |
| 101 | def parse_and_execute(args=ARGV) |
| 102 | begin |
| 103 | command, non_options = parse(args) |
| 104 | execute(command, non_options) |
| 105 | rescue HelpNeeded |
| 106 | $stderr.puts usage($!.command) |
| 107 | exit 1 |
| 108 | rescue VersionNeeded |
| 109 | puts "#{program_name} #{version}" |
| 110 | exit 0 |
| 111 | rescue NoCommandGiven, UnknownOptions, UnknownCommand |
| 112 | $stderr.puts "ERROR: #{$!.message}" |
| 113 | $stderr.puts usage($!.respond_to?(:command) ? $!.command : nil) |
| 114 | exit 1 |
| 115 | end |
| 116 | end |
| 117 | |
| 118 | def parse(args) |
| 119 | non_options = @option_parser.parse(args) |
| 120 | command = non_options.shift |
| 121 | raise NoCommandGiven unless command |
| 122 | return command, non_options |
| 123 | end |
| 124 | |
| 125 | def execute(command, non_options) |
| 126 | found = false |
| 127 | @commands.each do |command_name, options| |
| 128 | command_klass = options[:class] |
| 129 | aliases = [command_name] |
| 130 | aliases += command_klass.aliases if command_klass.respond_to?(:aliases) |
| 131 | return command_klass.new(non_options, @received_options).run if aliases.include?(command) |
| 132 | end |
| 133 | |
| 134 | raise UnknownCommand.new(command, self) |
| 135 | end |
| 136 | |
| 137 | def usage(command=nil) |
| 138 | message = [] |
| 139 | |
| 140 | if command then |
| 141 | command_klass = @commands[command][:class] |
| 142 | help = |
| 143 | if command_klass.respond_to?(:aliases) then |
| 144 | "#{command} (#{command_klass.aliases.join(", ")})" |
| 145 | else |
| 146 | "#{command}" |
| 147 | end |
| 148 | help = "#{help}: #{command_klass.help}" if command_klass.respond_to?(:help) |
| 149 | message << help |
| 150 | message << command_klass.detailed_help if command_klass.respond_to?(:detailed_help) |
| 151 | message << "" |
| 152 | message << "Valid options:" |
| 153 | @option_parser.summarize(message) |
| 154 | else |
| 155 | message << "usage: #{program_name.downcase} <SUBCOMMAND> [OPTIONS] [ARGS...]" |
| 156 | message << "Type '#{program_name.downcase} help <SUBCOMMAND>' for help on a specific subcommand." |
| 157 | message << "Type '#{program_name.downcase} version' to get this program's version." |
| 158 | message << "" |
| 159 | message << "Available subcommands are:" |
| 160 | @commands.sort.each do |command, options| |
| 161 | command_klass = options[:class] |
| 162 | if command_klass.respond_to?(:aliases) then |
| 163 | message << " #{command} (#{command_klass.aliases.join(", ")})" |
| 164 | else |
| 165 | message << " #{command}" |
| 166 | end |
| 167 | end |
| 168 | end |
| 169 | |
| 170 | message.map {|line| line.chomp}.join("\n") |
| 171 | end |
| 172 | |
| 173 | def program_name(value=nil) |
| 174 | value ? @program_name = value : @program_name |
| 175 | end |
| 176 | |
| 177 | def version(value=nil) |
| 178 | if value then |
| 179 | @version = value.respond_to?(:join) ? value.join(".") : value |
| 180 | else |
| 181 | @version |
| 182 | end |
| 183 | end |
| 184 | |
| 185 | def self.parse_and_execute(args=ARGV, &block) |
| 186 | self.new(&block).parse_and_execute(args) |
| 187 | end |
| 188 | end |
| 189 | end |
| toggle raw diff |
--- /dev/null
+++ b/lib/transat/parser.rb
@@ -0,0 +1,189 @@
+require "optparse"
+
+module Transat
+ class VersionNeeded < StandardError; end
+
+ class HelpNeeded < StandardError
+ attr_reader :command
+
+ def initialize(command)
+ @command = command
+ end
+ end
+
+ class NoCommandGiven < StandardError
+ def message
+ "No command given"
+ end
+ end
+
+ class UnknownOptions < StandardError
+ attr_reader :command
+
+ def initialize(command, unrecognized_options)
+ @command, @unrecognized_options = command, unrecognized_options
+ end
+
+ def message
+ "Command #{@command} does not accept options #{@unrecognized_options.join(", ")}"
+ end
+ end
+
+ class UnknownCommand < StandardError
+ def initialize(command, parser)
+ @command, @parser = command, parser
+ end
+
+ def message
+ "Unknown command: #{@command.inspect}"
+ end
+ end
+
+ class BaseCommand
+ attr_reader :non_options, :options
+ def initialize(non_options, options)
+ @non_options, @options = non_options, options
+ end
+ end
+
+ class VersionCommand < BaseCommand
+ def run
+ raise VersionNeeded
+ end
+ end
+
+ class HelpCommand < BaseCommand
+ def run
+ raise HelpNeeded.new(non_options.first)
+ end
+ end
+
+ class Parser
+ def initialize(&block)
+ @valid_options, @received_options, @commands = [], {}, {}
+ @option_parser = OptionParser.new
+
+ command(:help, Transat::HelpCommand)
+ command(:version, Transat::VersionCommand)
+ instance_eval(&block) if block_given?
+ end
+
+ def option(name, options={})
+ options[:long] = name.to_s.gsub("_", "-") unless options[:long]
+ @valid_options << name
+ @received_options[name] = nil
+
+ opt_args = []
+ opt_args << "-#{options[:short]}" if options.has_key?(:short)
+ opt_args << "--#{options[:long] || name}"
+ opt_args << "=#{options[:param_name]}" if options.has_key?(:param_name)
+ opt_args << options[:message]
+ case options[:type]
+ when :int, :integer
+ opt_args << Integer
+ when :float
+ opt_args << Float
+ when nil
+ # NOP
+ else
+ raise ArgumentError, "Option #{name} has a bad :type parameter: #{options[:type].inspect}"
+ end
+
+ @option_parser.on(*opt_args.compact) do |value|
+ @received_options[name] = value
+ end
+ end
+
+ def command(name, klass, options={})
+ @commands[name.to_s] = options.merge(:class => klass)
+ end
+
+ def parse_and_execute(args=ARGV)
+ begin
+ command, non_options = parse(args)
+ execute(command, non_options)
+ rescue HelpNeeded
+ $stderr.puts usage($!.command)
+ exit 1
+ rescue VersionNeeded
+ puts "#{program_name} #{version}"
+ exit 0
+ rescue NoCommandGiven, UnknownOptions, UnknownCommand
+ $stderr.puts "ERROR: #{$!.message}"
+ $stderr.puts usage($!.respond_to?(:command) ? $!.command : nil)
+ exit 1
+ end
+ end
+
+ def parse(args)
+ non_options = @option_parser.parse(args)
+ command = non_options.shift
+ raise NoCommandGiven unless command
+ return command, non_options
+ end
+
+ def execute(command, non_options)
+ found = false
+ @commands.each do |command_name, options|
+ command_klass = options[:class]
+ aliases = [command_name]
+ aliases += command_klass.aliases if command_klass.respond_to?(:aliases)
+ return command_klass.new(non_options, @received_options).run if aliases.include?(command)
+ end
+
+ raise UnknownCommand.new(command, self)
+ end
+
+ def usage(command=nil)
+ message = []
+
+ if command then
+ command_klass = @commands[command][:class]
+ help =
+ if command_klass.respond_to?(:aliases) then
+ "#{command} (#{command_klass.aliases.join(", ")})"
+ else
+ "#{command}"
+ end
+ help = "#{help}: #{command_klass.help}" if command_klass.respond_to?(:help)
+ message << help
+ message << command_klass.detailed_help if command_klass.respond_to?(:detailed_help)
+ message << ""
+ message << "Valid options:"
+ @option_parser.summarize(message)
+ else
+ message << "usage: #{program_name.downcase} <SUBCOMMAND> [OPTIONS] [ARGS...]"
+ message << &quo |