diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 95fa79409..60c289890 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -67,6 +67,13 @@ class ConsoleOptionParser { */ protected $_args = array(); +/** + * Subcommands for this Shell. + * + * @var array + */ + protected $_subcommands = array(); + /** * Construct an OptionParser so you can define its behavior * @@ -214,6 +221,33 @@ class ConsoleOptionParser { return $this; } +/** + * Append a subcommand to the subcommand list. + * Subcommands are usually methods on your Shell, but can also be used to document + * Tasks + * + * ### Params + * + * - `help` - Help text for the subcommand. + * - `parser` - A ConsoleOptionParser for the subcommand. This allows you to create method + * specific option parsers. When help is generated for a subcommand, if a parser is present + * it will be used. + * + * @param string $name Name of the subcommand + * @param array $params Array of params, see above. + * @return $this. + */ + public function addSubcommand($name, $params = array()) { + $defaults = array( + 'name' => $name, + 'help' => '', + 'parser' => null + ); + $options = array_merge($defaults, $params); + $this->_subcommands[$name] = $options; + return $this; + } + /** * Gets the arguments defined in the parser. * @@ -278,6 +312,20 @@ class ConsoleOptionParser { $out[] = 'Usage:'; $out[] = $this->_generateUsage(); $out[] = ''; + if (!empty($this->_subcommands)) { + $out[] = 'Subcommands:'; + $out[] = ''; + $max = 0; + foreach ($this->_subcommands as $description) { + $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; + } + $max += 2; + foreach ($this->_subcommands as $description) { + $out[] = $this->_subcommandHelp($description, $max); + } + $out[] = ''; + } + if (!empty($this->_options)) { $max = 0; foreach ($this->_options as $description) { @@ -296,7 +344,7 @@ class ConsoleOptionParser { foreach ($this->_args as $description) { $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; } - $max += 1; + $max += 2; $out[] = 'Arguments:'; $out[] = ''; foreach ($this->_args as $description) { @@ -319,6 +367,9 @@ class ConsoleOptionParser { */ protected function _generateUsage() { $usage = array('cake ' . $this->_command); + if (!empty($this->_subcommands)) { + $usage[] = '[subcommand]'; + } foreach ($this->_options as $definition) { $name = empty($definition['short']) ? '--' . $definition['name'] : '-' . $definition['short']; $default = ''; @@ -371,7 +422,20 @@ class ConsoleOptionParser { if (!$definition['required']) { $optional = ' (optional)'; } - return sprintf('%s %s%s', $name, $definition['help'], $optional); + return sprintf('%s%s%s', $name, $definition['help'], $optional); + } + +/** + * Generate help for a single subcommand. + * + * @return string + */ + protected function _subcommandHelp($definition, $width) { + $name = $definition['name']; + if (strlen($name) < $width) { + $name = str_pad($name, $width, ' '); + } + return $name . $definition['help']; } /** diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 4f1271a15..5a6321f1a 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -234,6 +234,19 @@ class ConsoleOptionParserTest extends CakeTestCase { $parser->parse(array('one')); } +/** + * test setting a subcommand up. + * + * @return void + */ + function testSubcommand() { + $parser = new ConsoleOptionParser(); + $result = $parser->addSubcommand('initdb', array( + 'help' => 'Initialize the database' + )); + $this->assertEquals($parser, $result, 'Adding a subcommand is not chainable'); + } + /** * test getting help with defined options. * @@ -296,7 +309,7 @@ TEXT; * * @return void */ - function testDescriptionAndEpilog() { + function testHelpDescriptionAndEpilog() { $parser = new ConsoleOptionParser('mycommand', false); $parser->description('Description text') ->epilog('epilog text') @@ -323,4 +336,33 @@ epilog text TEXT; $this->assertEquals($expected, $result, 'Help is wrong.'); } + +/** + * test that help() outputs subcommands. + * + * @return void + */ + function testHelpSubcommand() { + $parser = new ConsoleOptionParser('mycommand', false); + $parser->addSubcommand('method', array('help' => 'This is another command')) + ->addOption('test', array('help' => 'A test option.')); + + $result = $parser->help(); + $expected = <<Usage: +cake mycommand [subcommand] [-h] [--test] + +Subcommands: + +method This is another command + +Options: + +--help, -h Display this help. +--test A test option. + +TEXT; + $this->assertEquals($expected, $result, 'Help is not correct.'); + + } } \ No newline at end of file