From 079c55656bedc0599670d41835e1e7d0fd771072 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 01:40:12 -0400 Subject: [PATCH 001/207] Updating doc block for console error handler. --- cake/console/console_error_handler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/console/console_error_handler.php b/cake/console/console_error_handler.php index 10a8028fd..37412d2c1 100644 --- a/cake/console/console_error_handler.php +++ b/cake/console/console_error_handler.php @@ -14,7 +14,7 @@ * @link http://cakephp.org CakePHP(tm) Project * @package cake * @subpackage cake.cake.console - * @since CakePHP(tm) v 1.2.0.5074 + * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ From 0a86b7f2083bcb04851492cee1912393784d865c Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 01:48:06 -0400 Subject: [PATCH 002/207] Moving the ShellDispatcher into is own file. This makes testing easier and will make app/console easier to create. --- cake/console/cake.php | 636 +---------------------------- cake/console/shell_dispatcher.php | 642 ++++++++++++++++++++++++++++++ 2 files changed, 644 insertions(+), 634 deletions(-) create mode 100644 cake/console/shell_dispatcher.php diff --git a/cake/console/cake.php b/cake/console/cake.php index a445bb64a..1cd2e5bee 100644 --- a/cake/console/cake.php +++ b/cake/console/cake.php @@ -20,638 +20,6 @@ * @since CakePHP(tm) v 1.2.0.5012 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR. 'shell_dispatcher.php'); -/** - * Shell dispatcher - * - * @package cake - * @subpackage cake.cake.console - */ -class ShellDispatcher { - -/** - * Standard input stream. - * - * @var filehandle - * @access public - */ - public $stdin; - -/** - * Standard output stream. - * - * @var filehandle - * @access public - */ - public $stdout; - -/** - * Standard error stream. - * - * @var filehandle - * @access public - */ - public $stderr; - -/** - * Contains command switches parsed from the command line. - * - * @var array - * @access public - */ - public $params = array(); - -/** - * Contains arguments parsed from the command line. - * - * @var array - * @access public - */ - public $args = array(); - -/** - * The file name of the shell that was invoked. - * - * @var string - * @access public - */ - public $shell = null; - -/** - * The class name of the shell that was invoked. - * - * @var string - * @access public - */ - public $shellClass = null; - -/** - * The command called if public methods are available. - * - * @var string - * @access public - */ - public $shellCommand = null; - -/** - * The path locations of shells. - * - * @var array - * @access public - */ - public $shellPaths = array(); - -/** - * The path to the current shell location. - * - * @var string - * @access public - */ - public $shellPath = null; - -/** - * The name of the shell in camelized. - * - * @var string - * @access public - */ - public $shellName = null; - -/** - * TaskCollection object for the command - * - * @var TaskCollection - */ - protected $_Tasks; - -/** - * Constructor - * - * The execution of the script is stopped after dispatching the request with - * a status code of either 0 or 1 according to the result of the dispatch. - * - * @param array $args the argv - * @return void - */ - public function __construct($args = array()) { - set_time_limit(0); - - $this->__initConstants(); - $this->parseParams($args); - $this->_initEnvironment(); - $this->__buildPaths(); - $this->_stop($this->dispatch() === false ? 1 : 0); - } - -/** - * Defines core configuration. - * - * @access private - */ - function __initConstants() { - if (function_exists('ini_set')) { - ini_set('display_errors', '1'); - ini_set('error_reporting', E_ALL & ~E_DEPRECATED); - ini_set('html_errors', false); - ini_set('implicit_flush', true); - ini_set('max_execution_time', 0); - } - - if (!defined('CAKE_CORE_INCLUDE_PATH')) { - define('DS', DIRECTORY_SEPARATOR); - define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__)))); - define('DISABLE_DEFAULT_ERROR_HANDLING', false); - define('CAKEPHP_SHELL', true); - if (!defined('CORE_PATH')) { - if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ini_get('include_path'))) { - define('CORE_PATH', null); - } else { - define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); - } - } - } - } - -/** - * Defines current working environment. - * - */ - protected function _initEnvironment() { - $this->stdin = fopen('php://stdin', 'r'); - $this->stdout = fopen('php://stdout', 'w'); - $this->stderr = fopen('php://stderr', 'w'); - - if (!$this->__bootstrap()) { - $this->stderr("\nCakePHP Console: "); - $this->stderr("\nUnable to load Cake core:"); - $this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH); - $this->_stop(); - } - - if (!isset($this->args[0]) || !isset($this->params['working'])) { - $this->stderr("\nCakePHP Console: "); - $this->stderr('This file has been loaded incorrectly and cannot continue.'); - $this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,'); - $this->stderr('and check the manual for the correct usage of this command.'); - $this->stderr('(http://manual.cakephp.org/)'); - $this->_stop(); - } - - if (basename(__FILE__) != basename($this->args[0])) { - $this->stderr("\nCakePHP Console: "); - $this->stderr('Warning: the dispatcher may have been loaded incorrectly, which could lead to unexpected results...'); - if ($this->getInput('Continue anyway?', array('y', 'n'), 'y') == 'n') { - $this->_stop(); - } - } - - $this->shiftArgs(); - } - -/** - * Builds the shell paths. - * - * @access private - * @return void - */ - function __buildPaths() { - $paths = array(); - if (!class_exists('Folder')) { - require LIBS . 'folder.php'; - } - $plugins = App::objects('plugin', null, false); - foreach ((array)$plugins as $plugin) { - $pluginPath = App::pluginPath($plugin); - $path = $pluginPath . 'vendors' . DS . 'shells' . DS; - if (file_exists($path)) { - $paths[] = $path; - } - } - - $vendorPaths = array_values(App::path('vendors')); - foreach ($vendorPaths as $vendorPath) { - $path = rtrim($vendorPath, DS) . DS . 'shells' . DS; - if (file_exists($path)) { - $paths[] = $path; - } - } - - $this->shellPaths = array_values(array_unique(array_merge($paths, App::path('shells')))); - } - -/** - * Initializes the environment and loads the Cake core. - * - * @return boolean Success. - * @access private - */ - function __bootstrap() { - - define('ROOT', $this->params['root']); - define('APP_DIR', $this->params['app']); - define('APP_PATH', $this->params['working'] . DS); - define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS); - if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) { - define('TMP', CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'tmp' . DS); - } - - $boot = file_exists(ROOT . DS . APP_DIR . DS . 'config' . DS . 'bootstrap.php'); - require CORE_PATH . 'cake' . DS . 'bootstrap.php'; - require_once CORE_PATH . 'cake' . DS . 'console' . DS . 'console_error_handler.php'; - set_exception_handler(array('ConsoleErrorHandler', 'handleException')); - - if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) { - include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php'; - App::build(); - } - if (!defined('FULL_BASE_URL')) { - define('FULL_BASE_URL', '/'); - } - - return true; - } - -/** - * Clear the console - * - * @return void - */ - public function clear() { - if (empty($this->params['noclear'])) { - if ( DS === '/') { - passthru('clear'); - } else { - passthru('cls'); - } - } - } - -/** - * Dispatches a CLI request - * - * @return boolean - */ - public function dispatch() { - $arg = $this->shiftArgs(); - - if (!$arg) { - $this->help(); - return false; - } - if ($arg == 'help') { - $this->help(); - return true; - } - - list($plugin, $shell) = pluginSplit($arg); - $this->shell = $shell; - $this->shellName = Inflector::camelize($shell); - $this->shellClass = $this->shellName . 'Shell'; - - $arg = null; - - if (isset($this->args[0])) { - $arg = $this->args[0]; - $this->shellCommand = Inflector::variable($arg); - } - - $Shell = $this->_getShell($plugin); - - if (!$Shell) { - $title = sprintf(__('Error: Class %s could not be loaded.'), $this->shellClass); - $this->stderr($title . "\n"); - return false; - } - - $methods = array(); - - if (is_a($Shell, 'Shell')) { - $Shell->initialize(); - $Shell->loadTasks(); - - foreach ($Shell->taskNames as $task) { - if (is_a($Shell->{$task}, 'Shell')) { - $Shell->{$task}->initialize(); - $Shell->{$task}->loadTasks(); - } - } - - $task = Inflector::camelize($arg); - - if (in_array($task, $Shell->taskNames)) { - $this->shiftArgs(); - $Shell->{$task}->startup(); - - if (isset($this->args[0]) && $this->args[0] == 'help') { - if (method_exists($Shell->{$task}, 'help')) { - $Shell->{$task}->help(); - } else { - $this->help(); - } - return true; - } - return $Shell->{$task}->execute(); - } - $methods = array_diff(get_class_methods('Shell'), array('help')); - } - $methods = array_diff(get_class_methods($Shell), $methods); - $added = in_array(strtolower($arg), array_map('strtolower', $methods)); - $private = $arg[0] == '_' && method_exists($Shell, $arg); - - if (!$private) { - if ($added) { - $this->shiftArgs(); - $Shell->startup(); - return $Shell->{$arg}(); - } - if (method_exists($Shell, 'main')) { - $Shell->startup(); - return $Shell->main(); - } - } - - $title = sprintf(__('Error: Unknown %1$s command %2$s.'), $this->shellName, $arg); - $message = sprintf(__('For usage try `cake %s help`'), $this->shell); - $this->stderr($title . "\n" . $message . "\n"); - return false; - } - -/** - * Get shell to use, either plugin shell or application shell - * - * All paths in the shellPaths property are searched. - * shell, shellPath and shellClass properties are taken into account. - * - * @param string $plugin Optionally the name of a plugin - * @return mixed False if no shell could be found or an object on success - */ - protected function _getShell($plugin = null) { - foreach ($this->shellPaths as $path) { - $this->shellPath = $path . $this->shell . '.php'; - $pluginShellPath = DS . $plugin . DS . 'vendors' . DS . 'shells' . DS; - - if ((strpos($path, $pluginShellPath) !== false || !$plugin) && file_exists($this->shellPath)) { - $loaded = true; - break; - } - } - if (!isset($loaded)) { - return false; - } - - if (!class_exists('Shell')) { - require CONSOLE_LIBS . 'shell.php'; - } - - if (!class_exists($this->shellClass)) { - require $this->shellPath; - } - if (!class_exists($this->shellClass)) { - return false; - } - $Shell = new $this->shellClass($this); - return $Shell; - } - -/** - * Returns a TaskCollection object for Shells to use when loading their tasks. - * - * @return TaskCollection object. - */ - public function getTaskCollection() { - if (empty($this->_Tasks)) { - $this->_Tasks = new TaskCollection($this); - } - return $this->_Tasks; - } - -/** - * Prompts the user for input, and returns it. - * - * @param string $prompt Prompt text. - * @param mixed $options Array or string of options. - * @param string $default Default input value. - * @return Either the default value, or the user-provided input. - */ - public function getInput($prompt, $options = null, $default = null) { - if (!is_array($options)) { - $printOptions = ''; - } else { - $printOptions = '(' . implode('/', $options) . ')'; - } - - if ($default === null) { - $this->stdout($prompt . " $printOptions \n" . '> ', false); - } else { - $this->stdout($prompt . " $printOptions \n" . "[$default] > ", false); - } - $result = fgets($this->stdin); - - if ($result === false) { - exit; - } - $result = trim($result); - - if ($default != null && empty($result)) { - return $default; - } - return $result; - } - -/** - * Outputs to the stdout filehandle. - * - * @param string $string String to output. - * @param boolean $newline If true, the outputs gets an added newline. - * @return integer Returns the number of bytes output to stdout. - */ - public function stdout($string, $newline = true) { - if ($newline) { - return fwrite($this->stdout, $string . "\n"); - } else { - return fwrite($this->stdout, $string); - } - } - -/** - * Outputs to the stderr filehandle. - * - * @param string $string Error text to output. - */ - public function stderr($string) { - fwrite($this->stderr, $string); - } - -/** - * Parses command line options - * - * @param array $params Parameters to parse - */ - public function parseParams($params) { - $this->__parseParams($params); - $defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot'); - $params = array_merge($defaults, array_intersect_key($this->params, $defaults)); - $isWin = false; - foreach ($defaults as $default => $value) { - if (strpos($params[$default], '\\') !== false) { - $isWin = true; - break; - } - } - $params = str_replace('\\', '/', $params); - - if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) { - if (empty($this->params['app']) && $params['working'] != $params['root']) { - $params['root'] = dirname($params['working']); - $params['app'] = basename($params['working']); - } else { - $params['root'] = $params['working']; - } - } - - if ($params['app'][0] == '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) { - $params['root'] = dirname($params['app']); - } elseif (strpos($params['app'], '/')) { - $params['root'] .= '/' . dirname($params['app']); - } - - $params['app'] = basename($params['app']); - $params['working'] = rtrim($params['root'], '/') . '/' . $params['app']; - - if (!empty($matches[0]) || !empty($isWin)) { - $params = str_replace('/', '\\', $params); - } - - $this->params = array_merge($this->params, $params); - } - -/** - * Helper for recursively parsing params - * - * @return array params - * @access private - */ - function __parseParams($params) { - $count = count($params); - for ($i = 0; $i < $count; $i++) { - if (isset($params[$i])) { - if ($params[$i]{0} === '-') { - $key = substr($params[$i], 1); - $this->params[$key] = true; - unset($params[$i]); - if (isset($params[++$i])) { - if ($params[$i]{0} !== '-') { - $this->params[$key] = str_replace('"', '', $params[$i]); - unset($params[$i]); - } else { - $i--; - $this->__parseParams($params); - } - } - } else { - $this->args[] = $params[$i]; - unset($params[$i]); - } - - } - } - } - -/** - * Removes first argument and shifts other arguments up - * - * @return mixed Null if there are no arguments otherwise the shifted argument - */ - public function shiftArgs() { - return array_shift($this->args); - } - -/** - * Shows console help - * - */ - public function help() { - $this->clear(); - $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console"); - $this->stdout("---------------------------------------------------------------"); - $this->stdout("Current Paths:"); - $this->stdout(" -app: ". $this->params['app']); - $this->stdout(" -working: " . rtrim($this->params['working'], DS)); - $this->stdout(" -root: " . rtrim($this->params['root'], DS)); - $this->stdout(" -core: " . rtrim(CORE_PATH, DS)); - $this->stdout(""); - $this->stdout("Changing Paths:"); - $this->stdout("your working path should be the same as your application path"); - $this->stdout("to change your path use the '-app' param."); - $this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp"); - - $this->stdout("\nAvailable Shells:"); - $shellList = array(); - foreach ($this->shellPaths as $path) { - if (!is_dir($path)) { - continue; - } - $shells = App::objects('file', $path); - if (empty($shells)) { - continue; - } - if (preg_match('@plugins[\\\/]([^\\\/]*)@', $path, $matches)) { - $type = Inflector::camelize($matches[1]); - } elseif (preg_match('@([^\\\/]*)[\\\/]vendors[\\\/]@', $path, $matches)) { - $type = $matches[1]; - } elseif (strpos($path, CAKE_CORE_INCLUDE_PATH . DS . 'cake') === 0) { - $type = 'CORE'; - } else { - $type = 'app'; - } - foreach ($shells as $shell) { - if ($shell !== 'shell.php') { - $shell = str_replace('.php', '', $shell); - $shellList[$shell][$type] = $type; - } - } - } - if ($shellList) { - ksort($shellList); - if (DS === '/') { - $width = exec('tput cols') - 2; - } - if (empty($width)) { - $width = 80; - } - $columns = max(1, floor($width / 30)); - $rows = ceil(count($shellList) / $columns); - - foreach ($shellList as $shell => $types) { - sort($types); - $shellList[$shell] = str_pad($shell . ' [' . implode ($types, ', ') . ']', $width / $columns); - } - $out = array_chunk($shellList, $rows); - for ($i = 0; $i < $rows; $i++) { - $row = ''; - for ($j = 0; $j < $columns; $j++) { - if (!isset($out[$j][$i])) { - continue; - } - $row .= $out[$j][$i]; - } - $this->stdout(" " . $row); - } - } - $this->stdout("\nTo run a command, type 'cake shell_name [args]'"); - $this->stdout("To get help on a specific command, type 'cake shell_name help'"); - } - -/** - * Stop execution of the current script - * - * @param $status see http://php.net/exit for values - * @return void - */ - protected function _stop($status = 0) { - exit($status); - } -} -if (!defined('DISABLE_AUTO_DISPATCH')) { - $dispatcher = new ShellDispatcher($argv); -} +$dispatcher = new ShellDispatcher($argv); diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php new file mode 100644 index 000000000..3a1262536 --- /dev/null +++ b/cake/console/shell_dispatcher.php @@ -0,0 +1,642 @@ +__initConstants(); + $this->parseParams($args); + $this->_initEnvironment(); + $this->__buildPaths(); + $this->_stop($this->dispatch() === false ? 1 : 0); + } + +/** + * Defines core configuration. + * + * @access private + */ + function __initConstants() { + if (function_exists('ini_set')) { + ini_set('display_errors', '1'); + ini_set('error_reporting', E_ALL & ~E_DEPRECATED); + ini_set('html_errors', false); + ini_set('implicit_flush', true); + ini_set('max_execution_time', 0); + } + + if (!defined('CAKE_CORE_INCLUDE_PATH')) { + define('DS', DIRECTORY_SEPARATOR); + define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__)))); + define('DISABLE_DEFAULT_ERROR_HANDLING', false); + define('CAKEPHP_SHELL', true); + if (!defined('CORE_PATH')) { + if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ini_get('include_path'))) { + define('CORE_PATH', null); + } else { + define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); + } + } + } + } + +/** + * Defines current working environment. + * + */ + protected function _initEnvironment() { + $this->stdin = fopen('php://stdin', 'r'); + $this->stdout = fopen('php://stdout', 'w'); + $this->stderr = fopen('php://stderr', 'w'); + + if (!$this->__bootstrap()) { + $this->stderr("\nCakePHP Console: "); + $this->stderr("\nUnable to load Cake core:"); + $this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH); + $this->_stop(); + } + + if (!isset($this->args[0]) || !isset($this->params['working'])) { + $this->stderr("\nCakePHP Console: "); + $this->stderr('This file has been loaded incorrectly and cannot continue.'); + $this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,'); + $this->stderr('and check the manual for the correct usage of this command.'); + $this->stderr('(http://manual.cakephp.org/)'); + $this->_stop(); + } + + $this->shiftArgs(); + } + +/** + * Builds the shell paths. + * + * @access private + * @return void + */ + function __buildPaths() { + $paths = array(); + if (!class_exists('Folder')) { + require LIBS . 'folder.php'; + } + $plugins = App::objects('plugin', null, false); + foreach ((array)$plugins as $plugin) { + $pluginPath = App::pluginPath($plugin); + $path = $pluginPath . 'vendors' . DS . 'shells' . DS; + if (file_exists($path)) { + $paths[] = $path; + } + } + + $vendorPaths = array_values(App::path('vendors')); + foreach ($vendorPaths as $vendorPath) { + $path = rtrim($vendorPath, DS) . DS . 'shells' . DS; + if (file_exists($path)) { + $paths[] = $path; + } + } + + $this->shellPaths = array_values(array_unique(array_merge($paths, App::path('shells')))); + } + +/** + * Initializes the environment and loads the Cake core. + * + * @return boolean Success. + * @access private + */ + function __bootstrap() { + + define('ROOT', $this->params['root']); + define('APP_DIR', $this->params['app']); + define('APP_PATH', $this->params['working'] . DS); + define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS); + if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) { + define('TMP', CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'tmp' . DS); + } + + $boot = file_exists(ROOT . DS . APP_DIR . DS . 'config' . DS . 'bootstrap.php'); + require CORE_PATH . 'cake' . DS . 'bootstrap.php'; + require_once CORE_PATH . 'cake' . DS . 'console' . DS . 'console_error_handler.php'; + set_exception_handler(array('ConsoleErrorHandler', 'handleException')); + + if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) { + include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php'; + App::build(); + } + if (!defined('FULL_BASE_URL')) { + define('FULL_BASE_URL', '/'); + } + + return true; + } + +/** + * Clear the console + * + * @return void + */ + public function clear() { + if (empty($this->params['noclear'])) { + if ( DS === '/') { + passthru('clear'); + } else { + passthru('cls'); + } + } + } + +/** + * Dispatches a CLI request + * + * @return boolean + */ + public function dispatch() { + $arg = $this->shiftArgs(); + + if (!$arg) { + $this->help(); + return false; + } + if ($arg == 'help') { + $this->help(); + return true; + } + + list($plugin, $shell) = pluginSplit($arg); + $this->shell = $shell; + $this->shellName = Inflector::camelize($shell); + $this->shellClass = $this->shellName . 'Shell'; + + $arg = null; + + if (isset($this->args[0])) { + $arg = $this->args[0]; + $this->shellCommand = Inflector::variable($arg); + } + + $Shell = $this->_getShell($plugin); + + if (!$Shell) { + $title = sprintf(__('Error: Class %s could not be loaded.'), $this->shellClass); + $this->stderr($title . "\n"); + return false; + } + + $methods = array(); + + if (is_a($Shell, 'Shell')) { + $Shell->initialize(); + $Shell->loadTasks(); + + foreach ($Shell->taskNames as $task) { + if (is_a($Shell->{$task}, 'Shell')) { + $Shell->{$task}->initialize(); + $Shell->{$task}->loadTasks(); + } + } + + $task = Inflector::camelize($arg); + + if (in_array($task, $Shell->taskNames)) { + $this->shiftArgs(); + $Shell->{$task}->startup(); + + if (isset($this->args[0]) && $this->args[0] == 'help') { + if (method_exists($Shell->{$task}, 'help')) { + $Shell->{$task}->help(); + } else { + $this->help(); + } + return true; + } + return $Shell->{$task}->execute(); + } + $methods = array_diff(get_class_methods('Shell'), array('help')); + } + $methods = array_diff(get_class_methods($Shell), $methods); + $added = in_array(strtolower($arg), array_map('strtolower', $methods)); + $private = $arg[0] == '_' && method_exists($Shell, $arg); + + if (!$private) { + if ($added) { + $this->shiftArgs(); + $Shell->startup(); + return $Shell->{$arg}(); + } + if (method_exists($Shell, 'main')) { + $Shell->startup(); + return $Shell->main(); + } + } + + $title = sprintf(__('Error: Unknown %1$s command %2$s.'), $this->shellName, $arg); + $message = sprintf(__('For usage try `cake %s help`'), $this->shell); + $this->stderr($title . "\n" . $message . "\n"); + return false; + } + +/** + * Get shell to use, either plugin shell or application shell + * + * All paths in the shellPaths property are searched. + * shell, shellPath and shellClass properties are taken into account. + * + * @param string $plugin Optionally the name of a plugin + * @return mixed False if no shell could be found or an object on success + */ + protected function _getShell($plugin = null) { + foreach ($this->shellPaths as $path) { + $this->shellPath = $path . $this->shell . '.php'; + $pluginShellPath = DS . $plugin . DS . 'vendors' . DS . 'shells' . DS; + + if ((strpos($path, $pluginShellPath) !== false || !$plugin) && file_exists($this->shellPath)) { + $loaded = true; + break; + } + } + if (!isset($loaded)) { + return false; + } + + if (!class_exists('Shell')) { + require CONSOLE_LIBS . 'shell.php'; + } + + if (!class_exists($this->shellClass)) { + require $this->shellPath; + } + if (!class_exists($this->shellClass)) { + return false; + } + $Shell = new $this->shellClass($this); + return $Shell; + } + +/** + * Returns a TaskCollection object for Shells to use when loading their tasks. + * + * @return TaskCollection object. + */ + public function getTaskCollection() { + if (empty($this->_Tasks)) { + $this->_Tasks = new TaskCollection($this); + } + return $this->_Tasks; + } + +/** + * Prompts the user for input, and returns it. + * + * @param string $prompt Prompt text. + * @param mixed $options Array or string of options. + * @param string $default Default input value. + * @return Either the default value, or the user-provided input. + */ + public function getInput($prompt, $options = null, $default = null) { + if (!is_array($options)) { + $printOptions = ''; + } else { + $printOptions = '(' . implode('/', $options) . ')'; + } + + if ($default === null) { + $this->stdout($prompt . " $printOptions \n" . '> ', false); + } else { + $this->stdout($prompt . " $printOptions \n" . "[$default] > ", false); + } + $result = fgets($this->stdin); + + if ($result === false) { + exit; + } + $result = trim($result); + + if ($default != null && empty($result)) { + return $default; + } + return $result; + } + +/** + * Outputs to the stdout filehandle. + * + * @param string $string String to output. + * @param boolean $newline If true, the outputs gets an added newline. + * @return integer Returns the number of bytes output to stdout. + */ + public function stdout($string, $newline = true) { + if ($newline) { + return fwrite($this->stdout, $string . "\n"); + } else { + return fwrite($this->stdout, $string); + } + } + +/** + * Outputs to the stderr filehandle. + * + * @param string $string Error text to output. + */ + public function stderr($string) { + fwrite($this->stderr, $string); + } + +/** + * Parses command line options + * + * @param array $params Parameters to parse + */ + public function parseParams($params) { + $this->__parseParams($params); + $defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot'); + $params = array_merge($defaults, array_intersect_key($this->params, $defaults)); + $isWin = false; + foreach ($defaults as $default => $value) { + if (strpos($params[$default], '\\') !== false) { + $isWin = true; + break; + } + } + $params = str_replace('\\', '/', $params); + + if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) { + if (empty($this->params['app']) && $params['working'] != $params['root']) { + $params['root'] = dirname($params['working']); + $params['app'] = basename($params['working']); + } else { + $params['root'] = $params['working']; + } + } + + if ($params['app'][0] == '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) { + $params['root'] = dirname($params['app']); + } elseif (strpos($params['app'], '/')) { + $params['root'] .= '/' . dirname($params['app']); + } + + $params['app'] = basename($params['app']); + $params['working'] = rtrim($params['root'], '/') . '/' . $params['app']; + + if (!empty($matches[0]) || !empty($isWin)) { + $params = str_replace('/', '\\', $params); + } + + $this->params = array_merge($this->params, $params); + } + +/** + * Helper for recursively parsing params + * + * @return array params + * @access private + */ + function __parseParams($params) { + $count = count($params); + for ($i = 0; $i < $count; $i++) { + if (isset($params[$i])) { + if ($params[$i]{0} === '-') { + $key = substr($params[$i], 1); + $this->params[$key] = true; + unset($params[$i]); + if (isset($params[++$i])) { + if ($params[$i]{0} !== '-') { + $this->params[$key] = str_replace('"', '', $params[$i]); + unset($params[$i]); + } else { + $i--; + $this->__parseParams($params); + } + } + } else { + $this->args[] = $params[$i]; + unset($params[$i]); + } + + } + } + } + +/** + * Removes first argument and shifts other arguments up + * + * @return mixed Null if there are no arguments otherwise the shifted argument + */ + public function shiftArgs() { + return array_shift($this->args); + } + +/** + * Shows console help + * + */ + public function help() { + $this->clear(); + $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console"); + $this->stdout("---------------------------------------------------------------"); + $this->stdout("Current Paths:"); + $this->stdout(" -app: ". $this->params['app']); + $this->stdout(" -working: " . rtrim($this->params['working'], DS)); + $this->stdout(" -root: " . rtrim($this->params['root'], DS)); + $this->stdout(" -core: " . rtrim(CORE_PATH, DS)); + $this->stdout(""); + $this->stdout("Changing Paths:"); + $this->stdout("your working path should be the same as your application path"); + $this->stdout("to change your path use the '-app' param."); + $this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp"); + + $this->stdout("\nAvailable Shells:"); + $shellList = array(); + foreach ($this->shellPaths as $path) { + if (!is_dir($path)) { + continue; + } + $shells = App::objects('file', $path); + if (empty($shells)) { + continue; + } + if (preg_match('@plugins[\\\/]([^\\\/]*)@', $path, $matches)) { + $type = Inflector::camelize($matches[1]); + } elseif (preg_match('@([^\\\/]*)[\\\/]vendors[\\\/]@', $path, $matches)) { + $type = $matches[1]; + } elseif (strpos($path, CAKE_CORE_INCLUDE_PATH . DS . 'cake') === 0) { + $type = 'CORE'; + } else { + $type = 'app'; + } + foreach ($shells as $shell) { + if ($shell !== 'shell.php') { + $shell = str_replace('.php', '', $shell); + $shellList[$shell][$type] = $type; + } + } + } + if ($shellList) { + ksort($shellList); + if (DS === '/') { + $width = exec('tput cols') - 2; + } + if (empty($width)) { + $width = 80; + } + $columns = max(1, floor($width / 30)); + $rows = ceil(count($shellList) / $columns); + + foreach ($shellList as $shell => $types) { + sort($types); + $shellList[$shell] = str_pad($shell . ' [' . implode ($types, ', ') . ']', $width / $columns); + } + $out = array_chunk($shellList, $rows); + for ($i = 0; $i < $rows; $i++) { + $row = ''; + for ($j = 0; $j < $columns; $j++) { + if (!isset($out[$j][$i])) { + continue; + } + $row .= $out[$j][$i]; + } + $this->stdout(" " . $row); + } + } + $this->stdout("\nTo run a command, type 'cake shell_name [args]'"); + $this->stdout("To get help on a specific command, type 'cake shell_name help'"); + } + +/** + * Stop execution of the current script + * + * @param $status see http://php.net/exit for values + * @return void + */ + protected function _stop($status = 0) { + exit($status); + } +} \ No newline at end of file From 9b8ce2d7fa7d0205f5f5337431efc3dce78ee691 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 01:53:42 -0400 Subject: [PATCH 003/207] Updating includes for shell + task includes to point at the new file. --- cake/tests/cases/console/libs/acl.test.php | 16 ++-------------- cake/tests/cases/console/libs/api.test.php | 15 ++------------- cake/tests/cases/console/libs/bake.test.php | 12 +----------- cake/tests/cases/console/libs/schema.test.php | 15 ++------------- cake/tests/cases/console/libs/shell.test.php | 13 +------------ .../cases/console/libs/task_collection.test.php | 11 ++--------- .../cases/console/libs/tasks/controller.test.php | 12 +----------- .../cases/console/libs/tasks/db_config.test.php | 12 +----------- .../cases/console/libs/tasks/extract.test.php | 12 +----------- .../cases/console/libs/tasks/fixture.test.php | 8 +------- .../cases/console/libs/tasks/model.test.php | 12 +----------- .../cases/console/libs/tasks/plugin.test.php | 12 +----------- .../cases/console/libs/tasks/project.test.php | 12 +----------- .../cases/console/libs/tasks/template.test.php | 12 +----------- .../tests/cases/console/libs/tasks/test.test.php | 12 +----------- .../tests/cases/console/libs/tasks/view.test.php | 12 +----------- cake/tests/cases/console/libs/testsuite.test.php | 12 +----------- 17 files changed, 21 insertions(+), 189 deletions(-) diff --git a/cake/tests/cases/console/libs/acl.test.php b/cake/tests/cases/console/libs/acl.test.php index 5df62fdde..e604639d4 100644 --- a/cake/tests/cases/console/libs/acl.test.php +++ b/cake/tests/cases/console/libs/acl.test.php @@ -19,20 +19,8 @@ */ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - -if (!class_exists('AclShell')) { - require CAKE . 'console' . DS . 'libs' . DS . 'acl.php'; -} +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; +require_once CAKE . 'console' . DS . 'libs' . DS . 'acl.php'; /** * AclShellTest class diff --git a/cake/tests/cases/console/libs/api.test.php b/cake/tests/cases/console/libs/api.test.php index fda9b1ba7..fbd54559b 100644 --- a/cake/tests/cases/console/libs/api.test.php +++ b/cake/tests/cases/console/libs/api.test.php @@ -19,20 +19,9 @@ */ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; +require_once CAKE . 'console' . DS . 'libs' . DS . 'api.php'; -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - -if (!class_exists('ApiShell')) { - require CAKE . 'console' . DS . 'libs' . DS . 'api.php'; -} /** * ApiShellTest class diff --git a/cake/tests/cases/console/libs/bake.test.php b/cake/tests/cases/console/libs/bake.test.php index e457b4278..3143f0ba4 100644 --- a/cake/tests/cases/console/libs/bake.test.php +++ b/cake/tests/cases/console/libs/bake.test.php @@ -21,17 +21,7 @@ App::import('Shell', 'Shell', false); App::import('Core', 'Controller'); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'bake.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'model.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'controller.php'; diff --git a/cake/tests/cases/console/libs/schema.test.php b/cake/tests/cases/console/libs/schema.test.php index 33c43260e..b59f87027 100644 --- a/cake/tests/cases/console/libs/schema.test.php +++ b/cake/tests/cases/console/libs/schema.test.php @@ -20,20 +20,9 @@ App::import('Shell', 'Shell', false); App::import('Model', 'CakeSchema', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; +require_once CAKE . 'console' . DS . 'libs' . DS . 'schema.php'; -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - -if (!class_exists('SchemaShell')) { - require CAKE . 'console' . DS . 'libs' . DS . 'schema.php'; -} /** * Test for Schema database management diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index 157701079..f77de465a 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -22,18 +22,7 @@ App::import('Core', 'Folder'); App::import('Shell', 'Shell', false); - -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; /** * TestShell class diff --git a/cake/tests/cases/console/libs/task_collection.test.php b/cake/tests/cases/console/libs/task_collection.test.php index e9f9f69c8..bbbbf7314 100644 --- a/cake/tests/cases/console/libs/task_collection.test.php +++ b/cake/tests/cases/console/libs/task_collection.test.php @@ -17,15 +17,8 @@ * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; + App::import('Shell', 'TaskCollection', false); App::import('Shell', 'Shell', false); diff --git a/cake/tests/cases/console/libs/tasks/controller.test.php b/cake/tests/cases/console/libs/tasks/controller.test.php index 844efbcca..e9a980e23 100644 --- a/cake/tests/cases/console/libs/tasks/controller.test.php +++ b/cake/tests/cases/console/libs/tasks/controller.test.php @@ -21,17 +21,7 @@ App::import('Core', 'ClassRegistry'); App::import('View', 'Helper', false); App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'project.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'controller.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'model.php'; diff --git a/cake/tests/cases/console/libs/tasks/db_config.test.php b/cake/tests/cases/console/libs/tasks/db_config.test.php index 81e5ed46c..6b1aee614 100644 --- a/cake/tests/cases/console/libs/tasks/db_config.test.php +++ b/cake/tests/cases/console/libs/tasks/db_config.test.php @@ -19,17 +19,7 @@ */ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'db_config.php'; diff --git a/cake/tests/cases/console/libs/tasks/extract.test.php b/cake/tests/cases/console/libs/tasks/extract.test.php index 44898b72b..2f7d08bfa 100644 --- a/cake/tests/cases/console/libs/tasks/extract.test.php +++ b/cake/tests/cases/console/libs/tasks/extract.test.php @@ -22,17 +22,7 @@ App::import('Core', 'Folder'); App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'extract.php'; /** diff --git a/cake/tests/cases/console/libs/tasks/fixture.test.php b/cake/tests/cases/console/libs/tasks/fixture.test.php index ebe0bbb5c..98e5409b9 100644 --- a/cake/tests/cases/console/libs/tasks/fixture.test.php +++ b/cake/tests/cases/console/libs/tasks/fixture.test.php @@ -23,13 +23,7 @@ if (!defined('DISABLE_AUTO_DISPATCH')) { define('DISABLE_AUTO_DISPATCH', true); } -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'template.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'fixture.php'; diff --git a/cake/tests/cases/console/libs/tasks/model.test.php b/cake/tests/cases/console/libs/tasks/model.test.php index 105acb6e2..f7cbcb2ac 100644 --- a/cake/tests/cases/console/libs/tasks/model.test.php +++ b/cake/tests/cases/console/libs/tasks/model.test.php @@ -21,17 +21,7 @@ */ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'model.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'fixture.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'template.php'; diff --git a/cake/tests/cases/console/libs/tasks/plugin.test.php b/cake/tests/cases/console/libs/tasks/plugin.test.php index 4bac70386..cdaf78085 100644 --- a/cake/tests/cases/console/libs/tasks/plugin.test.php +++ b/cake/tests/cases/console/libs/tasks/plugin.test.php @@ -22,17 +22,7 @@ App::import('Shell', 'Shell', false); App::import('Core', array('File')); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'plugin.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'model.php'; diff --git a/cake/tests/cases/console/libs/tasks/project.test.php b/cake/tests/cases/console/libs/tasks/project.test.php index 0d6f89919..0460071a5 100644 --- a/cake/tests/cases/console/libs/tasks/project.test.php +++ b/cake/tests/cases/console/libs/tasks/project.test.php @@ -23,17 +23,7 @@ App::import('Shell', 'Shell', false); App::import('Core', 'File'); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'project.php'; /** diff --git a/cake/tests/cases/console/libs/tasks/template.test.php b/cake/tests/cases/console/libs/tasks/template.test.php index 19df7e8c9..e0cbc652a 100644 --- a/cake/tests/cases/console/libs/tasks/template.test.php +++ b/cake/tests/cases/console/libs/tasks/template.test.php @@ -22,17 +22,7 @@ */ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'template.php'; /** diff --git a/cake/tests/cases/console/libs/tasks/test.test.php b/cake/tests/cases/console/libs/tasks/test.test.php index 6d6942ced..513df3ba7 100644 --- a/cake/tests/cases/console/libs/tasks/test.test.php +++ b/cake/tests/cases/console/libs/tasks/test.test.php @@ -23,17 +23,7 @@ App::import('Shell', 'Shell', false); App::import('Controller', 'Controller', false); App::import('Model', 'Model', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'test.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'template.php'; diff --git a/cake/tests/cases/console/libs/tasks/view.test.php b/cake/tests/cases/console/libs/tasks/view.test.php index 4ba2edf69..4d062ed7c 100644 --- a/cake/tests/cases/console/libs/tasks/view.test.php +++ b/cake/tests/cases/console/libs/tasks/view.test.php @@ -21,17 +21,7 @@ */ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'view.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'controller.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'template.php'; diff --git a/cake/tests/cases/console/libs/testsuite.test.php b/cake/tests/cases/console/libs/testsuite.test.php index b790e72a6..f29e50bc4 100644 --- a/cake/tests/cases/console/libs/testsuite.test.php +++ b/cake/tests/cases/console/libs/testsuite.test.php @@ -20,17 +20,7 @@ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} - +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'testsuite.php'; class TestSuiteShellTest extends CakeTestCase { From 3d65b68f1dcbe1059fc534ac972407ce3591d46e Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 12:02:33 -0400 Subject: [PATCH 004/207] Renaming cake.test Updating AllShells suite. --- cake/tests/cases/console/all_shells.test.php | 2 +- .../{cake.test.php => shell_dispatcher.test.php} | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) rename cake/tests/cases/console/{cake.test.php => shell_dispatcher.test.php} (99%) diff --git a/cake/tests/cases/console/all_shells.test.php b/cake/tests/cases/console/all_shells.test.php index 98afe5ad7..25e452950 100644 --- a/cake/tests/cases/console/all_shells.test.php +++ b/cake/tests/cases/console/all_shells.test.php @@ -38,7 +38,7 @@ class AllShellsTest extends PHPUnit_Framework_TestSuite { $path = CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS; - $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'cake.test.php'); + $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'shell_dispatcher.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'console_error_handler.test.php'); $suite->addTestDirectory($path); return $suite; diff --git a/cake/tests/cases/console/cake.test.php b/cake/tests/cases/console/shell_dispatcher.test.php similarity index 99% rename from cake/tests/cases/console/cake.test.php rename to cake/tests/cases/console/shell_dispatcher.test.php index 48afb4d6b..6805f2474 100644 --- a/cake/tests/cases/console/cake.test.php +++ b/cake/tests/cases/console/shell_dispatcher.test.php @@ -17,17 +17,8 @@ * @since CakePHP(tm) v 1.2.0.5432 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - -if (!class_exists('ShellDispatcher')) { - ob_start(); - $argv = false; - require CAKE . 'console' . DS . 'cake.php'; - ob_end_clean(); -} +require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CONSOLE_LIBS . 'shell.php'; /** From 7dea9b0dbd1bf48ed45ced9873eafdd99ee15faf Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 14:54:41 -0400 Subject: [PATCH 005/207] Starting to refactor console output into ConsoleOutput. --- cake/console/console_output.php | 76 +++++++++++++++ .../cases/console/console_output.test.php | 92 +++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 cake/console/console_output.php create mode 100644 cake/tests/cases/console/console_output.test.php diff --git a/cake/console/console_output.php b/cake/console/console_output.php new file mode 100644 index 000000000..ef30bc595 --- /dev/null +++ b/cake/console/console_output.php @@ -0,0 +1,76 @@ +_output = fopen($stream, 'w'); + } + +/** + * Outputs a single or multiple messages to stdout. If no parameters + * are passed outputs just a newline. + * + * @param mixed $message A string or a an array of strings to output + * @param integer $newlines Number of newlines to append + * @return integer Returns the number of bytes returned from writing to stdout. + */ + public function write($message, $newlines = 1) { + if (is_array($message)) { + $message = implode(self::LF, $message); + } + return $this->_write($message . str_repeat(self::LF, $newlines)); + } + +/** + * Writes a message to the output stream + * + * @param string $message Message to write. + * @return boolean success + */ + protected function _write($message) { + return fwrite($this->_output, $message); + } + +/** + * clean up and close handles + * + * @return void + */ + public function __destruct() { + fclose($this->_output); + } +} \ No newline at end of file diff --git a/cake/tests/cases/console/console_output.test.php b/cake/tests/cases/console/console_output.test.php new file mode 100644 index 000000000..b4f13c816 --- /dev/null +++ b/cake/tests/cases/console/console_output.test.php @@ -0,0 +1,92 @@ + + * Copyright 2005-2010, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.console + * @since CakePHP(tm) v 1.2.0.5432 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +require_once CAKE . 'console' . DS . 'console_output.php'; + +class ConsoleOutputTest extends CakeTestCase { + +/** + * setup + * + * @return void + */ + function setUp() { + parent::setUp(); + $this->output = $this->getMock('ConsoleOutput', array('_write')); + } + +/** + * tearDown + * + * @return void + */ + function tearDown() { + unset($this->output); + } + +/** + * test writing with no new line + * + * @return void + */ + function testWriteNoNewLine() { + $this->output->expects($this->once())->method('_write') + ->with('Some output'); + + $this->output->write('Some output', false); + } + +/** + * test writing with no new line + * + * @return void + */ + function testWriteNewLine() { + $this->output->expects($this->once())->method('_write') + ->with('Some output' . PHP_EOL); + + $this->output->write('Some output'); + } + +/** + * test write() with multiple new lines + * + * @return void + */ + function testWriteMultipleNewLines() { + $this->output->expects($this->once())->method('_write') + ->with('Some output' . PHP_EOL . PHP_EOL . PHP_EOL . PHP_EOL); + + $this->output->write('Some output', 4); + } + +/** + * test writing an array of messages. + * + * @return void + */ + function testWriteArray() { + $this->output->expects($this->once())->method('_write') + ->with('Line' . PHP_EOL . 'Line' . PHP_EOL . 'Line' . PHP_EOL); + + $this->output->write(array('Line', 'Line', 'Line')); + } + +} \ No newline at end of file From aec1770abc3149a1a82c2817175e98a696348f13 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 15:41:31 -0400 Subject: [PATCH 006/207] Adding style format manipulation methods. --- cake/console/console_output.php | 103 +++++++++++++++++- .../cases/console/console_output.test.php | 28 +++++ 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index ef30bc595..7fc4b6c86 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -1,7 +1,6 @@ out('Overwrite: foo.php was overwritten.');` + * + * This would create orange 'Overwrite:' text, while the rest of the text would remain the normal colour. + * See ConsoleOutput::styles() to learn more about defining your own styles. + * + * @package cake.console + */ class ConsoleOutput { /** * File handle for output. @@ -31,6 +54,63 @@ class ConsoleOutput { */ const LF = PHP_EOL; +/** + * text colors used in coloured output. + * + * @var array + */ + protected static $_foregroundColors = array( + 'black' => 30, + 'red' => 31, + 'green' => 32, + 'yellow' => 33, + 'blue' => 34, + 'magenta' => 35, + 'cyan' => 36, + 'white' => 37 + ); + +/** + * background colours used in coloured output. + * + * @var array + */ + protected static $_backgroundColors = array( + 'black' => 40, + 'red' => 41, + 'green' => 42, + 'yellow' => 43, + 'blue' => 44, + 'magenta' => 45, + 'cyan' => 46, + 'white' => 47 + ); + +/** + * formatting options for coloured output + * + * @var string + */ + protected static $_options = array( + 'bold' => 1, + 'underscore' => 4, + 'blink' => 5, + 'reverse' => 7, + 'conceal' => 8 + ); + +/** + * Styles that are available as tags in console output. + * You can modify these styles with ConsoleOutput::styles() + * + * @var array + */ + protected static $_styles = array( + 'error' => array('text' => 'red'), + 'warning' => array('text' => 'yellow'), + 'info' => array('text' => 'cyan') + ); + /** * Construct the output object. * @@ -65,6 +145,25 @@ class ConsoleOutput { return fwrite($this->_output, $message); } +/** + * Get the current styles offered, or append new ones in. + * + * @param string $style The style to get or create. + * @param mixed $definition The array definition of the style to change or create a style + * or false to remove a style. + * @return mixed + */ + function styles($style = null, $definition = null) { + if (is_string($style) && $definition === null) { + return isset(self::$_styles[$style]) ? self::$_styles[$style] : null; + } + if ($definition === false) { + unset(self::$_styles[$style]); + return true; + } + self::$_styles[$style] = $definition; + } + /** * clean up and close handles * diff --git a/cake/tests/cases/console/console_output.test.php b/cake/tests/cases/console/console_output.test.php index b4f13c816..b3c2efd47 100644 --- a/cake/tests/cases/console/console_output.test.php +++ b/cake/tests/cases/console/console_output.test.php @@ -89,4 +89,32 @@ class ConsoleOutputTest extends CakeTestCase { $this->output->write(array('Line', 'Line', 'Line')); } +/** + * test getting a style. + * + * @return void + */ + function testStylesGet() { + $result = $this->output->styles('error'); + $expected = array('text' => 'red'); + $this->assertEqual($result, $expected); + + $this->assertNull($this->output->styles('made_up_goop')); + } + +/** + * test adding a style. + * + * @return void + */ + function testStylesAdding() { + $this->output->styles('test', array('text' => 'red', 'background' => 'black')); + $result = $this->output->styles('test'); + $expected = array('text' => 'red', 'background' => 'black'); + $this->assertEquals($expected, $result); + + $this->assertTrue($this->output->styles('test', false), 'Removing a style should return true.'); + $this->assertNull($this->output->styles('test'), 'Removed styles should be null.'); + } + } \ No newline at end of file From d5b5fbee3b25190d098990e6dc4ebb4766fd3ec6 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 18:54:34 -0400 Subject: [PATCH 007/207] Adding formatting methods for coloured output. --- cake/console/console_output.php | 68 ++++++++++++++++++- .../cases/console/console_output.test.php | 42 +++++++++++- 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index 7fc4b6c86..5df2f7820 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -37,7 +37,8 @@ * `$this->out('Overwrite: foo.php was overwritten.');` * * This would create orange 'Overwrite:' text, while the rest of the text would remain the normal colour. - * See ConsoleOutput::styles() to learn more about defining your own styles. + * See ConsoleOutput::styles() to learn more about defining your own styles. Nested styles are not supported + * at this time. * * @package cake.console */ @@ -132,7 +133,47 @@ class ConsoleOutput { if (is_array($message)) { $message = implode(self::LF, $message); } - return $this->_write($message . str_repeat(self::LF, $newlines)); + return $this->_write($this->styleText($message . str_repeat(self::LF, $newlines))); + } + +/** + * Apply styling to text. + * + * @param string $text Text with styling tags. + * @return string String with color codes added. + */ + public function styleText($text) { + return preg_replace_callback( + '/<(?[a-z0-9-_]+)>(?.*)<\/(\1)>/i', array($this, '_replaceTags'), $text + ); + } + +/** + * Replace tags with color codes. + * + * @param array $matches. + * @return string + */ + protected function _replaceTags($matches) { + $style = $this->styles($matches['tag']); + if (empty($style)) { + return $matches['text']; + } + + $styleInfo = array(); + if (!empty($style['text']) && isset(self::$_foregroundColors[$style['text']])) { + $styleInfo[] = self::$_foregroundColors[$style['text']]; + } + if (!empty($style['background']) && isset(self::$_foregroundColors[$style['background']])) { + $styleInfo[] = self::$_foregroundColors[$style['background']]; + } + unset($style['text'], $style['background']); + foreach ($style as $option => $value) { + if ($value) { + $styleInfo[] = self::$_options[$option]; + } + } + return "\033[" . implode($styleInfo, ';') . 'm' . $matches['text'] . "\033[0m"; } /** @@ -148,12 +189,32 @@ class ConsoleOutput { /** * Get the current styles offered, or append new ones in. * + * ### Get a style definition + * + * `$this->output->styles('error');` + * + * ### Get all the style definitions + * + * `$this->output->styles();` + * + * ### Create or modify an existing style + * + * `$this->output->styles('annoy', array('text' => 'purple', 'background' => 'yellow', 'blink' => true));` + * + * ### Remove a style + * + * `$this->output->styles('annoy', false);` + * * @param string $style The style to get or create. * @param mixed $definition The array definition of the style to change or create a style * or false to remove a style. - * @return mixed + * @return mixed If you are getting styles, the style or null will be returned. If you are creating/modifying + * styles true will be returned. */ function styles($style = null, $definition = null) { + if ($style === null && $definition === null) { + return self::$_styles; + } if (is_string($style) && $definition === null) { return isset(self::$_styles[$style]) ? self::$_styles[$style] : null; } @@ -162,6 +223,7 @@ class ConsoleOutput { return true; } self::$_styles[$style] = $definition; + return true; } /** diff --git a/cake/tests/cases/console/console_output.test.php b/cake/tests/cases/console/console_output.test.php index b3c2efd47..da060267e 100644 --- a/cake/tests/cases/console/console_output.test.php +++ b/cake/tests/cases/console/console_output.test.php @@ -98,8 +98,12 @@ class ConsoleOutputTest extends CakeTestCase { $result = $this->output->styles('error'); $expected = array('text' => 'red'); $this->assertEqual($result, $expected); - + $this->assertNull($this->output->styles('made_up_goop')); + + $result = $this->output->styles(); + $this->assertNotEmpty($result, 'error', 'Error is missing'); + $this->assertNotEmpty($result, 'warning', 'Warning is missing'); } /** @@ -117,4 +121,40 @@ class ConsoleOutputTest extends CakeTestCase { $this->assertNull($this->output->styles('test'), 'Removed styles should be null.'); } +/** + * test formatting text with styles. + * + * @return void + */ + function testFormattingSimple() { + $this->output->expects($this->once())->method('_write') + ->with("\033[31mError:\033[0m Something bad"); + + $this->output->write('Error: Something bad', false); + } + +/** + * test formatting text with missing styles. + * + * @return void + */ + function testFormattingMissingStyleName() { + $this->output->expects($this->once())->method('_write') + ->with("Error: Something bad"); + + $this->output->write('Error: Something bad', false); + } + +/** + * test formatting text with multiple styles. + * + * @return void + */ + function testFormattingMultipleStylesName() { + $this->output->expects($this->once())->method('_write') + ->with("\033[31mBad\033[0m \033[33mWarning\033[0m Regular"); + + $this->output->write('Bad Warning Regular', false); + } + } \ No newline at end of file From 90d5c12b3e6a91544bcf82cc43331160218b41fc Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 19:01:27 -0400 Subject: [PATCH 008/207] Fixing issues with background colours, and adding tests for options. --- cake/console/console_output.php | 6 +++--- .../cases/console/console_output.test.php | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index 5df2f7820..424df97cd 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -94,7 +94,7 @@ class ConsoleOutput { */ protected static $_options = array( 'bold' => 1, - 'underscore' => 4, + 'underline' => 4, 'blink' => 5, 'reverse' => 7, 'conceal' => 8 @@ -164,8 +164,8 @@ class ConsoleOutput { if (!empty($style['text']) && isset(self::$_foregroundColors[$style['text']])) { $styleInfo[] = self::$_foregroundColors[$style['text']]; } - if (!empty($style['background']) && isset(self::$_foregroundColors[$style['background']])) { - $styleInfo[] = self::$_foregroundColors[$style['background']]; + if (!empty($style['background']) && isset(self::$_backgroundColors[$style['background']])) { + $styleInfo[] = self::$_backgroundColors[$style['background']]; } unset($style['text'], $style['background']); foreach ($style as $option => $value) { diff --git a/cake/tests/cases/console/console_output.test.php b/cake/tests/cases/console/console_output.test.php index da060267e..7509dfbe0 100644 --- a/cake/tests/cases/console/console_output.test.php +++ b/cake/tests/cases/console/console_output.test.php @@ -133,6 +133,25 @@ class ConsoleOutputTest extends CakeTestCase { $this->output->write('Error: Something bad', false); } +/** + * test formatting with custom styles. + * + * @return void + */ + function testFormattingCustom() { + $this->output->styles('annoying', array( + 'text' => 'magenta', + 'background' => 'cyan', + 'blink' => true, + 'underline' => true + )); + + $this->output->expects($this->once())->method('_write') + ->with("\033[35;46;5;4mAnnoy:\033[0m Something bad"); + + $this->output->write('Annoy: Something bad', false); + } + /** * test formatting text with missing styles. * From 5c55c289f761aa1e2efe969133038a39058db7d0 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 19:13:48 -0400 Subject: [PATCH 009/207] Adding support for uncoloured output for windows environments without ansicon. --- cake/console/console_output.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index 424df97cd..dd00f49e4 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -50,6 +50,13 @@ class ConsoleOutput { */ protected $_output; +/** + * Is set to true for consoles that can take pretty output. (Not windows). + * + * @var boolean + */ + protected $_prettyOutput = true; + /** * Constant for a newline. */ @@ -115,10 +122,17 @@ class ConsoleOutput { /** * Construct the output object. * + * Checks for a pretty console enviornment. Ansicon allows pretty consoles + * on windows, and is supported. + * * @return void */ public function __construct($stream = 'php://stdout') { $this->_output = fopen($stream, 'w'); + + if (DS == '\\') { + $this->_prettyOutput = (bool)env('ANSICON'); + } } /** @@ -143,6 +157,9 @@ class ConsoleOutput { * @return string String with color codes added. */ public function styleText($text) { + if (!$this->_prettyOutput) { + return strip_tags($text); + } return preg_replace_callback( '/<(?[a-z0-9-_]+)>(?.*)<\/(\1)>/i', array($this, '_replaceTags'), $text ); From ce4fe64a61815654bdf61fd0969a5dd6d0a3e502 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 21:40:12 -0400 Subject: [PATCH 010/207] Initial integration of ConsoleOutput into cake console tools. --- cake/console/console_output.php | 2 +- cake/console/shell_dispatcher.php | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index dd00f49e4..36cc7b16c 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -1,6 +1,6 @@ stdin = fopen('php://stdin', 'r'); - $this->stdout = fopen('php://stdout', 'w'); - $this->stderr = fopen('php://stderr', 'w'); + $this->stdout = new ConsoleOutput('php://stdout'); + $this->stderr = new ConsoleOutput('php://stderr'); if (!$this->__bootstrap()) { $this->stderr("\nCakePHP Console: "); @@ -454,11 +456,7 @@ class ShellDispatcher { * @return integer Returns the number of bytes output to stdout. */ public function stdout($string, $newline = true) { - if ($newline) { - return fwrite($this->stdout, $string . "\n"); - } else { - return fwrite($this->stdout, $string); - } + return $this->stdout->write($string, $newline); } /** @@ -467,7 +465,7 @@ class ShellDispatcher { * @param string $string Error text to output. */ public function stderr($string) { - fwrite($this->stderr, $string); + $this->stderr->write($string, false); } /** From ffbb4e6b455e583451d6e366840c2f97457d0f12 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 21:53:23 -0400 Subject: [PATCH 011/207] Integrating ConsoleOutput with ConsoleErrorHandler. Updating test cases to match new output. --- cake/console/console_error_handler.php | 18 +++++++----------- cake/console/libs/shell.php | 2 +- .../console/console_error_handler.test.php | 14 ++++++++------ cake/tests/cases/console/libs/shell.test.php | 4 ++-- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/cake/console/console_error_handler.php b/cake/console/console_error_handler.php index 37412d2c1..5dda75a3c 100644 --- a/cake/console/console_error_handler.php +++ b/cake/console/console_error_handler.php @@ -17,8 +17,8 @@ * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ - App::import('Core', 'ErrorHandler'); +require_once 'console_output.php'; /** * Error Handler for Cake console. Does simple printing of the @@ -44,7 +44,7 @@ class ConsoleErrorHandler extends ErrorHandler { * @param array $messages Error messages */ function __construct($error) { - $this->stderr = fopen('php://stderr', 'w'); + $this->stderr = new ConsoleOutput('php://stderr'); parent::__construct($error); } @@ -105,15 +105,11 @@ class ConsoleErrorHandler extends ErrorHandler { * @return void */ public function _outputMessage($template = null) { - $this->stderr($this->error->getMessage() . "\n" . $this->error->getTraceAsString()); + $this->stderr->write(sprintf( + __("Error: %s\n%s"), + $this->error->getMessage(), + $this->error->getTraceAsString() + )); } -/** - * Outputs to the stderr filehandle. - * - * @param string $string Error text to output. - */ - public function stderr($string) { - fwrite($this->stderr, "Error: ". $string . "\n"); - } } diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 85b15946c..c7d517079 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -360,7 +360,7 @@ class Shell extends Object { * @param string $message An optional error message */ public function error($title, $message = null) { - $this->err(sprintf(__('Error: %s'), $title)); + $this->err(sprintf(__('Error: %s'), $title)); if (!empty($message)) { $this->err($message); diff --git a/cake/tests/cases/console/console_error_handler.test.php b/cake/tests/cases/console/console_error_handler.test.php index d6e3b261e..d1171d2b1 100644 --- a/cake/tests/cases/console/console_error_handler.test.php +++ b/cake/tests/cases/console/console_error_handler.test.php @@ -32,7 +32,9 @@ class ConsoleErrorHandlerTest extends CakeTestCase { * @return Mock object */ function getErrorHandler($exception) { - return $this->getMock('ConsoleErrorHandler', array('stderr'), array($exception)); + $error = new ConsoleErrorHandler($exception); + $error->stderr = $this->getMock('ConsoleOutput'); + return $error; } /** @@ -44,7 +46,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase { $exception = new MissingActionException('Missing action'); $error = $this->getErrorHandler($exception); - $error->expects($this->once())->method('stderr') + $error->stderr->expects($this->once())->method('write') ->with($this->stringContains('Missing action')); $error->render(); @@ -59,7 +61,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase { $exception = new InvalidArgumentException('Too many parameters.'); $error = $this->getErrorHandler($exception); - $error->expects($this->once())->method('stderr') + $error->stderr->expects($this->once())->method('write') ->with($this->stringContains('Too many parameters.')); $error->render(); @@ -74,7 +76,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase { $exception = new NotFoundException('dont use me in cli.'); $error = $this->getErrorHandler($exception); - $error->expects($this->once())->method('stderr') + $error->stderr->expects($this->once())->method('write') ->with($this->stringContains('dont use me in cli.')); $error->render(); @@ -89,7 +91,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase { $exception = new InternalErrorException('dont use me in cli.'); $error = $this->getErrorHandler($exception); - $error->expects($this->once())->method('stderr') + $error->stderr->expects($this->once())->method('write') ->with($this->stringContains('dont use me in cli.')); $error->render(); @@ -104,6 +106,6 @@ class ConsoleErrorHandlerTest extends CakeTestCase { $exception = new InternalErrorException('dont use me in cli.'); $error = new ConsoleErrorHandler($exception); - $this->assertTrue(is_resource($error->stderr), 'No handle.'); + $this->assertType('ConsoleOutput', $error->stderr, 'No handle.'); } } \ No newline at end of file diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index f77de465a..95f9e14fa 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -325,11 +325,11 @@ class ShellTest extends CakeTestCase { public function testError() { $this->Shell->Dispatch->expects($this->at(0)) ->method('stderr') - ->with("Error: Foo Not Found\n"); + ->with("Error: Foo Not Found\n"); $this->Shell->Dispatch->expects($this->at(1)) ->method('stderr') - ->with("Error: Foo Not Found\n"); + ->with("Error: Foo Not Found\n"); $this->Shell->Dispatch->expects($this->at(2)) ->method('stderr') From 317e32f07bc877b00f42ba38d14c66d704417bba Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 23:00:48 -0400 Subject: [PATCH 012/207] Making ShellDispatcher use exceptions instead of returning false and doing other goofy things. Adding MissingShellMethodException, MissingShellClassException and MissingShellFileException for use with ShellDispatcher. Removing duplicated tests, and refactoring them into separate tests with expected exceptions. --- cake/console/shell_dispatcher.php | 15 +- cake/libs/exceptions.php | 27 ++ .../cases/console/console_output.test.php | 2 +- .../cases/console/shell_dispatcher.test.php | 231 ++++-------------- 4 files changed, 84 insertions(+), 191 deletions(-) diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index d604f15d8..d5d0bc233 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -307,12 +307,6 @@ class ShellDispatcher { $Shell = $this->_getShell($plugin); - if (!$Shell) { - $title = sprintf(__('Error: Class %s could not be loaded.'), $this->shellClass); - $this->stderr($title . "\n"); - return false; - } - $methods = array(); if (is_a($Shell, 'Shell')) { @@ -360,10 +354,7 @@ class ShellDispatcher { } } - $title = sprintf(__('Error: Unknown %1$s command %2$s.'), $this->shellName, $arg); - $message = sprintf(__('For usage try `cake %s help`'), $this->shell); - $this->stderr($title . "\n" . $message . "\n"); - return false; + throw new MissingShellMethodException(array('shell' => $this->shell, 'method' => $arg)); } /** @@ -386,7 +377,7 @@ class ShellDispatcher { } } if (!isset($loaded)) { - return false; + throw new MissingShellFileException(array('shell' => $this->shell . '.php')); } if (!class_exists('Shell')) { @@ -397,7 +388,7 @@ class ShellDispatcher { require $this->shellPath; } if (!class_exists($this->shellClass)) { - return false; + throw new MissingShellClassException(array('shell' => $this->shell)); } $Shell = new $this->shellClass($this); return $Shell; diff --git a/cake/libs/exceptions.php b/cake/libs/exceptions.php index daa1e7f39..7fdd069a9 100644 --- a/cake/libs/exceptions.php +++ b/cake/libs/exceptions.php @@ -313,6 +313,33 @@ class MissingTaskClassException extends CakeException { protected $_messageTemplate = 'Task class "%s" is missing.'; } +/** + * Used when a shell method cannot be found. + * + * @package cake.libs + */ +class MissingShellMethodException extends CakeException { + protected $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s help`"; +} + +/** + * Used when a shell class cannot be found. + * + * @package cake.libs + */ +class MissingShellClassException extends CakeException { + protected $_messageTemplate = "Shell class %s could not be loaded."; +} + +/** + * Used when a shell class cannot be found. + * + * @package cake.libs + */ +class MissingShellFileException extends CakeException { + protected $_messageTemplate = "Shell file %s could not be loaded."; +} + /** * Exception class to be thrown when a database table is not found in the datasource * diff --git a/cake/tests/cases/console/console_output.test.php b/cake/tests/cases/console/console_output.test.php index 7509dfbe0..00cf0822e 100644 --- a/cake/tests/cases/console/console_output.test.php +++ b/cake/tests/cases/console/console_output.test.php @@ -1,6 +1,6 @@ dispatch(); $this->assertNull($result); $this->assertEqual($Dispatcher->args, array()); + } - $Shell = new MockWithMainShell($Dispatcher); - $this->mockObjects[] = $Shell; +/** + * test missing shell exceptions on underscored (private methods) + * + * @expectedException MissingShellMethodException + * @return void + */ + function testMissingShellMethodExceptionPrivateMethod() { + $Dispatcher = new TestShellDispatcher(); - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->never())->method('hr'); - $Shell->expects($this->once())->method('startup'); - $Shell->expects($this->once())->method('main'); - $Dispatcher->TestShell = $Shell; + $methods = get_class_methods('Shell'); + array_push($methods, 'main', '_secret'); - $Dispatcher->args = array('mock_with_main', 'hr'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('hr')); - - $Shell = new MockWithMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main', 'dispatch'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('dispatch')); - - $Shell = new MockWithMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main', 'idontexist'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('idontexist')); - - $Shell = new MockWithMainShell($Dispatcher); - $this->mockObjects[] = $Shell; + $Shell = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MissingShellPrivateMethod'); $Shell->expects($this->never())->method('main'); $Shell->expects($this->never())->method('startup'); $Shell->expects($this->never())->method('_secret'); $Dispatcher->TestShell = $Shell; - $Dispatcher->args = array('mock_with_main', '_secret'); + $Dispatcher->args = array('missing_shell_private_method', '_secret'); + $result = $Dispatcher->dispatch(); + } + +/** + * test exception when calling shell class methods. + * + * @expectedException MissingShellMethodException + * @return void + */ + function testMissingShellMethodBaseClassMethod() { + $Dispatcher = new TestShellDispatcher(); + + $Shell = $this->getMock('Shell', array(), array(&$Dispatcher), 'MissingShellBaseClass'); + $Shell->expects($this->never())->method('main'); + $Shell->expects($this->never())->method('startup'); + $Shell->expects($this->never())->method('hr'); + $Dispatcher->TestShell = $Shell; + + $Dispatcher->args = array('missing_shell_base_class', 'hr'); + $result = $Dispatcher->dispatch(); + } + +/** + * test missing shell exception on missing method. + * + * @expectedException MissingShellMethodException + * @return void + */ + function testMissingShellMethodExceptionMissingMethod() { + $Dispatcher = new TestShellDispatcher(); + + $methods = get_class_methods('Shell'); + + $Shell = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MissingShellNoMethod'); + $Shell->expects($this->never())->method('main'); + $Shell->expects($this->never())->method('startup'); + $Dispatcher->TestShell = $Shell; + + $Dispatcher->args = array('missing_shell_method_no_method', 'idontexist'); $result = $Dispatcher->dispatch(); - $this->assertFalse($result); } /** @@ -579,16 +593,6 @@ class ShellDispatcherTest extends CakeTestCase { array_push($methods, 'initDb', '_secret'); $Shell = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MockWithoutMainShell'); - $Shell->expects($this->once())->method('initialize'); - $Shell->expects($this->once())->method('loadTasks'); - $Shell->expects($this->never())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main'); - $result = $Dispatcher->dispatch(); - $this->assertFalse($result); - $this->assertEqual($Dispatcher->args, array()); - $Shell = new MockWithoutMainShell($Dispatcher); $this->mockObjects[] = $Shell; $Shell->expects($this->once())->method('initDb')->will($this->returnValue(true)); @@ -601,45 +605,6 @@ class ShellDispatcherTest extends CakeTestCase { $result = $Dispatcher->dispatch(); $this->assertTrue($result); $this->assertEqual($Dispatcher->args, array()); - - $Shell = new MockWithoutMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->never())->method('hr'); - $Shell->expects($this->never())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main', 'hr'); - $result = $Dispatcher->dispatch(); - $this->assertFalse($result); - $this->assertEqual($Dispatcher->args, array('hr')); - - $Shell = new MockWithoutMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->never())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main', 'dispatch'); - $result = $Dispatcher->dispatch(); - $this->assertFalse($result); - - $Shell = new MockWithoutMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->never())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main', 'idontexist'); - $result = $Dispatcher->dispatch(); - $this->assertFalse($result); - - $Shell = new MockWithoutMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->never())->method('startup'); - $Shell->expects($this->never())->method('_secret'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main', '_secret'); - $result = $Dispatcher->dispatch(); - $this->assertFalse($result); } /** @@ -651,7 +616,7 @@ class ShellDispatcherTest extends CakeTestCase { $Dispatcher = new TestShellDispatcher(); $methods = get_class_methods('Object'); array_push($methods, 'main', 'initdb', 'initialize', 'loadTasks', 'startup', '_secret'); - $Shell = $this->getMock('Object', $methods, array(&$Dispatcher), 'MockWithMainNotAShell'); + $Shell = $this->getMock('Object', $methods, array(), 'MockWithMainNotAShell'); $Shell->expects($this->never())->method('initialize'); $Shell->expects($this->never())->method('loadTasks'); @@ -673,51 +638,6 @@ class ShellDispatcherTest extends CakeTestCase { $Dispatcher->args = array('mock_with_main_not_a', 'initdb'); $result = $Dispatcher->dispatch(); $this->assertTrue($result); - - $Shell = new MockWithMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main_not_a', 'hr'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('hr')); - - - $Shell = new MockWithMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main_not_a', 'dispatch'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('dispatch')); - - $Shell = new MockWithMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main_not_a', 'idontexist'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('idontexist')); - - $Shell = new MockWithMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->never())->method('_secret'); - $Shell->expects($this->never())->method('main'); - $Shell->expects($this->never())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main_not_a', '_secret'); - $result = $Dispatcher->dispatch(); - $this->assertFalse($result); } /** @@ -751,51 +671,6 @@ class ShellDispatcherTest extends CakeTestCase { $Dispatcher->args = array('mock_without_main_not_a', 'initdb'); $result = $Dispatcher->dispatch(); $this->assertTrue($result); - - $Shell = new MockWithoutMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main_not_a', 'hr'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('hr')); - - - $Shell = new MockWithoutMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main_not_a', 'dispatch'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('dispatch')); - - $Shell = new MockWithoutMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main_not_a', 'idontexist'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('idontexist')); - - $Shell = new MockWithoutMainNotAShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->never())->method('_secret'); - $Shell->expects($this->never())->method('main'); - $Shell->expects($this->never())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_without_main_not_a', '_secret'); - $result = $Dispatcher->dispatch(); - $this->assertFalse($result); } /** From 02c4e00556c3f8da505f8fb7e8e6f3504b234011 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 23:42:24 -0400 Subject: [PATCH 013/207] Starting to move command list out to a separate class so ShellDispatcher can stop having stderr/stdout connections. --- cake/console/libs/command_list.php | 102 ++++++++++++++++ cake/console/shell_dispatcher.php | 2 +- .../cases/console/libs/command_list.test.php | 112 ++++++++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 cake/console/libs/command_list.php create mode 100644 cake/tests/cases/console/libs/command_list.test.php diff --git a/cake/console/libs/command_list.php b/cake/console/libs/command_list.php new file mode 100644 index 000000000..566152221 --- /dev/null +++ b/cake/console/libs/command_list.php @@ -0,0 +1,102 @@ +out("\nWelcome to CakePHP v" . Configure::version() . " Console"); + $this->out("---------------------------------------------------------------"); + $this->out("Current Paths:"); + $this->out(" -app: ". $this->params['app']); + $this->out(" -working: " . rtrim($this->params['working'], DS)); + $this->out(" -root: " . rtrim($this->params['root'], DS)); + $this->out(" -core: " . rtrim(CORE_PATH, DS)); + $this->out(""); + $this->out("Changing Paths:"); + $this->out("your working path should be the same as your application path"); + $this->out("to change your path use the '-app' param."); + $this->out("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp"); + + $this->out("\nAvailable Shells:"); + $shellList = array(); + foreach ($this->Dispatch->shellPaths as $path) { + if (!is_dir($path)) { + continue; + } + $shells = App::objects('file', $path); + if (empty($shells)) { + continue; + } + if (preg_match('@plugins[\\\/]([^\\\/]*)@', $path, $matches)) { + $type = Inflector::camelize($matches[1]); + } elseif (preg_match('@([^\\\/]*)[\\\/]vendors[\\\/]@', $path, $matches)) { + $type = $matches[1]; + } elseif (strpos($path, CAKE_CORE_INCLUDE_PATH . DS . 'cake') === 0) { + $type = 'CORE'; + } else { + $type = 'app'; + } + foreach ($shells as $shell) { + if ($shell !== 'shell.php') { + $shell = str_replace('.php', '', $shell); + $shellList[$shell][$type] = $type; + } + } + } + if ($shellList) { + ksort($shellList); + if (DS === '/') { + $width = exec('tput cols') - 2; + } + if (empty($width)) { + $width = 80; + } + $columns = max(1, floor($width / 30)); + $rows = ceil(count($shellList) / $columns); + + foreach ($shellList as $shell => $types) { + sort($types); + $shellList[$shell] = str_pad($shell . ' [' . implode ($types, ', ') . ']', $width / $columns); + } + $out = array_chunk($shellList, $rows); + for ($i = 0; $i < $rows; $i++) { + $row = ''; + for ($j = 0; $j < $columns; $j++) { + if (!isset($out[$j][$i])) { + continue; + } + $row .= $out[$j][$i]; + } + $this->out(" " . $row); + } + } + $this->out("\nTo run a command, type 'cake shell_name [args]'"); + $this->out("To get help on a specific command, type 'cake shell_name help'"); + } +} diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index d5d0bc233..d772d5cc0 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -353,7 +353,6 @@ class ShellDispatcher { return $Shell->main(); } } - throw new MissingShellMethodException(array('shell' => $this->shell, 'method' => $arg)); } @@ -548,6 +547,7 @@ class ShellDispatcher { * */ public function help() { + // Make Command List and display it here. $this->clear(); $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console"); $this->stdout("---------------------------------------------------------------"); diff --git a/cake/tests/cases/console/libs/command_list.test.php b/cake/tests/cases/console/libs/command_list.test.php new file mode 100644 index 000000000..602226cca --- /dev/null +++ b/cake/tests/cases/console/libs/command_list.test.php @@ -0,0 +1,112 @@ +output .= $message; + } +} + +class CommandListTest extends CakeTestCase { +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + App::build(array( + 'plugins' => array( + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS + ), + 'shells' => array( + CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS, + TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'shells' . DS + ) + ), true); + App::objects('plugins', null, false); + + $this->Dispatcher = $this->getMock( + 'ShellDispatcher', + array('getInput', 'stderr', '_stop', '_initEnvironment', 'dispatch', 'clear') + ); + $this->Dispatcher->stdout = new TestStringOutput(); + + $this->Shell = $this->getMock( + 'CommandListShell', + array('in', '_stop'), + array(&$this->Dispatcher) + ); + } + +/** + * teardown + * + * @return void + */ + function tearDown() { + unset($this->Dispatcher, $this->Shell); + } + +/** + * test that main finds core shells. + * + * @return void + */ + function testMain() { + $this->Shell->main(); + $output = $this->Dispatcher->stdout->output; + + $expected = "/example \[.*TestPlugin, TestPluginTwo.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/welcome \[.*TestPluginTwo.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/acl \[.*CORE.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/api \[.*CORE.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/bake \[.*CORE.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/console \[.*CORE.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/i18n \[.*CORE.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/schema \[.*CORE.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/testsuite \[.*CORE.*\]/"; + $this->assertPattern($expected, $output); + + $expected = "/sample \[.*test_app.*\]/"; + $this->assertPattern($expected, $output); + } +} From 7719df72c25712d1afb6a6bec80dfd635098561f Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 23:46:17 -0400 Subject: [PATCH 014/207] Removing code from ShellDispatcher that is now in CommandList. --- cake/console/libs/command_list.php | 2 - cake/console/shell_dispatcher.php | 74 ++---------------------------- 2 files changed, 3 insertions(+), 73 deletions(-) diff --git a/cake/console/libs/command_list.php b/cake/console/libs/command_list.php index 566152221..c178e7169 100644 --- a/cake/console/libs/command_list.php +++ b/cake/console/libs/command_list.php @@ -30,8 +30,6 @@ class CommandListShell extends Shell { * @return void */ public function main() { - $this->out("\nWelcome to CakePHP v" . Configure::version() . " Console"); - $this->out("---------------------------------------------------------------"); $this->out("Current Paths:"); $this->out(" -app: ". $this->params['app']); $this->out(" -working: " . rtrim($this->params['working'], DS)); diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index d772d5cc0..0fce9ad23 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -543,80 +543,12 @@ class ShellDispatcher { } /** - * Shows console help + * Shows console help. Performs an internal dispatch to the CommandList Shell * */ public function help() { - // Make Command List and display it here. - $this->clear(); - $this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console"); - $this->stdout("---------------------------------------------------------------"); - $this->stdout("Current Paths:"); - $this->stdout(" -app: ". $this->params['app']); - $this->stdout(" -working: " . rtrim($this->params['working'], DS)); - $this->stdout(" -root: " . rtrim($this->params['root'], DS)); - $this->stdout(" -core: " . rtrim(CORE_PATH, DS)); - $this->stdout(""); - $this->stdout("Changing Paths:"); - $this->stdout("your working path should be the same as your application path"); - $this->stdout("to change your path use the '-app' param."); - $this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp"); - - $this->stdout("\nAvailable Shells:"); - $shellList = array(); - foreach ($this->shellPaths as $path) { - if (!is_dir($path)) { - continue; - } - $shells = App::objects('file', $path); - if (empty($shells)) { - continue; - } - if (preg_match('@plugins[\\\/]([^\\\/]*)@', $path, $matches)) { - $type = Inflector::camelize($matches[1]); - } elseif (preg_match('@([^\\\/]*)[\\\/]vendors[\\\/]@', $path, $matches)) { - $type = $matches[1]; - } elseif (strpos($path, CAKE_CORE_INCLUDE_PATH . DS . 'cake') === 0) { - $type = 'CORE'; - } else { - $type = 'app'; - } - foreach ($shells as $shell) { - if ($shell !== 'shell.php') { - $shell = str_replace('.php', '', $shell); - $shellList[$shell][$type] = $type; - } - } - } - if ($shellList) { - ksort($shellList); - if (DS === '/') { - $width = exec('tput cols') - 2; - } - if (empty($width)) { - $width = 80; - } - $columns = max(1, floor($width / 30)); - $rows = ceil(count($shellList) / $columns); - - foreach ($shellList as $shell => $types) { - sort($types); - $shellList[$shell] = str_pad($shell . ' [' . implode ($types, ', ') . ']', $width / $columns); - } - $out = array_chunk($shellList, $rows); - for ($i = 0; $i < $rows; $i++) { - $row = ''; - for ($j = 0; $j < $columns; $j++) { - if (!isset($out[$j][$i])) { - continue; - } - $row .= $out[$j][$i]; - } - $this->stdout(" " . $row); - } - } - $this->stdout("\nTo run a command, type 'cake shell_name [args]'"); - $this->stdout("To get help on a specific command, type 'cake shell_name help'"); + $this->args = array('command_list'); + $this->dispatch('command_list'); } /** From 9a6b04d5b358b03e925a737ce2356af58818908e Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 23:48:43 -0400 Subject: [PATCH 015/207] Removing double header output and adding some pizazz to the command list. --- cake/console/libs/command_list.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cake/console/libs/command_list.php b/cake/console/libs/command_list.php index c178e7169..d48a9ae4f 100644 --- a/cake/console/libs/command_list.php +++ b/cake/console/libs/command_list.php @@ -30,18 +30,18 @@ class CommandListShell extends Shell { * @return void */ public function main() { - $this->out("Current Paths:"); + $this->out("Current Paths:", 2); $this->out(" -app: ". $this->params['app']); $this->out(" -working: " . rtrim($this->params['working'], DS)); $this->out(" -root: " . rtrim($this->params['root'], DS)); $this->out(" -core: " . rtrim(CORE_PATH, DS)); $this->out(""); - $this->out("Changing Paths:"); - $this->out("your working path should be the same as your application path"); + $this->out("Changing Paths:", 2); + $this->out("Your working path should be the same as your application path"); $this->out("to change your path use the '-app' param."); - $this->out("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp"); + $this->out("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp", 2); - $this->out("\nAvailable Shells:"); + $this->out("Available Shells:", 2); $shellList = array(); foreach ($this->Dispatch->shellPaths as $path) { if (!is_dir($path)) { @@ -94,7 +94,8 @@ class CommandListShell extends Shell { $this->out(" " . $row); } } - $this->out("\nTo run a command, type 'cake shell_name [args]'"); - $this->out("To get help on a specific command, type 'cake shell_name help'"); + $this->out(); + $this->out("To run a command, type 'cake shell_name [args]'"); + $this->out("To get help on a specific command, type 'cake shell_name help'", 2); } } From b11f2bfb1f9d94141924106f8721678547e88eb9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 23:50:17 -0400 Subject: [PATCH 016/207] Adding ConsoleOutput to the AllShells group. --- cake/tests/cases/console/all_shells.test.php | 1 + 1 file changed, 1 insertion(+) diff --git a/cake/tests/cases/console/all_shells.test.php b/cake/tests/cases/console/all_shells.test.php index 25e452950..150309458 100644 --- a/cake/tests/cases/console/all_shells.test.php +++ b/cake/tests/cases/console/all_shells.test.php @@ -39,6 +39,7 @@ class AllShellsTest extends PHPUnit_Framework_TestSuite { $path = CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS; $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'shell_dispatcher.test.php'); + $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'console_output.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'console_error_handler.test.php'); $suite->addTestDirectory($path); return $suite; From 749e9c19d569b2226c4e18c6c289e2cfab424b5c Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 3 Oct 2010 23:58:07 -0400 Subject: [PATCH 017/207] Adding success style. Adding some additional formatting to the default welcome method. --- cake/console/console_output.php | 3 ++- cake/console/libs/shell.php | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index 36cc7b16c..9a5f42152 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -116,7 +116,8 @@ class ConsoleOutput { protected static $_styles = array( 'error' => array('text' => 'red'), 'warning' => array('text' => 'yellow'), - 'info' => array('text' => 'cyan') + 'info' => array('text' => 'cyan'), + 'success' => array('text' => 'green') ); /** diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index c7d517079..8861bc4f9 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -192,7 +192,7 @@ class Shell extends Object { protected function _welcome() { $this->Dispatch->clear(); $this->out(); - $this->out('Welcome to CakePHP v' . Configure::version() . ' Console'); + $this->out('Welcome to CakePHP v' . Configure::version() . ' Console'); $this->hr(); $this->out('App : '. $this->params['app']); $this->out('Path: '. $this->params['working']); @@ -345,10 +345,11 @@ class Shell extends Object { * Outputs a series of minus characters to the standard output, acts as a visual separator. * * @param integer $newlines Number of newlines to pre- and append + * @param integer $width Width of the line, defaults to 63 */ - public function hr($newlines = 0) { + public function hr($newlines = 0, $width = 63) { $this->out(null, $newlines); - $this->out('---------------------------------------------------------------'); + $this->out(str_repeat('-', $width)); $this->out(null, $newlines); } From b6602f1d0dd7c69d696e20ae3e9add583c83407c Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 00:07:37 -0400 Subject: [PATCH 018/207] Converting more stderr() use into exceptions. --- cake/console/shell_dispatcher.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index 0fce9ad23..bf73e9175 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -180,19 +180,16 @@ class ShellDispatcher { $this->stderr = new ConsoleOutput('php://stderr'); if (!$this->__bootstrap()) { - $this->stderr("\nCakePHP Console: "); - $this->stderr("\nUnable to load Cake core:"); - $this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH); - $this->_stop(); + $message = "Unable to load CakePHP core.\nMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH; + throw new RuntimeException($message); } if (!isset($this->args[0]) || !isset($this->params['working'])) { - $this->stderr("\nCakePHP Console: "); - $this->stderr('This file has been loaded incorrectly and cannot continue.'); - $this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,'); - $this->stderr('and check the manual for the correct usage of this command.'); - $this->stderr('(http://manual.cakephp.org/)'); - $this->_stop(); + $message = "This file has been loaded incorrectly and cannot continue.\n" . + "Please make sure that " . DIRECTORY_SEPARATOR . "cake" . DIRECTORY_SEPARATOR . "console is in your system path,\n" . + "and check the cookbook for the correct usage of this command.\n" . + "(http://book.cakephp.org/)"; + throw new RuntimeException($message); } $this->shiftArgs(); From a55098b00b7e0f48b1367ed97e7c6e436999d739 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 00:23:54 -0400 Subject: [PATCH 019/207] Making TaskCollection require a Shell instead of a ShellDispatcher. This will help reduce the coupling between ShellDispatcher and other objects. Since ShellDispatcher never directly uses or interacts with TaskCollection, it doesn't make much sense for it to have one. Instead shells will either get their own, or be passed one in. --- cake/console/libs/task_collection.php | 12 ++++++------ .../cases/console/libs/task_collection.test.php | 15 +++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/cake/console/libs/task_collection.php b/cake/console/libs/task_collection.php index d1ec21733..b0fc6aea1 100644 --- a/cake/console/libs/task_collection.php +++ b/cake/console/libs/task_collection.php @@ -20,11 +20,11 @@ App::import('Core', 'ObjectCollection'); class TaskCollection extends ObjectCollection { /** - * Shell Dispatcher to give to tasks. and use to find tasks. + * Shell to give to tasks. and use to find tasks. * * @var array */ - protected $_Dispatch; + protected $_Shell; /** * Constructor @@ -32,8 +32,8 @@ class TaskCollection extends ObjectCollection { * @param array $paths Array of paths to search for tasks on . * @return void */ - public function __construct(ShellDispatcher $Dispatcher) { - $this->_Dispatch = $Dispatcher; + public function __construct(Shell $Shell) { + $this->_Shell = $Shell; } /** * Loads/constructs a task. Will return the instance in the registry if it already exists. @@ -60,7 +60,7 @@ class TaskCollection extends ObjectCollection { } } - $this->_loaded[$name] = new $taskClass($this->_Dispatch); + $this->_loaded[$name] = new $taskClass($this->_Shell); if ($enable === true) { $this->_enabled[] = $name; } @@ -75,7 +75,7 @@ class TaskCollection extends ObjectCollection { * @throws MissingTaskFileException */ protected function _getPath($file) { - foreach ($this->_Dispatch->shellPaths as $path) { + foreach ($this->_Shell->shellPaths as $path) { $taskPath = $path . 'tasks' . DS . $file . '.php'; if (file_exists($taskPath)) { return $taskPath; diff --git a/cake/tests/cases/console/libs/task_collection.test.php b/cake/tests/cases/console/libs/task_collection.test.php index bbbbf7314..94935106b 100644 --- a/cake/tests/cases/console/libs/task_collection.test.php +++ b/cake/tests/cases/console/libs/task_collection.test.php @@ -17,7 +17,6 @@ * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; App::import('Shell', 'TaskCollection', false); App::import('Shell', 'Shell', false); @@ -29,9 +28,9 @@ class TaskCollectionTest extends CakeTestCase { * @return void */ function setup() { - $dispatcher = $this->getMock('ShellDispatcher', array(), array(), '', false); - $dispatcher->shellPaths = App::path('shells'); - $this->Tasks = new TaskCollection($dispatcher); + $shell = $this->getMock('Shell', array(), array(), '', false); + $shell->shellPaths = App::path('shells'); + $this->Tasks = new TaskCollection($shell); } /** @@ -87,10 +86,10 @@ class TaskCollectionTest extends CakeTestCase { * @return void */ function testLoadPluginTask() { - $dispatcher = $this->getMock('ShellDispatcher', array(), array(), '', false); - $dispatcher->shellPaths = App::path('shells'); - $dispatcher->shellPaths[] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'vendors' . DS . 'shells' . DS; - $this->Tasks = new TaskCollection($dispatcher); + $shell = $this->getMock('Shell', array(), array(), '', false); + $shell->shellPaths = App::path('shells'); + $shell->shellPaths[] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'vendors' . DS . 'shells' . DS; + $this->Tasks = new TaskCollection($shell); $result = $this->Tasks->load('TestPlugin.OtherTask'); $this->assertType('OtherTaskTask', $result, 'Task class is wrong.'); From 131433739e7e6c5c3c37c69b2d50de129a0aaa5a Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 00:40:04 -0400 Subject: [PATCH 020/207] Removing dead test stubs and test methods. Moving clear() into Shell, it should be a shell method. Changing shell methods to use ConsoleOutput objects, instead of methods on Dispatcher. --- cake/console/cake.php | 2 +- cake/console/libs/shell.php | 60 +++++++++++--- cake/console/shell_dispatcher.php | 81 +++---------------- .../cases/console/shell_dispatcher.test.php | 76 ----------------- 4 files changed, 60 insertions(+), 159 deletions(-) diff --git a/cake/console/cake.php b/cake/console/cake.php index 1cd2e5bee..161c40f4c 100644 --- a/cake/console/cake.php +++ b/cake/console/cake.php @@ -22,4 +22,4 @@ */ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR. 'shell_dispatcher.php'); -$dispatcher = new ShellDispatcher($argv); +ShellDispatcher::run($argv); diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 8861bc4f9..a7f52fa72 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -18,6 +18,7 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::import('Shell', 'TaskCollection'); +require_once CAKE . 'console' . DS . 'console_output.php'; /** * Base class for command-line utilities for automating programmer chores. @@ -139,12 +140,26 @@ class Shell extends Object { */ public $Tasks; +/** + * stdout object. + * + * @var ConsoleOutput + */ + public $stdout; + +/** + * stderr object. + * + * @var ConsoleOutput + */ + public $stderr; + /** * Constructs this Shell instance. * */ - function __construct(&$dispatch) { - $vars = array('params', 'args', 'shell', 'shellCommand' => 'command'); + function __construct(&$dispatch, $stdout = null, $stderr = null) { + $vars = array('params', 'args', 'shell', 'shellCommand' => 'command', 'shellPaths'); foreach ($vars as $key => $var) { if (is_string($key)) { @@ -161,8 +176,18 @@ class Shell extends Object { if ($this->alias == null) { $this->alias = $this->name; } + $this->Dispatch =& $dispatch; - $this->Tasks = $dispatch->getTaskCollection(); + $this->Tasks = new TaskCollection($this); + + $this->stdout = $stdout; + $this->stderr = $stderr; + if ($this->stdout == null) { + $this->stdout = new ConsoleOutput('php://stdout'); + } + if ($this->stderr == null) { + $this->stderr = new ConsoleOutput('php://stderr'); + } } /** @@ -190,7 +215,7 @@ class Shell extends Object { * */ protected function _welcome() { - $this->Dispatch->clear(); + $this->clear(); $this->out(); $this->out('Welcome to CakePHP v' . Configure::version() . ' Console'); $this->hr(); @@ -310,10 +335,7 @@ class Shell extends Object { * @return integer Returns the number of bytes returned from writing to stdout. */ public function out($message = null, $newlines = 1) { - if (is_array($message)) { - $message = implode($this->nl(), $message); - } - return $this->Dispatch->stdout($message . $this->nl($newlines), false); + return $this->stdout->write($message, $newlines); } /** @@ -324,10 +346,7 @@ class Shell extends Object { * @param integer $newlines Number of newlines to append */ public function err($message = null, $newlines = 1) { - if (is_array($message)) { - $message = implode($this->nl(), $message); - } - $this->Dispatch->stderr($message . $this->nl($newlines)); + $this->stderr->write($message, $newlines); } /** @@ -338,7 +357,7 @@ class Shell extends Object { * @return string */ function nl($multiplier = 1) { - return str_repeat("\n", $multiplier); + return str_repeat(ConsoleOutput::LF, $multiplier); } /** @@ -369,6 +388,21 @@ class Shell extends Object { $this->_stop(1); } +/** + * Clear the console + * + * @return void + */ + public function clear() { + if (empty($this->params['noclear'])) { + if ( DS === '/') { + passthru('clear'); + } else { + passthru('cls'); + } + } + } + /** * Will check the number args matches otherwise throw an error * diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index bf73e9175..c11f3988b 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -17,7 +17,6 @@ * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -require_once 'console_output.php'; /** * Shell dispatcher handles dispatching cli commands. @@ -35,22 +34,6 @@ class ShellDispatcher { */ public $stdin; -/** - * Standard output stream. - * - * @var filehandle - * @access public - */ - public $stdout; - -/** - * Standard error stream. - * - * @var filehandle - * @access public - */ - public $stderr; - /** * Contains command switches parsed from the command line. * @@ -138,7 +121,16 @@ class ShellDispatcher { $this->parseParams($args); $this->_initEnvironment(); $this->__buildPaths(); - $this->_stop($this->dispatch() === false ? 1 : 0); + } + +/** + * Run the dispatcher + * + * @return void + */ + public static function run($argv) { + $dispatcher = new ShellDispatcher($argv); + $dispatcher->_stop($dispatcher->dispatch() === false ? 1 : 0); } /** @@ -176,8 +168,6 @@ class ShellDispatcher { */ protected function _initEnvironment() { $this->stdin = fopen('php://stdin', 'r'); - $this->stdout = new ConsoleOutput('php://stdout'); - $this->stderr = new ConsoleOutput('php://stderr'); if (!$this->__bootstrap()) { $message = "Unable to load CakePHP core.\nMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH; @@ -258,21 +248,6 @@ class ShellDispatcher { return true; } -/** - * Clear the console - * - * @return void - */ - public function clear() { - if (empty($this->params['noclear'])) { - if ( DS === '/') { - passthru('clear'); - } else { - passthru('cls'); - } - } - } - /** * Dispatches a CLI request * @@ -306,7 +281,7 @@ class ShellDispatcher { $methods = array(); - if (is_a($Shell, 'Shell')) { + if ($Shell instanceof Shell) { $Shell->initialize(); $Shell->loadTasks(); @@ -390,18 +365,6 @@ class ShellDispatcher { return $Shell; } -/** - * Returns a TaskCollection object for Shells to use when loading their tasks. - * - * @return TaskCollection object. - */ - public function getTaskCollection() { - if (empty($this->_Tasks)) { - $this->_Tasks = new TaskCollection($this); - } - return $this->_Tasks; - } - /** * Prompts the user for input, and returns it. * @@ -435,26 +398,6 @@ class ShellDispatcher { return $result; } -/** - * Outputs to the stdout filehandle. - * - * @param string $string String to output. - * @param boolean $newline If true, the outputs gets an added newline. - * @return integer Returns the number of bytes output to stdout. - */ - public function stdout($string, $newline = true) { - return $this->stdout->write($string, $newline); - } - -/** - * Outputs to the stderr filehandle. - * - * @param string $string Error text to output. - */ - public function stderr($string) { - $this->stderr->write($string, false); - } - /** * Parses command line options * @@ -545,7 +488,7 @@ class ShellDispatcher { */ public function help() { $this->args = array('command_list'); - $this->dispatch('command_list'); + $this->dispatch(); } /** diff --git a/cake/tests/cases/console/shell_dispatcher.test.php b/cake/tests/cases/console/shell_dispatcher.test.php index 670cff75e..20cd34be4 100644 --- a/cake/tests/cases/console/shell_dispatcher.test.php +++ b/cake/tests/cases/console/shell_dispatcher.test.php @@ -37,22 +37,6 @@ class TestShellDispatcher extends ShellDispatcher { */ public $params = array(); -/** - * stdout property - * - * @var string - * @access public - */ - public $stdout = ''; - -/** - * stderr property - * - * @var string - * @access public - */ - public $stderr = ''; - /** * stopped property * @@ -77,28 +61,6 @@ class TestShellDispatcher extends ShellDispatcher { protected function _initEnvironment() { } -/** - * stderr method - * - * @return void - */ - public function stderr($string) { - $this->stderr .= rtrim($string, ' '); - } - -/** - * stdout method - * - * @return void - */ - public function stdout($string, $newline = true) { - if ($newline) { - $this->stdout .= rtrim($string, ' ') . "\n"; - } else { - $this->stdout .= rtrim($string, ' '); - } - } - /** * clear method * @@ -757,42 +719,4 @@ class ShellDispatcherTest extends CakeTestCase { $this->assertIdentical($Dispatcher->args, array()); } -/** - * testHelpCommand method - * - * @return void - */ - public function testHelpCommand() { - $Dispatcher = new TestShellDispatcher(); - - $expected = "/example \[.*TestPlugin, TestPluginTwo.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/welcome \[.*TestPluginTwo.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/acl \[.*CORE.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/api \[.*CORE.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/bake \[.*CORE.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/console \[.*CORE.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/i18n \[.*CORE.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/schema \[.*CORE.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/testsuite \[.*CORE.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - - $expected = "/sample \[.*test_app.*\]/"; - $this->assertPattern($expected, $Dispatcher->stdout); - } } From 76c6decb5250c3423ad7f544cac6c3336f098497 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 22:37:20 -0400 Subject: [PATCH 021/207] Updating tests for Shell so they pass with the changes in the Shell internals. --- cake/tests/cases/console/libs/shell.test.php | 91 +++++++++++--------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index 95f9e14fa..a82b8694b 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -39,6 +39,7 @@ class TestShell extends Shell { * @access public */ public $name = 'TestShell'; + /** * stopped property * @@ -107,7 +108,9 @@ class ShellTest extends CakeTestCase { 'ShellDispatcher', array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear') ); - $this->Shell =& new TestShell($this->Dispatcher); + $output = $this->getMock('ConsoleOutput'); + $error = $this->getMock('ConsoleOutput'); + $this->Shell =& new TestShell($this->Dispatcher, $output, $error); } /** @@ -129,6 +132,8 @@ class ShellTest extends CakeTestCase { $this->assertEquals($this->Dispatcher, $this->Shell->Dispatch); $this->assertEqual($this->Shell->name, 'TestShell'); $this->assertEqual($this->Shell->alias, 'TestShell'); + $this->assertType('ConsoleOutput', $this->Shell->stdout); + $this->assertType('ConsoleOutput', $this->Shell->stderr); } /** @@ -221,21 +226,21 @@ class ShellTest extends CakeTestCase { * @return void */ public function testOut() { - $this->Shell->Dispatch->expects($this->at(0)) - ->method('stdout') - ->with("Just a test\n", false); + $this->Shell->stdout->expects($this->at(0)) + ->method('write') + ->with("Just a test", 1); - $this->Shell->Dispatch->expects($this->at(1)) - ->method('stdout') - ->with("Just\na\ntest\n", false); + $this->Shell->stdout->expects($this->at(1)) + ->method('write') + ->with(array('Just', 'a', 'test'), 1); - $this->Shell->Dispatch->expects($this->at(2)) - ->method('stdout') - ->with("Just\na\ntest\n\n", false); + $this->Shell->stdout->expects($this->at(2)) + ->method('write') + ->with(array('Just', 'a', 'test'), 2); - $this->Shell->Dispatch->expects($this->at(3)) - ->method('stdout') - ->with("\n", false); + $this->Shell->stdout->expects($this->at(3)) + ->method('write') + ->with('', 1); $this->Shell->out('Just a test'); @@ -252,21 +257,21 @@ class ShellTest extends CakeTestCase { * @return void */ public function testErr() { - $this->Shell->Dispatch->expects($this->at(0)) - ->method('stderr') - ->with("Just a test\n"); + $this->Shell->stderr->expects($this->at(0)) + ->method('write') + ->with("Just a test", 1); - $this->Shell->Dispatch->expects($this->at(1)) - ->method('stderr') - ->with("Just\na\ntest\n"); + $this->Shell->stderr->expects($this->at(1)) + ->method('write') + ->with(array('Just', 'a', 'test'), 1); - $this->Shell->Dispatch->expects($this->at(2)) - ->method('stderr') - ->with("Just\na\ntest\n\n"); + $this->Shell->stderr->expects($this->at(2)) + ->method('write') + ->with(array('Just', 'a', 'test'), 2); - $this->Shell->Dispatch->expects($this->at(3)) - ->method('stderr') - ->with("\n"); + $this->Shell->stderr->expects($this->at(3)) + ->method('write') + ->with('', 1); $this->Shell->err('Just a test'); @@ -298,17 +303,17 @@ class ShellTest extends CakeTestCase { public function testHr() { $bar = '---------------------------------------------------------------'; - $this->Shell->Dispatch->expects($this->at(0))->method('stdout')->with('', false); - $this->Shell->Dispatch->expects($this->at(1))->method('stdout')->with($bar . "\n", false); - $this->Shell->Dispatch->expects($this->at(2))->method('stdout')->with('', false); + $this->Shell->stdout->expects($this->at(0))->method('write')->with('', 0); + $this->Shell->stdout->expects($this->at(1))->method('write')->with($bar, 1); + $this->Shell->stdout->expects($this->at(2))->method('write')->with('', 0); - $this->Shell->Dispatch->expects($this->at(3))->method('stdout')->with("\n", false); - $this->Shell->Dispatch->expects($this->at(4))->method('stdout')->with($bar . "\n", false); - $this->Shell->Dispatch->expects($this->at(5))->method('stdout')->with("\n", false); + $this->Shell->stdout->expects($this->at(3))->method('write')->with("", true); + $this->Shell->stdout->expects($this->at(4))->method('write')->with($bar, 1); + $this->Shell->stdout->expects($this->at(5))->method('write')->with("", true); - $this->Shell->Dispatch->expects($this->at(6))->method('stdout')->with("\n\n", false); - $this->Shell->Dispatch->expects($this->at(7))->method('stdout')->with($bar . "\n", false); - $this->Shell->Dispatch->expects($this->at(8))->method('stdout')->with("\n\n", false); + $this->Shell->stdout->expects($this->at(6))->method('write')->with("", 2); + $this->Shell->stdout->expects($this->at(7))->method('write')->with($bar, 1); + $this->Shell->stdout->expects($this->at(8))->method('write')->with("", 2); $this->Shell->hr(); @@ -323,17 +328,17 @@ class ShellTest extends CakeTestCase { * @return void */ public function testError() { - $this->Shell->Dispatch->expects($this->at(0)) - ->method('stderr') - ->with("Error: Foo Not Found\n"); + $this->Shell->stderr->expects($this->at(0)) + ->method('write') + ->with("Error: Foo Not Found", 1); - $this->Shell->Dispatch->expects($this->at(1)) - ->method('stderr') - ->with("Error: Foo Not Found\n"); + $this->Shell->stderr->expects($this->at(1)) + ->method('write') + ->with("Error: Foo Not Found", 1); - $this->Shell->Dispatch->expects($this->at(2)) - ->method('stderr') - ->with("Searched all...\n"); + $this->Shell->stderr->expects($this->at(2)) + ->method('write') + ->with("Searched all...", 1); $this->Shell->error('Foo Not Found'); $this->assertIdentical($this->Shell->stopped, 1); From cd8d29ef1cb80b2841668037bfb416047ba80501 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 23:03:57 -0400 Subject: [PATCH 022/207] Removing conceal, because its a stupid option. --- cake/console/console_output.php | 1 - 1 file changed, 1 deletion(-) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index 9a5f42152..59dad2da9 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -104,7 +104,6 @@ class ConsoleOutput { 'underline' => 4, 'blink' => 5, 'reverse' => 7, - 'conceal' => 8 ); /** From d64078db2dd699324eb8f78baed0ed7642626e00 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 23:18:59 -0400 Subject: [PATCH 023/207] Adding ConsoleInput for reading stdin. --- cake/tests/cases/console/console_input.php | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 cake/tests/cases/console/console_input.php diff --git a/cake/tests/cases/console/console_input.php b/cake/tests/cases/console/console_input.php new file mode 100644 index 000000000..d6dba1703 --- /dev/null +++ b/cake/tests/cases/console/console_input.php @@ -0,0 +1,50 @@ +_input = fopen('php://stdin', 'r'); + } + +/** + * Read a value from the stream + * + * @return mixed The value of the stream + */ + public function read() { + return fgets($this->_input); + } +} \ No newline at end of file From 67f03afa021ec8cf1a07f80c5e6ca99a328ac527 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 23:25:54 -0400 Subject: [PATCH 024/207] Moving ConsoleInput to the correct directory. --- cake/{tests/cases => }/console/console_input.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename cake/{tests/cases => }/console/console_input.php (95%) diff --git a/cake/tests/cases/console/console_input.php b/cake/console/console_input.php similarity index 95% rename from cake/tests/cases/console/console_input.php rename to cake/console/console_input.php index d6dba1703..33127ae9b 100644 --- a/cake/tests/cases/console/console_input.php +++ b/cake/console/console_input.php @@ -36,7 +36,7 @@ class ConsoleInput { * @return void */ public function __construct($handle = 'php://stdin') { - $this->_input = fopen('php://stdin', 'r'); + $this->_input = fopen($handle, 'r'); } /** From e816a49a6f4bf67d0845c71de68e1f8c9715710a Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 4 Oct 2010 23:26:38 -0400 Subject: [PATCH 025/207] Moving ShellDispatcher::getInput() into Shell as a protected method. --- cake/console/libs/shell.php | 44 ++++++++++++++++++++++++++++--- cake/console/shell_dispatcher.php | 2 -- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index a7f52fa72..fbf026981 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -19,6 +19,7 @@ */ App::import('Shell', 'TaskCollection'); require_once CAKE . 'console' . DS . 'console_output.php'; +require_once CAKE . 'console' . DS . 'console_input.php'; /** * Base class for command-line utilities for automating programmer chores. @@ -158,7 +159,7 @@ class Shell extends Object { * Constructs this Shell instance. * */ - function __construct(&$dispatch, $stdout = null, $stderr = null) { + function __construct(&$dispatch, $stdout = null, $stderr = null, $stdin = null) { $vars = array('params', 'args', 'shell', 'shellCommand' => 'command', 'shellPaths'); foreach ($vars as $key => $var) { @@ -182,12 +183,16 @@ class Shell extends Object { $this->stdout = $stdout; $this->stderr = $stderr; + $this->stdin = $stdin; if ($this->stdout == null) { $this->stdout = new ConsoleOutput('php://stdout'); } if ($this->stderr == null) { $this->stderr = new ConsoleOutput('php://stderr'); } + if ($this->stdin == null) { + $this->stdin = new ConsoleInput('php://stdin'); + } } /** @@ -305,7 +310,7 @@ class Shell extends Object { if (!$this->interactive) { return $default; } - $in = $this->Dispatch->getInput($prompt, $options, $default); + $in = $this->_getInput($prompt, $options, $default); if ($options && is_string($options)) { if (strpos($options, ',')) { @@ -318,7 +323,7 @@ class Shell extends Object { } if (is_array($options)) { while ($in == '' || ($in && (!in_array(strtolower($in), $options) && !in_array(strtoupper($in), $options)) && !in_array($in, $options))) { - $in = $this->Dispatch->getInput($prompt, $options, $default); + $in = $this->_getInput($prompt, $options, $default); } } if ($in) { @@ -326,6 +331,39 @@ class Shell extends Object { } } +/** + * Prompts the user for input, and returns it. + * + * @param string $prompt Prompt text. + * @param mixed $options Array or string of options. + * @param string $default Default input value. + * @return Either the default value, or the user-provided input. + */ + protected function _getInput($prompt, $options, $default) { + if (!is_array($options)) { + $printOptions = ''; + } else { + $printOptions = '(' . implode('/', $options) . ')'; + } + + if ($default === null) { + $this->stdout->write($prompt . " $printOptions \n" . '> ', 0); + } else { + $this->stdout->write($prompt . " $printOptions \n" . "[$default] > ", 0); + } + $result = $this->stdin->read(); + + if ($result === false) { + $this->_stop(1); + } + $result = trim($result); + + if ($default != null && empty($result)) { + return $default; + } + return $result; + } + /** * Outputs a single or multiple messages to stdout. If no parameters * are passed outputs just a newline. diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index c11f3988b..d01a72acf 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -167,8 +167,6 @@ class ShellDispatcher { * */ protected function _initEnvironment() { - $this->stdin = fopen('php://stdin', 'r'); - if (!$this->__bootstrap()) { $message = "Unable to load CakePHP core.\nMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH; throw new RuntimeException($message); From 7921ef1282f3e26b954346d2643b7565d29e7401 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 5 Oct 2010 22:44:47 -0400 Subject: [PATCH 026/207] Fixing Shell tests to use ConsoleInput. --- cake/console/libs/shell.php | 7 ++++ cake/tests/cases/console/libs/shell.test.php | 38 +++++++++----------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index fbf026981..d0fd9022d 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -155,6 +155,13 @@ class Shell extends Object { */ public $stderr; +/** + * stdin object + * + * @var ConsoleInput + */ + public $stdin; + /** * Constructs this Shell instance. * diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index a82b8694b..6f12c46fb 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -110,7 +110,8 @@ class ShellTest extends CakeTestCase { ); $output = $this->getMock('ConsoleOutput'); $error = $this->getMock('ConsoleOutput'); - $this->Shell =& new TestShell($this->Dispatcher, $output, $error); + $in = $this->getMock('ConsoleInput'); + $this->Shell =& new TestShell($this->Dispatcher, $output, $error, $in); } /** @@ -174,29 +175,24 @@ class ShellTest extends CakeTestCase { * @return void */ public function testIn() { - $this->Dispatcher->expects($this->at(0)) - ->method('getInput') - ->with('Just a test?', array('y', 'n'), 'n') + $this->Shell->stdin->expects($this->at(0)) + ->method('read') ->will($this->returnValue('n')); - $this->Dispatcher->expects($this->at(1)) - ->method('getInput') - ->with('Just a test?', array('y', 'n'), 'n') + $this->Shell->stdin->expects($this->at(1)) + ->method('read') ->will($this->returnValue('Y')); - $this->Dispatcher->expects($this->at(2)) - ->method('getInput') - ->with('Just a test?', 'y,n', 'n') + $this->Shell->stdin->expects($this->at(2)) + ->method('read') ->will($this->returnValue('y')); - $this->Dispatcher->expects($this->at(3)) - ->method('getInput') - ->with('Just a test?', 'y/n', 'n') + $this->Shell->stdin->expects($this->at(3)) + ->method('read') ->will($this->returnValue('y')); - $this->Dispatcher->expects($this->at(4)) - ->method('getInput') - ->with('Just a test?', 'y', 'y') + $this->Shell->stdin->expects($this->at(4)) + ->method('read') ->will($this->returnValue('y')); $result = $this->Shell->in('Just a test?', array('y', 'n'), 'n'); @@ -471,14 +467,12 @@ class ShellTest extends CakeTestCase { $this->Shell->interactive = true; - $this->Shell->Dispatch->expects($this->at(5)) - ->method('getInput') - ->withAnyParameters() + $this->Shell->stdin->expects($this->at(0)) + ->method('read') ->will($this->returnValue('n')); - $this->Shell->Dispatch->expects($this->at(9)) - ->method('getInput') - ->withAnyParameters() + $this->Shell->stdin->expects($this->at(1)) + ->method('read') ->will($this->returnValue('y')); From a3023430c8016ad20fd20d52d6d6030ba359f396 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 5 Oct 2010 23:51:08 -0400 Subject: [PATCH 027/207] Updating test cases to use new Shell internals. Cleaning up some mocks. --- cake/tests/cases/console/libs/acl.test.php | 2 +- cake/tests/cases/console/libs/api.test.php | 2 +- cake/tests/cases/console/libs/bake.test.php | 2 +- cake/tests/cases/console/libs/command_list.test.php | 11 ++++++----- cake/tests/cases/console/libs/testsuite.test.php | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cake/tests/cases/console/libs/acl.test.php b/cake/tests/cases/console/libs/acl.test.php index e604639d4..94869be49 100644 --- a/cake/tests/cases/console/libs/acl.test.php +++ b/cake/tests/cases/console/libs/acl.test.php @@ -51,7 +51,7 @@ class AclShellTest extends CakeTestCase { $this->Dispatcher = $this->getMock( 'ShellDispatcher', - array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'dispatch', 'clear') + array('_stop', '_initEnvironment', 'dispatch') ); $this->Task = $this->getMock( 'AclShell', diff --git a/cake/tests/cases/console/libs/api.test.php b/cake/tests/cases/console/libs/api.test.php index fbd54559b..e4d5f5478 100644 --- a/cake/tests/cases/console/libs/api.test.php +++ b/cake/tests/cases/console/libs/api.test.php @@ -40,7 +40,7 @@ class ApiShellTest extends CakeTestCase { parent::setUp(); $this->Dispatcher = $this->getMock( 'ShellDispatcher', - array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'dispatch', 'clear') + array('_stop', '_initEnvironment', 'dispatch') ); $this->Shell = $this->getMock( 'ApiShell', diff --git a/cake/tests/cases/console/libs/bake.test.php b/cake/tests/cases/console/libs/bake.test.php index 3143f0ba4..acefbc732 100644 --- a/cake/tests/cases/console/libs/bake.test.php +++ b/cake/tests/cases/console/libs/bake.test.php @@ -52,7 +52,7 @@ class BakeShellTest extends CakeTestCase { parent::setUp(); $this->Dispatcher = $this->getMock( 'ShellDispatcher', - array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear') + array('_stop', '_initEnvironment') ); $this->Shell = $this->getMock( 'BakeShell', diff --git a/cake/tests/cases/console/libs/command_list.test.php b/cake/tests/cases/console/libs/command_list.test.php index 602226cca..0572d7616 100644 --- a/cake/tests/cases/console/libs/command_list.test.php +++ b/cake/tests/cases/console/libs/command_list.test.php @@ -50,14 +50,15 @@ class CommandListTest extends CakeTestCase { $this->Dispatcher = $this->getMock( 'ShellDispatcher', - array('getInput', 'stderr', '_stop', '_initEnvironment', 'dispatch', 'clear') + array('_stop', '_initEnvironment', 'dispatch') ); - $this->Dispatcher->stdout = new TestStringOutput(); + $out = new TestStringOutput(); + $in = $this->getMock('ConsoleInput'); $this->Shell = $this->getMock( 'CommandListShell', - array('in', '_stop'), - array(&$this->Dispatcher) + array('in', '_stop', 'clear'), + array(&$this->Dispatcher, $out, null, $in) ); } @@ -77,7 +78,7 @@ class CommandListTest extends CakeTestCase { */ function testMain() { $this->Shell->main(); - $output = $this->Dispatcher->stdout->output; + $output = $this->Shell->stdout->output; $expected = "/example \[.*TestPlugin, TestPluginTwo.*\]/"; $this->assertPattern($expected, $output); diff --git a/cake/tests/cases/console/libs/testsuite.test.php b/cake/tests/cases/console/libs/testsuite.test.php index f29e50bc4..dfeac8634 100644 --- a/cake/tests/cases/console/libs/testsuite.test.php +++ b/cake/tests/cases/console/libs/testsuite.test.php @@ -34,11 +34,11 @@ class TestSuiteShellTest extends CakeTestCase { public function setUp() { $this->Dispatcher = $this->getMock( 'ShellDispatcher', - array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear') + array('_stop', '_initEnvironment') ); $this->Shell = $this->getMock( 'TestSuiteShell', - array('in', 'out', 'hr', 'help', 'error', 'err', '_stop', 'initialize', 'run'), + array('in', 'out', 'hr', 'help', 'error', 'err', '_stop', 'initialize', 'run', 'clear'), array(&$this->Dispatcher) ); $this->Shell->Dispatch->shellPaths = App::path('shells'); From 73ad3043a2c9a6fc0a62c39d1f7b8d208bc16004 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 6 Oct 2010 00:15:51 -0400 Subject: [PATCH 028/207] Fixing more console tests to use new internals. Making TaskCollection pass the stdout, stdin, stderr to Tasks they create. This allows for more flexible dependency injection and makes testing easier. --- cake/console/libs/task_collection.php | 4 ++- cake/console/libs/tasks/fixture.php | 4 +-- .../console/libs/tasks/controller.test.php | 14 +++++----- .../console/libs/tasks/db_config.test.php | 16 +++++++---- .../cases/console/libs/tasks/fixture.test.php | 28 +++++++++---------- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/cake/console/libs/task_collection.php b/cake/console/libs/task_collection.php index b0fc6aea1..a3b162166 100644 --- a/cake/console/libs/task_collection.php +++ b/cake/console/libs/task_collection.php @@ -60,7 +60,9 @@ class TaskCollection extends ObjectCollection { } } - $this->_loaded[$name] = new $taskClass($this->_Shell); + $this->_loaded[$name] = new $taskClass( + $this->_Shell, $this->_Shell->stdout, $this->_Shell->stderr, $this->_Shell->stdin + ); if ($enable === true) { $this->_enabled[] = $name; } diff --git a/cake/console/libs/tasks/fixture.php b/cake/console/libs/tasks/fixture.php index 1cc9f5ae7..fd04b6287 100644 --- a/cake/console/libs/tasks/fixture.php +++ b/cake/console/libs/tasks/fixture.php @@ -54,8 +54,8 @@ class FixtureTask extends BakeTask { * Override initialize * */ - public function __construct(&$dispatch) { - parent::__construct($dispatch); + public function __construct(&$dispatch, $stdout = null, $stderr = null, $stdin = null) { + parent::__construct($dispatch, $stdout, $stderr, $stdin); $this->path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS; } diff --git a/cake/tests/cases/console/libs/tasks/controller.test.php b/cake/tests/cases/console/libs/tasks/controller.test.php index e9a980e23..6c469a195 100644 --- a/cake/tests/cases/console/libs/tasks/controller.test.php +++ b/cake/tests/cases/console/libs/tasks/controller.test.php @@ -67,12 +67,12 @@ class ControllerTaskTest extends CakeTestCase { * @return void */ public function setUp() { - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); + $out = $this->getMock('ConsoleOutput'); + $in = $this->getMock('ConsoleInput'); $this->Task = $this->getMock('ControllerTask', array('in', 'out', 'err', 'hr', 'createFile', '_stop', '_checkUnitTest'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->Task->name = 'ControllerTask'; $this->Task->Dispatch->shellPaths = App::path('shells'); @@ -81,13 +81,13 @@ class ControllerTaskTest extends CakeTestCase { $this->Task->Model = $this->getMock('ModelTask', array('in', 'out', 'err', 'createFile', '_stop', '_checkUnitTest'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->Task->Project = $this->getMock('ProjectTask', array('in', 'out', 'err', 'createFile', '_stop', '_checkUnitTest', 'getPrefix'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); - $this->Task->Test = $this->getMock('TestTask', array(), array(&$this->Dispatcher)); + $this->Task->Test = $this->getMock('TestTask', array(), array(&$this->Dispatcher, $out, $out, $in)); } /** diff --git a/cake/tests/cases/console/libs/tasks/db_config.test.php b/cake/tests/cases/console/libs/tasks/db_config.test.php index 6b1aee614..1ea686daf 100644 --- a/cake/tests/cases/console/libs/tasks/db_config.test.php +++ b/cake/tests/cases/console/libs/tasks/db_config.test.php @@ -60,12 +60,13 @@ class DbConfigTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $out = $this->getMock('ConsoleOutput'); + $in = $this->getMock('ConsoleInput'); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('DbConfigTask', array('in', 'out', 'err', 'hr', 'createFile', '_stop', '_checkUnitTest', '_verify'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->Task->Dispatch->shellPaths = App::path('shells'); @@ -114,7 +115,12 @@ class DbConfigTaskTest extends CakeTestCase { */ public function testExecuteIntoInteractive() { $this->Task->initialize(); - $this->Task = $this->getMock('DbConfigTask', array('in', '_stop', 'createFile'), array(&$this->Dispatcher)); + + $out = $this->getMock('ConsoleOutput'); + $this->Task = $this->getMock( + 'DbConfigTask', + array('in', '_stop', 'createFile'), array(&$this->Dispatcher, $out, $out) + ); $this->Task->expects($this->once())->method('_stop'); $this->Task->expects($this->at(0))->method('in')->will($this->returnValue('default')); //name diff --git a/cake/tests/cases/console/libs/tasks/fixture.test.php b/cake/tests/cases/console/libs/tasks/fixture.test.php index 98e5409b9..6e25291c2 100644 --- a/cake/tests/cases/console/libs/tasks/fixture.test.php +++ b/cake/tests/cases/console/libs/tasks/fixture.test.php @@ -19,10 +19,6 @@ */ App::import('Shell', 'Shell', false); -if (!defined('DISABLE_AUTO_DISPATCH')) { - define('DISABLE_AUTO_DISPATCH', true); -} - require_once CAKE . 'console' . DS . 'shell_dispatcher.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'template.php'; require_once CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'fixture.php'; @@ -50,18 +46,19 @@ class FixtureTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $out = $this->getMock('ConsoleOutput'); + $in = $this->getMock('ConsoleInput'); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('FixtureTask', - array('in', 'err', 'createFile', '_stop'), - array(&$this->Dispatcher) + array('in', 'err', 'createFile', '_stop', 'clear'), + array(&$this->Dispatcher, $out, $out, $in) ); $this->Task->Model = $this->getMock('Shell', - array('in', 'out', 'erro', 'createFile', 'getName', 'getTable', 'listAll'), - array(&$this->Dispatcher) + array('in', 'out', 'error', 'createFile', 'getName', 'getTable', 'listAll'), + array(&$this->Dispatcher, $out, $out, $in) ); - $this->Task->Template =& new TemplateTask($this->Dispatcher); + $this->Task->Template = new TemplateTask($this->Dispatcher, $out, $out, $in); $this->Task->Dispatch->shellPaths = App::path('shells'); $this->Task->Template->initialize(); } @@ -82,8 +79,11 @@ class FixtureTaskTest extends CakeTestCase { * @return void */ public function testConstruct() { + $out = $this->getMock('ConsoleOutput'); + $in = $this->getMock('ConsoleInput'); + $this->Dispatcher->params['working'] = DS . 'my' . DS . 'path'; - $Task = new FixtureTask($this->Dispatcher); + $Task = new FixtureTask($this->Dispatcher, $out, $out, $in); $expected = DS . 'my' . DS . 'path' . DS . 'tests' . DS . 'fixtures' . DS; $this->assertEqual($Task->path, $expected); @@ -137,7 +137,7 @@ class FixtureTaskTest extends CakeTestCase { * * @return void */ - public function testImportRecordsFromDatabaseWithConditions() { + public function testImportRecordsFromDatabaseWithConditionsPoo() { $this->Task->interactive = true; $this->Task->expects($this->at(0))->method('in') ->will($this->returnValue('WHERE 1=1 LIMIT 10')); From 41db1485aa41b5e43f17e0a58e1d2a693ec2ff40 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 6 Oct 2010 22:52:42 -0400 Subject: [PATCH 029/207] Updating test cases to use new objects. Preventing ConsoleOutput and ConsoleInput constructors from being run as it causes too many files open exceptions. --- .../console/console_error_handler.test.php | 2 +- cake/tests/cases/console/libs/acl.test.php | 5 +++- cake/tests/cases/console/libs/api.test.php | 5 +++- cake/tests/cases/console/libs/bake.test.php | 5 +++- .../cases/console/libs/command_list.test.php | 4 ++-- cake/tests/cases/console/libs/schema.test.php | 18 +++++++-------- cake/tests/cases/console/libs/shell.test.php | 6 ++--- .../console/libs/tasks/controller.test.php | 4 ++-- .../console/libs/tasks/db_config.test.php | 6 ++--- .../cases/console/libs/tasks/extract.test.php | 12 ++++++---- .../cases/console/libs/tasks/fixture.test.php | 8 +++---- .../cases/console/libs/tasks/model.test.php | 23 ++++++++++++------- .../cases/console/libs/tasks/plugin.test.php | 15 +++++++----- .../cases/console/libs/tasks/project.test.php | 9 ++++---- .../console/libs/tasks/template.test.php | 17 +++++++------- .../cases/console/libs/tasks/test.test.php | 17 +++++++------- .../cases/console/libs/tasks/view.test.php | 15 ++++++------ 17 files changed, 98 insertions(+), 73 deletions(-) diff --git a/cake/tests/cases/console/console_error_handler.test.php b/cake/tests/cases/console/console_error_handler.test.php index d1171d2b1..0a089f09f 100644 --- a/cake/tests/cases/console/console_error_handler.test.php +++ b/cake/tests/cases/console/console_error_handler.test.php @@ -33,7 +33,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase { */ function getErrorHandler($exception) { $error = new ConsoleErrorHandler($exception); - $error->stderr = $this->getMock('ConsoleOutput'); + $error->stderr = $this->getMock('ConsoleOutput', array(), array(), '', false); return $error; } diff --git a/cake/tests/cases/console/libs/acl.test.php b/cake/tests/cases/console/libs/acl.test.php index 94869be49..e8597ce22 100644 --- a/cake/tests/cases/console/libs/acl.test.php +++ b/cake/tests/cases/console/libs/acl.test.php @@ -49,6 +49,9 @@ class AclShellTest extends CakeTestCase { Configure::write('Acl.database', 'test'); Configure::write('Acl.classname', 'DbAcl'); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + $this->Dispatcher = $this->getMock( 'ShellDispatcher', array('_stop', '_initEnvironment', 'dispatch') @@ -56,7 +59,7 @@ class AclShellTest extends CakeTestCase { $this->Task = $this->getMock( 'AclShell', array('in', 'out', 'hr', 'createFile', 'error', 'err'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $collection = new ComponentCollection(); $this->Task->Acl = new AclComponent($collection); diff --git a/cake/tests/cases/console/libs/api.test.php b/cake/tests/cases/console/libs/api.test.php index e4d5f5478..685ddb091 100644 --- a/cake/tests/cases/console/libs/api.test.php +++ b/cake/tests/cases/console/libs/api.test.php @@ -38,6 +38,9 @@ class ApiShellTest extends CakeTestCase { */ public function setUp() { parent::setUp(); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + $this->Dispatcher = $this->getMock( 'ShellDispatcher', array('_stop', '_initEnvironment', 'dispatch') @@ -45,7 +48,7 @@ class ApiShellTest extends CakeTestCase { $this->Shell = $this->getMock( 'ApiShell', array('in', 'out', 'createFile', 'hr', '_stop'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); } diff --git a/cake/tests/cases/console/libs/bake.test.php b/cake/tests/cases/console/libs/bake.test.php index acefbc732..9b546339f 100644 --- a/cake/tests/cases/console/libs/bake.test.php +++ b/cake/tests/cases/console/libs/bake.test.php @@ -50,6 +50,9 @@ class BakeShellTest extends CakeTestCase { */ public function setUp() { parent::setUp(); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + $this->Dispatcher = $this->getMock( 'ShellDispatcher', array('_stop', '_initEnvironment') @@ -57,7 +60,7 @@ class BakeShellTest extends CakeTestCase { $this->Shell = $this->getMock( 'BakeShell', array('in', 'out', 'hr', 'err', 'createFile', '_stop', '_checkUnitTest'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->Shell->Dispatch->shellPaths = App::path('shells'); } diff --git a/cake/tests/cases/console/libs/command_list.test.php b/cake/tests/cases/console/libs/command_list.test.php index 0572d7616..486f3a88f 100644 --- a/cake/tests/cases/console/libs/command_list.test.php +++ b/cake/tests/cases/console/libs/command_list.test.php @@ -53,12 +53,12 @@ class CommandListTest extends CakeTestCase { array('_stop', '_initEnvironment', 'dispatch') ); $out = new TestStringOutput(); - $in = $this->getMock('ConsoleInput'); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); $this->Shell = $this->getMock( 'CommandListShell', array('in', '_stop', 'clear'), - array(&$this->Dispatcher, $out, null, $in) + array(&$this->Dispatcher, $out, $out, $in) ); } diff --git a/cake/tests/cases/console/libs/schema.test.php b/cake/tests/cases/console/libs/schema.test.php index b59f87027..7d343dc9c 100644 --- a/cake/tests/cases/console/libs/schema.test.php +++ b/cake/tests/cases/console/libs/schema.test.php @@ -108,15 +108,16 @@ class SchemaShellTest extends CakeTestCase { * * @return void */ - public function setup() { - $this->Dispatcher = $this->getMock( - 'ShellDispatcher', - array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear') - ); + public function setUp() { + parent::setUp(); + + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Shell = $this->getMock( 'SchemaShell', array('in', 'out', 'hr', 'createFile', 'error', 'err', '_stop'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); } @@ -125,13 +126,12 @@ class SchemaShellTest extends CakeTestCase { * * @return void */ - public function teardown() { - ClassRegistry::flush(); + public function tearDown() { + parent::tearDown(); if (!empty($this->file) && $this->file instanceof File) { $this->file->delete(); unset($this->file); } - App::build(); } /** diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index 6f12c46fb..fed9894bd 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -108,9 +108,9 @@ class ShellTest extends CakeTestCase { 'ShellDispatcher', array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear') ); - $output = $this->getMock('ConsoleOutput'); - $error = $this->getMock('ConsoleOutput'); - $in = $this->getMock('ConsoleInput'); + $output = $this->getMock('ConsoleOutput', array(), array(), '', false); + $error = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); $this->Shell =& new TestShell($this->Dispatcher, $output, $error, $in); } diff --git a/cake/tests/cases/console/libs/tasks/controller.test.php b/cake/tests/cases/console/libs/tasks/controller.test.php index 6c469a195..1e68acbd0 100644 --- a/cake/tests/cases/console/libs/tasks/controller.test.php +++ b/cake/tests/cases/console/libs/tasks/controller.test.php @@ -68,8 +68,8 @@ class ControllerTaskTest extends CakeTestCase { */ public function setUp() { $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); - $out = $this->getMock('ConsoleOutput'); - $in = $this->getMock('ConsoleInput'); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); $this->Task = $this->getMock('ControllerTask', array('in', 'out', 'err', 'hr', 'createFile', '_stop', '_checkUnitTest'), array(&$this->Dispatcher, $out, $out, $in) diff --git a/cake/tests/cases/console/libs/tasks/db_config.test.php b/cake/tests/cases/console/libs/tasks/db_config.test.php index 1ea686daf..a03660869 100644 --- a/cake/tests/cases/console/libs/tasks/db_config.test.php +++ b/cake/tests/cases/console/libs/tasks/db_config.test.php @@ -60,8 +60,8 @@ class DbConfigTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $out = $this->getMock('ConsoleOutput'); - $in = $this->getMock('ConsoleInput'); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('DbConfigTask', @@ -116,7 +116,7 @@ class DbConfigTaskTest extends CakeTestCase { public function testExecuteIntoInteractive() { $this->Task->initialize(); - $out = $this->getMock('ConsoleOutput'); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); $this->Task = $this->getMock( 'DbConfigTask', array('in', '_stop', 'createFile'), array(&$this->Dispatcher, $out, $out) diff --git a/cake/tests/cases/console/libs/tasks/extract.test.php b/cake/tests/cases/console/libs/tasks/extract.test.php index 2f7d08bfa..cd2393183 100644 --- a/cake/tests/cases/console/libs/tasks/extract.test.php +++ b/cake/tests/cases/console/libs/tasks/extract.test.php @@ -39,10 +39,11 @@ class ExtractTaskTest extends CakeTestCase { * @return void */ public function setUp() { - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); - $this->Task =& new ExtractTask($this->Dispatcher); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); + $this->Task = new ExtractTask($this->Dispatcher, $out, $out, $in); } /** @@ -51,7 +52,8 @@ class ExtractTaskTest extends CakeTestCase { * @return void */ public function tearDown() { - ClassRegistry::flush(); + parent::tearDown(); + unset($this->Task, $this->Dispatcher); } /** diff --git a/cake/tests/cases/console/libs/tasks/fixture.test.php b/cake/tests/cases/console/libs/tasks/fixture.test.php index 6e25291c2..7b8e82c01 100644 --- a/cake/tests/cases/console/libs/tasks/fixture.test.php +++ b/cake/tests/cases/console/libs/tasks/fixture.test.php @@ -46,8 +46,8 @@ class FixtureTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $out = $this->getMock('ConsoleOutput'); - $in = $this->getMock('ConsoleInput'); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('FixtureTask', @@ -79,8 +79,8 @@ class FixtureTaskTest extends CakeTestCase { * @return void */ public function testConstruct() { - $out = $this->getMock('ConsoleOutput'); - $in = $this->getMock('ConsoleInput'); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); $this->Dispatcher->params['working'] = DS . 'my' . DS . 'path'; $Task = new FixtureTask($this->Dispatcher, $out, $out, $in); diff --git a/cake/tests/cases/console/libs/tasks/model.test.php b/cake/tests/cases/console/libs/tasks/model.test.php index f7cbcb2ac..33dd42a37 100644 --- a/cake/tests/cases/console/libs/tasks/model.test.php +++ b/cake/tests/cases/console/libs/tasks/model.test.php @@ -49,12 +49,13 @@ class ModelTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('ModelTask', array('in', 'err', 'createFile', '_stop', '_checkUnitTest'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->_setupOtherMocks(); } @@ -65,9 +66,12 @@ class ModelTaskTest extends CakeTestCase { * @return void */ protected function _useMockedOut() { + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + $this->Task = $this->getMock('ModelTask', array('in', 'out', 'err', 'hr', 'createFile', '_stop', '_checkUnitTest'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->_setupOtherMocks(); } @@ -78,9 +82,12 @@ class ModelTaskTest extends CakeTestCase { * @return void */ protected function _setupOtherMocks() { - $this->Task->Fixture = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher)); - $this->Task->Test = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher)); - $this->Task->Template =& new TemplateTask($this->Task->Dispatch); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Task->Fixture = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher, $out, $out, $in)); + $this->Task->Test = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher, $out, $out, $in)); + $this->Task->Template =& new TemplateTask($this->Task->Dispatch, $out, $out, $in); $this->Task->name = 'ModelTask'; $this->Task->interactive = true; diff --git a/cake/tests/cases/console/libs/tasks/plugin.test.php b/cake/tests/cases/console/libs/tasks/plugin.test.php index cdaf78085..5fda7fda2 100644 --- a/cake/tests/cases/console/libs/tasks/plugin.test.php +++ b/cake/tests/cases/console/libs/tasks/plugin.test.php @@ -41,13 +41,13 @@ class PluginTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('PluginTask', - array('in', 'err', 'createFile', '_stop'), - array(&$this->Dispatcher) + array('in', 'err', 'createFile', '_stop', 'clear'), + array(&$this->Dispatcher, $out, $out, $in) ); $this->Task->path = TMP . 'tests' . DS; @@ -221,7 +221,10 @@ class PluginTaskTest extends CakeTestCase { * @return void */ public function testExecuteWithTwoArgs() { - $this->Task->Model = $this->getMock('ModelTask', array(), array(&$this->Dispatcher)); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Task->Model = $this->getMock('ModelTask', array(), array(&$this->Dispatcher, $out, $out, $in)); $this->Task->expects($this->at(0))->method('in')->will($this->returnValue($this->_testPath)); diff --git a/cake/tests/cases/console/libs/tasks/project.test.php b/cake/tests/cases/console/libs/tasks/project.test.php index 0460071a5..855908ed4 100644 --- a/cake/tests/cases/console/libs/tasks/project.test.php +++ b/cake/tests/cases/console/libs/tasks/project.test.php @@ -41,12 +41,13 @@ class ProjectTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment', 'clear')); $this->Task = $this->getMock('ProjectTask', array('in', 'err', 'createFile', '_stop'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->Dispatcher->shellPaths = App::path('shells'); $this->Task->path = TMP . 'tests' . DS; diff --git a/cake/tests/cases/console/libs/tasks/template.test.php b/cake/tests/cases/console/libs/tasks/template.test.php index e0cbc652a..752af6d51 100644 --- a/cake/tests/cases/console/libs/tasks/template.test.php +++ b/cake/tests/cases/console/libs/tasks/template.test.php @@ -39,12 +39,14 @@ class TemplateTaskTest extends CakeTestCase { * @return void */ public function setup() { - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + parent::setUp(); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('TemplateTask', - array('in', 'err', 'createFile', '_stop'), - array(&$this->Dispatcher) + array('in', 'err', 'createFile', '_stop', 'clear'), + array(&$this->Dispatcher, $out, $out, $in) ); $this->Task->Dispatch->shellPaths = App::path('shells'); } @@ -54,10 +56,9 @@ class TemplateTaskTest extends CakeTestCase { * * @return void */ - public function teardown() { + public function tearDown() { + parent::tearDown(); unset($this->Task, $this->Dispatcher); - ClassRegistry::flush(); - App::build(); } /** diff --git a/cake/tests/cases/console/libs/tasks/test.test.php b/cake/tests/cases/console/libs/tasks/test.test.php index 513df3ba7..4508e82e1 100644 --- a/cake/tests/cases/console/libs/tasks/test.test.php +++ b/cake/tests/cases/console/libs/tasks/test.test.php @@ -239,16 +239,17 @@ class TestTaskTest extends CakeTestCase { */ public function setup() { parent::setup(); - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('TestTask', array('in', 'err', 'createFile', '_stop', 'isLoadableClass'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); $this->Dispatcher->shellPaths = App::path('shells'); $this->Task->name = 'TestTask'; - $this->Task->Template = new TemplateTask($this->Dispatcher); + $this->Task->Template = new TemplateTask($this->Dispatcher, $out, $out, $in); } /** @@ -256,9 +257,9 @@ class TestTaskTest extends CakeTestCase { * * @return void */ - public function teardown() { - parent::teardown(); - ClassRegistry::flush(); + public function tearDown() { + parent::tearDown(); + unset($this->Task, $this->Dispatcher); } /** diff --git a/cake/tests/cases/console/libs/tasks/view.test.php b/cake/tests/cases/console/libs/tasks/view.test.php index 4d062ed7c..c43b55a4e 100644 --- a/cake/tests/cases/console/libs/tasks/view.test.php +++ b/cake/tests/cases/console/libs/tasks/view.test.php @@ -222,16 +222,17 @@ class ViewTaskTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $this->Dispatcher = $this->getMock('ShellDispatcher', array( - 'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear' - )); + $out = $this->getMock('ConsoleOutput', array(), array(), '', false); + $in = $this->getMock('ConsoleInput', array(), array(), '', false); + + $this->Dispatcher = $this->getMock('ShellDispatcher', array('_stop', '_initEnvironment')); $this->Task = $this->getMock('ViewTask', array('in', 'err', 'createFile', '_stop'), - array(&$this->Dispatcher) + array(&$this->Dispatcher, $out, $out, $in) ); - $this->Task->Template = new TemplateTask($this->Dispatcher); - $this->Task->Controller = $this->getMock('ControllerTask', array(), array(&$this->Dispatcher)); - $this->Task->Project = $this->getMock('ProjectTask', array(), array(&$this->Dispatcher)); + $this->Task->Template = new TemplateTask($this->Dispatcher, $out, $out, $in); + $this->Task->Controller = $this->getMock('ControllerTask', array(), array(&$this->Dispatcher, $out, $out, $in)); + $this->Task->Project = $this->getMock('ProjectTask', array(), array(&$this->Dispatcher, $out, $out, $in)); $this->Dispatcher->shellPaths = App::path('shells'); $this->Task->path = TMP; From d914c0aaeab45d4399a046fa1f37b5bc2bff7405 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 6 Oct 2010 23:18:31 -0400 Subject: [PATCH 030/207] Adding ConsoleOptionParser and its test. --- cake/console/console_option_parser.php | 52 +++++++++++++++++++ .../console/console_option_parser.test.php | 25 +++++++++ 2 files changed, 77 insertions(+) create mode 100644 cake/console/console_option_parser.php create mode 100644 cake/tests/cases/console/console_option_parser.test.php diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php new file mode 100644 index 000000000..e0c6e1ee5 --- /dev/null +++ b/cake/console/console_option_parser.php @@ -0,0 +1,52 @@ + + * Copyright 2005-2010, Cake Software Foundation, Inc. + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package cake + * @subpackage cake.tests.cases.console + * @since CakePHP(tm) v 2.0 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +require_once CAKE . 'console' . DS . 'console_option_parser.php'; + +class ConsoleOptionParserTest extends CakeTestCase { + +} \ No newline at end of file From 694537933e726794237d8c4cc4b16bcd1cf4b3d2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 7 Oct 2010 21:18:22 -0400 Subject: [PATCH 031/207] Adding description() and epilog() to ConsoleOptionParser. Starting to build out addOption. --- cake/console/console_option_parser.php | 63 ++++++++++++++++++- .../console/console_option_parser.test.php | 37 ++++++++++- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index e0c6e1ee5..3fb534bac 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -27,6 +27,15 @@ * @subpackage cake.cake.console */ class ConsoleOptionParser { + + protected $_description = null; + + protected $_epilog = null; + + protected $_options = array(); + + protected $_args = array(); + /** * Construct an OptionParser for a given ARGV array. * @@ -44,9 +53,59 @@ class ConsoleOptionParser { * By providing help text for your positional arguments and named arguments, the ConsoleOptionParser * can generate a help display for you. You can view the help for shells by using the `--help` or `-h` switch. * - * @param array $args The array of arguments with the Shell/Task stripped off. */ - public function __construct($args) { + public function __construct() { + + } + +/** + * Get or set the description text for shell/task + * + * @param string $text The text to set, or null if you want to read + * @return mixed If reading, the value of the description. If setting $this will be returned + */ + public function description($text = null) { + if ($text !== null) { + $this->_description = $text; + return $this; + } + return $this->_description; + } + +/** + * Get or set an epilog to the parser. The epilog is added to the end of + * the options and arguments listing when help is generated. + * + * @param string $text Text when setting or null when reading. + * @return mixed If reading, the value of the epilog. If setting $this will be returned. + */ + public function epilog($text = null) { + if ($text !== null) { + $this->_epilog = $text; + return $this; + } + return $this->_epilog; + } + +/** + * Add an option to the option parser. Options allow you to define optional or required + * parameters for your console application. Options are defined by the parameters they use. + * + * ### Params + * + * - `shortcut` - The single letter variant for this option, leave undefined for none. + * - `required` - Set to true to force this option to be required. An exception will be thrown + * when this option is not present. + * - `description` - Description for this option. + * - `type` - Require a certain type. Available types are `int` and `string`. If the options + * value is the wrong type an exception will be raised. + * - `default` - The default value for this option. If not defined the default will be null. + * + * @param string $name The name you want to the value to be parsed out as when options are parsed. + * @param array $params An array of parameters that define the behavior of the option + * @return ConsoleOptionParser returns $this. + */ + public function addOption($name, $params = array()) { } } \ No newline at end of file diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index ccfa379b2..906a01091 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -21,5 +21,40 @@ require_once CAKE . 'console' . DS . 'console_option_parser.php'; class ConsoleOptionParserTest extends CakeTestCase { - + +/** + * test setting the console description + * + * @return void + */ + function testDescription() { + $parser = new ConsoleOptionParser(); + $result = $parser->description('A test'); + + $this->assertEquals($parser, $result, 'Setting description is not chainable'); + $this->assertEquals('A test', $parser->description(), 'getting value is wrong.'); + } + +/** + * test setting the console epliog + * + * @return void + */ + function testEpilog() { + $parser = new ConsoleOptionParser(); + $result = $parser->epilog('A test'); + + $this->assertEquals($parser, $result, 'Setting epilog is not chainable'); + $this->assertEquals('A test', $parser->epilog(), 'getting value is wrong.'); + } + +/** + * test adding an option. + * + * @return void + */ + function testAddOption() { + $parser = new ConsoleOptionParser(); + $result = $parser->addOption('test'); + } } \ No newline at end of file From d5d9adb92b0f9774da708a8e2a912549f0625997 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 7 Oct 2010 21:46:19 -0400 Subject: [PATCH 032/207] Starting to implement options and option parsing. --- cake/console/console_option_parser.php | 26 ++++++++++++++++--- .../console/console_option_parser.test.php | 19 ++++++++++++-- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 3fb534bac..bdcdf32b3 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -98,14 +98,34 @@ class ConsoleOptionParser { * when this option is not present. * - `description` - Description for this option. * - `type` - Require a certain type. Available types are `int` and `string`. If the options - * value is the wrong type an exception will be raised. + * value is the wrong type an exception will be raised. Leave undefined to accept anything. * - `default` - The default value for this option. If not defined the default will be null. * - * @param string $name The name you want to the value to be parsed out as when options are parsed. + * @param string $name The long name you want to the value to be parsed out as when options are parsed. * @param array $params An array of parameters that define the behavior of the option - * @return ConsoleOptionParser returns $this. + * @return returns $this. */ public function addOption($name, $params = array()) { + $defaults = array( + 'shortcut' => null, + 'required' => false, + 'description' => '', + 'type' => null, + 'default' => null + ); + $this->_options[$name] = array_merge($defaults, $params); + return $this; + } + +/** + * Parse the argv array into a set of params and args. + * + * @param array $argv Array of args (argv) to parse + * @return Array array($params, $args) + */ + public function parse($argv) { + $params = $args = array(); + return array($params, $args); } } \ No newline at end of file diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 906a01091..868540f1b 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -49,12 +49,27 @@ class ConsoleOptionParserTest extends CakeTestCase { } /** - * test adding an option. + * test adding an option returns self. * * @return void */ - function testAddOption() { + function testAddOptionReturnSelf() { $parser = new ConsoleOptionParser(); $result = $parser->addOption('test'); + $this->assertEquals($parser, $result, 'Did not return $this from addOption'); + } + +/** + * test adding an option and using the shortcut value for parsing. + * + * @return void + */ + function testAddOptionShortcut() { + $parser = new ConsoleOptionParser(); + $parser->addOption('test', array( + 'shortcut' => 't' + )); + $result = $parser->parse(array('-t', 'value')); + $this->assertEqual(array('test' => 'value'), $result[0], 'Short parameter did not parse out'); } } \ No newline at end of file From 7b1b835bc161ae819fa76ddc418ab681a94c41a8 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 7 Oct 2010 22:44:02 -0400 Subject: [PATCH 033/207] Adding very basic implementation for parsing long and short options. --- cake/console/console_option_parser.php | 75 +++++++++++++++++-- .../console/console_option_parser.test.php | 28 +++++++ 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index bdcdf32b3..d312607c8 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -98,8 +98,8 @@ class ConsoleOptionParser { * when this option is not present. * - `description` - Description for this option. * - `type` - Require a certain type. Available types are `int` and `string`. If the options - * value is the wrong type an exception will be raised. Leave undefined to accept anything. - * - `default` - The default value for this option. If not defined the default will be null. + * value is the wrong type an exception will be raised. Leave undefined to accept anything. + * - `default` - The default value for this option. If not defined the default will be true. * * @param string $name The long name you want to the value to be parsed out as when options are parsed. * @param array $params An array of parameters that define the behavior of the option @@ -107,13 +107,18 @@ class ConsoleOptionParser { */ public function addOption($name, $params = array()) { $defaults = array( + 'name' => $name, 'shortcut' => null, 'required' => false, 'description' => '', 'type' => null, - 'default' => null + 'default' => true ); - $this->_options[$name] = array_merge($defaults, $params); + $options = array_merge($defaults, $params); + $this->_options[$name] = $options; + if (!empty($options['shortcut'])) { + $this->_options[$options['shortcut']] = $options; + } return $this; } @@ -125,7 +130,67 @@ class ConsoleOptionParser { */ public function parse($argv) { $params = $args = array(); - + $this->_tokens = $argv; + while ($token = array_shift($this->_tokens)) { + if (substr($token, 0, 2) == '--') { + $params = $this->_parseLongOption($token, $params); + } elseif (substr($token, 0, 1) == '-') { + $params = $this->_parseShortOption($token, $params); + } + } return array($params, $args); } + +/** + * Parse the value for a long option out of $this->_tokens + * + * @param string $option The option to parse. + * @param array $params The params to append the parsed value into + * @return array Params with $option added in. + */ + protected function _parseLongOption($option, $params) { + $name = substr($option, 2); + return $this->_parseOptionName($name, $params); + } + +/** + * Parse the value for a short option out of $this->_tokens + * + * @param string $option The option to parse. + * @param array $params The params to append the parsed value into + * @return array Params with $option added in. + */ + protected function _parseShortOption($option, $params) { + $key = substr($option, 1); + $name = $this->_options[$key]['name']; + return $this->_parseOptionName($name, $params); + } + +/** + * Parse an option by its name index. + * + * @param string $option The option to parse. + * @param array $params The params to append the parsed value into + * @return array Params with $option added in. + */ + protected function _parseOptionName($name, $params) { + $definition = $this->_options[$name]; + $nextValue = $this->_nextToken(); + if (empty($nextValue)) { + $value = $definition['default']; + } else if ($nextValue{0} != '-') { + $value = $nextValue; + } + $params[$name] = $value; + return $params; + } + +/** + * Find the next token in the argv set. + * + * @return next token or '' + */ + protected function _nextToken() { + return isset($this->_tokens[0]) ? $this->_tokens[0] : ''; + } } \ No newline at end of file diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 868540f1b..23c7a546c 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -59,6 +59,34 @@ class ConsoleOptionParserTest extends CakeTestCase { $this->assertEquals($parser, $result, 'Did not return $this from addOption'); } +/** + * test adding an option and using the long value for parsing. + * + * @return void + */ + function testAddOptionLong() { + $parser = new ConsoleOptionParser(); + $parser->addOption('test', array( + 'shortcut' => 't' + )); + $result = $parser->parse(array('--test', 'value')); + $this->assertEqual(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); + } + +/** + * test adding an option and using the default. + * + * @return void + */ + function testAddOptionDefault() { + $parser = new ConsoleOptionParser(); + $parser->addOption('test', array( + 'default' => 'default value', + )); + $result = $parser->parse(array('--test')); + $this->assertEqual(array('test' => 'default value'), $result[0], 'Default value did not parse out'); + } + /** * test adding an option and using the shortcut value for parsing. * From 2c2c9a38d2317deb6c6a3ef92b7e2369f923f5f0 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 7 Oct 2010 23:08:32 -0400 Subject: [PATCH 034/207] Removing required, and type from option flag as they didn't really make sense to include at this point. Adding support for --foo=bar type parameters. --- cake/console/console_option_parser.php | 12 ++++++------ .../cases/console/console_option_parser.test.php | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index d312607c8..62e8abb67 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -94,11 +94,7 @@ class ConsoleOptionParser { * ### Params * * - `shortcut` - The single letter variant for this option, leave undefined for none. - * - `required` - Set to true to force this option to be required. An exception will be thrown - * when this option is not present. - * - `description` - Description for this option. - * - `type` - Require a certain type. Available types are `int` and `string`. If the options - * value is the wrong type an exception will be raised. Leave undefined to accept anything. + * - `description` - Description for this option. Used when generating help for the option. * - `default` - The default value for this option. If not defined the default will be true. * * @param string $name The long name you want to the value to be parsed out as when options are parsed. @@ -111,7 +107,6 @@ class ConsoleOptionParser { 'shortcut' => null, 'required' => false, 'description' => '', - 'type' => null, 'default' => true ); $options = array_merge($defaults, $params); @@ -150,6 +145,10 @@ class ConsoleOptionParser { */ protected function _parseLongOption($option, $params) { $name = substr($option, 2); + if (strpos($name, '=') !== false) { + list($name, $value) = explode('=', $name, 2); + array_unshift($this->_tokens, $value); + } return $this->_parseOptionName($name, $params); } @@ -188,6 +187,7 @@ class ConsoleOptionParser { /** * Find the next token in the argv set. * + * @param string * @return next token or '' */ protected function _nextToken() { diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 23c7a546c..bf5a7afac 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -73,6 +73,20 @@ class ConsoleOptionParserTest extends CakeTestCase { $this->assertEqual(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); } +/** + * test adding an option and using the long value for parsing. + * + * @return void + */ + function testAddOptionLongEquals() { + $parser = new ConsoleOptionParser(); + $parser->addOption('test', array( + 'shortcut' => 't' + )); + $result = $parser->parse(array('--test=value')); + $this->assertEqual(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); + } + /** * test adding an option and using the default. * From 07bda824f09a7492e6c260c27203c253be51964b Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 7 Oct 2010 23:13:56 -0400 Subject: [PATCH 035/207] Adding test for parsing multiple options. --- cake/console/console_option_parser.php | 6 ++--- .../console/console_option_parser.test.php | 24 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 62e8abb67..75f7f8a18 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -175,10 +175,10 @@ class ConsoleOptionParser { protected function _parseOptionName($name, $params) { $definition = $this->_options[$name]; $nextValue = $this->_nextToken(); - if (empty($nextValue)) { - $value = $definition['default']; - } else if ($nextValue{0} != '-') { + if (!empty($nextValue) && $nextValue{0} != '-') { $value = $nextValue; + } else { + $value = $definition['default']; } $params[$name] = $value; return $params; diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index bf5a7afac..a0c3455e9 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -70,7 +70,7 @@ class ConsoleOptionParserTest extends CakeTestCase { 'shortcut' => 't' )); $result = $parser->parse(array('--test', 'value')); - $this->assertEqual(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); + $this->assertEquals(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); } /** @@ -84,7 +84,7 @@ class ConsoleOptionParserTest extends CakeTestCase { 'shortcut' => 't' )); $result = $parser->parse(array('--test=value')); - $this->assertEqual(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); + $this->assertEquals(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); } /** @@ -98,7 +98,7 @@ class ConsoleOptionParserTest extends CakeTestCase { 'default' => 'default value', )); $result = $parser->parse(array('--test')); - $this->assertEqual(array('test' => 'default value'), $result[0], 'Default value did not parse out'); + $this->assertEquals(array('test' => 'default value'), $result[0], 'Default value did not parse out'); } /** @@ -112,6 +112,22 @@ class ConsoleOptionParserTest extends CakeTestCase { 'shortcut' => 't' )); $result = $parser->parse(array('-t', 'value')); - $this->assertEqual(array('test' => 'value'), $result[0], 'Short parameter did not parse out'); + $this->assertEquals(array('test' => 'value'), $result[0], 'Short parameter did not parse out'); + } + +/** + * test multiple options at once. + * + * @return void + */ + function testMultipleOptions() { + $parser = new ConsoleOptionParser(); + $parser->addOption('test') + ->addOption('connection') + ->addOption('table', array('shortcut' => 't')); + + $result = $parser->parse(array('--test', 'value', '-t', '--connection', 'postgres')); + $expected = array('test' => 'value', 'table' => true, 'connection' => 'postgres'); + $this->assertEquals($expected, $result[0], 'multiple options did not parse'); } } \ No newline at end of file From 01a7fc70b7fc34f0022ed275acc25244c224a465 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 7 Oct 2010 23:14:31 -0400 Subject: [PATCH 036/207] Removing dead code. --- cake/console/console_option_parser.php | 1 - 1 file changed, 1 deletion(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 75f7f8a18..f7ca5f209 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -105,7 +105,6 @@ class ConsoleOptionParser { $defaults = array( 'name' => $name, 'shortcut' => null, - 'required' => false, 'description' => '', 'default' => true ); From 3b9921a390382ff8ebc4fec06bd592affeb62c53 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 00:16:50 -0400 Subject: [PATCH 037/207] Adding support for -abc style combination of short options. Renaming shortcut to short as its shorter :) --- cake/console/console_option_parser.php | 21 ++++++++++--- .../console/console_option_parser.test.php | 31 +++++++++++++++---- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index f7ca5f209..1970d9f62 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -48,6 +48,10 @@ class ConsoleOptionParser { * preceeded by one - and are only one character long. They usually match with a long option, * and provide a more terse alternative. * + * #### Using switches + * + * Switches can be defined with both long and short forms. + * * ### Providing Help text * * By providing help text for your positional arguments and named arguments, the ConsoleOptionParser @@ -93,7 +97,7 @@ class ConsoleOptionParser { * * ### Params * - * - `shortcut` - The single letter variant for this option, leave undefined for none. + * - `short` - The single letter variant for this option, leave undefined for none. * - `description` - Description for this option. Used when generating help for the option. * - `default` - The default value for this option. If not defined the default will be true. * @@ -104,14 +108,14 @@ class ConsoleOptionParser { public function addOption($name, $params = array()) { $defaults = array( 'name' => $name, - 'shortcut' => null, + 'short' => null, 'description' => '', 'default' => true ); $options = array_merge($defaults, $params); $this->_options[$name] = $options; - if (!empty($options['shortcut'])) { - $this->_options[$options['shortcut']] = $options; + if (!empty($options['short'])) { + $this->_options[$options['short']] = $options; } return $this; } @@ -153,6 +157,8 @@ class ConsoleOptionParser { /** * Parse the value for a short option out of $this->_tokens + * If the $option is a combination of multiple shortcuts like -otf + * they will be shifted onto the token stack and parsed individually. * * @param string $option The option to parse. * @param array $params The params to append the parsed value into @@ -160,6 +166,13 @@ class ConsoleOptionParser { */ protected function _parseShortOption($option, $params) { $key = substr($option, 1); + if (strlen($key) > 1) { + $flags = str_split($key); + $key = $flags[0]; + for ($i = 1, $len = count($flags); $i < $len; $i++) { + array_unshift($this->_tokens, '-' . $flags[$i]); + } + } $name = $this->_options[$key]['name']; return $this->_parseOptionName($name, $params); } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index a0c3455e9..cb36d08d5 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -67,7 +67,7 @@ class ConsoleOptionParserTest extends CakeTestCase { function testAddOptionLong() { $parser = new ConsoleOptionParser(); $parser->addOption('test', array( - 'shortcut' => 't' + 'short' => 't' )); $result = $parser->parse(array('--test', 'value')); $this->assertEquals(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); @@ -81,7 +81,7 @@ class ConsoleOptionParserTest extends CakeTestCase { function testAddOptionLongEquals() { $parser = new ConsoleOptionParser(); $parser->addOption('test', array( - 'shortcut' => 't' + 'short' => 't' )); $result = $parser->parse(array('--test=value')); $this->assertEquals(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); @@ -102,19 +102,38 @@ class ConsoleOptionParserTest extends CakeTestCase { } /** - * test adding an option and using the shortcut value for parsing. + * test adding an option and using the short value for parsing. * * @return void */ - function testAddOptionShortcut() { + function testAddOptionShort() { $parser = new ConsoleOptionParser(); $parser->addOption('test', array( - 'shortcut' => 't' + 'short' => 't' )); $result = $parser->parse(array('-t', 'value')); $this->assertEquals(array('test' => 'value'), $result[0], 'Short parameter did not parse out'); } +/** + * test adding an multiple shorts. + * + * @return void + */ + function testAddOptionMultipleShort() { + $parser = new ConsoleOptionParser(); + $parser->addOption('test', array('short' => 't')) + ->addOption('file', array('short' => 'f')) + ->addOption('output', array('short' => 'o')); + + $result = $parser->parse(array('-o', '-t', '-f')); + $expected = array('file' => true, 'test' => true, 'output' => true); + $this->assertEquals($expected, $result[0], 'Short parameter did not parse out'); + + $result = $parser->parse(array('-otf')); + $this->assertEquals($expected, $result[0], 'Short parameter did not parse out'); + } + /** * test multiple options at once. * @@ -124,7 +143,7 @@ class ConsoleOptionParserTest extends CakeTestCase { $parser = new ConsoleOptionParser(); $parser->addOption('test') ->addOption('connection') - ->addOption('table', array('shortcut' => 't')); + ->addOption('table', array('short' => 't')); $result = $parser->parse(array('--test', 'value', '-t', '--connection', 'postgres')); $expected = array('test' => 'value', 'table' => true, 'connection' => 'postgres'); From b392f300bada268581a603fac390a882b7535ca1 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 00:21:47 -0400 Subject: [PATCH 038/207] Adding docs about how options work. --- cake/console/console_option_parser.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 1970d9f62..e601a2c9b 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -37,20 +37,31 @@ class ConsoleOptionParser { protected $_args = array(); /** - * Construct an OptionParser for a given ARGV array. + * Construct an OptionParser so you can define its behavior * - * ### Positional arguments - * - * ### Switches + * ### Options * * Named arguments come in two forms, long and short. Long arguments are preceeded * by two - and give a more verbose option name. i.e. `--version`. Short arguments are * preceeded by one - and are only one character long. They usually match with a long option, * and provide a more terse alternative. * - * #### Using switches + * #### Using Options * - * Switches can be defined with both long and short forms. + * Options can be defined with both long and short forms. By using `$parser->addOption()` + * you can define new options. The name of the option is used as its long form, and you + * can supply an additional short form, with the `short` option. + * + * Calling options can be done using syntax similar to most *nix command line tools. Long options + * cane either include an `=` or leave it out. + * + * `cake myshell command --connection default --name=something` + * + * Short options can be defined singally or in groups. + * + * `cake myshell command -cn` + * + * ### Positional arguments * * ### Providing Help text * From 6f1dae208cf09bacf1d0d36a50081d05f6ed81a2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 00:24:07 -0400 Subject: [PATCH 039/207] Adding method skeleton --- cake/console/console_option_parser.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index e601a2c9b..ba11d031f 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -131,6 +131,15 @@ class ConsoleOptionParser { return $this; } +/** + * Add a positional argument to the option parser. + * + * @return void + */ + public function addArgument($name, $params = array()) { + + } + /** * Parse the argv array into a set of params and args. * @@ -151,7 +160,8 @@ class ConsoleOptionParser { } /** - * Parse the value for a long option out of $this->_tokens + * Parse the value for a long option out of $this->_tokens. Will handle + * options with an `=` in them. * * @param string $option The option to parse. * @param array $params The params to append the parsed value into From 3e402e2dfb4cc0c83cee70ac7c5d0b0ecad3bee0 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 00:57:09 -0400 Subject: [PATCH 040/207] Adding support for positional arguments. Adding tests and support for positional arguments. Renaming description key to help as its shorter. --- cake/console/console_option_parser.php | 66 +++++++++++++++++-- .../console/console_option_parser.test.php | 44 ++++++++++++- 2 files changed, 105 insertions(+), 5 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index ba11d031f..9ab83f4af 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -63,6 +63,12 @@ class ConsoleOptionParser { * * ### Positional arguments * + * If no positional arguments are defined, all of them will be parsed. If you define positional + * arguments any arguments greater than those defined will cause exceptions. Additionally you can + * declare arguments as optional, by setting the required param to false. + * + * `$parser->addArgument('model', array('required' => false));` + * * ### Providing Help text * * By providing help text for your positional arguments and named arguments, the ConsoleOptionParser @@ -109,7 +115,7 @@ class ConsoleOptionParser { * ### Params * * - `short` - The single letter variant for this option, leave undefined for none. - * - `description` - Description for this option. Used when generating help for the option. + * - `help` - Help text for this option. Used when generating help for the option. * - `default` - The default value for this option. If not defined the default will be true. * * @param string $name The long name you want to the value to be parsed out as when options are parsed. @@ -120,7 +126,7 @@ class ConsoleOptionParser { $defaults = array( 'name' => $name, 'short' => null, - 'description' => '', + 'help' => '', 'default' => true ); $options = array_merge($defaults, $params); @@ -134,10 +140,38 @@ class ConsoleOptionParser { /** * Add a positional argument to the option parser. * - * @return void + * ### Params + * + * - `help` The help text to display for this argument. + * - `required` Whether this parameter is required. + * - `index` The index for the arg, if left undefined the argument will be put + * onto the end of the arguments. If you define the same index twice the first + * option will be overwritten. + * + * @param string $name The name of the argument. + * @param array $params Parameters for the argument, see above. + * @return $this. */ public function addArgument($name, $params = array()) { - + $index = count($this->_args); + $defaults = array( + 'name' => $name, + 'help' => '', + 'index' => $index, + 'required' => false + ); + $options = array_merge($defaults, $params); + $this->_args[$options['index']] = $options; + return $this; + } + +/** + * Gets the arguments defined in the parser. + * + * @return array Array of argument descriptions + */ + public function arguments() { + return $this->_args; } /** @@ -145,6 +179,7 @@ class ConsoleOptionParser { * * @param array $argv Array of args (argv) to parse * @return Array array($params, $args) + * @throws InvalidArgumentException When an invalid parameter is encountered. */ public function parse($argv) { $params = $args = array(); @@ -154,6 +189,8 @@ class ConsoleOptionParser { $params = $this->_parseLongOption($token, $params); } elseif (substr($token, 0, 1) == '-') { $params = $this->_parseShortOption($token, $params); + } else { + $args = $this->_parseArg($token, $args); } } return array($params, $args); @@ -217,6 +254,27 @@ class ConsoleOptionParser { return $params; } +/** + * Checks that the argument doesn't exceed the declared arguments. + * + * @param string $argument The argument to append + * @param array $args The array of parsed args to append to. + * @return array Args + */ + protected function _parseArg($argument, $args) { + if (empty($this->_args)) { + array_push($args, $argument); + return $args; + } + $position = 0; + $next = count($args); + if (!isset($this->_args[$next])) { + throw new InvalidArgumentException(__('Too many arguments.')); + } + array_push($args, $argument); + return $args; + } + /** * Find the next token in the argv set. * diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index cb36d08d5..0f8a977d4 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -129,7 +129,7 @@ class ConsoleOptionParserTest extends CakeTestCase { $result = $parser->parse(array('-o', '-t', '-f')); $expected = array('file' => true, 'test' => true, 'output' => true); $this->assertEquals($expected, $result[0], 'Short parameter did not parse out'); - + $result = $parser->parse(array('-otf')); $this->assertEquals($expected, $result[0], 'Short parameter did not parse out'); } @@ -149,4 +149,46 @@ class ConsoleOptionParserTest extends CakeTestCase { $expected = array('test' => 'value', 'table' => true, 'connection' => 'postgres'); $this->assertEquals($expected, $result[0], 'multiple options did not parse'); } + +/** + * test positional argument parsing. + * + * @return void + */ + function testPositionalArgument() { + $parser = new ConsoleOptionParser(); + $result = $parser->addArgument('name', array('help' => 'An argument')); + $this->assertEquals($parser, $result, 'Should returnn this'); + } + +/** + * test overwriting positional arguments. + * + * @return void + */ + function testPositionalArgOverwrite() { + $parser = new ConsoleOptionParser(); + $parser->addArgument('name', array('help' => 'An argument')) + ->addArgument('other', array('index' => 0)); + + $result = $parser->arguments(); + $this->assertEquals(1, count($result), 'Overwrite did not occur'); + } + +/** + * test parsing arguments. + * + * @expectedException InvalidArgumentException + * @return void + */ + function testParseArgument() { + $parser = new ConsoleOptionParser(); + $parser->addArgument('name', array('help' => 'An argument')) + ->addArgument('other'); + + $expected = array('one', 'two'); + $result = $parser->parse($expected); + $this->assertEquals($expected, $result[1], 'Arguments are not as expected'); + $result = $parser->parse(array('one', 'two', 'three')); + } } \ No newline at end of file From 505e59ac66d1e2b8a825954b4d876c7763897fdf Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 10:33:54 -0400 Subject: [PATCH 041/207] Adding support for boolean options. --- cake/console/console_option_parser.php | 7 +++++-- .../console/console_option_parser.test.php | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 9ab83f4af..ffb6c23ed 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -117,6 +117,7 @@ class ConsoleOptionParser { * - `short` - The single letter variant for this option, leave undefined for none. * - `help` - Help text for this option. Used when generating help for the option. * - `default` - The default value for this option. If not defined the default will be true. + * - `boolean` - The option uses no value, its just a boolean switch. Defaults to false. * * @param string $name The long name you want to the value to be parsed out as when options are parsed. * @param array $params An array of parameters that define the behavior of the option @@ -127,7 +128,8 @@ class ConsoleOptionParser { 'name' => $name, 'short' => null, 'help' => '', - 'default' => true + 'default' => true, + 'boolean' => false ); $options = array_merge($defaults, $params); $this->_options[$name] = $options; @@ -245,7 +247,8 @@ class ConsoleOptionParser { protected function _parseOptionName($name, $params) { $definition = $this->_options[$name]; $nextValue = $this->_nextToken(); - if (!empty($nextValue) && $nextValue{0} != '-') { + if (!$definition['boolean'] && !empty($nextValue) && $nextValue{0} != '-') { + array_shift($this->_tokens); $value = $nextValue; } else { $value = $definition['default']; diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 0f8a977d4..63b22d5ad 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -150,6 +150,22 @@ class ConsoleOptionParserTest extends CakeTestCase { $this->assertEquals($expected, $result[0], 'multiple options did not parse'); } +/** + * test that boolean options work + * + * @return void + */ + function testOptionWithBooleanParam() { + $parser = new ConsoleOptionParser(); + $parser->addOption('no-commit', array('boolean' => true)) + ->addOption('table', array('short' => 't')); + + $result = $parser->parse(array('--table', 'posts', '--no-commit', 'arg1', 'arg2')); + $expected = array(array('table' => 'posts', 'no-commit' => true), array('arg1', 'arg2')); + $this->assertEquals($expected, $result, 'Boolean option did not parse correctly.'); + + } + /** * test positional argument parsing. * @@ -189,6 +205,7 @@ class ConsoleOptionParserTest extends CakeTestCase { $expected = array('one', 'two'); $result = $parser->parse($expected); $this->assertEquals($expected, $result[1], 'Arguments are not as expected'); + $result = $parser->parse(array('one', 'two', 'three')); } } \ No newline at end of file From f5ad54e97e564ba97383e57d8018ad863ab322d5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 10:41:11 -0400 Subject: [PATCH 042/207] Adding required argument checking. --- cake/console/console_option_parser.php | 37 +++++++++++++++++-- .../console/console_option_parser.test.php | 16 +++++++- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index ffb6c23ed..5c4bd1904 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -28,12 +28,36 @@ */ class ConsoleOptionParser { +/** + * Description text - displays before options when help is generated + * + * @see ConsoleOptionParser::description() + * @var string + */ protected $_description = null; - + +/** + * Epilog text - displays after options when help is generated + * + * @see ConsoleOptionParser::epilog() + * @var string + */ protected $_epilog = null; - + +/** + * Option definitions. + * + * @see ConsoleOptionParser::addOption() + * @var array + */ protected $_options = array(); - + +/** + * Positional argument definitions. + * + * @see ConsoleOptionParser::addArgument() + * @var array + */ protected $_args = array(); /** @@ -195,6 +219,13 @@ class ConsoleOptionParser { $args = $this->_parseArg($token, $args); } } + foreach ($this->_args as $i => $arg) { + if ($arg['required'] && !isset($args[$i])) { + throw new RuntimeException( + sprintf(__('Missing required arguments. %s is required.'), $arg['name']) + ); + } + } return array($params, $args); } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 63b22d5ad..2b7863483 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -197,7 +197,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @expectedException InvalidArgumentException * @return void */ - function testParseArgument() { + function testParseArgumentTooMany() { $parser = new ConsoleOptionParser(); $parser->addArgument('name', array('help' => 'An argument')) ->addArgument('other'); @@ -208,4 +208,18 @@ class ConsoleOptionParserTest extends CakeTestCase { $result = $parser->parse(array('one', 'two', 'three')); } + +/** + * test that when there are not enough arguments an exception is raised + * + * @expectedException RuntimeException + * @return void + */ + function testPositionalArgNotEnough() { + $parser = new ConsoleOptionParser(); + $parser->addArgument('name', array('required' => true)) + ->addArgument('other', array('required' => true)); + + $parser->parse(array('one')); + } } \ No newline at end of file From a3259743f73e5da2b824d6f07d049f7d660cc17f Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 11:01:22 -0400 Subject: [PATCH 043/207] Making error output style have an underline. Adding a comment style which has blue text. --- cake/console/console_output.php | 12 +++++++----- cake/tests/cases/console/console_output.test.php | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cake/console/console_output.php b/cake/console/console_output.php index 59dad2da9..2746c8285 100644 --- a/cake/console/console_output.php +++ b/cake/console/console_output.php @@ -24,9 +24,10 @@ * Can generate colourzied output on consoles that support it. There are a few * built in styles * - * - `error` Error messages. Bright red text. - * - `warning` Warning messages. Bright orange text - * - `info` Informational messages. Cyan text. + * - `error` Error messages. + * - `warning` Warning messages. + * - `info` Informational messages. + * - `comment` Additional text. * * By defining styles with addStyle() you can create custom console styles. * @@ -113,10 +114,11 @@ class ConsoleOutput { * @var array */ protected static $_styles = array( - 'error' => array('text' => 'red'), + 'error' => array('text' => 'red', 'underline' => true), 'warning' => array('text' => 'yellow'), 'info' => array('text' => 'cyan'), - 'success' => array('text' => 'green') + 'success' => array('text' => 'green'), + 'comment' => array('text' => 'blue') ); /** diff --git a/cake/tests/cases/console/console_output.test.php b/cake/tests/cases/console/console_output.test.php index 00cf0822e..166830f2b 100644 --- a/cake/tests/cases/console/console_output.test.php +++ b/cake/tests/cases/console/console_output.test.php @@ -96,7 +96,7 @@ class ConsoleOutputTest extends CakeTestCase { */ function testStylesGet() { $result = $this->output->styles('error'); - $expected = array('text' => 'red'); + $expected = array('text' => 'red', 'underline' => true); $this->assertEqual($result, $expected); $this->assertNull($this->output->styles('made_up_goop')); @@ -128,7 +128,7 @@ class ConsoleOutputTest extends CakeTestCase { */ function testFormattingSimple() { $this->output->expects($this->once())->method('_write') - ->with("\033[31mError:\033[0m Something bad"); + ->with("\033[31;4mError:\033[0m Something bad"); $this->output->write('Error: Something bad', false); } @@ -171,7 +171,7 @@ class ConsoleOutputTest extends CakeTestCase { */ function testFormattingMultipleStylesName() { $this->output->expects($this->once())->method('_write') - ->with("\033[31mBad\033[0m \033[33mWarning\033[0m Regular"); + ->with("\033[31;4mBad\033[0m \033[33mWarning\033[0m Regular"); $this->output->write('Bad Warning Regular', false); } From cea6cef727b8a280f95ae6172a3bccf84599d107 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 12:42:34 -0400 Subject: [PATCH 044/207] Adding smart defaults for verbose, quiet, and help options. Adding help formatting for options. --- cake/console/console_option_parser.php | 94 ++++++++++++++++++- .../console/console_option_parser.test.php | 24 +++++ 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 5c4bd1904..1bf516c31 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -52,6 +52,13 @@ class ConsoleOptionParser { */ protected $_options = array(); +/** + * Map of short -> long options, generated when using addOption() + * + * @var string + */ + protected $_shortOptions = array(); + /** * Positional argument definitions. * @@ -99,8 +106,24 @@ class ConsoleOptionParser { * can generate a help display for you. You can view the help for shells by using the `--help` or `-h` switch. * */ - public function __construct() { + public function __construct($command = null, $defaultOptions = true) { + $this->_command = $command; + $this->addOption('help', array( + 'short' => 'h', + 'help' => 'Display this help.', + 'boolean' => true + )); + + if ($defaultOptions) { + $this->addOption('verbose', array( + 'short' => 'v', + 'help' => __('Enable verbose output.') + ))->addOption('quiet', array( + 'short' => 'q', + 'help' => __('Enable quiet output.') + )); + } } /** @@ -158,7 +181,7 @@ class ConsoleOptionParser { $options = array_merge($defaults, $params); $this->_options[$name] = $options; if (!empty($options['short'])) { - $this->_options[$options['short']] = $options; + $this->_shortOptions[$options['short']] = $name; } return $this; } @@ -200,6 +223,15 @@ class ConsoleOptionParser { return $this->_args; } +/** + * Get the defined options in the parser. + * + * @return array + */ + public function options() { + return $this->_options; + } + /** * Parse the argv array into a set of params and args. * @@ -229,6 +261,62 @@ class ConsoleOptionParser { return array($params, $args); } +/** + * Gets formatted help for this parser object. + * Generates help text based on the description, options, arguments and epilog + * in the parser. + * + * @return string + */ + public function help() { + $out = array(); + $out[] = 'Usage:'; + $out[] = 'cake ' . $this->_command . $this->_generateUsage(); + $out[] = ''; + if (!empty($this->_options)) { + $max = 0; + foreach ($this->_options as $description) { + $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; + } + $max += 3; + $out[] = 'Options:'; + $out[] = ''; + foreach ($this->_options as $description) { + $out[] = $this->_optionHelp($description, $max); + } + } + return implode("\n", $out); + } + +/** + * Generate the usage for a shell based on its arguments and options. + * + * @return void + */ + protected function _generateUsage() { + + } + +/** + * Generate the usage for a single option. + * + * @return string + */ + protected function _optionHelp($definition, $nameWidth) { + $default = $short = ''; + if (!empty($definition['default']) && $definition['default'] !== true) { + $default = sprintf(__(' (default: %s)'), $definition['default']); + } + if (!empty($definition['short'])) { + $short = ', -' . $definition['short']; + } + $name = sprintf('--%s%s', $definition['name'], $short); + if (strlen($name) < $nameWidth) { + $name = str_pad($name, $nameWidth, ' '); + } + return sprintf('%s %s%s', $name, $definition['help'], $default); + } + /** * Parse the value for a long option out of $this->_tokens. Will handle * options with an `=` in them. @@ -264,7 +352,7 @@ class ConsoleOptionParser { array_unshift($this->_tokens, '-' . $flags[$i]); } } - $name = $this->_options[$key]['name']; + $name = $this->_shortOptions[$key]; return $this->_parseOptionName($name, $params); } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 2b7863483..3b99fa710 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -222,4 +222,28 @@ class ConsoleOptionParserTest extends CakeTestCase { $parser->parse(array('one')); } + +/** + * test getting help with defined options. + * + * @return void + */ + function testGetHelpWithOptions() { + $parser = new ConsoleOptionParser('mycommand', false); + $parser->addOption('test', array('short' => 't', 'help' => 'A test option.')) + ->addOption('connection', array('help' => 'The connection to use.', 'default' => 'default')); + + $result = $parser->help(); + $expected = <<Usage: +cake mycommand [-h] [-t] [--connection default] + +Options: + +--help, -h Display this help. +--test, -t A test option. +--connection The connection to use. (default: default) +TEXT; + $this->assertEquals($expected, $result, 'Help does not match'); + } } \ No newline at end of file From 733394d5848139bab7c1ba79da19f3c5afde8637 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 12:48:36 -0400 Subject: [PATCH 045/207] Adding usage generation for options. --- cake/console/console_option_parser.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 1bf516c31..3139bc670 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -271,7 +271,7 @@ class ConsoleOptionParser { public function help() { $out = array(); $out[] = 'Usage:'; - $out[] = 'cake ' . $this->_command . $this->_generateUsage(); + $out[] = $this->_generateUsage(); $out[] = ''; if (!empty($this->_options)) { $max = 0; @@ -290,11 +290,22 @@ class ConsoleOptionParser { /** * Generate the usage for a shell based on its arguments and options. + * Usage strings favour short options over the long ones. and optional args will + * be indicated with [] * - * @return void + * @return string */ protected function _generateUsage() { - + $usage = array('cake ' . $this->_command); + foreach ($this->_options as $definition) { + $name = empty($definition['short']) ? '--' . $definition['name'] : '-' . $definition['short']; + $default = ''; + if (!empty($definition['default']) && $definition['default'] !== true) { + $default = ' ' . $definition['default']; + } + $usage[] = sprintf('[%s%s]', $name, $default); + } + return implode(' ', $usage); } /** From 0eb68226ce7410a9cc3cc2dfe12141a0fa12ccf6 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 12:49:59 -0400 Subject: [PATCH 046/207] Adding ConsoleOptionParser to the AllShells test. --- cake/tests/cases/console/all_shells.test.php | 1 + 1 file changed, 1 insertion(+) diff --git a/cake/tests/cases/console/all_shells.test.php b/cake/tests/cases/console/all_shells.test.php index 150309458..f830839bb 100644 --- a/cake/tests/cases/console/all_shells.test.php +++ b/cake/tests/cases/console/all_shells.test.php @@ -41,6 +41,7 @@ class AllShellsTest extends PHPUnit_Framework_TestSuite { $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'shell_dispatcher.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'console_output.test.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'console_error_handler.test.php'); + $suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'console_option_parser.test.php'); $suite->addTestDirectory($path); return $suite; } From 09adc38b6c713384482be1c2d59726e35e65d32d Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 12:53:38 -0400 Subject: [PATCH 047/207] Doing some fixes as the longest option + a short would cause incorrect formatting. --- cake/console/console_option_parser.php | 2 +- .../cases/console/console_option_parser.test.php | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 3139bc670..71bc3d913 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -278,7 +278,7 @@ class ConsoleOptionParser { foreach ($this->_options as $description) { $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; } - $max += 3; + $max += 6; $out[] = 'Options:'; $out[] = ''; foreach ($this->_options as $description) { diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 3b99fa710..b7e96c516 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -230,19 +230,21 @@ class ConsoleOptionParserTest extends CakeTestCase { */ function testGetHelpWithOptions() { $parser = new ConsoleOptionParser('mycommand', false); - $parser->addOption('test', array('short' => 't', 'help' => 'A test option.')) - ->addOption('connection', array('help' => 'The connection to use.', 'default' => 'default')); + $parser->addOption('test', array('help' => 'A test option.')) + ->addOption('connection', array( + 'short' => 'c', 'help' => 'The connection to use.', 'default' => 'default' + )); $result = $parser->help(); $expected = <<Usage: -cake mycommand [-h] [-t] [--connection default] +cake mycommand [-h] [--test] [-c default] Options: ---help, -h Display this help. ---test, -t A test option. ---connection The connection to use. (default: default) +--help, -h Display this help. +--test A test option. +--connection, -c The connection to use. (default: default) TEXT; $this->assertEquals($expected, $result, 'Help does not match'); } From 501e63e45d857052e9c8d72f646c972c2d96a0b9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 13:08:03 -0400 Subject: [PATCH 048/207] Adding argument help generation in the long help and usage string. Fixing output of option help, formatting was calculated in 2 places. --- cake/console/console_option_parser.php | 42 ++++++++++++++++++- .../console/console_option_parser.test.php | 37 ++++++++++++++-- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 71bc3d913..b3a25e997 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -278,12 +278,26 @@ class ConsoleOptionParser { foreach ($this->_options as $description) { $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; } - $max += 6; + $max += 8; $out[] = 'Options:'; $out[] = ''; foreach ($this->_options as $description) { $out[] = $this->_optionHelp($description, $max); } + $out[] = ''; + } + if (!empty($this->_args)) { + $max = 0; + foreach ($this->_args as $description) { + $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; + } + $max += 1; + $out[] = 'Arguments:'; + $out[] = ''; + foreach ($this->_args as $description) { + $out[] = $this->_argumentHelp($description, $max); + } + $out[] = ''; } return implode("\n", $out); } @@ -305,6 +319,13 @@ class ConsoleOptionParser { } $usage[] = sprintf('[%s%s]', $name, $default); } + foreach ($this->_args as $definition) { + $name = $definition['name']; + if (!$definition['required']) { + $name = '[' . $name . ']'; + } + $usage[] = $name; + } return implode(' ', $usage); } @@ -325,7 +346,24 @@ class ConsoleOptionParser { if (strlen($name) < $nameWidth) { $name = str_pad($name, $nameWidth, ' '); } - return sprintf('%s %s%s', $name, $definition['help'], $default); + return sprintf('%s%s%s', $name, $definition['help'], $default); + } + +/** + * Generate the usage for a single argument. + * + * @return string + */ + protected function _argumentHelp($definition, $width) { + $name = $definition['name']; + if (strlen($name) < $width) { + $name = str_pad($name, $width, ' '); + } + $optional = ''; + if (!$definition['required']) { + $optional = ' (optional)'; + } + return sprintf('%s %s%s', $name, $definition['help'], $optional); } /** diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index b7e96c516..d527ec79f 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -242,9 +242,40 @@ cake mycommand [-h] [--test] [-c default] Options: ---help, -h Display this help. ---test A test option. ---connection, -c The connection to use. (default: default) +--help, -h Display this help. +--test A test option. +--connection, -c The connection to use. (default: default) + +TEXT; + $this->assertEquals($expected, $result, 'Help does not match'); + } + +/** + * test getting help with defined options. + * + * @return void + */ + function testGetHelpWithOptionsAndArguments() { + $parser = new ConsoleOptionParser('mycommand', false); + $parser->addOption('test', array('help' => 'A test option.')) + ->addArgument('model', array('help' => 'The model to make.', 'required' => true)) + ->addArgument('other_longer', array('help' => 'Another argument.')); + + $result = $parser->help(); + $expected = <<Usage: +cake mycommand [-h] [--test] model [other_longer] + +Options: + +--help, -h Display this help. +--test A test option. + +Arguments: + +model The model to make. +other_longer Another argument. (optional) + TEXT; $this->assertEquals($expected, $result, 'Help does not match'); } From d3c95bc2c4b51cb68ef536ea742c78ca54717376 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 20:00:22 -0400 Subject: [PATCH 049/207] Adding epilog and description to the generate help. --- cake/console/console_option_parser.php | 7 ++++ .../console/console_option_parser.test.php | 37 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index b3a25e997..25d011b44 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -270,6 +270,10 @@ class ConsoleOptionParser { */ public function help() { $out = array(); + if (!empty($this->_description)) { + $out[] = $this->_description; + $out[] = ''; + } $out[] = 'Usage:'; $out[] = $this->_generateUsage(); $out[] = ''; @@ -299,6 +303,9 @@ class ConsoleOptionParser { } $out[] = ''; } + if (!empty($this->_epilog)) { + $out[] = $this->_epilog; + } return implode("\n", $out); } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index d527ec79f..2dcfd0af0 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -228,7 +228,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * * @return void */ - function testGetHelpWithOptions() { + function testHelpWithOptions() { $parser = new ConsoleOptionParser('mycommand', false); $parser->addOption('test', array('help' => 'A test option.')) ->addOption('connection', array( @@ -255,7 +255,7 @@ TEXT; * * @return void */ - function testGetHelpWithOptionsAndArguments() { + function testHelpWithOptionsAndArguments() { $parser = new ConsoleOptionParser('mycommand', false); $parser->addOption('test', array('help' => 'A test option.')) ->addArgument('model', array('help' => 'The model to make.', 'required' => true)) @@ -279,4 +279,37 @@ other_longer Another argument. (optional) TEXT; $this->assertEquals($expected, $result, 'Help does not match'); } + +/** + * test description and epilog in the help + * + * @return void + */ + function testDescriptionAndEpilog() { + $parser = new ConsoleOptionParser('mycommand', false); + $parser->description('Description text') + ->epilog('epilog text') + ->addOption('test', array('help' => 'A test option.')) + ->addArgument('model', array('help' => 'The model to make.', 'required' => true)); + + $result = $parser->help(); + $expected = <<Usage: +cake mycommand [-h] [--test] model + +Options: + +--help, -h Display this help. +--test A test option. + +Arguments: + +model The model to make. + +epilog text +TEXT; + $this->assertEquals($expected, $result, 'Help is wrong.'); + } } \ No newline at end of file From 7f5b5c4fbd83837a1f3bc89673c1fffbd0f80567 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 20:13:27 -0400 Subject: [PATCH 050/207] Adding exception for unknown option usage. --- cake/console/console_option_parser.php | 3 +++ .../cases/console/console_option_parser.test.php | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 25d011b44..ef8cafdfa 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -420,6 +420,9 @@ class ConsoleOptionParser { * @return array Params with $option added in. */ protected function _parseOptionName($name, $params) { + if (!isset($this->_options[$name])) { + throw new InvalidArgumentException(sprintf(__('Unknown option `%s`'), $name)); + } $definition = $this->_options[$name]; $nextValue = $this->_nextToken(); if (!$definition['boolean'] && !empty($nextValue) && $nextValue{0} != '-') { diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 2dcfd0af0..4f1271a15 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -159,11 +159,22 @@ class ConsoleOptionParserTest extends CakeTestCase { $parser = new ConsoleOptionParser(); $parser->addOption('no-commit', array('boolean' => true)) ->addOption('table', array('short' => 't')); - + $result = $parser->parse(array('--table', 'posts', '--no-commit', 'arg1', 'arg2')); $expected = array(array('table' => 'posts', 'no-commit' => true), array('arg1', 'arg2')); $this->assertEquals($expected, $result, 'Boolean option did not parse correctly.'); - + } + +/** + * test parsing options that do not exist. + * + * @expectedException InvalidArgumentException + */ + function testOptionThatDoesNotExist() { + $parser = new ConsoleOptionParser(); + $parser->addOption('no-commit', array('boolean' => true)); + + $result = $parser->parse(array('--fail', 'other')); } /** From 0d522f3bd4a4b9c6fcd67f80017d108db2486135 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 20:33:31 -0400 Subject: [PATCH 051/207] Changing ShellDispatcher to use --help and -h like the option parser does. --- cake/console/console_option_parser.php | 1 + cake/console/libs/command_list.php | 2 +- cake/console/shell_dispatcher.php | 52 +++----------------------- 3 files changed, 8 insertions(+), 47 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index ef8cafdfa..95fa79409 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -238,6 +238,7 @@ class ConsoleOptionParser { * @param array $argv Array of args (argv) to parse * @return Array array($params, $args) * @throws InvalidArgumentException When an invalid parameter is encountered. + * RuntimeException when required arguments are not supplied. */ public function parse($argv) { $params = $args = array(); diff --git a/cake/console/libs/command_list.php b/cake/console/libs/command_list.php index d48a9ae4f..79faceb5b 100644 --- a/cake/console/libs/command_list.php +++ b/cake/console/libs/command_list.php @@ -96,6 +96,6 @@ class CommandListShell extends Shell { } $this->out(); $this->out("To run a command, type 'cake shell_name [args]'"); - $this->out("To get help on a specific command, type 'cake shell_name help'", 2); + $this->out("To get help on a specific command, type 'cake shell_name --help'", 2); } } diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index d01a72acf..04e9904a3 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -17,6 +17,7 @@ * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +require_once 'console_option_parser.php'; /** * Shell dispatcher handles dispatching cli commands. @@ -26,14 +27,6 @@ */ class ShellDispatcher { -/** - * Standard input stream. - * - * @var filehandle - * @access public - */ - public $stdin; - /** * Contains command switches parsed from the command line. * @@ -252,18 +245,18 @@ class ShellDispatcher { * @return boolean */ public function dispatch() { - $arg = $this->shiftArgs(); + $command = $this->shiftArgs(); - if (!$arg) { + if (!$command) { $this->help(); return false; } - if ($arg == 'help') { + if (in_array($command, array('help', '--help', '-h'))) { $this->help(); return true; } - - list($plugin, $shell) = pluginSplit($arg); + + list($plugin, $shell) = pluginSplit($command); $this->shell = $shell; $this->shellName = Inflector::camelize($shell); $this->shellClass = $this->shellName . 'Shell'; @@ -363,39 +356,6 @@ class ShellDispatcher { return $Shell; } -/** - * Prompts the user for input, and returns it. - * - * @param string $prompt Prompt text. - * @param mixed $options Array or string of options. - * @param string $default Default input value. - * @return Either the default value, or the user-provided input. - */ - public function getInput($prompt, $options = null, $default = null) { - if (!is_array($options)) { - $printOptions = ''; - } else { - $printOptions = '(' . implode('/', $options) . ')'; - } - - if ($default === null) { - $this->stdout($prompt . " $printOptions \n" . '> ', false); - } else { - $this->stdout($prompt . " $printOptions \n" . "[$default] > ", false); - } - $result = fgets($this->stdin); - - if ($result === false) { - exit; - } - $result = trim($result); - - if ($default != null && empty($result)) { - return $default; - } - return $result; - } - /** * Parses command line options * From 4d199cf8a8212182c3b3cfcabc0c79345b905f80 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 20:53:10 -0400 Subject: [PATCH 052/207] Removing Shell::_loadDbConfig() and Shell::$DbConfig They did not make sense in Shell as no other core shells even use these properties/methods. --- cake/console/libs/shell.php | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index d0fd9022d..720cfcc7e 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -45,15 +45,6 @@ class Shell extends Object { */ public $interactive = true; -/** - * Holds the DATABASE_CONFIG object for the app. Null if database.php could not be found, - * or the app does not exist. - * - * @var DATABASE_CONFIG - * @access public - */ - public $DbConfig = null; - /** * Contains command switches parsed from the command line. * @@ -236,22 +227,6 @@ class Shell extends Object { $this->hr(); } -/** - * Loads database file and constructs DATABASE_CONFIG class - * makes $this->DbConfig available to subclasses - * - * @return bool - */ - protected function _loadDbConfig() { - if (config('database') && class_exists('DATABASE_CONFIG')) { - $this->DbConfig =& new DATABASE_CONFIG(); - return true; - } - $this->err('Database config could not be loaded.'); - $this->out('Run `bake` to create the database configuration.'); - return false; - } - /** * if public $uses = true * Loads AppModel file and constructs AppModel class From fbcc9c1fc1f88ddf46e30488d6f66ac6310139f2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 20:55:00 -0400 Subject: [PATCH 053/207] Making Tasks lazy load. This allows task initialization to be moved out of ShellDispatcher where it does not belong, as tasks are similar to components. Updating parts of TaskCollection, as the Dispatcher is still required to be passed around. --- cake/console/libs/shell.php | 36 ++++++++++++++++--- cake/console/libs/task_collection.php | 6 ++-- cake/console/shell_dispatcher.php | 13 ++----- .../console/libs/task_collection.test.php | 7 ++-- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 720cfcc7e..a15da2e50 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -61,6 +61,13 @@ class Shell extends Object { */ public $args = array(); +/** + * Shell paths + * + * @var string + */ + public $shellPaths = array(); + /** * The file name of the shell that was invoked. * @@ -132,6 +139,13 @@ class Shell extends Object { */ public $Tasks; +/** + * Normalized map of tasks. + * + * @var string + */ + protected $_taskMap = array(); + /** * stdout object. * @@ -177,7 +191,7 @@ class Shell extends Object { } $this->Dispatch =& $dispatch; - $this->Tasks = new TaskCollection($this); + $this->Tasks = new TaskCollection($this, $dispatch); $this->stdout = $stdout; $this->stderr = $stderr; @@ -272,14 +286,28 @@ class Shell extends Object { if ($this->tasks === true || empty($this->tasks) || empty($this->Tasks)) { return true; } - $tasks = TaskCollection::normalizeObjectArray((array)$this->tasks); - foreach ($tasks as $task => $properties) { - $this->{$task} = $this->Tasks->load($properties['class'], $properties['settings']); + $this->_taskMap = TaskCollection::normalizeObjectArray((array)$this->tasks); + foreach ($this->_taskMap as $task => $properties) { $this->taskNames[] = $task; } return true; } +/** + * Overload get for lazy building of tasks + * + * @return void + */ + public function __get($name) { + if (empty($this->{$name}) && in_array($name, $this->taskNames)) { + $properties = $this->_taskMap[$name]; + $this->{$name} = $this->Tasks->load($properties['class'], $properties['settings']); + $this->{$name}->initialize(); + $this->{$name}->loadTasks(); + } + return $this->{$name}; + } + /** * Prompts the user for input, and returns it. * diff --git a/cake/console/libs/task_collection.php b/cake/console/libs/task_collection.php index a3b162166..eecd3f578 100644 --- a/cake/console/libs/task_collection.php +++ b/cake/console/libs/task_collection.php @@ -25,6 +25,7 @@ class TaskCollection extends ObjectCollection { * @var array */ protected $_Shell; + protected $_Dispatch; /** * Constructor @@ -32,8 +33,9 @@ class TaskCollection extends ObjectCollection { * @param array $paths Array of paths to search for tasks on . * @return void */ - public function __construct(Shell $Shell) { + public function __construct(Shell $Shell, ShellDispatcher $dispatcher) { $this->_Shell = $Shell; + $this->_Dispatch = $dispatcher; } /** * Loads/constructs a task. Will return the instance in the registry if it already exists. @@ -61,7 +63,7 @@ class TaskCollection extends ObjectCollection { } $this->_loaded[$name] = new $taskClass( - $this->_Shell, $this->_Shell->stdout, $this->_Shell->stderr, $this->_Shell->stdin + $this->_Dispatch, $this->_Shell->stdout, $this->_Shell->stderr, $this->_Shell->stdin ); if ($enable === true) { $this->_enabled[] = $name; diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index 04e9904a3..267cf4cb4 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -184,9 +184,7 @@ class ShellDispatcher { */ function __buildPaths() { $paths = array(); - if (!class_exists('Folder')) { - require LIBS . 'folder.php'; - } + $plugins = App::objects('plugin', null, false); foreach ((array)$plugins as $plugin) { $pluginPath = App::pluginPath($plugin); @@ -272,17 +270,10 @@ class ShellDispatcher { $methods = array(); - if ($Shell instanceof Shell) { + if ($Shell instanceof Shell) { $Shell->initialize(); $Shell->loadTasks(); - foreach ($Shell->taskNames as $task) { - if (is_a($Shell->{$task}, 'Shell')) { - $Shell->{$task}->initialize(); - $Shell->{$task}->loadTasks(); - } - } - $task = Inflector::camelize($arg); if (in_array($task, $Shell->taskNames)) { diff --git a/cake/tests/cases/console/libs/task_collection.test.php b/cake/tests/cases/console/libs/task_collection.test.php index 94935106b..b277c6c60 100644 --- a/cake/tests/cases/console/libs/task_collection.test.php +++ b/cake/tests/cases/console/libs/task_collection.test.php @@ -30,7 +30,8 @@ class TaskCollectionTest extends CakeTestCase { function setup() { $shell = $this->getMock('Shell', array(), array(), '', false); $shell->shellPaths = App::path('shells'); - $this->Tasks = new TaskCollection($shell); + $dispatcher = $this->getMock('ShellDispatcher', array(), array(), '', false); + $this->Tasks = new TaskCollection($shell, $dispatcher); } /** @@ -86,10 +87,12 @@ class TaskCollectionTest extends CakeTestCase { * @return void */ function testLoadPluginTask() { + $dispatcher = $this->getMock('ShellDispatcher', array(), array(), '', false); $shell = $this->getMock('Shell', array(), array(), '', false); $shell->shellPaths = App::path('shells'); $shell->shellPaths[] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'vendors' . DS . 'shells' . DS; - $this->Tasks = new TaskCollection($shell); + $dispatcher->shellPaths = $shell->shellPaths; + $this->Tasks = new TaskCollection($shell, $dispatcher); $result = $this->Tasks->load('TestPlugin.OtherTask'); $this->assertType('OtherTaskTask', $result, 'Task class is wrong.'); From 830238c2dd2df288b5bb6e45fde81375b3d6d30c Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 21:34:51 -0400 Subject: [PATCH 054/207] Removing $this->AppModel from shells. Having a partially constructed model in the shell is not useful, and gives the impression that it will work correctly. Which it doesn't. --- cake/console/libs/shell.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index a15da2e50..02aef7a1b 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -254,11 +254,6 @@ class Shell extends Object { return; } - if ($this->uses === true && App::import('Model', 'AppModel')) { - $this->AppModel =& new AppModel(false, false, false); - return true; - } - if ($this->uses !== true && !empty($this->uses)) { $uses = is_array($this->uses) ? $this->uses : array($this->uses); From 18c5a624457069aa2335314c0729e7ae27312de9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 21:40:16 -0400 Subject: [PATCH 055/207] Removing a dead test and adding tests for hasTask --- cake/console/libs/shell.php | 20 ++++++++++++++++++ cake/tests/cases/console/libs/shell.test.php | 22 +++++++++++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 02aef7a1b..90cfc6711 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -288,6 +288,26 @@ class Shell extends Object { return true; } +/** + * Check to see if this shell has a task with the provided name. + * + * @param string $task The task name to check. + * @return boolean Success + */ + public function hasTask($task) { + return isset($this->_taskMap[Inflector::camelize($task)]); + } + +/** + * Check to see if this shell has a callable method by the given name. + * + * @param string $name The method name to check. + * @return boolean + */ + public function hasMethod($name) { + + } + /** * Overload get for lazy building of tasks * diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index fed9894bd..fc449b34c 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -161,11 +161,6 @@ class ShellTest extends CakeTestCase { $this->assertIsA($this->Shell->Comment, 'Comment'); $this->assertEqual($this->Shell->modelClass, 'Comment'); - $this->Shell->uses = true; - $this->Shell->initialize(); - $this->assertTrue(isset($this->Shell->AppModel)); - $this->assertIsA($this->Shell->AppModel, 'AppModel'); - App::build(); } @@ -566,4 +561,21 @@ class ShellTest extends CakeTestCase { $Folder->delete(); } + +/** + * test hasTask method + * + * @return void + */ + function testHasTask() { + $this->Shell->tasks = array('Extract', 'DbConfig'); + $this->Shell->loadTasks(); + + $this->assertTrue($this->Shell->hasTask('extract')); + $this->assertTrue($this->Shell->hasTask('Extract')); + $this->assertFalse($this->Shell->hasTask('random')); + + $this->assertTrue($this->Shell->hasTask('db_config')); + $this->assertTrue($this->Shell->hasTask('DbConfig')); + } } From cd18c8214c64858092def81e295b028ac669d33c Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 21:52:12 -0400 Subject: [PATCH 056/207] Adding Shell::hasMethod and tests for it. --- cake/console/libs/shell.php | 16 ++++++++++++- cake/tests/cases/console/libs/shell.test.php | 24 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 90cfc6711..35f6e5b24 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -305,7 +305,21 @@ class Shell extends Object { * @return boolean */ public function hasMethod($name) { - + if (empty($this->_reflection)) { + $this->_reflection = new ReflectionClass($this); + } + try { + $method = $this->_reflection->getMethod($name); + if (!$method->isPublic() || substr($name, 0, 1) === '_') { + return false; + } + if ($method->getDeclaringClass() != $this->_reflection) { + return false; + } + return true; + } catch (ReflectionException $e) { + return false; + } } /** diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index fc449b34c..fd91aa5e9 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -57,6 +57,18 @@ class TestShell extends Shell { protected function _stop($status = 0) { $this->stopped = $status; } + + public function do_something() { + + } + + public function _secret() { + + } + + protected function no_access() { + + } } /** @@ -578,4 +590,16 @@ class ShellTest extends CakeTestCase { $this->assertTrue($this->Shell->hasTask('db_config')); $this->assertTrue($this->Shell->hasTask('DbConfig')); } + +/** + * test the hasMethod + * + * @return void + */ + function testHasMethod() { + $this->assertTrue($this->Shell->hasMethod('do_something')); + $this->assertFalse($this->Shell->hasMethod('hr'), 'hr is callable'); + $this->assertFalse($this->Shell->hasMethod('_secret'), '_secret is callable'); + $this->assertFalse($this->Shell->hasMethod('no_access'), 'no_access is callable'); + } } From 3d351e7760bf78fb6fa5392a606ce256b0c3a6a2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 22:48:34 -0400 Subject: [PATCH 057/207] Dramatically changing how ShellDispatcher and Shell interact. Shell::runCommand is now responsible for delegating out to tasks and correctly checking methods to run. ShellDispatcher no longer parses parameters and instead only pulls out the path information that it needs to setup the environment. --- cake/console/libs/shell.php | 37 +++++++++++ cake/console/shell_dispatcher.php | 102 +++++++++++------------------- 2 files changed, 75 insertions(+), 64 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 35f6e5b24..7daabc081 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -322,6 +322,43 @@ class Shell extends Object { } } +/** + * Runs the Shell with the provided argv + * + * @param array $argv Array of arguments to run the shell with. This array should be missing the shell name. + * @return void + */ + public function runCommand($command, $argv) { + $this->startup(); + if (!empty($command) && $this->hasTask($command)) { + return $this->{$command}->runCommand('execute', $argv); + } + + $this->parser = $this->_getOptionParser(); + list($this->params, $this->args) = $this->parser->parse($argv); + if (isset($this->params['help'])) { + return $this->out($this->parser->help()); + } + if ($this->hasMethod($command)) { + return $this->{$command}(); + } + if ($this->hasMethod('main')) { + return $this->main(); + } + throw new RuntimeException(sprintf(__('Unhandled method `%s`'), $command)); + } + +/** + * Gets the option parser instance and configures it. + * By overriding this method you can configure the ConsoleOptionParser before returning it. + * + * @return ConsoleOptionParser + */ + protected function _getOptionParser() { + $parser = new ConsoleOptionParser($this->name); + return $parser; + } + /** * Overload get for lazy building of tasks * diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index 267cf4cb4..338278748 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -243,64 +243,43 @@ class ShellDispatcher { * @return boolean */ public function dispatch() { - $command = $this->shiftArgs(); + $shell = $this->shiftArgs(); - if (!$command) { + if (!$shell) { $this->help(); return false; } - if (in_array($command, array('help', '--help', '-h'))) { + if (in_array($shell, array('help', '--help', '-h'))) { $this->help(); return true; } - list($plugin, $shell) = pluginSplit($command); + list($plugin, $shell) = pluginSplit($shell); $this->shell = $shell; $this->shellName = Inflector::camelize($shell); $this->shellClass = $this->shellName . 'Shell'; - $arg = null; - - if (isset($this->args[0])) { - $arg = $this->args[0]; - $this->shellCommand = Inflector::variable($arg); - } - $Shell = $this->_getShell($plugin); - - $methods = array(); + + $command = null; + if (isset($this->args[0])) { + $command = $this->args[0]; + } if ($Shell instanceof Shell) { $Shell->initialize(); $Shell->loadTasks(); - - $task = Inflector::camelize($arg); - - if (in_array($task, $Shell->taskNames)) { - $this->shiftArgs(); - $Shell->{$task}->startup(); - - if (isset($this->args[0]) && $this->args[0] == 'help') { - if (method_exists($Shell->{$task}, 'help')) { - $Shell->{$task}->help(); - } else { - $this->help(); - } - return true; - } - return $Shell->{$task}->execute(); - } - $methods = array_diff(get_class_methods('Shell'), array('help')); + return $Shell->runCommand($command, $this->args); } - $methods = array_diff(get_class_methods($Shell), $methods); - $added = in_array(strtolower($arg), array_map('strtolower', $methods)); - $private = $arg[0] == '_' && method_exists($Shell, $arg); + $methods = array_diff(get_class_methods($Shell), get_class_methods('Shell')); + $added = in_array($command, $methods); + $private = $arg[0] == '_' && method_exists($Shell, $command); if (!$private) { if ($added) { $this->shiftArgs(); $Shell->startup(); - return $Shell->{$arg}(); + return $Shell->{$command}(); } if (method_exists($Shell, 'main')) { $Shell->startup(); @@ -348,13 +327,19 @@ class ShellDispatcher { } /** - * Parses command line options + * Parses command line options and extracts the directory paths from $params * * @param array $params Parameters to parse */ - public function parseParams($params) { - $this->__parseParams($params); - $defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot'); + public function parseParams($args) { + $this->_parsePaths($args); + + $defaults = array( + 'app' => 'app', + 'root' => dirname(dirname(dirname(__FILE__))), + 'working' => null, + 'webroot' => 'webroot' + ); $params = array_merge($defaults, array_intersect_key($this->params, $defaults)); $isWin = false; foreach ($defaults as $default => $value) { @@ -391,35 +376,24 @@ class ShellDispatcher { } /** - * Helper for recursively parsing params + * Parses out the paths from from the argv * - * @return array params - * @access private + * @return void */ - function __parseParams($params) { - $count = count($params); - for ($i = 0; $i < $count; $i++) { - if (isset($params[$i])) { - if ($params[$i]{0} === '-') { - $key = substr($params[$i], 1); - $this->params[$key] = true; - unset($params[$i]); - if (isset($params[++$i])) { - if ($params[$i]{0} !== '-') { - $this->params[$key] = str_replace('"', '', $params[$i]); - unset($params[$i]); - } else { - $i--; - $this->__parseParams($params); - } - } - } else { - $this->args[] = $params[$i]; - unset($params[$i]); - } - + protected function _parsePaths($args) { + $parsed = array(); + $keys = array('-working', '--working', '-app', '--app', '-root', '--root'); + foreach ($keys as $key) { + $index = array_search($key, $args); + if ($index !== false) { + $keyname = str_replace('-', '', $key); + $valueIndex = $index + 1; + $parsed[$keyname] = $args[$valueIndex]; + array_splice($args, $index, 2); } } + $this->args = $args; + $this->params = $parsed; } /** From cea9dadaa2ac0e5b23a02269ce878306dde8ab3f Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 23:09:19 -0400 Subject: [PATCH 058/207] Fixing ShellDispatcher::parseParams tests to reflect the changes in what they do. Adding tests for Shell::runCommand(). --- cake/tests/cases/console/libs/shell.test.php | 51 +++++++++++++++++++ .../cases/console/shell_dispatcher.test.php | 13 ++--- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index fd91aa5e9..45a25075a 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -602,4 +602,55 @@ class ShellTest extends CakeTestCase { $this->assertFalse($this->Shell->hasMethod('_secret'), '_secret is callable'); $this->assertFalse($this->Shell->hasMethod('no_access'), 'no_access is callable'); } + +/** + * test run command calling main. + * + * @return void + */ + function testRunCommandMain() { + $methods = get_class_methods('Shell'); + $Mock = $this->getMock('Shell', array('main', 'startup'), array(), '', false); + + $Mock->expects($this->once())->method('main')->will($this->returnValue(true)); + $Mock->expects($this->once())->method('startup'); + $result = $Mock->runCommand(null, array()); + $this->assertTrue($result); + } + +/** + * test run command causing exception on Shell method. + * + * @expectedException RuntimeException + * @return void + */ + function testRunCommandBaseclassMethod() { + $methods = get_class_methods('Shell'); + $Mock = $this->getMock('Shell', array('startup'), array(), '', false); + + $Mock->expects($this->once())->method('startup'); + $Mock->expects($this->never())->method('hr'); + $result = $Mock->runCommand('hr', array()); + } + +/** + * test that a --help causes help to show. + * + * @return void + */ + function testHelpParamTriggeringHelp() { + $Parser = $this->getMock('ConsoleOptionParser', array(), array(), '', false); + $Parser->expects($this->once())->method('parse') + ->with(array('--help')) + ->will($this->returnValue(array(array('help' => true), array()))); + $Parser->expects($this->once())->method('help'); + + $Shell = $this->getMock('Shell', array('_getOptionParser', 'out', 'startup'), array(), '', false); + $Shell->expects($this->once())->method('_getOptionParser') + ->will($this->returnValue($Parser)); + $Shell->expects($this->once())->method('out'); + $Shell->expects($this->once())->method('startup'); + + $Shell->runCommand(null, array('--help')); + } } diff --git a/cake/tests/cases/console/shell_dispatcher.test.php b/cake/tests/cases/console/shell_dispatcher.test.php index 20cd34be4..4ae1f3f74 100644 --- a/cake/tests/cases/console/shell_dispatcher.test.php +++ b/cake/tests/cases/console/shell_dispatcher.test.php @@ -230,7 +230,6 @@ class ShellDispatcherTest extends CakeTestCase { ); $expected = array( 'app' => 'new', - 'dry' => true, 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'), 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH), 'webroot' => 'webroot' @@ -256,15 +255,14 @@ class ShellDispatcherTest extends CakeTestCase { 'webroot' => 'webroot', 'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'), 'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH), - 'dry' => true, - 'f' => true, - 'name' => 'DbAcl' ); $Dispatcher->params = $Dispatcher->args = array(); $Dispatcher->parseParams($params); $this->assertEqual($expected, $Dispatcher->params); - $expected = array('./console/cake.php', 'schema', 'run', 'create'); + $expected = array( + './console/cake.php', 'schema', 'run', 'create', '-dry', '-f', '-name', 'DbAcl' + ); $this->assertEqual($expected, $Dispatcher->args); $params = array( @@ -283,15 +281,11 @@ class ShellDispatcherTest extends CakeTestCase { 'webroot' => 'webroot', 'working' => '/cake/1.2.x.x/app', 'root' => '/cake/1.2.x.x', - 'dry' => true, - 'name' => 'DbAcl' ); $Dispatcher->params = $Dispatcher->args = array(); $Dispatcher->parseParams($params); $this->assertEqual($expected, $Dispatcher->params); - $expected = array('/cake/1.2.x.x/cake/console/cake.php', 'schema', 'run', 'create'); - $this->assertEqual($expected, $Dispatcher->args); $params = array( 'cake.php', '-working', @@ -344,7 +338,6 @@ class ShellDispatcherTest extends CakeTestCase { 'webroot' => 'webroot', 'working' => 'C:\wamp\www\apps\cake\app', 'root' => 'C:\wamp\www\apps\cake', - 'url' => 'http://example.com/some/url/with/a/path' ); $Dispatcher->params = $Dispatcher->args = array(); $Dispatcher->parseParams($params); From 79d1739778d54088074249980ee86d265d831e69 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 23:30:56 -0400 Subject: [PATCH 059/207] Adding tests for task methods in runCommand. Moving startup() call to the dispatcher so nested runCommand calls don't double output the startup content. --- cake/console/libs/shell.php | 2 +- cake/console/shell_dispatcher.php | 1 + cake/tests/cases/console/libs/shell.test.php | 20 ++++++++++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 7daabc081..3b9fe1304 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -329,8 +329,8 @@ class Shell extends Object { * @return void */ public function runCommand($command, $argv) { - $this->startup(); if (!empty($command) && $this->hasTask($command)) { + $command = Inflector::camelize($command); return $this->{$command}->runCommand('execute', $argv); } diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index 338278748..a7616a5d1 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -269,6 +269,7 @@ class ShellDispatcher { if ($Shell instanceof Shell) { $Shell->initialize(); $Shell->loadTasks(); + $Shell->startup(); return $Shell->runCommand($command, $this->args); } $methods = array_diff(get_class_methods($Shell), get_class_methods('Shell')); diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index 45a25075a..f57650d6e 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -613,7 +613,6 @@ class ShellTest extends CakeTestCase { $Mock = $this->getMock('Shell', array('main', 'startup'), array(), '', false); $Mock->expects($this->once())->method('main')->will($this->returnValue(true)); - $Mock->expects($this->once())->method('startup'); $result = $Mock->runCommand(null, array()); $this->assertTrue($result); } @@ -628,7 +627,6 @@ class ShellTest extends CakeTestCase { $methods = get_class_methods('Shell'); $Mock = $this->getMock('Shell', array('startup'), array(), '', false); - $Mock->expects($this->once())->method('startup'); $Mock->expects($this->never())->method('hr'); $result = $Mock->runCommand('hr', array()); } @@ -638,7 +636,7 @@ class ShellTest extends CakeTestCase { * * @return void */ - function testHelpParamTriggeringHelp() { + function testRunCommandTriggeringHelp() { $Parser = $this->getMock('ConsoleOptionParser', array(), array(), '', false); $Parser->expects($this->once())->method('parse') ->with(array('--help')) @@ -649,8 +647,22 @@ class ShellTest extends CakeTestCase { $Shell->expects($this->once())->method('_getOptionParser') ->will($this->returnValue($Parser)); $Shell->expects($this->once())->method('out'); - $Shell->expects($this->once())->method('startup'); $Shell->runCommand(null, array('--help')); } + +/** + * test that runCommand will call runCommand on the task. + * + * @return void + */ + function testRunCommandHittingTask() { + $Shell = $this->getMock('Shell', array('hasTask'), array(), '', false); + $task = $this->getMock('Shell', array('execute'), array(), '', false); + $Shell->tasks = array('RunCommand'); + $Shell->expects($this->once())->method('hasTask')->will($this->returnValue(true)); + $Shell->RunCommand = $task; + + $Shell->runCommand('run_command', array()); + } } From e70089891d77091e6e348294ca8d1768ba2be975 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 9 Oct 2010 23:42:38 -0400 Subject: [PATCH 060/207] Adding some more array_unshift. These fix issues where tasks would receive their name in the argv which is not correct. --- cake/console/libs/shell.php | 2 ++ cake/tests/cases/console/libs/shell.test.php | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 3b9fe1304..0f9b113c3 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -331,6 +331,7 @@ class Shell extends Object { public function runCommand($command, $argv) { if (!empty($command) && $this->hasTask($command)) { $command = Inflector::camelize($command); + array_shift($argv); return $this->{$command}->runCommand('execute', $argv); } @@ -340,6 +341,7 @@ class Shell extends Object { return $this->out($this->parser->help()); } if ($this->hasMethod($command)) { + array_shift($argv); return $this->{$command}(); } if ($this->hasMethod('main')) { diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index f57650d6e..c28b8d8d4 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -658,11 +658,13 @@ class ShellTest extends CakeTestCase { */ function testRunCommandHittingTask() { $Shell = $this->getMock('Shell', array('hasTask'), array(), '', false); - $task = $this->getMock('Shell', array('execute'), array(), '', false); - $Shell->tasks = array('RunCommand'); - $Shell->expects($this->once())->method('hasTask')->will($this->returnValue(true)); + $task = $this->getMock('Shell', array('execute', 'runCommand'), array(), '', false); + $task->expects($this->any())->method('runCommand') + ->with('execute', array('one', 'value')); + + $Shell->expects($this->any())->method('hasTask')->will($this->returnValue(true)); $Shell->RunCommand = $task; - $Shell->runCommand('run_command', array()); + $Shell->runCommand('run_command', array('run_command', 'one', 'value')); } } From 193721a0eb35a126ec27ebb77ad15da9499925d5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 00:21:16 -0400 Subject: [PATCH 061/207] Fixing notice errors in ShellDispatcher Removing ShellDispatcher tests that are now inside Shell. Changing exception type to match the one used in ShellDispatcher. --- cake/console/libs/shell.php | 22 ++- cake/console/shell_dispatcher.php | 3 +- cake/tests/cases/console/libs/shell.test.php | 19 ++- .../cases/console/shell_dispatcher.test.php | 160 ++---------------- 4 files changed, 45 insertions(+), 159 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 0f9b113c3..bd19044ce 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -329,9 +329,19 @@ class Shell extends Object { * @return void */ public function runCommand($command, $argv) { - if (!empty($command) && $this->hasTask($command)) { - $command = Inflector::camelize($command); + $isTask = $this->hasTask($command); + $isMethod = $this->hasMethod($command); + $isMain = $this->hasMethod('main'); + + if ($isTask || $isMethod) { array_shift($argv); + } + if ($isTask || $isMethod || $isMain) { + $this->startup(); + } + + if ($isTask) { + $command = Inflector::camelize($command); return $this->{$command}->runCommand('execute', $argv); } @@ -340,14 +350,14 @@ class Shell extends Object { if (isset($this->params['help'])) { return $this->out($this->parser->help()); } - if ($this->hasMethod($command)) { - array_shift($argv); + + if ($isMethod) { return $this->{$command}(); } - if ($this->hasMethod('main')) { + if ($isMain) { return $this->main(); } - throw new RuntimeException(sprintf(__('Unhandled method `%s`'), $command)); + throw new MissingShellMethodException(array('shell' => get_class($this), 'method' => $command)); } /** diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index a7616a5d1..f77a6bcdb 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -269,12 +269,11 @@ class ShellDispatcher { if ($Shell instanceof Shell) { $Shell->initialize(); $Shell->loadTasks(); - $Shell->startup(); return $Shell->runCommand($command, $this->args); } $methods = array_diff(get_class_methods($Shell), get_class_methods('Shell')); $added = in_array($command, $methods); - $private = $arg[0] == '_' && method_exists($Shell, $command); + $private = $command[0] == '_' && method_exists($Shell, $command); if (!$private) { if ($added) { diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index c28b8d8d4..e21b47f29 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -620,7 +620,7 @@ class ShellTest extends CakeTestCase { /** * test run command causing exception on Shell method. * - * @expectedException RuntimeException + * @expectedException MissingShellMethodException * @return void */ function testRunCommandBaseclassMethod() { @@ -631,6 +631,20 @@ class ShellTest extends CakeTestCase { $result = $Mock->runCommand('hr', array()); } +/** + * test run command causing exception on Shell method. + * + * @expectedException MissingShellMethodException + * @return void + */ + function testRunCommandMissingMethod() { + $methods = get_class_methods('Shell'); + $Mock = $this->getMock('Shell', array('startup'), array(), '', false); + + $Mock->expects($this->never())->method('idontexist'); + $result = $Mock->runCommand('idontexist', array()); + } + /** * test that a --help causes help to show. * @@ -657,11 +671,12 @@ class ShellTest extends CakeTestCase { * @return void */ function testRunCommandHittingTask() { - $Shell = $this->getMock('Shell', array('hasTask'), array(), '', false); + $Shell = $this->getMock('Shell', array('hasTask', 'startup'), array(), '', false); $task = $this->getMock('Shell', array('execute', 'runCommand'), array(), '', false); $task->expects($this->any())->method('runCommand') ->with('execute', array('one', 'value')); + $Shell->expects($this->once())->method('startup'); $Shell->expects($this->any())->method('hasTask')->will($this->returnValue(true)); $Shell->RunCommand = $task; diff --git a/cake/tests/cases/console/shell_dispatcher.test.php b/cake/tests/cases/console/shell_dispatcher.test.php index 4ae1f3f74..2b6b354f6 100644 --- a/cake/tests/cases/console/shell_dispatcher.test.php +++ b/cake/tests/cases/console/shell_dispatcher.test.php @@ -438,103 +438,20 @@ class ShellDispatcherTest extends CakeTestCase { */ public function testDispatchShellWithMain() { $Dispatcher = new TestShellDispatcher(); - $methods = get_class_methods('Shell'); - array_push($methods, 'main', '_secret'); - $Mock = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MockWithMainShell'); + $Mock = $this->getMock('Shell', array(), array(&$Dispatcher), 'MockWithMainShell'); - $Mock->expects($this->once())->method('main')->will($this->returnValue(true)); $Mock->expects($this->once())->method('initialize'); $Mock->expects($this->once())->method('loadTasks'); - $Mock->expects($this->once())->method('startup'); + $Mock->expects($this->once())->method('runCommand') + ->with(null, array()) + ->will($this->returnValue(true)); + $Dispatcher->TestShell = $Mock; $Dispatcher->args = array('mock_with_main'); $result = $Dispatcher->dispatch(); $this->assertTrue($result); $this->assertEqual($Dispatcher->args, array()); - - $Shell = new MockWithMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('main')->will($this->returnValue(true)); - $Shell->expects($this->once())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main', 'initdb'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array('initdb')); - - $Shell = new MockWithMainShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('startup'); - $Shell->expects($this->once())->method('help'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_with_main', 'help'); - $result = $Dispatcher->dispatch(); - $this->assertNull($result); - $this->assertEqual($Dispatcher->args, array()); - } - -/** - * test missing shell exceptions on underscored (private methods) - * - * @expectedException MissingShellMethodException - * @return void - */ - function testMissingShellMethodExceptionPrivateMethod() { - $Dispatcher = new TestShellDispatcher(); - - $methods = get_class_methods('Shell'); - array_push($methods, 'main', '_secret'); - - $Shell = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MissingShellPrivateMethod'); - $Shell->expects($this->never())->method('main'); - $Shell->expects($this->never())->method('startup'); - $Shell->expects($this->never())->method('_secret'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('missing_shell_private_method', '_secret'); - $result = $Dispatcher->dispatch(); - } - -/** - * test exception when calling shell class methods. - * - * @expectedException MissingShellMethodException - * @return void - */ - function testMissingShellMethodBaseClassMethod() { - $Dispatcher = new TestShellDispatcher(); - - $Shell = $this->getMock('Shell', array(), array(&$Dispatcher), 'MissingShellBaseClass'); - $Shell->expects($this->never())->method('main'); - $Shell->expects($this->never())->method('startup'); - $Shell->expects($this->never())->method('hr'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('missing_shell_base_class', 'hr'); - $result = $Dispatcher->dispatch(); - } - -/** - * test missing shell exception on missing method. - * - * @expectedException MissingShellMethodException - * @return void - */ - function testMissingShellMethodExceptionMissingMethod() { - $Dispatcher = new TestShellDispatcher(); - - $methods = get_class_methods('Shell'); - - $Shell = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MissingShellNoMethod'); - $Shell->expects($this->never())->method('main'); - $Shell->expects($this->never())->method('startup'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('missing_shell_method_no_method', 'idontexist'); - $result = $Dispatcher->dispatch(); } /** @@ -544,22 +461,22 @@ class ShellDispatcherTest extends CakeTestCase { */ public function testDispatchShellWithoutMain() { $Dispatcher = new TestShellDispatcher(); - $methods = get_class_methods('Shell'); - array_push($methods, 'initDb', '_secret'); - $Shell = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MockWithoutMainShell'); + $Shell = $this->getMock('Shell', array(), array(&$Dispatcher), 'MockWithoutMainShell'); $Shell = new MockWithoutMainShell($Dispatcher); $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('initDb')->will($this->returnValue(true)); + $Shell->expects($this->once())->method('initialize'); $Shell->expects($this->once())->method('loadTasks'); - $Shell->expects($this->once())->method('startup'); + $Shell->expects($this->once())->method('runCommand') + ->with('initdb', array('initdb')) + ->will($this->returnValue(true)); + $Dispatcher->TestShell = $Shell; $Dispatcher->args = array('mock_without_main', 'initdb'); $result = $Dispatcher->dispatch(); $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array()); } /** @@ -628,61 +545,6 @@ class ShellDispatcherTest extends CakeTestCase { $this->assertTrue($result); } -/** - * Verify that a task is called instead of the shell if the first arg equals - * the name of the task - * - * @return void - */ - public function testDispatchTask() { - $Dispatcher = new TestShellDispatcher(); - $mainMethods = $executeMethods = get_class_methods('Shell'); - array_push($mainMethods, 'main'); - array_push($executeMethods, 'execute'); - - $Week = $this->getMock('Shell', $mainMethods, array(&$Dispatcher), 'MockWeekShell'); - $Sunday = $this->getMock('Shell', $executeMethods, array(&$Dispatcher), 'MockOnSundayTask'); - - $Shell = new MockWeekShell($Dispatcher); - $this->mockObjects[] = $Shell; - $Shell->expects($this->once())->method('initialize'); - $Shell->expects($this->once())->method('loadTasks'); - $Shell->expects($this->never())->method('startup'); - $Shell->expects($this->never())->method('main'); - - $Task = new MockOnSundayTask($Dispatcher); - $this->mockObjects[] = $Task; - $Task->expects($this->once())->method('execute')->will($this->returnValue(true)); - $Task->expects($this->once())->method('initialize');; - $Task->expects($this->once())->method('loadTasks'); - $Task->expects($this->once())->method('startup'); - $Task->expects($this->once())->method('execute'); - - $Shell->MockOnSunday = $Task; - $Shell->taskNames = array('MockOnSunday'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_week', 'mock_on_sunday'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - $this->assertEqual($Dispatcher->args, array()); - - $Shell = new MockWeekShell($Dispatcher); - $Task = new MockOnSundayTask($Dispatcher); - array_push($this->mockObjects, $Shell, $Task); - - $Task->expects($this->never())->method('execute'); - $Task->expects($this->once())->method('help'); - - $Shell->MockOnSunday = $Task; - $Shell->taskNames = array('MockOnSunday'); - $Dispatcher->TestShell = $Shell; - - $Dispatcher->args = array('mock_week', 'mock_on_sunday', 'help'); - $result = $Dispatcher->dispatch(); - $this->assertTrue($result); - } - /** * Verify shifting of arguments * From f1ee46749bda81456efd2721291fcc7b622dcd3a Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 00:23:25 -0400 Subject: [PATCH 062/207] Fixing failing tests in Schema shell. --- cake/console/libs/schema.php | 7 +++++++ cake/tests/cases/console/libs/schema.test.php | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cake/console/libs/schema.php b/cake/console/libs/schema.php index 45e7795d9..bd9cc36dc 100644 --- a/cake/console/libs/schema.php +++ b/cake/console/libs/schema.php @@ -40,6 +40,13 @@ class SchemaShell extends Shell { */ private $__dry = null; +/** + * Schema class being used. + * + * @var CakeSchema + */ + public $Schema; + /** * Override initialize * diff --git a/cake/tests/cases/console/libs/schema.test.php b/cake/tests/cases/console/libs/schema.test.php index 7d343dc9c..41adf9c62 100644 --- a/cake/tests/cases/console/libs/schema.test.php +++ b/cake/tests/cases/console/libs/schema.test.php @@ -146,7 +146,7 @@ class SchemaShellTest extends CakeTestCase { $this->assertEqual(strtolower($this->Shell->Schema->name), strtolower(APP_DIR)); $this->assertEqual($this->Shell->Schema->file, 'schema.php'); - unset($this->Shell->Schema); + $this->Shell->Schema = null; $this->Shell->params = array( 'name' => 'TestSchema' ); @@ -156,7 +156,7 @@ class SchemaShellTest extends CakeTestCase { $this->assertEqual($this->Shell->Schema->connection, 'default'); $this->assertEqual($this->Shell->Schema->path, APP . 'config' . DS . 'schema'); - unset($this->Shell->Schema); + $this->Shell->Schema = null; $this->Shell->params = array( 'file' => 'other_file.php', 'connection' => 'test', From c0d15a420c268ec84b6a4a645087fd09a8cc2a6a Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 00:40:34 -0400 Subject: [PATCH 063/207] Changing name of parser attribute. --- cake/console/libs/shell.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index bd19044ce..41466efb3 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -37,6 +37,13 @@ class Shell extends Object { */ public $Dispatch = null; +/** + * An instance of ConsoleOptionParser that has been configured for this class. + * + * @var ConsoleOptionParser + */ + public $OptionParser; + /** * If true, the script will ask for permission to perform actions. * @@ -345,10 +352,10 @@ class Shell extends Object { return $this->{$command}->runCommand('execute', $argv); } - $this->parser = $this->_getOptionParser(); - list($this->params, $this->args) = $this->parser->parse($argv); + $this->OptionParser = $this->_getOptionParser(); + list($this->params, $this->args) = $this->OptionParser->parse($argv); if (isset($this->params['help'])) { - return $this->out($this->parser->help()); + return $this->out($this->OptionParser->help()); } if ($isMethod) { From 2a2428a6940e3df687bee8979cfba5dbdf942ba1 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 00:41:10 -0400 Subject: [PATCH 064/207] Updating ApiShell to use ConsoleOptionParser correctly. Removing old help method. --- cake/console/libs/api.php | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/cake/console/libs/api.php b/cake/console/libs/api.php index 26acdccb0..acd20456b 100644 --- a/cake/console/libs/api.php +++ b/cake/console/libs/api.php @@ -60,7 +60,7 @@ class ApiShell extends Shell { */ public function main() { if (empty($this->args)) { - return $this->help(); + return $this->out($this->OptionParser->help()); } $type = strtolower($this->args[0]); @@ -78,7 +78,6 @@ class ApiShell extends Shell { $file = Inflector::underscore($this->args[1]); $class = Inflector::camelize($file); } - $objects = App::objects('class', $path); if (in_array($class, $objects)) { if (in_array($type, array('behavior', 'component', 'helper')) && $type !== $file) { @@ -88,19 +87,18 @@ class ApiShell extends Shell { } } else { - $this->err(sprintf(__('%s not found'), $class)); - $this->_stop(); + $this->error(sprintf(__('%s not found'), $class)); } $parsed = $this->__parseClass($path . $file .'.php', $class); if (!empty($parsed)) { - if (isset($this->params['m'])) { - if (!isset($parsed[$this->params['m']])) { - $this->err(sprintf(__('%s::%s() could not be found'), $class, $this->params['m'])); + if (isset($this->params['method'])) { + if (!isset($parsed[$this->params['method']])) { + $this->err(sprintf(__('%s::%s() could not be found'), $class, $this->params['method'])); $this->_stop(); } - $method = $parsed[$this->params['m']]; + $method = $parsed[$this->params['method']]; $this->out($class .'::'.$method['method'] . $method['parameters']); $this->hr(); $this->out($method['comment'], true); @@ -136,6 +134,23 @@ class ApiShell extends Shell { } } +/** + * Get and configure the optionparser. + * + * @return ConsoleOptionParser + */ + protected function _getOptionParser() { + $parser = parent::_getOptionParser(); + $parser->addArgument('type', array( + 'help' => 'Either a full path or type of class (model, behavior, controller, component, view, helper)' + ))->addArgument('className', array( + 'help' => 'A CakePHP core class name (e.g: Component, HtmlHelper).' + ))->addOption('method', array( + 'short' => 'm', + 'help' => __('The specific method you want help on.') + ))->description(__('Lookup doc block comments for classes in CakePHP.')); + return $parser; + } /** * Show help for this shell. * From 41b8affa82132034dc36561c21a2087f29dd6127 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 00:59:45 -0400 Subject: [PATCH 065/207] Updating i18n shell to start using new OptionParser class. Removing more dead attributes from Shell. Making Shell::$name the name used to invoke the shell, not the classname. This makes it similar to other objects with names. --- cake/console/libs/i18n.php | 14 +++++++++-- cake/console/libs/shell.php | 41 ++++++++----------------------- cake/console/shell_dispatcher.php | 2 +- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/cake/console/libs/i18n.php b/cake/console/libs/i18n.php index a9d2888a4..319191b95 100644 --- a/cake/console/libs/i18n.php +++ b/cake/console/libs/i18n.php @@ -65,7 +65,7 @@ class I18nShell extends Shell { * */ public function main() { - $this->out(__('I18n Shell')); + $this->out(__('I18n Shell')); $this->hr(); $this->out(__('[E]xtract POT file from sources')); $this->out(__('[I]nitialize i18n database table')); @@ -81,7 +81,7 @@ class I18nShell extends Shell { $this->initdb(); break; case 'h': - $this->help(); + $this->out($this->OptionParser->help()); break; case 'q': exit(0); @@ -102,6 +102,16 @@ class I18nShell extends Shell { $this->Dispatch->dispatch(); } +/** + * Get and configure the Option parser + * + * @return ConsoleOptionParser + */ + protected function _getOptionParser() { + $parser = parent::_getOptionParser(); + return $parser->description(__('I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')); + } + /** * Show help screen. * diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 41466efb3..fb7b5bdab 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -83,14 +83,6 @@ class Shell extends Object { */ public $shell = null; -/** - * The class name of the shell that was invoked. - * - * @var string - * @access public - */ - public $className = null; - /** * The command called if public methods are available. * @@ -107,14 +99,6 @@ class Shell extends Object { */ public $name = null; -/** - * An alias for the shell - * - * @var string - * @access public - */ - public $alias = null; - /** * Contains tasks to load and instantiate * @@ -179,22 +163,18 @@ class Shell extends Object { * */ function __construct(&$dispatch, $stdout = null, $stderr = null, $stdin = null) { - $vars = array('params', 'args', 'shell', 'shellCommand' => 'command', 'shellPaths'); + $vars = array('shell', 'shellCommand' => 'command', 'shellPaths'); foreach ($vars as $key => $var) { if (is_string($key)) { - $this->{$var} =& $dispatch->{$key}; + $this->{$var} = $dispatch->{$key}; } else { - $this->{$var} =& $dispatch->{$var}; + $this->{$var} = $dispatch->{$var}; } } if ($this->name == null) { - $this->name = get_class($this); - } - - if ($this->alias == null) { - $this->alias = $this->name; + $this->name = Inflector::underscore(str_replace('Shell', '', get_class($this))); } $this->Dispatch =& $dispatch; @@ -243,8 +223,8 @@ class Shell extends Object { $this->out(); $this->out('Welcome to CakePHP v' . Configure::version() . ' Console'); $this->hr(); - $this->out('App : '. $this->params['app']); - $this->out('Path: '. $this->params['working']); + $this->out('App : '. $this->Dispatch->params['app']); + $this->out('Path: '. $this->Dispatch->params['working']); $this->hr(); } @@ -343,21 +323,20 @@ class Shell extends Object { if ($isTask || $isMethod) { array_shift($argv); } + + $this->OptionParser = $this->_getOptionParser(); + list($this->params, $this->args) = $this->OptionParser->parse($argv); + if ($isTask || $isMethod || $isMain) { $this->startup(); } - if ($isTask) { $command = Inflector::camelize($command); return $this->{$command}->runCommand('execute', $argv); } - - $this->OptionParser = $this->_getOptionParser(); - list($this->params, $this->args) = $this->OptionParser->parse($argv); if (isset($this->params['help'])) { return $this->out($this->OptionParser->help()); } - if ($isMethod) { return $this->{$command}(); } diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php index f77a6bcdb..31a86230f 100644 --- a/cake/console/shell_dispatcher.php +++ b/cake/console/shell_dispatcher.php @@ -260,7 +260,7 @@ class ShellDispatcher { $this->shellClass = $this->shellName . 'Shell'; $Shell = $this->_getShell($plugin); - + $command = null; if (isset($this->args[0])) { $command = $this->args[0]; From 278f699fdad602db48b2caf661a6b820e91467d3 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 01:02:23 -0400 Subject: [PATCH 066/207] Updating extract task. --- cake/console/libs/tasks/extract.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cake/console/libs/tasks/extract.php b/cake/console/libs/tasks/extract.php index a03790cad..8cbf27a96 100644 --- a/cake/console/libs/tasks/extract.php +++ b/cake/console/libs/tasks/extract.php @@ -103,8 +103,8 @@ class ExtractTask extends Shell { if (isset($this->params['paths'])) { $this->__paths = explode(',', $this->params['paths']); } else { - $defaultPath = $this->params['working']; - $message = sprintf(__("What is the full path you would like to extract?\nExample: %s\n[Q]uit [D]one"), $this->params['root'] . DS . 'myapp'); + $defaultPath = $this->Dispatch->params['working']; + $message = sprintf(__("What is the full path you would like to extract?\nExample: %s\n[Q]uit [D]one"), $this->Dispatch->params['root'] . DS . 'myapp'); while (true) { $response = $this->in($message, null, $defaultPath); if (strtoupper($response) === 'Q') { From 56339e05f3869f5a438214287a74809e56d5e56e Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 01:23:20 -0400 Subject: [PATCH 067/207] Adding subcommand to help output. Subcommands can be shell methods or tasks. Both should support additional parsers. Which is not complete yet. --- cake/console/console_option_parser.php | 68 ++++++++++++++++++- .../console/console_option_parser.test.php | 44 +++++++++++- 2 files changed, 109 insertions(+), 3 deletions(-) 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 From fe7c7a1b12530115ceea90c27b66bf8015c392d5 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 01:24:08 -0400 Subject: [PATCH 068/207] Adding some colour to Shell::createFile() Removing the now dead Shell::help(). The OptionParser approach provides much better help information than this method did. --- cake/console/libs/shell.php | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index fb7b5bdab..9815ab06d 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -548,11 +548,11 @@ class Shell extends Object { $this->out(sprintf(__('Creating file %s'), $path)); if (is_file($path) && $this->interactive === true) { - $prompt = sprintf(__('File `%s` exists, overwrite?'), $path); + $prompt = sprintf(__('File `%s` exists, overwrite?'), $path); $key = $this->in($prompt, array('y', 'n', 'q'), 'n'); if (strtolower($key) == 'q') { - $this->out(__('Quitting.'), 2); + $this->out(__('Quitting.'), 2); $this->_stop(); } elseif (strtolower($key) != 'y') { $this->out(sprintf(__('Skip `%s`'), $path), 2); @@ -566,27 +566,14 @@ class Shell extends Object { if ($File = new File($path, true)) { $data = $File->prepare($contents); $File->write($data); - $this->out(sprintf(__('Wrote `%s`'), $path)); + $this->out(sprintf(__('Wrote `%s`'), $path)); return true; } else { - $this->err(sprintf(__('Could not write to `%s`.'), $path), 2); + $this->err(sprintf(__('Could not write to `%s`.'), $path), 2); return false; } } -/** - * Outputs usage text on the standard output. Implement it in subclasses. - * - */ - public function help() { - if ($this->command != null) { - $this->err("Unknown {$this->name} command `{$this->command}`."); - $this->err("For usage, try `cake {$this->shell} help`.", 2); - } else { - $this->Dispatch->help(); - } - } - /** * Action to create a Unit Test * From 3be24d0b0b6b18e6f7f42d601bbaedccb19a2707 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 01:32:20 -0400 Subject: [PATCH 069/207] Adding subcommand docs for i18n shell. Adding execute checks to shell, this is a temporary fix that stops extra clears being done. --- cake/console/libs/i18n.php | 4 +++- cake/console/libs/shell.php | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cake/console/libs/i18n.php b/cake/console/libs/i18n.php index 319191b95..4c920367e 100644 --- a/cake/console/libs/i18n.php +++ b/cake/console/libs/i18n.php @@ -109,7 +109,9 @@ class I18nShell extends Shell { */ protected function _getOptionParser() { $parser = parent::_getOptionParser(); - return $parser->description(__('I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')); + return $parser->description(__('I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')) + ->addSubcommand('initdb', array('help' => __('Initialize the i18n table.'))) + ->addSubcommand('extract', array('help' => __('Extract the po translations from your application'))); } /** diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 9815ab06d..44b891593 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -320,14 +320,14 @@ class Shell extends Object { $isMethod = $this->hasMethod($command); $isMain = $this->hasMethod('main'); - if ($isTask || $isMethod) { + if ($isTask || $isMethod && $command !== 'execute') { array_shift($argv); } $this->OptionParser = $this->_getOptionParser(); list($this->params, $this->args) = $this->OptionParser->parse($argv); - if ($isTask || $isMethod || $isMain) { + if (($isTask || $isMethod || $isMain) && $command !== 'execute' ) { $this->startup(); } if ($isTask) { From ab794b300e167fa83aa2531a8a934893f6c200c8 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 01:54:51 -0400 Subject: [PATCH 070/207] Adding support for help generating help for subcommands. Adding tests for subcommand help generation. --- cake/console/console_option_parser.php | 26 +++++++++++++++- .../console/console_option_parser.test.php | 31 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 60c289890..5082da8e4 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -133,6 +133,20 @@ class ConsoleOptionParser { } } +/** + * Get or set the command name for shell/task + * + * @param string $text The text to set, or null if you want to read + * @return mixed If reading, the value of the command. If setting $this will be returned + */ + public function command($text = null) { + if ($text !== null) { + $this->_command = $text; + return $this; + } + return $this->_command; + } + /** * Get or set the description text for shell/task * @@ -301,9 +315,19 @@ class ConsoleOptionParser { * Generates help text based on the description, options, arguments and epilog * in the parser. * + * @param string $subcommand If present and a valid subcommand that has a linked parser. + * That subcommands help will be shown instead. * @return string */ - public function help() { + public function help($subcommand = null) { + if ( + isset($this->_subcommands[$subcommand]) && + $this->_subcommands[$subcommand]['parser'] instanceof self + ) { + $subparser = $this->_subcommands[$subcommand]['parser']; + $subparser->command($this->command() . ' ' . $subparser->command()); + return $subparser->help(); + } $out = array(); if (!empty($this->_description)) { $out[] = $this->_description; diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 5a6321f1a..6d9d09c69 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -363,6 +363,35 @@ method This is another command TEXT; $this->assertEquals($expected, $result, 'Help is not correct.'); - + } + +/** + * test that help() with a command param shows the help for a subcommand + * + * @return void + */ + function testHelpSubcommandHelp() { + $subParser = new ConsoleOptionParser('method', false); + $subParser->addOption('connection', array('help' => 'Db connection.')); + + $parser = new ConsoleOptionParser('mycommand', false); + $parser->addSubcommand('method', array( + 'help' => 'This is another command', + 'parser' => $subParser + )) + ->addOption('test', array('help' => 'A test option.')); + + $result = $parser->help('method'); + $expected = <<Usage: +cake mycommand method [-h] [--connection] + +Options: + +--help, -h Display this help. +--connection Db connection. + +TEXT; + $this->assertEquals($expected, $result, 'Help is not correct.'); } } \ No newline at end of file From 72e1a96a9a7a55a2b6873bf0a40279e0750d9699 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 01:56:23 -0400 Subject: [PATCH 071/207] Renaming a Shell::_getOptionParser -> getOptionParser Made the method public so subparser help would be easy to wire up. Moving help generation above task invocation so it always displays. --- cake/console/libs/api.php | 4 ++-- cake/console/libs/i18n.php | 9 ++++++--- cake/console/libs/shell.php | 12 ++++++------ cake/console/libs/tasks/extract.php | 15 +++++++++++++++ cake/tests/cases/console/libs/shell.test.php | 4 ++-- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/cake/console/libs/api.php b/cake/console/libs/api.php index acd20456b..d0c19da7e 100644 --- a/cake/console/libs/api.php +++ b/cake/console/libs/api.php @@ -139,8 +139,8 @@ class ApiShell extends Shell { * * @return ConsoleOptionParser */ - protected function _getOptionParser() { - $parser = parent::_getOptionParser(); + public function getOptionParser() { + $parser = parent::getOptionParser(); $parser->addArgument('type', array( 'help' => 'Either a full path or type of class (model, behavior, controller, component, view, helper)' ))->addArgument('className', array( diff --git a/cake/console/libs/i18n.php b/cake/console/libs/i18n.php index 4c920367e..24cc1d8cd 100644 --- a/cake/console/libs/i18n.php +++ b/cake/console/libs/i18n.php @@ -107,11 +107,14 @@ class I18nShell extends Shell { * * @return ConsoleOptionParser */ - protected function _getOptionParser() { - $parser = parent::_getOptionParser(); + public function getOptionParser() { + $parser = parent::getOptionParser(); return $parser->description(__('I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')) ->addSubcommand('initdb', array('help' => __('Initialize the i18n table.'))) - ->addSubcommand('extract', array('help' => __('Extract the po translations from your application'))); + ->addSubcommand('extract', array( + 'help' => __('Extract the po translations from your application'), + 'parser' => $this->Extract->getOptionParser() + )); } /** diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 44b891593..bca1e5a3b 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -174,7 +174,7 @@ class Shell extends Object { } if ($this->name == null) { - $this->name = Inflector::underscore(str_replace('Shell', '', get_class($this))); + $this->name = Inflector::underscore(str_replace(array('Shell', 'Task'), '', get_class($this))); } $this->Dispatch =& $dispatch; @@ -324,19 +324,19 @@ class Shell extends Object { array_shift($argv); } - $this->OptionParser = $this->_getOptionParser(); + $this->OptionParser = $this->getOptionParser(); list($this->params, $this->args) = $this->OptionParser->parse($argv); if (($isTask || $isMethod || $isMain) && $command !== 'execute' ) { $this->startup(); } + if (isset($this->params['help'])) { + return $this->out($this->OptionParser->help($command)); + } if ($isTask) { $command = Inflector::camelize($command); return $this->{$command}->runCommand('execute', $argv); } - if (isset($this->params['help'])) { - return $this->out($this->OptionParser->help()); - } if ($isMethod) { return $this->{$command}(); } @@ -352,7 +352,7 @@ class Shell extends Object { * * @return ConsoleOptionParser */ - protected function _getOptionParser() { + public function getOptionParser() { $parser = new ConsoleOptionParser($this->name); return $parser; } diff --git a/cake/console/libs/tasks/extract.php b/cake/console/libs/tasks/extract.php index 8cbf27a96..713ae299b 100644 --- a/cake/console/libs/tasks/extract.php +++ b/cake/console/libs/tasks/extract.php @@ -182,6 +182,21 @@ class ExtractTask extends Shell { $this->out(__('Done.')); } +/** + * Get & configure the option parser + * + * @return void + */ + public function getOptionParser() { + $parser = parent::getOptionParser(); + return $parser->description(__('CakePHP Language String Extraction:')) + ->addOption('app', array('help' => __('directory where your application is located.'))) + ->addOption('paths', array('help' => __('comma separted list of paths, full paths are needed.'))) + ->addOption('merge', array('help' => __('[yes|no] Merge all domain strings into the default.po file.'))) + ->addOption('output', array('help' => __('Full path to output directory.'))) + ->addOption('files', array('help' => __('comma separated list of files, full paths are needed.'))); + } + /** * Show help options * diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index e21b47f29..5fd0b119c 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -657,8 +657,8 @@ class ShellTest extends CakeTestCase { ->will($this->returnValue(array(array('help' => true), array()))); $Parser->expects($this->once())->method('help'); - $Shell = $this->getMock('Shell', array('_getOptionParser', 'out', 'startup'), array(), '', false); - $Shell->expects($this->once())->method('_getOptionParser') + $Shell = $this->getMock('Shell', array('getOptionParser', 'out', 'startup'), array(), '', false); + $Shell->expects($this->once())->method('getOptionParser') ->will($this->returnValue($Parser)); $Shell->expects($this->once())->method('out'); From ad367fed9a55c5d20675e77903a66e06adfd1333 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 11:28:27 -0400 Subject: [PATCH 072/207] Adding addOptions and addArguments as convience methods for adding multiple options/arguments. --- cake/console/console_option_parser.php | 30 ++++++++++++++++ .../console/console_option_parser.test.php | 34 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 5082da8e4..f59cc5e36 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -235,6 +235,36 @@ class ConsoleOptionParser { return $this; } +/** + * Add multiple arugments at once. Take an array of arugment defintions. + * The keys are used as the argument names, and the values as params for the argument. + * + * @param array $args Array of arguments to add. + * @see ConsoleOptionParser::addArgument() + * @return $this + */ + public function addArguments(array $args) { + foreach ($args as $name => $params) { + $this->addArgument($name, $params); + } + return $this; + } + +/** + * Add multiple options at once. Takes an array of option definitions. + * The keys are used as option names, and the values as params for the option. + * + * @param array $options Array of options to add. + * @see ConsoleOptionParser::addOption() + * @return $this + */ + public function addOptions(array $options) { + foreach ($options as $name => $params) { + $this->addOption($name, $params); + } + return $this; + } + /** * Append a subcommand to the subcommand list. * Subcommands are usually methods on your Shell, but can also be used to document diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 6d9d09c69..6efcb2c3a 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -150,6 +150,23 @@ class ConsoleOptionParserTest extends CakeTestCase { $this->assertEquals($expected, $result[0], 'multiple options did not parse'); } +/** + * Test adding multiple options. + * + * @return void + */ + function testAddOptions() { + $parser = new ConsoleOptionParser('something', false); + $result = $parser->addOptions(array( + 'name' => array('help' => 'The name'), + 'other' => array('help' => 'The other arg') + )); + $this->assertEquals($parser, $result, 'addOptions is not chainable.'); + + $result = $parser->options(); + $this->assertEquals(3, count($result), 'Not enough options'); + } + /** * test that boolean options work * @@ -234,6 +251,23 @@ class ConsoleOptionParserTest extends CakeTestCase { $parser->parse(array('one')); } +/** + * Test adding multiple arguments. + * + * @return void + */ + function testAddArguments() { + $parser = new ConsoleOptionParser(); + $result = $parser->addArguments(array( + 'name' => array('help' => 'The name'), + 'other' => array('help' => 'The other arg') + )); + $this->assertEquals($parser, $result, 'addArguments is not chainable.'); + + $result = $parser->arguments(); + $this->assertEquals(2, count($result), 'Not enough arguments'); + } + /** * test setting a subcommand up. * From 27a4f3c0d7ad294966c3a4e567535c8c3c0aeb7f Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 12:24:37 -0400 Subject: [PATCH 073/207] Adding test to make sure tags that are unknown are not removed. --- cake/tests/cases/console/console_output.test.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cake/tests/cases/console/console_output.test.php b/cake/tests/cases/console/console_output.test.php index 166830f2b..e4a63b478 100644 --- a/cake/tests/cases/console/console_output.test.php +++ b/cake/tests/cases/console/console_output.test.php @@ -133,6 +133,18 @@ class ConsoleOutputTest extends CakeTestCase { $this->output->write('Error: Something bad', false); } +/** + * test that formatting doesn't eat tags it doesn't know about. + * + * @return void + */ + function testFormattingNotEatingTags() { + $this->output->expects($this->once())->method('_write') + ->with(" Something bad"); + + $this->output->write(' Something bad', false); + } + /** * test formatting with custom styles. * From 12fba8852831ab3f3af9f0b99314203f7358d327 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 14:17:25 -0400 Subject: [PATCH 074/207] Refactoring options into a separate object. There are changes that need to be made for boolean and options with choices, and having a separate object will make them much easier to do. --- cake/console/console_option_parser.php | 144 ++++++++++++++++++------- 1 file changed, 107 insertions(+), 37 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index f59cc5e36..496b9d49f 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -184,8 +184,12 @@ class ConsoleOptionParser { * * - `short` - The single letter variant for this option, leave undefined for none. * - `help` - Help text for this option. Used when generating help for the option. - * - `default` - The default value for this option. If not defined the default will be true. + * - `default` - The default value for this option. Defaults are added into the parsed params when the + * attached option is not provided. Using default and boolean together will not work. + * are added into the parsed parameters when the option is undefined. * - `boolean` - The option uses no value, its just a boolean switch. Defaults to false. + * If an option is defined as boolean, it will always be added to the parsed params. If no present + * it will be false, if present it will be true. * * @param string $name The long name you want to the value to be parsed out as when options are parsed. * @param array $params An array of parameters that define the behavior of the option @@ -197,10 +201,11 @@ class ConsoleOptionParser { 'short' => null, 'help' => '', 'default' => true, - 'boolean' => false + 'boolean' => false, + 'choices' => array() ); $options = array_merge($defaults, $params); - $this->_options[$name] = $options; + $this->_options[$name] = new ConsoleInputOption($options); if (!empty($options['short'])) { $this->_shortOptions[$options['short']] = $name; } @@ -383,13 +388,13 @@ class ConsoleOptionParser { if (!empty($this->_options)) { $max = 0; foreach ($this->_options as $description) { - $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; + $max = (strlen($description->name) > $max) ? strlen($description->name) : $max; } $max += 8; $out[] = 'Options:'; $out[] = ''; - foreach ($this->_options as $description) { - $out[] = $this->_optionHelp($description, $max); + foreach ($this->_options as $option) { + $out[] = $option->help($max); } $out[] = ''; } @@ -424,13 +429,8 @@ class ConsoleOptionParser { if (!empty($this->_subcommands)) { $usage[] = '[subcommand]'; } - foreach ($this->_options as $definition) { - $name = empty($definition['short']) ? '--' . $definition['name'] : '-' . $definition['short']; - $default = ''; - if (!empty($definition['default']) && $definition['default'] !== true) { - $default = ' ' . $definition['default']; - } - $usage[] = sprintf('[%s%s]', $name, $default); + foreach ($this->_options as $option) { + $usage[] = $option->usage(); } foreach ($this->_args as $definition) { $name = $definition['name']; @@ -442,26 +442,6 @@ class ConsoleOptionParser { return implode(' ', $usage); } -/** - * Generate the usage for a single option. - * - * @return string - */ - protected function _optionHelp($definition, $nameWidth) { - $default = $short = ''; - if (!empty($definition['default']) && $definition['default'] !== true) { - $default = sprintf(__(' (default: %s)'), $definition['default']); - } - if (!empty($definition['short'])) { - $short = ', -' . $definition['short']; - } - $name = sprintf('--%s%s', $definition['name'], $short); - if (strlen($name) < $nameWidth) { - $name = str_pad($name, $nameWidth, ' '); - } - return sprintf('%s%s%s', $name, $definition['help'], $default); - } - /** * Generate the usage for a single argument. * @@ -542,13 +522,13 @@ class ConsoleOptionParser { if (!isset($this->_options[$name])) { throw new InvalidArgumentException(sprintf(__('Unknown option `%s`'), $name)); } - $definition = $this->_options[$name]; + $option = $this->_options[$name]; $nextValue = $this->_nextToken(); - if (!$definition['boolean'] && !empty($nextValue) && $nextValue{0} != '-') { + if (!$option->isBoolean() && !empty($nextValue) && $nextValue{0} != '-') { array_shift($this->_tokens); $value = $nextValue; } else { - $value = $definition['default']; + $value = $option->defaultValue(); } $params[$name] = $value; return $params; @@ -584,4 +564,94 @@ class ConsoleOptionParser { protected function _nextToken() { return isset($this->_tokens[0]) ? $this->_tokens[0] : ''; } -} \ No newline at end of file +} + + +/** + * An object to represent a single option used in the command line. + * ConsoleOptionParser creates these when you use addOption() + * + * @see ConsoleOptionParser::addOption() + * @package cake.console + */ +class ConsoleInputOption { + +/** + * Make a new Input Option + * + * @param mixed $name The long name of the option, or an array with all the properites. + * @param string $short The short alias for this option + * @param string $help The help text for this option + * @param boolean $boolean Whether this option is a boolean option. Boolean options don't consume extra tokens + * @param string $default The default value for this option. + * @param arraty $choices Valid choices for this option. + * @return void + */ + public function __construct($name, $short = null, $help = '', $boolean = false, $default = '', $choices = array()) { + if (is_array($name) && isset($name['name'])) { + foreach ($name as $key => $value) { + $this->{$key} = $value; + } + } else { + $this->name = $name; + $this->short = $short; + $this->help = $help; + $this->boolean = $boolean; + $this->default = $default; + $this->choices = $choices; + } + } + +/** + * Generate the help for this this option. + * + * @param int $width The width to make the name of the option. + * @return string + */ + public function help($width = 0) { + $default = $short = ''; + if (!empty($this->default) && $this->default !== true) { + $default = sprintf(__(' (default: %s)'), $this->default); + } + if (!empty($this->short)) { + $short = ', -' . $this->short; + } + $name = sprintf('--%s%s', $this->name, $short); + if (strlen($name) < $width) { + $name = str_pad($name, $width, ' '); + } + return sprintf('%s%s%s', $name, $this->help, $default); + } + +/** + * Get the usage value for this option + * + * @return string + */ + public function usage() { + $name = empty($this->short) ? '--' . $this->name : '-' . $this->short; + $default = ''; + if (!empty($this->default) && $this->default !== true) { + $default = ' ' . $this->default; + } + return sprintf('[%s%s]', $name, $default); + } + +/** + * Get the default value for this option + * + * @return void + */ + public function defaultValue() { + return $this->default; + } + +/** + * Check if this option is a boolean option + * + * @return boolean + */ + public function isBoolean() { + return (bool) $this->boolean; + } +} From 2774493577b9ef6dc369c53dc93a58d857bd39a3 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 14:31:47 -0400 Subject: [PATCH 075/207] Refactoring arguments into a separate object. --- cake/console/console_option_parser.php | 128 ++++++++++++++++++------- 1 file changed, 96 insertions(+), 32 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 496b9d49f..53c8c21b8 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -228,15 +228,17 @@ class ConsoleOptionParser { * @return $this. */ public function addArgument($name, $params = array()) { - $index = count($this->_args); $defaults = array( 'name' => $name, 'help' => '', - 'index' => $index, + 'index' => count($this->_args), 'required' => false ); $options = array_merge($defaults, $params); - $this->_args[$options['index']] = $options; + $index = $options['index']; + unset($options['index']); + + $this->_args[$index] = new ConsoleInputArgument($options); return $this; } @@ -336,9 +338,9 @@ class ConsoleOptionParser { } } foreach ($this->_args as $i => $arg) { - if ($arg['required'] && !isset($args[$i])) { + if ($arg->isRequired() && !isset($args[$i])) { throw new RuntimeException( - sprintf(__('Missing required arguments. %s is required.'), $arg['name']) + sprintf(__('Missing required arguments. %s is required.'), $arg->name()) ); } } @@ -400,14 +402,14 @@ class ConsoleOptionParser { } if (!empty($this->_args)) { $max = 0; - foreach ($this->_args as $description) { - $max = (strlen($description['name']) > $max) ? strlen($description['name']) : $max; + foreach ($this->_args as $argument) { + $max = (strlen($argument->name) > $max) ? strlen($argument->name) : $max; } $max += 2; $out[] = 'Arguments:'; $out[] = ''; - foreach ($this->_args as $description) { - $out[] = $this->_argumentHelp($description, $max); + foreach ($this->_args as $argument) { + $out[] = $argument->help($max); } $out[] = ''; } @@ -432,33 +434,12 @@ class ConsoleOptionParser { foreach ($this->_options as $option) { $usage[] = $option->usage(); } - foreach ($this->_args as $definition) { - $name = $definition['name']; - if (!$definition['required']) { - $name = '[' . $name . ']'; - } - $usage[] = $name; + foreach ($this->_args as $argument) { + $usage[] = $argument->usage(); } return implode(' ', $usage); } -/** - * Generate the usage for a single argument. - * - * @return string - */ - protected function _argumentHelp($definition, $width) { - $name = $definition['name']; - if (strlen($name) < $width) { - $name = str_pad($name, $width, ' '); - } - $optional = ''; - if (!$definition['required']) { - $optional = ' (optional)'; - } - return sprintf('%s%s%s', $name, $definition['help'], $optional); - } - /** * Generate help for a single subcommand. * @@ -655,3 +636,86 @@ class ConsoleInputOption { return (bool) $this->boolean; } } + + + +/** + * An object to represent a single argument used in the command line. + * ConsoleOptionParser creates these when you use addArgument() + * + * @see ConsoleOptionParser::addArgument() + * @package cake.console + */ +class ConsoleInputArgument { + +/** + * Make a new Input Argument + * + * @param mixed $name The long name of the option, or an array with all the properites. + * @param string $help The help text for this option + * @param boolean $required Whether this argument is required. Missing required args will trigger exceptions + * @param arraty $choices Valid choices for this option. + * @return void + */ + public function __construct($name, $help = '', $required = false, $choices = array()) { + if (is_array($name) && isset($name['name'])) { + foreach ($name as $key => $value) { + $this->{$key} = $value; + } + } else { + $this->name = $name; + $this->help = $help; + $this->required = $required; + $this->choices = $choices; + } + } + +/** + * Get the name of the argument + * + * @return void + */ + public function name() { + return $this->name; + } + +/** + * Generate the help for this this argument. + * + * @param int $width The width to make the name of the option. + * @return string + */ + public function help($width = 0) { + $name = $this->name; + if (strlen($name) < $width) { + $name = str_pad($name, $width, ' '); + } + $optional = ''; + if (!$this->isRequired()) { + $optional = ' (optional)'; + } + return sprintf('%s%s%s', $name, $this->help, $optional); + } + +/** + * Get the usage value for this argument + * + * @return string + */ + public function usage() { + $name = $this->name; + if (!$this->isRequired()) { + $name = '[' . $name . ']'; + } + return $name; + } + +/** + * Check if this argument is a required argument + * + * @return boolean + */ + public function isRequired() { + return (bool) $this->required; + } +} From 5e8222ef3906a6132b992939487a9c87848d9ab2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 14:37:22 -0400 Subject: [PATCH 076/207] Pulling out classes into separate files. --- cake/console/console_input_argument.php | 99 ++++++++++++++ cake/console/console_input_option.php | 108 +++++++++++++++ cake/console/console_option_parser.php | 175 +----------------------- 3 files changed, 209 insertions(+), 173 deletions(-) create mode 100644 cake/console/console_input_argument.php create mode 100644 cake/console/console_input_option.php diff --git a/cake/console/console_input_argument.php b/cake/console/console_input_argument.php new file mode 100644 index 000000000..3c0c64868 --- /dev/null +++ b/cake/console/console_input_argument.php @@ -0,0 +1,99 @@ + $value) { + $this->{$key} = $value; + } + } else { + $this->name = $name; + $this->help = $help; + $this->required = $required; + $this->choices = $choices; + } + } + +/** + * Get the name of the argument + * + * @return string + */ + public function name() { + return $this->name; + } + +/** + * Generate the help for this this argument. + * + * @param int $width The width to make the name of the option. + * @return string + */ + public function help($width = 0) { + $name = $this->name; + if (strlen($name) < $width) { + $name = str_pad($name, $width, ' '); + } + $optional = ''; + if (!$this->isRequired()) { + $optional = ' (optional)'; + } + return sprintf('%s%s%s', $name, $this->help, $optional); + } + +/** + * Get the usage value for this argument + * + * @return string + */ + public function usage() { + $name = $this->name; + if (!$this->isRequired()) { + $name = '[' . $name . ']'; + } + return $name; + } + +/** + * Check if this argument is a required argument + * + * @return boolean + */ + public function isRequired() { + return (bool) $this->required; + } +} \ No newline at end of file diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php new file mode 100644 index 000000000..1827bf32d --- /dev/null +++ b/cake/console/console_input_option.php @@ -0,0 +1,108 @@ + $value) { + $this->{$key} = $value; + } + } else { + $this->name = $name; + $this->short = $short; + $this->help = $help; + $this->boolean = $boolean; + $this->default = $default; + $this->choices = $choices; + } + } + +/** + * Generate the help for this this option. + * + * @param int $width The width to make the name of the option. + * @return string + */ + public function help($width = 0) { + $default = $short = ''; + if (!empty($this->default) && $this->default !== true) { + $default = sprintf(__(' (default: %s)'), $this->default); + } + if (!empty($this->short)) { + $short = ', -' . $this->short; + } + $name = sprintf('--%s%s', $this->name, $short); + if (strlen($name) < $width) { + $name = str_pad($name, $width, ' '); + } + return sprintf('%s%s%s', $name, $this->help, $default); + } + +/** + * Get the usage value for this option + * + * @return string + */ + public function usage() { + $name = empty($this->short) ? '--' . $this->name : '-' . $this->short; + $default = ''; + if (!empty($this->default) && $this->default !== true) { + $default = ' ' . $this->default; + } + return sprintf('[%s%s]', $name, $default); + } + +/** + * Get the default value for this option + * + * @return void + */ + public function defaultValue() { + return $this->default; + } + +/** + * Check if this option is a boolean option + * + * @return boolean + */ + public function isBoolean() { + return (bool) $this->boolean; + } +} diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 53c8c21b8..aab7429ad 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -17,6 +17,8 @@ * @since CakePHP(tm) v 2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ +require_once 'console_input_option.php'; +require_once 'console_input_argument.php'; /** * Handles parsing the ARGV in the command line and provides support @@ -546,176 +548,3 @@ class ConsoleOptionParser { return isset($this->_tokens[0]) ? $this->_tokens[0] : ''; } } - - -/** - * An object to represent a single option used in the command line. - * ConsoleOptionParser creates these when you use addOption() - * - * @see ConsoleOptionParser::addOption() - * @package cake.console - */ -class ConsoleInputOption { - -/** - * Make a new Input Option - * - * @param mixed $name The long name of the option, or an array with all the properites. - * @param string $short The short alias for this option - * @param string $help The help text for this option - * @param boolean $boolean Whether this option is a boolean option. Boolean options don't consume extra tokens - * @param string $default The default value for this option. - * @param arraty $choices Valid choices for this option. - * @return void - */ - public function __construct($name, $short = null, $help = '', $boolean = false, $default = '', $choices = array()) { - if (is_array($name) && isset($name['name'])) { - foreach ($name as $key => $value) { - $this->{$key} = $value; - } - } else { - $this->name = $name; - $this->short = $short; - $this->help = $help; - $this->boolean = $boolean; - $this->default = $default; - $this->choices = $choices; - } - } - -/** - * Generate the help for this this option. - * - * @param int $width The width to make the name of the option. - * @return string - */ - public function help($width = 0) { - $default = $short = ''; - if (!empty($this->default) && $this->default !== true) { - $default = sprintf(__(' (default: %s)'), $this->default); - } - if (!empty($this->short)) { - $short = ', -' . $this->short; - } - $name = sprintf('--%s%s', $this->name, $short); - if (strlen($name) < $width) { - $name = str_pad($name, $width, ' '); - } - return sprintf('%s%s%s', $name, $this->help, $default); - } - -/** - * Get the usage value for this option - * - * @return string - */ - public function usage() { - $name = empty($this->short) ? '--' . $this->name : '-' . $this->short; - $default = ''; - if (!empty($this->default) && $this->default !== true) { - $default = ' ' . $this->default; - } - return sprintf('[%s%s]', $name, $default); - } - -/** - * Get the default value for this option - * - * @return void - */ - public function defaultValue() { - return $this->default; - } - -/** - * Check if this option is a boolean option - * - * @return boolean - */ - public function isBoolean() { - return (bool) $this->boolean; - } -} - - - -/** - * An object to represent a single argument used in the command line. - * ConsoleOptionParser creates these when you use addArgument() - * - * @see ConsoleOptionParser::addArgument() - * @package cake.console - */ -class ConsoleInputArgument { - -/** - * Make a new Input Argument - * - * @param mixed $name The long name of the option, or an array with all the properites. - * @param string $help The help text for this option - * @param boolean $required Whether this argument is required. Missing required args will trigger exceptions - * @param arraty $choices Valid choices for this option. - * @return void - */ - public function __construct($name, $help = '', $required = false, $choices = array()) { - if (is_array($name) && isset($name['name'])) { - foreach ($name as $key => $value) { - $this->{$key} = $value; - } - } else { - $this->name = $name; - $this->help = $help; - $this->required = $required; - $this->choices = $choices; - } - } - -/** - * Get the name of the argument - * - * @return void - */ - public function name() { - return $this->name; - } - -/** - * Generate the help for this this argument. - * - * @param int $width The width to make the name of the option. - * @return string - */ - public function help($width = 0) { - $name = $this->name; - if (strlen($name) < $width) { - $name = str_pad($name, $width, ' '); - } - $optional = ''; - if (!$this->isRequired()) { - $optional = ' (optional)'; - } - return sprintf('%s%s%s', $name, $this->help, $optional); - } - -/** - * Get the usage value for this argument - * - * @return string - */ - public function usage() { - $name = $this->name; - if (!$this->isRequired()) { - $name = '[' . $name . ']'; - } - return $name; - } - -/** - * Check if this argument is a required argument - * - * @return boolean - */ - public function isRequired() { - return (bool) $this->required; - } -} From 8261581b3b7584b6f8f7ae68e931a11da901265d Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 14:41:08 -0400 Subject: [PATCH 077/207] Pulling out some duplicated code into methods. --- cake/console/console_input_option.php | 9 +++++++++ cake/console/console_option_parser.php | 25 +++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php index 1827bf32d..a5e8fa4b7 100644 --- a/cake/console/console_input_option.php +++ b/cake/console/console_input_option.php @@ -53,6 +53,15 @@ class ConsoleInputOption { } } +/** + * Get the name of the argument + * + * @return string + */ + public function name() { + return $this->name; + } + /** * Generate the help for this this option. * diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index aab7429ad..0e95e2349 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -390,11 +390,7 @@ class ConsoleOptionParser { } if (!empty($this->_options)) { - $max = 0; - foreach ($this->_options as $description) { - $max = (strlen($description->name) > $max) ? strlen($description->name) : $max; - } - $max += 8; + $max = $this->_getMaxLength($this->_options) + 8; $out[] = 'Options:'; $out[] = ''; foreach ($this->_options as $option) { @@ -403,11 +399,7 @@ class ConsoleOptionParser { $out[] = ''; } if (!empty($this->_args)) { - $max = 0; - foreach ($this->_args as $argument) { - $max = (strlen($argument->name) > $max) ? strlen($argument->name) : $max; - } - $max += 2; + $max = $this->_getMaxLength($this->_args) + 2; $out[] = 'Arguments:'; $out[] = ''; foreach ($this->_args as $argument) { @@ -547,4 +539,17 @@ class ConsoleOptionParser { protected function _nextToken() { return isset($this->_tokens[0]) ? $this->_tokens[0] : ''; } + +/** + * Iterate over a collection and find the longest named thing. + * + * @return integer + */ + protected function _getMaxLength($collection) { + $max = 0; + foreach ($collection as $item) { + $max = (strlen($item->name()) > $max) ? strlen($item->name()) : $max; + } + return $max; + } } From dc9a05d49c55a9e6e3c87795819ce12bbaaeb689 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 14:50:20 -0400 Subject: [PATCH 078/207] Extracting subcommand as a separate object. This allows the internals of ConsoleOptionParser to be more uniform and consistent. --- cake/console/console_input_subcommand.php | 84 +++++++++++++++++++++++ cake/console/console_option_parser.php | 30 ++------ 2 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 cake/console/console_input_subcommand.php diff --git a/cake/console/console_input_subcommand.php b/cake/console/console_input_subcommand.php new file mode 100644 index 000000000..6ee2ddfd4 --- /dev/null +++ b/cake/console/console_input_subcommand.php @@ -0,0 +1,84 @@ + $value) { + $this->{$key} = $value; + } + } else { + $this->name = $name; + $this->help = $help; + $this->parser = $parser; + } + } + +/** + * Get the name of the subcommand + * + * @return string + */ + public function name() { + return $this->name; + } + +/** + * Generate the help for this this subcommand. + * + * @param int $width The width to make the name of the subcommand. + * @return string + */ + public function help($width = 0) { + $name = $this->name; + if (strlen($name) < $width) { + $name = str_pad($name, $width, ' '); + } + return $name . $this->help; + } + +/** + * Get the usage value for this option + * + * @return string + */ + public function parser() { + if ($this->parser instanceof ConsoleOptionParser) { + return $this->parser; + } + return false; + } +} diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 0e95e2349..ccc1670cc 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -19,6 +19,7 @@ */ require_once 'console_input_option.php'; require_once 'console_input_argument.php'; +require_once 'console_input_subcommand.php'; /** * Handles parsing the ARGV in the command line and provides support @@ -297,7 +298,7 @@ class ConsoleOptionParser { 'parser' => null ); $options = array_merge($defaults, $params); - $this->_subcommands[$name] = $options; + $this->_subcommands[$name] = new ConsoleInputSubcommand($options); return $this; } @@ -361,9 +362,9 @@ class ConsoleOptionParser { public function help($subcommand = null) { if ( isset($this->_subcommands[$subcommand]) && - $this->_subcommands[$subcommand]['parser'] instanceof self + $this->_subcommands[$subcommand]->parser() instanceof self ) { - $subparser = $this->_subcommands[$subcommand]['parser']; + $subparser = $this->_subcommands[$subcommand]->parser(); $subparser->command($this->command() . ' ' . $subparser->command()); return $subparser->help(); } @@ -378,13 +379,9 @@ class ConsoleOptionParser { 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); + $max = $this->_getMaxLength($this->_subcommands) + 2; + foreach ($this->_subcommands as $command) { + $out[] = $command->help($max); } $out[] = ''; } @@ -434,19 +431,6 @@ class ConsoleOptionParser { return implode(' ', $usage); } -/** - * 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']; - } - /** * Parse the value for a long option out of $this->_tokens. Will handle * options with an `=` in them. From 49006b2bbbefc2077637e53b672737ef8269fb03 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 14:51:21 -0400 Subject: [PATCH 079/207] Adding accessor method. --- cake/console/console_option_parser.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index ccc1670cc..d37fd6898 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -320,6 +320,15 @@ class ConsoleOptionParser { return $this->_options; } +/** + * Get the array of defined subcommands + * + * @return array + */ + public function subcommands() { + return $this->_subcommands; + } + /** * Parse the argv array into a set of params and args. * From 8b46645f6a8746af87a858ab8cf2859a533d6509 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 14:55:15 -0400 Subject: [PATCH 080/207] Making properties protected. --- cake/console/console_input_argument.php | 22 +++++++------ cake/console/console_input_option.php | 40 ++++++++++++----------- cake/console/console_input_subcommand.php | 24 ++++++++------ 3 files changed, 47 insertions(+), 39 deletions(-) diff --git a/cake/console/console_input_argument.php b/cake/console/console_input_argument.php index 3c0c64868..9e2ea6009 100644 --- a/cake/console/console_input_argument.php +++ b/cake/console/console_input_argument.php @@ -26,6 +26,8 @@ */ class ConsoleInputArgument { + protected $_name, $_help, $_required, $_choices; + /** * Make a new Input Argument * @@ -38,13 +40,13 @@ class ConsoleInputArgument { public function __construct($name, $help = '', $required = false, $choices = array()) { if (is_array($name) && isset($name['name'])) { foreach ($name as $key => $value) { - $this->{$key} = $value; + $this->{'_' . $key} = $value; } } else { - $this->name = $name; - $this->help = $help; - $this->required = $required; - $this->choices = $choices; + $this->_name = $name; + $this->_help = $help; + $this->_required = $required; + $this->_choices = $choices; } } @@ -54,7 +56,7 @@ class ConsoleInputArgument { * @return string */ public function name() { - return $this->name; + return $this->_name; } /** @@ -64,7 +66,7 @@ class ConsoleInputArgument { * @return string */ public function help($width = 0) { - $name = $this->name; + $name = $this->_name; if (strlen($name) < $width) { $name = str_pad($name, $width, ' '); } @@ -72,7 +74,7 @@ class ConsoleInputArgument { if (!$this->isRequired()) { $optional = ' (optional)'; } - return sprintf('%s%s%s', $name, $this->help, $optional); + return sprintf('%s%s%s', $name, $this->_help, $optional); } /** @@ -81,7 +83,7 @@ class ConsoleInputArgument { * @return string */ public function usage() { - $name = $this->name; + $name = $this->_name; if (!$this->isRequired()) { $name = '[' . $name . ']'; } @@ -94,6 +96,6 @@ class ConsoleInputArgument { * @return boolean */ public function isRequired() { - return (bool) $this->required; + return (bool) $this->_required; } } \ No newline at end of file diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php index a5e8fa4b7..aff525d28 100644 --- a/cake/console/console_input_option.php +++ b/cake/console/console_input_option.php @@ -27,6 +27,8 @@ */ class ConsoleInputOption { + protected $_name, $_short, $_help, $_boolean, $_default, $_choices; + /** * Make a new Input Option * @@ -41,15 +43,15 @@ class ConsoleInputOption { public function __construct($name, $short = null, $help = '', $boolean = false, $default = '', $choices = array()) { if (is_array($name) && isset($name['name'])) { foreach ($name as $key => $value) { - $this->{$key} = $value; + $this->{'_' . $key} = $value; } } else { - $this->name = $name; - $this->short = $short; - $this->help = $help; - $this->boolean = $boolean; - $this->default = $default; - $this->choices = $choices; + $this->_name = $name; + $this->_short = $short; + $this->_help = $help; + $this->_boolean = $boolean; + $this->_default = $default; + $this->_choices = $choices; } } @@ -59,7 +61,7 @@ class ConsoleInputOption { * @return string */ public function name() { - return $this->name; + return $this->_name; } /** @@ -70,17 +72,17 @@ class ConsoleInputOption { */ public function help($width = 0) { $default = $short = ''; - if (!empty($this->default) && $this->default !== true) { - $default = sprintf(__(' (default: %s)'), $this->default); + if (!empty($this->_default) && $this->_default !== true) { + $default = sprintf(__(' (default: %s)'), $this->_default); } - if (!empty($this->short)) { - $short = ', -' . $this->short; + if (!empty($this->_short)) { + $short = ', -' . $this->_short; } - $name = sprintf('--%s%s', $this->name, $short); + $name = sprintf('--%s%s', $this->_name, $short); if (strlen($name) < $width) { $name = str_pad($name, $width, ' '); } - return sprintf('%s%s%s', $name, $this->help, $default); + return sprintf('%s%s%s', $name, $this->_help, $default); } /** @@ -89,10 +91,10 @@ class ConsoleInputOption { * @return string */ public function usage() { - $name = empty($this->short) ? '--' . $this->name : '-' . $this->short; + $name = empty($this->_short) ? '--' . $this->_name : '-' . $this->_short; $default = ''; - if (!empty($this->default) && $this->default !== true) { - $default = ' ' . $this->default; + if (!empty($this->_default) && $this->_default !== true) { + $default = ' ' . $this->_default; } return sprintf('[%s%s]', $name, $default); } @@ -103,7 +105,7 @@ class ConsoleInputOption { * @return void */ public function defaultValue() { - return $this->default; + return $this->_default; } /** @@ -112,6 +114,6 @@ class ConsoleInputOption { * @return boolean */ public function isBoolean() { - return (bool) $this->boolean; + return (bool) $this->_boolean; } } diff --git a/cake/console/console_input_subcommand.php b/cake/console/console_input_subcommand.php index 6ee2ddfd4..ee71a2af7 100644 --- a/cake/console/console_input_subcommand.php +++ b/cake/console/console_input_subcommand.php @@ -27,6 +27,10 @@ */ class ConsoleInputSubcommand { + protected $_name; + protected $_help; + protected $_parser; + /** * Make a new Subcommand * @@ -38,12 +42,12 @@ class ConsoleInputSubcommand { public function __construct($name, $help = '', $parser = null) { if (is_array($name) && isset($name['name'])) { foreach ($name as $key => $value) { - $this->{$key} = $value; + $this->{'_' . $key} = $value; } } else { - $this->name = $name; - $this->help = $help; - $this->parser = $parser; + $this->_name = $name; + $this->_help = $help; + $this->_parser = $parser; } } @@ -53,7 +57,7 @@ class ConsoleInputSubcommand { * @return string */ public function name() { - return $this->name; + return $this->_name; } /** @@ -63,21 +67,21 @@ class ConsoleInputSubcommand { * @return string */ public function help($width = 0) { - $name = $this->name; + $name = $this->_name; if (strlen($name) < $width) { $name = str_pad($name, $width, ' '); } - return $name . $this->help; + return $name . $this->_help; } /** * Get the usage value for this option * - * @return string + * @return mixed Either false or a ConsoleOptionParser */ public function parser() { - if ($this->parser instanceof ConsoleOptionParser) { - return $this->parser; + if ($this->_parser instanceof ConsoleOptionParser) { + return $this->_parser; } return false; } From 56292b658c07d9605ca7d657bc2d69bd84a6840c Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 15:04:16 -0400 Subject: [PATCH 081/207] Adding option value validation to ConsoleOptionParser. --- cake/console/console_input_option.php | 17 +++++++++++++++++ cake/console/console_option_parser.php | 12 +++++++----- .../console/console_option_parser.test.php | 17 +++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php index aff525d28..0899d6df4 100644 --- a/cake/console/console_input_option.php +++ b/cake/console/console_input_option.php @@ -116,4 +116,21 @@ class ConsoleInputOption { public function isBoolean() { return (bool) $this->_boolean; } + +/** + * Check that a value is a valid choice for this option. + * + * @return boolean + */ + public function validChoice($value) { + if (empty($this->_choices)) { + return true; + } + if (!in_array($value, $this->_choices)) { + throw new InvalidArgumentException( + sprintf(__('"%s" is not a valid option for --%s'), $value, $this->_name) + ); + } + return true; + } } diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index d37fd6898..bbd3533a9 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -454,7 +454,7 @@ class ConsoleOptionParser { list($name, $value) = explode('=', $name, 2); array_unshift($this->_tokens, $value); } - return $this->_parseOptionName($name, $params); + return $this->_parseOption($name, $params); } /** @@ -476,7 +476,7 @@ class ConsoleOptionParser { } } $name = $this->_shortOptions[$key]; - return $this->_parseOptionName($name, $params); + return $this->_parseOption($name, $params); } /** @@ -486,7 +486,7 @@ class ConsoleOptionParser { * @param array $params The params to append the parsed value into * @return array Params with $option added in. */ - protected function _parseOptionName($name, $params) { + protected function _parseOption($name, $params) { if (!isset($this->_options[$name])) { throw new InvalidArgumentException(sprintf(__('Unknown option `%s`'), $name)); } @@ -498,8 +498,10 @@ class ConsoleOptionParser { } else { $value = $option->defaultValue(); } - $params[$name] = $value; - return $params; + if ($option->validChoice($value)) { + $params[$name] = $value; + return $params; + } } /** diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 6efcb2c3a..be144f629 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -194,6 +194,23 @@ class ConsoleOptionParserTest extends CakeTestCase { $result = $parser->parse(array('--fail', 'other')); } +/** + * test that options with choices enforce them. + * + * @expectedException InvalidArgumentException + * @return void + */ + function testOptionWithChoices() { + $parser = new ConsoleOptionParser(); + $parser->addOption('name', array('choices' => array('mark', 'jose'))); + + $result = $parser->parse(array('--name', 'mark')); + $expected = array('name' => 'mark'); + $this->assertEquals($expected, $result[0], 'Got the correct value.'); + + $result = $parser->parse(array('--name', 'jimmy')); + } + /** * test positional argument parsing. * From 8ee158226a358046d1c5a2d63c6da3702c1e9e5d Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 15:46:37 -0400 Subject: [PATCH 082/207] Changing error message. --- cake/console/console_input_option.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php index 0899d6df4..466012ae1 100644 --- a/cake/console/console_input_option.php +++ b/cake/console/console_input_option.php @@ -128,7 +128,7 @@ class ConsoleInputOption { } if (!in_array($value, $this->_choices)) { throw new InvalidArgumentException( - sprintf(__('"%s" is not a valid option for --%s'), $value, $this->_name) + sprintf(__('"%s" is not a valid value for --%s'), $value, $this->_name) ); } return true; From 4e910121441a187f1ef308948a35fbc625396707 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 15:47:22 -0400 Subject: [PATCH 083/207] Adding validation for positional arguments. You can use choices option to give a list of valid values that an argument can have. --- cake/console/console_input_argument.php | 17 +++++++++++++++++ cake/console/console_option_parser.php | 8 +++++--- .../console/console_option_parser.test.php | 17 +++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/cake/console/console_input_argument.php b/cake/console/console_input_argument.php index 9e2ea6009..b7bd479fc 100644 --- a/cake/console/console_input_argument.php +++ b/cake/console/console_input_argument.php @@ -98,4 +98,21 @@ class ConsoleInputArgument { public function isRequired() { return (bool) $this->_required; } + +/** + * Check that $value is a valid choice for this argument. + * + * @return boolean + */ + public function validChoice($value) { + if (empty($this->_choices)) { + return true; + } + if (!in_array($value, $this->_choices)) { + throw new InvalidArgumentException( + sprintf(__('"%s" is not a valid value for %s'), $value, $this->_name) + ); + } + return true; + } } \ No newline at end of file diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index bbd3533a9..29730ed6f 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -516,13 +516,15 @@ class ConsoleOptionParser { array_push($args, $argument); return $args; } - $position = 0; $next = count($args); if (!isset($this->_args[$next])) { throw new InvalidArgumentException(__('Too many arguments.')); } - array_push($args, $argument); - return $args; + $index = max($next - 1, 0); + if ($this->_args[$index]->validChoice($argument)) { + array_push($args, $argument); + return $args; + } } /** diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index be144f629..4e9e45d08 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -268,6 +268,23 @@ class ConsoleOptionParserTest extends CakeTestCase { $parser->parse(array('one')); } +/** + * test that arguments with choices enforce them. + * + * @expectedException InvalidArgumentException + * @return void + */ + function testPositionalArgWithChoices() { + $parser = new ConsoleOptionParser(); + $parser->addArgument('name', array('choices' => array('mark', 'jose'))); + + $result = $parser->parse(array('mark')); + $expected = array('mark'); + $this->assertEquals($expected, $result[1], 'Got the correct value.'); + + $result = $parser->parse(array('jimmy')); + } + /** * Test adding multiple arguments. * From ed0476861eabb983bbaac75e68ac4aa4399aae76 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 15:48:38 -0400 Subject: [PATCH 084/207] Adding documentation for choices options. --- cake/console/console_option_parser.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 29730ed6f..9edc735f6 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -193,6 +193,8 @@ class ConsoleOptionParser { * - `boolean` - The option uses no value, its just a boolean switch. Defaults to false. * If an option is defined as boolean, it will always be added to the parsed params. If no present * it will be false, if present it will be true. + * - `choices` A list of valid choices for this option. If left empty all values are valid.. + * An exception will be raised when parse() encounters an invalid value. * * @param string $name The long name you want to the value to be parsed out as when options are parsed. * @param array $params An array of parameters that define the behavior of the option @@ -225,6 +227,8 @@ class ConsoleOptionParser { * - `index` The index for the arg, if left undefined the argument will be put * onto the end of the arguments. If you define the same index twice the first * option will be overwritten. + * - `choices` A list of valid choices for this argument. If left empty all values are valid.. + * An exception will be raised when parse() encounters an invalid value. * * @param string $name The name of the argument. * @param array $params Parameters for the argument, see above. From b398877887c8190f296025b63b2c7dced6f1bcf1 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 15:49:01 -0400 Subject: [PATCH 085/207] Adding missing default value. --- cake/console/console_option_parser.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 9edc735f6..3154514ca 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -239,7 +239,8 @@ class ConsoleOptionParser { 'name' => $name, 'help' => '', 'index' => count($this->_args), - 'required' => false + 'required' => false, + 'choices' => array() ); $options = array_merge($defaults, $params); $index = $options['index']; From ef027f6d0e1d34d05cacbeeb6bb8c2a63f7acb98 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 16:07:41 -0400 Subject: [PATCH 086/207] Adding choices to usage and generated help text. --- cake/console/console_input_argument.php | 6 ++++ cake/console/console_input_option.php | 6 ++++ .../console/console_option_parser.test.php | 34 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/cake/console/console_input_argument.php b/cake/console/console_input_argument.php index b7bd479fc..65358689e 100644 --- a/cake/console/console_input_argument.php +++ b/cake/console/console_input_argument.php @@ -74,6 +74,9 @@ class ConsoleInputArgument { if (!$this->isRequired()) { $optional = ' (optional)'; } + if (!empty($this->_choices)) { + $optional .= sprintf(' (choices: %s)', implode('|', $this->_choices)); + } return sprintf('%s%s%s', $name, $this->_help, $optional); } @@ -84,6 +87,9 @@ class ConsoleInputArgument { */ public function usage() { $name = $this->_name; + if (!empty($this->_choices)) { + $name = implode('|', $this->_choices); + } if (!$this->isRequired()) { $name = '[' . $name . ']'; } diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php index 466012ae1..852abadd2 100644 --- a/cake/console/console_input_option.php +++ b/cake/console/console_input_option.php @@ -75,6 +75,9 @@ class ConsoleInputOption { if (!empty($this->_default) && $this->_default !== true) { $default = sprintf(__(' (default: %s)'), $this->_default); } + if (!empty($this->_choices)) { + $default .= sprintf(' (choices: %s)', implode('|', $this->_choices)); + } if (!empty($this->_short)) { $short = ', -' . $this->_short; } @@ -96,6 +99,9 @@ class ConsoleInputOption { if (!empty($this->_default) && $this->_default !== true) { $default = ' ' . $this->_default; } + if (!empty($this->_choices)) { + $default = ' ' . implode('|', $this->_choices); + } return sprintf('[%s%s]', $name, $default); } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 4e9e45d08..a764d1136 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -368,6 +368,40 @@ cake mycommand [-h] [--test] model [other_longer] model The model to make. other_longer Another argument. (optional) +TEXT; + $this->assertEquals($expected, $result, 'Help does not match'); + } + +/** + * test help() with options and arguments that have choices. + * + * @return void + */ + function testHelpWithChoices() { + $parser = new ConsoleOptionParser('mycommand', false); + $parser->addOption('test', array('help' => 'A test option.', 'choices' => array('one', 'two'))) + ->addArgument('type', array( + 'help' => 'Resource type.', + 'choices' => array('aco', 'aro'), + 'required' => true + )) + ->addArgument('other_longer', array('help' => 'Another argument.')); + + $result = $parser->help(); + $expected = <<Usage: +cake mycommand [-h] [--test one|two] aco|aro [other_longer] + +Options: + +--help, -h Display this help. +--test A test option. (choices: one|two) + +Arguments: + +type Resource type. (choices: aco|aro) +other_longer Another argument. (optional) + TEXT; $this->assertEquals($expected, $result, 'Help does not match'); } From 1fec75e6ee39e34117dfa88a8e9abf5d0d8ff018 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 16:19:34 -0400 Subject: [PATCH 087/207] Adding omitted __() --- cake/console/console_input_argument.php | 4 ++-- cake/console/console_input_option.php | 2 +- cake/console/libs/i18n.php | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cake/console/console_input_argument.php b/cake/console/console_input_argument.php index 65358689e..f212d2375 100644 --- a/cake/console/console_input_argument.php +++ b/cake/console/console_input_argument.php @@ -72,10 +72,10 @@ class ConsoleInputArgument { } $optional = ''; if (!$this->isRequired()) { - $optional = ' (optional)'; + $optional = __(' (optional)'); } if (!empty($this->_choices)) { - $optional .= sprintf(' (choices: %s)', implode('|', $this->_choices)); + $optional .= sprintf(__(' (choices: %s)'), implode('|', $this->_choices)); } return sprintf('%s%s%s', $name, $this->_help, $optional); } diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php index 852abadd2..af66be87b 100644 --- a/cake/console/console_input_option.php +++ b/cake/console/console_input_option.php @@ -76,7 +76,7 @@ class ConsoleInputOption { $default = sprintf(__(' (default: %s)'), $this->_default); } if (!empty($this->_choices)) { - $default .= sprintf(' (choices: %s)', implode('|', $this->_choices)); + $default .= sprintf(__(' (choices: %s)'), implode('|', $this->_choices)); } if (!empty($this->_short)) { $short = ', -' . $this->_short; diff --git a/cake/console/libs/i18n.php b/cake/console/libs/i18n.php index 24cc1d8cd..afa601dc4 100644 --- a/cake/console/libs/i18n.php +++ b/cake/console/libs/i18n.php @@ -110,7 +110,9 @@ class I18nShell extends Shell { public function getOptionParser() { $parser = parent::getOptionParser(); return $parser->description(__('I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')) - ->addSubcommand('initdb', array('help' => __('Initialize the i18n table.'))) + ->addSubcommand('initdb', array( + 'help' => __('Initialize the i18n table.') + )) ->addSubcommand('extract', array( 'help' => __('Extract the po translations from your application'), 'parser' => $this->Extract->getOptionParser() From 7a31eb41e93726d05932b7789f22e2afe8e81382 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 16:44:22 -0400 Subject: [PATCH 088/207] Adding ability to define a subcommand parser as an array. This makes defining subcommands a bit easier as you don't have to build a pile of objects first. --- cake/console/console_input_subcommand.php | 4 +++ cake/console/console_option_parser.php | 31 ++++++++++++++++++ .../console/console_option_parser.test.php | 32 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/cake/console/console_input_subcommand.php b/cake/console/console_input_subcommand.php index ee71a2af7..e9f623292 100644 --- a/cake/console/console_input_subcommand.php +++ b/cake/console/console_input_subcommand.php @@ -49,6 +49,10 @@ class ConsoleInputSubcommand { $this->_help = $help; $this->_parser = $parser; } + if (is_array($this->_parser)) { + $this->_parser['command'] = $this->_name; + $this->_parser = ConsoleOptionParser::buildFromArray($this->_parser); + } } /** diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 3154514ca..dc581bd9b 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -136,6 +136,37 @@ class ConsoleOptionParser { } } +/** + * Static factory method for creating new OptionParsers so you can chain methods off of them. + * + * @return ConsoleOptionParser + */ + public static function create($command, $defaultOptions = true) { + return new ConsoleOptionParser($command, $defaultOptions); + } + +/** + * Build a parser from an array. + * + * @return ConsoleOptionParser + */ + public static function buildFromArray($spec) { + $parser = new ConsoleOptionParser($spec['command']); + if (!empty($spec['arguments'])) { + $parser->addArguments($spec['arguments']); + } + if (!empty($spec['options'])) { + $parser->addOptions($spec['options']); + } + if (!empty($spec['description'])) { + $parser->description($spec['description']); + } + if (!empty($spec['epilog'])) { + $parser->epilog($spec['epilog']); + } + return $parser; + } + /** * Get or set the command name for shell/task * diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index a764d1136..ceebac2dc 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -496,4 +496,36 @@ cake mycommand method [-h] [--connection] TEXT; $this->assertEquals($expected, $result, 'Help is not correct.'); } + +/** + * test building a parser from an array. + * + * @return void + */ + function testBuildFromArray() { + $spec = array( + 'command' => 'test', + 'arguments' => array( + 'name' => array('help' => 'The name'), + 'other' => array('help' => 'The other arg') + ), + 'options' => array( + 'name' => array('help' => 'The name'), + 'other' => array('help' => 'The other arg') + ), + 'description' => 'description text', + 'epilog' => 'epilog text' + ); + $parser = ConsoleOptionParser::buildFromArray($spec); + + $this->assertEquals($spec['description'], $parser->description()); + $this->assertEquals($spec['epilog'], $parser->epilog()); + + $options = $parser->options(); + $this->assertTrue(isset($options['name'])); + $this->assertTrue(isset($options['other'])); + + $args = $parser->arguments(); + $this->assertEquals(2, count($args)); + } } \ No newline at end of file From 412864c510c70fd4e9d47faa9778d1e0f9efd882 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 17:15:44 -0400 Subject: [PATCH 089/207] Porting AclShell help to use the OptionParser. --- cake/console/libs/acl.php | 221 +++++++++++------- .../console/console_option_parser.test.php | 2 +- 2 files changed, 133 insertions(+), 90 deletions(-) diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php index 99f117ad2..d56a7a2ac 100644 --- a/cake/console/libs/acl.php +++ b/cake/console/libs/acl.php @@ -66,6 +66,7 @@ class AclShell extends Shell { * */ public function startup() { + parent::startup(); if (isset($this->params['connection'])) { $this->connection = $this->params['connection']; } @@ -373,98 +374,140 @@ class AclShell extends Shell { } /** - * Show help screen. + * Get the option parser. * + * @return void */ - public function help() { - $head = "-----------------------------------------------\n"; - $head .= __('Usage: cake acl ...') . "\n"; - $head .= "-----------------------------------------------\n"; - $head .= __('Commands:') . "\n"; - - $commands = array( - 'create' => "create aro|aco \n" . - "\t" . __("Creates a new ACL object under the parent") . "\n" . - "\t" . __("specified by , an id/alias.") . "\n" . - "\t" . __("The and references can be") . "\n" . - "\t" . __("in one of the following formats:") . "\n\n" . - "\t\t- " . __(". - The node will be bound to a") . "\n" . - "\t\t" . __("specific record of the given model.") . "\n\n" . - "\t\t- " . __(" - The node will be given a string alias,") . "\n" . - "\t\t" . __(" (or path, in the case of )") . "\n" . - "\t\t " . __("i.e. 'John'. When used with ,") . "\n" . - "\t\t" . __("this takes the form of an alias path,") . "\n" . - "\t\t " . __("i.e. //.") . "\n\n" . - "\t" . __("To add a node at the root level,") . "\n" . - "\t" . __("enter 'root' or '/' as the parameter.") . "\n", - - 'delete' => "delete aro|aco \n" . - "\t" . __("Deletes the ACL object with the given reference") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'setparent' => "setParent aro|aco \n" . - "\t" . __("Moves the ACL object specified by beneath") . "\n" . - "\t" . __("the parent ACL object specified by .") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'getpath' => "getPath aro|aco \n" . - "\t" . __("Returns the path to the ACL object specified by . This command") . "\n" . - "\t" . __("is useful in determining the inhertiance of permissions for a certain") . "\n" . - "\t" . __("object in the tree.") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'check' => "check [] " . __("or") . " all\n" . - "\t" . __("Use this command to check ACL permissions.") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'grant' => "grant [] " . __("or") . " all\n" . - "\t" . __("Use this command to grant ACL permissions. Once executed, the ARO") . "\n" . - "\t" . __("specified (and its children, if any) will have ALLOW access to the") . "\n" . - "\t" . __("specified ACO action (and the ACO's children, if any).") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'deny' => "deny []" . __("or") . " all\n" . - "\t" . __("Use this command to deny ACL permissions. Once executed, the ARO") . "\n" . - "\t" . __("specified (and its children, if any) will have DENY access to the") . "\n" . - "\t" . __("specified ACO action (and the ACO's children, if any).") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'inherit' => "inherit []" . __("or") . " all\n" . - "\t" . __("Use this command to force a child ARO object to inherit its") . "\n" . - "\t" . __("permissions settings from its parent.") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'view' => "view aro|aco []\n" . - "\t" . __("The view command will return the ARO or ACO tree.") . "\n" . - "\t" . __("The optional node parameter allows you to return") . "\n" . - "\t" . __("only a portion of the requested tree.") . "\n" . - "\t" . __("For more detailed parameter usage info,") . "\n" . - "\t" . __("see help for the 'create' command."), - - 'initdb' => "initdb\n". - "\t" . __("Uses this command : cake schema run create DbAcl"), - - 'help' => "help []\n" . - "\t" . __("Displays this help message, or a message on a specific command.") + public function getOptionParser() { + $parser = parent::getOptionParser(); + + $type = array( + 'choices' => array('aro', 'aco'), + 'required' => true, + 'help' => __('Type of node to create.') ); - - $this->out($head); - if (!isset($this->args[0])) { - foreach ($commands as $cmd) { - $this->out("{$cmd}\n\n"); - } - } elseif (isset($commands[strtolower($this->args[0])])) { - $this->out($commands[strtolower($this->args[0])] . "\n\n"); - } else { - $this->out(sprintf(__("Command '%s' not found"), $this->args[0])); - } + + $parser->description('A console tool for managing the DbAcl') + ->addSubcommand('create', array( + 'help' => __('Create a new ACL node'), + 'parser' => array( + 'description' => __('Creates a new ACL object under the parent'), + 'arguments' => array( + 'type' => $type, + 'parent' => array( + 'help' => __('The node selector for the parent.'), + ), + 'alias' => array( + 'help' => __('The alias to use for the newly created node.') + ) + ) + ) + ))->addSubcommand('delete', array( + 'help' => __('Deletes the ACL object with the given reference'), + 'parser' => array( + 'description' => __('Delete an ACL node.'), + 'arguments' => array( + 'type' => $type, + 'node' => array( + 'help' => __('The node identifier to delete.'), + 'required' => true, + ) + ) + ) + ))->addSubcommand('setparent', array( + 'help' => __('Moves the ACL node under a new parent.'), + 'parser' => array( + 'description' => __('Moves the ACL object specified by beneath '), + 'arguments' => array( + 'type' => $type, + 'node' => array( + 'help' => __('The node to move'), + 'required' => true, + ), + 'parent' => array( + 'help' => __('The new parent for .'), + 'required' => true + ) + ) + ) + ))->addSubcommand('getpath', array( + 'help' => __('Print out the path to an ACL node.'), + 'parser' => array( + 'description' => __("Returns the path to the ACL object specified by . \nThis command is useful in determining the inhertiance of permissions for a \ncertain object in the tree." + ), + 'arguments' => array( + 'type' => $type, + 'node' => array( + 'help' => __('The node to get the path of'), + 'required' => true, + ) + ) + ) + ))->addSubcommand('check', array( + 'help' => __('Check the permissions between an ACO and ARO.'), + 'parser' => array( + 'description' => __("Use this command to grant ACL permissions. Once executed, the ARO \nspecified (and its children, if any) will have ALLOW access to the \nspecified ACO action (and the ACO's children, if any)."), + 'arguments' => array( + 'aro' => array('help' => __('ARO to check.'), 'required' => true), + 'aco' => array('help' => __('ACO to check.'), 'required' => true), + 'action' => array('help' => __('Action to check')) + ) + ) + ))->addSubcommand('grant', array( + 'help' => __('Grant an ARO permissions to an ACO.'), + 'parser' => array( + 'description' => __("Use this command to grant ACL permissions. Once executed, the ARO \nspecified (and its children, if any) will have ALLOW access to the \nspecified ACO action (and the ACO's children, if any)."), + 'arguments' => array( + 'aro' => array('help' => __('ARO to grant permission to.'), 'required' => true), + 'aco' => array('help' => __('ACO to grant access to.'), 'required' => true), + 'action' => array('help' => __('Action to grant')) + ) + ) + ))->addSubcommand('deny', array( + 'help' => __('Deny an ARO permissions to an ACO.'), + 'parser' => array( + 'description' => __("Use this command to deny ACL permissions. Once executed, the ARO \nspecified (and its children, if any) will have DENY access to the \nspecified ACO action (and the ACO's children, if any)."), + 'arguments' => array( + 'aro' => array('help' => __('ARO to deny.'), 'required' => true), + 'aco' => array('help' => __('ACO to deny.'), 'required' => true), + 'action' => array('help' => __('Action to deny')) + ) + ) + ))->addSubcommand('inherit', array( + 'help' => __('Inherit an ARO\'s parent permissions.'), + 'parser' => array( + 'description' => __("Use this command to force a child ARO object to inherit its \npermissions settings from its parent."), + 'arguments' => array( + 'aro' => array('help' => __('ARO to have permisssions inherit.'), 'required' => true), + 'aco' => array('help' => __('ACO to inherit permissions on.'), 'required' => true), + 'action' => array('help' => __('Action to inherit')) + ) + ) + ))->addSubcommand('view', array( + 'help' => __('View a tree or a single node\'s subtree.'), + 'parser' => array( + 'description' => __("The view command will return the ARO or ACO tree. \nThe optional node parameter allows you to return \nonly a portion of the requested tree."), + 'arguments' => array( + 'type' => $type, + 'node' => array('help' => __('The optional node to view the subtree of.')) + ) + ) + ))->addSubcommand('initdb', array( + 'help' => __('Initialize the DbAcl tables. Uses this command : cake schema run create DbAcl') + ))->epilog( + implode("\n", array( + 'Node and parent arguments can be in one of the following formats:', + '', + ' - . - The node will be bound to a specific record of the given model.', + '', + ' - - The node will be given a string alias (or path, in the case of )', + " i.e. 'John'. When used with , this takes the form of an alias path,", + " i.e. //.", + '', + "To add a node at the root level, enter 'root' or '/' as the parameter." + )) + ); + return $parser; } /** diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index ceebac2dc..37691f345 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -524,7 +524,7 @@ TEXT; $options = $parser->options(); $this->assertTrue(isset($options['name'])); $this->assertTrue(isset($options['other'])); - + $args = $parser->arguments(); $this->assertEquals(2, count($args)); } From b5630a5e308ca91f721ec3be40121e66e016a225 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 17:18:51 -0400 Subject: [PATCH 090/207] Making description() and epilog() accept arrays. --- cake/console/console_option_parser.php | 10 ++++++++-- .../tests/cases/console/console_option_parser.test.php | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index dc581bd9b..632969f53 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -184,11 +184,14 @@ class ConsoleOptionParser { /** * Get or set the description text for shell/task * - * @param string $text The text to set, or null if you want to read + * @param mixed $text The text to set, or null if you want to read. . If an array the text will be imploded with "\n" * @return mixed If reading, the value of the description. If setting $this will be returned */ public function description($text = null) { if ($text !== null) { + if (is_array($text)) { + $text = implode("\n", $text); + } $this->_description = $text; return $this; } @@ -199,11 +202,14 @@ class ConsoleOptionParser { * Get or set an epilog to the parser. The epilog is added to the end of * the options and arguments listing when help is generated. * - * @param string $text Text when setting or null when reading. + * @param mixed $text Text when setting or null when reading. If an array the text will be imploded with "\n" * @return mixed If reading, the value of the epilog. If setting $this will be returned. */ public function epilog($text = null) { if ($text !== null) { + if (is_array($text)) { + $text = implode("\n", $text); + } $this->_epilog = $text; return $this; } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 37691f345..b89f888b6 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -33,6 +33,9 @@ class ConsoleOptionParserTest extends CakeTestCase { $this->assertEquals($parser, $result, 'Setting description is not chainable'); $this->assertEquals('A test', $parser->description(), 'getting value is wrong.'); + + $result = $parser->description(array('A test', 'something')); + $this->assertEquals("A test\nsomething", $parser->description(), 'getting value is wrong.'); } /** @@ -46,6 +49,9 @@ class ConsoleOptionParserTest extends CakeTestCase { $this->assertEquals($parser, $result, 'Setting epilog is not chainable'); $this->assertEquals('A test', $parser->epilog(), 'getting value is wrong.'); + + $result = $parser->epilog(array('A test', 'something')); + $this->assertEquals("A test\nsomething", $parser->epilog(), 'getting value is wrong.'); } /** From ae04ae6e8a7d944d16288fb121747e0323ae2c34 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 17:19:14 -0400 Subject: [PATCH 091/207] Removing implode(), as its done in the OptionParser now. --- cake/console/libs/acl.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php index d56a7a2ac..c3a3ad7a3 100644 --- a/cake/console/libs/acl.php +++ b/cake/console/libs/acl.php @@ -495,7 +495,7 @@ class AclShell extends Shell { ))->addSubcommand('initdb', array( 'help' => __('Initialize the DbAcl tables. Uses this command : cake schema run create DbAcl') ))->epilog( - implode("\n", array( + array( 'Node and parent arguments can be in one of the following formats:', '', ' - . - The node will be bound to a specific record of the given model.', @@ -505,7 +505,7 @@ class AclShell extends Shell { " i.e. //.", '', "To add a node at the root level, enter 'root' or '/' as the parameter." - )) + ) ); return $parser; } From 822aeb3883abc4f80093c409b1e22c893bad3115 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 17:23:30 -0400 Subject: [PATCH 092/207] Reformatting help text. --- cake/console/libs/acl.php | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php index c3a3ad7a3..e26aec102 100644 --- a/cake/console/libs/acl.php +++ b/cake/console/libs/acl.php @@ -433,7 +433,10 @@ class AclShell extends Shell { ))->addSubcommand('getpath', array( 'help' => __('Print out the path to an ACL node.'), 'parser' => array( - 'description' => __("Returns the path to the ACL object specified by . \nThis command is useful in determining the inhertiance of permissions for a \ncertain object in the tree." + 'description' => array( + __("Returns the path to the ACL object specified by ."), + __("This command is useful in determining the inhertiance of permissions"), + __("for a certain object in the tree.") ), 'arguments' => array( 'type' => $type, @@ -446,7 +449,11 @@ class AclShell extends Shell { ))->addSubcommand('check', array( 'help' => __('Check the permissions between an ACO and ARO.'), 'parser' => array( - 'description' => __("Use this command to grant ACL permissions. Once executed, the ARO \nspecified (and its children, if any) will have ALLOW access to the \nspecified ACO action (and the ACO's children, if any)."), + 'description' => array( + __("Use this command to grant ACL permissions. Once executed, the ARO "), + __("specified (and its children, if any) will have ALLOW access to the"), + __("specified ACO action (and the ACO's children, if any).") + ), 'arguments' => array( 'aro' => array('help' => __('ARO to check.'), 'required' => true), 'aco' => array('help' => __('ACO to check.'), 'required' => true), @@ -456,7 +463,11 @@ class AclShell extends Shell { ))->addSubcommand('grant', array( 'help' => __('Grant an ARO permissions to an ACO.'), 'parser' => array( - 'description' => __("Use this command to grant ACL permissions. Once executed, the ARO \nspecified (and its children, if any) will have ALLOW access to the \nspecified ACO action (and the ACO's children, if any)."), + 'description' => array( + __("Use this command to grant ACL permissions. Once executed, the ARO"), + __("specified (and its children, if any) will have ALLOW access to the"), + __("specified ACO action (and the ACO's children, if any).") + ), 'arguments' => array( 'aro' => array('help' => __('ARO to grant permission to.'), 'required' => true), 'aco' => array('help' => __('ACO to grant access to.'), 'required' => true), @@ -466,7 +477,11 @@ class AclShell extends Shell { ))->addSubcommand('deny', array( 'help' => __('Deny an ARO permissions to an ACO.'), 'parser' => array( - 'description' => __("Use this command to deny ACL permissions. Once executed, the ARO \nspecified (and its children, if any) will have DENY access to the \nspecified ACO action (and the ACO's children, if any)."), + 'description' => array( + __("Use this command to deny ACL permissions. Once executed, the ARO"), + __("specified (and its children, if any) will have DENY access to the"), + __("specified ACO action (and the ACO's children, if any).") + ), 'arguments' => array( 'aro' => array('help' => __('ARO to deny.'), 'required' => true), 'aco' => array('help' => __('ACO to deny.'), 'required' => true), @@ -476,7 +491,10 @@ class AclShell extends Shell { ))->addSubcommand('inherit', array( 'help' => __('Inherit an ARO\'s parent permissions.'), 'parser' => array( - 'description' => __("Use this command to force a child ARO object to inherit its \npermissions settings from its parent."), + 'description' => array( + __("Use this command to force a child ARO object to inherit its"), + __("permissions settings from its parent.") + ), 'arguments' => array( 'aro' => array('help' => __('ARO to have permisssions inherit.'), 'required' => true), 'aco' => array('help' => __('ACO to inherit permissions on.'), 'required' => true), @@ -486,7 +504,11 @@ class AclShell extends Shell { ))->addSubcommand('view', array( 'help' => __('View a tree or a single node\'s subtree.'), 'parser' => array( - 'description' => __("The view command will return the ARO or ACO tree. \nThe optional node parameter allows you to return \nonly a portion of the requested tree."), + 'description' => array( + __("The view command will return the ARO or ACO tree."), + __("The optional node parameter allows you to return"), + __("only a portion of the requested tree.") + ), 'arguments' => array( 'type' => $type, 'node' => array('help' => __('The optional node to view the subtree of.')) From 1cf2613a8d96805b0d13b36cf48c44c2805e29d9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sun, 10 Oct 2010 17:32:36 -0400 Subject: [PATCH 093/207] Starting to convert bake to use option parsers. --- cake/console/libs/bake.php | 41 +++++++++++++++++++++++++++ cake/console/libs/tasks/db_config.php | 2 +- cake/console/libs/tasks/fixture.php | 2 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/cake/console/libs/bake.php b/cake/console/libs/bake.php index 047dda1c0..58ddb4ad6 100644 --- a/cake/console/libs/bake.php +++ b/cake/console/libs/bake.php @@ -193,6 +193,47 @@ class BakeShell extends Shell { $this->_stop(); } +/** + * get the option parser. + * + * @return void + */ + public function getOptionParser() { + $parser = parent::getOptionParser(); + return $parser->description(array( + 'The Bake script generates controllers, views and models for your application.', + 'If run with no command line arguments, Bake guides the user through the class', + 'creation process. You can customize the generation process by telling Bake', + 'where different parts of your application are using command line arguments.' + ))->addSubcommand('all', array( + 'help' => __('Bake a complete MVC. optional of a Model'), + ))->addSubcommand('project', array( + 'help' => __('Bake a new app folder in the path supplied or in current directory if no path is specified'), + 'parser' => $this->Project->getOptionParser() + ))->addSubcommand('plugin', array( + 'help' => __('Bake a new plugin folder in the path supplied or in current directory if no path is specified.'), + 'parser' => $this->Plugin->getOptionParser() + ))->addSubcommand('db_config', array( + 'help' => __('Bake a database.php file in config directory.'), + 'parser' => $this->DbConfig->getOptionParser() + ))->addSubcommand('model', array( + 'help' => __('Bake a model.'), + 'parser' => $this->Model->getOptionParser() + ))->addSubcommand('view', array( + 'help' => __('Bake views for controllers.'), + 'parser' => $this->View->getOptionParser() + ))->addSubcommand('controller', array( + 'help' => __('Bake a controller.'), + 'parser' => $this->Controller->getOptionParser() + ))->addSubcommand('fixture', array( + 'help' => __('Bake a fixture.'), + 'parser' => $this->Fixture->getOptionParser() + ))->addSubcommand('test', array( + 'help' => __('Bake a unit test.'), + 'parser' => $this->Test->getOptionParser() + )); + } + /** * Displays help contents * diff --git a/cake/console/libs/tasks/db_config.php b/cake/console/libs/tasks/db_config.php index c62cbe1f3..8e4513736 100644 --- a/cake/console/libs/tasks/db_config.php +++ b/cake/console/libs/tasks/db_config.php @@ -60,7 +60,7 @@ class DbConfigTask extends Shell { * @var string */ public function initialize() { - $this->path = $this->params['working'] . DS . 'config' . DS; + $this->path = $this->Dispatch->params['working'] . DS . 'config' . DS; } /** diff --git a/cake/console/libs/tasks/fixture.php b/cake/console/libs/tasks/fixture.php index fd04b6287..49455459a 100644 --- a/cake/console/libs/tasks/fixture.php +++ b/cake/console/libs/tasks/fixture.php @@ -56,7 +56,7 @@ class FixtureTask extends BakeTask { */ public function __construct(&$dispatch, $stdout = null, $stderr = null, $stdin = null) { parent::__construct($dispatch, $stdout, $stderr, $stdin); - $this->path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS; + $this->path = $this->Dispatch->params['working'] . DS . 'tests' . DS . 'fixtures' . DS; } /** From 970a6c8d79a7dd5ea6129f8d38e4146452cdee56 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 01:32:09 -0400 Subject: [PATCH 094/207] Adding the ability for parse() to use a subcommand parser. Adding text on how to get help on subcommands. --- cake/console/console_option_parser.php | 17 +++++++++-- .../console/console_option_parser.test.php | 30 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 632969f53..6719f8127 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -372,14 +372,20 @@ class ConsoleOptionParser { } /** - * Parse the argv array into a set of params and args. + * Parse the argv array into a set of params and args. If $command is not null + * and $command is equal to a subcommand that has a parser, that parser will be used + * to parse the $argv * - * @param array $argv Array of args (argv) to parse + * @param array $argv Array of args (argv) to parse. + * @param string $command The subcommand to use for parsing. * @return Array array($params, $args) * @throws InvalidArgumentException When an invalid parameter is encountered. * RuntimeException when required arguments are not supplied. */ - public function parse($argv) { + public function parse($argv, $command = null) { + if (isset($this->_subcommands[$command]) && $this->_subcommands[$command]->parser()) { + return $this->_subcommands[$command]->parser()->parse($argv); + } $params = $args = array(); $this->_tokens = $argv; while ($token = array_shift($this->_tokens)) { @@ -435,6 +441,11 @@ class ConsoleOptionParser { $out[] = $command->help($max); } $out[] = ''; + $out[] = sprintf( + __('To see help on a subcommand use `cake %s [subcommand] --help`'), + $this->command() + ); + $out[] = ''; } if (!empty($this->_options)) { diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index b89f888b6..7751a8d01 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -464,6 +464,8 @@ cake mycommand [subcommand] [-h] [--test] method This is another command +To see help on a subcommand use `cake mycommand [subcommand] --help` + Options: --help, -h Display this help. @@ -534,4 +536,32 @@ TEXT; $args = $parser->arguments(); $this->assertEquals(2, count($args)); } + +/** + * test that parse() takes a subcommand argument, and that the subcommand parser + * is used. + * + * @return void + */ + function testParsingWithSubParser() { + $parser = new ConsoleOptionParser(); + $parser->addOption('primary') + ->addArgument('one', array('required' => true, 'choices' => array('a', 'b'))) + ->addArgument('two', array('required' => true)) + ->addSubcommand('sub', array( + 'parser' => array( + 'options' => array( + 'secondary' => array('boolean' => true), + 'fourth' => array('help' => 'fourth option') + ), + 'arguments' => array( + 'sub_arg' => array('choices' => array('c', 'd')) + ) + ) + )); + + $result = $parser->parse(array('--secondary', '--fourth', '4', 'c'), 'sub'); + $expected = array(array('secondary' => true, 'fourth' => '4'), array('c')); + $this->assertEquals($expected, $result, 'Sub parser did not parse request.'); + } } \ No newline at end of file From 9540f9aeff7c0c69b198235c3f10141f3d3b89ba Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 01:32:41 -0400 Subject: [PATCH 095/207] Removing dead help method. --- cake/console/libs/bake.php | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/cake/console/libs/bake.php b/cake/console/libs/bake.php index 58ddb4ad6..cea5e6bc7 100644 --- a/cake/console/libs/bake.php +++ b/cake/console/libs/bake.php @@ -234,33 +234,4 @@ class BakeShell extends Shell { )); } -/** - * Displays help contents - * - */ - public function help() { - $this->out('CakePHP Bake:'); - $this->hr(); - $this->out('The Bake script generates controllers, views and models for your application.'); - $this->out('If run with no command line arguments, Bake guides the user through the class'); - $this->out('creation process. You can customize the generation process by telling Bake'); - $this->out('where different parts of your application are using command line arguments.'); - $this->hr(); - $this->out("Usage: cake bake ..."); - $this->hr(); - $this->out('Params:'); - $this->out("\t-app Absolute/Relative path to your app folder.\n"); - $this->out('Commands:'); - $this->out("\n\tbake help\n\t\tshows this help message."); - $this->out("\n\tbake all \n\t\tbakes complete MVC. optional of a Model"); - $this->out("\n\tbake project \n\t\tbakes a new app folder in the path supplied\n\t\tor in current directory if no path is specified"); - $this->out("\n\tbake plugin \n\t\tbakes a new plugin folder in the path supplied\n\t\tor in current directory if no path is specified."); - $this->out("\n\tbake db_config\n\t\tbakes a database.php file in config directory."); - $this->out("\n\tbake model\n\t\tbakes a model. run 'bake model help' for more info"); - $this->out("\n\tbake view\n\t\tbakes views. run 'bake view help' for more info"); - $this->out("\n\tbake controller\n\t\tbakes a controller. run 'bake controller help' for more info"); - $this->out("\n\tbake fixture\n\t\tbakes fixtures. run 'bake fixture help' for more info."); - $this->out("\n\tbake test\n\t\tbakes unit tests. run 'bake test help' for more info."); - $this->out(); - } } From 8c990a9e083a59e7c1110d2eeee5a04370efa907 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 01:33:24 -0400 Subject: [PATCH 096/207] Fixing failing tests caused by API changes. --- cake/console/libs/command_list.php | 6 +++--- cake/console/libs/shell.php | 2 +- cake/console/libs/testsuite.php | 11 +++-------- cake/tests/cases/console/libs/acl.test.php | 2 +- cake/tests/cases/console/libs/shell.test.php | 1 - cake/tests/cases/console/libs/testsuite.test.php | 4 ++-- 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/cake/console/libs/command_list.php b/cake/console/libs/command_list.php index 79faceb5b..96cfae877 100644 --- a/cake/console/libs/command_list.php +++ b/cake/console/libs/command_list.php @@ -31,9 +31,9 @@ class CommandListShell extends Shell { */ public function main() { $this->out("Current Paths:", 2); - $this->out(" -app: ". $this->params['app']); - $this->out(" -working: " . rtrim($this->params['working'], DS)); - $this->out(" -root: " . rtrim($this->params['root'], DS)); + $this->out(" -app: ". $this->Dispatch->params['app']); + $this->out(" -working: " . rtrim($this->Dispatch->params['working'], DS)); + $this->out(" -root: " . rtrim($this->Dispatch->params['root'], DS)); $this->out(" -core: " . rtrim(CORE_PATH, DS)); $this->out(""); $this->out("Changing Paths:", 2); diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index bca1e5a3b..77c71267b 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -325,7 +325,7 @@ class Shell extends Object { } $this->OptionParser = $this->getOptionParser(); - list($this->params, $this->args) = $this->OptionParser->parse($argv); + list($this->params, $this->args) = $this->OptionParser->parse($argv, $command); if (($isTask || $isMethod || $isMain) && $command !== 'execute' ) { $this->startup(); diff --git a/cake/console/libs/testsuite.php b/cake/console/libs/testsuite.php index 265cf5486..58e36026f 100644 --- a/cake/console/libs/testsuite.php +++ b/cake/console/libs/testsuite.php @@ -74,9 +74,6 @@ class TestSuiteShell extends Shell { if (isset($this->args[1])) { $params['case'] = Inflector::underscore($this->args[1]); } - if (isset($this->params['filter'])) { - $this->params['-filter'] = $this->params['filter']; - } return $params; } @@ -88,11 +85,9 @@ class TestSuiteShell extends Shell { protected function runnerOptions() { $options = array(); foreach ($this->params as $param => $value) { - if ($param[0] === '-') { - $options[] = '-' . $param; - if (is_string($value)) { - $options[] = $value; - } + $options[] = '--' . $param; + if (is_string($value)) { + $options[] = $value; } } return $options; diff --git a/cake/tests/cases/console/libs/acl.test.php b/cake/tests/cases/console/libs/acl.test.php index e8597ce22..3bfd9dcfe 100644 --- a/cake/tests/cases/console/libs/acl.test.php +++ b/cake/tests/cases/console/libs/acl.test.php @@ -58,7 +58,7 @@ class AclShellTest extends CakeTestCase { ); $this->Task = $this->getMock( 'AclShell', - array('in', 'out', 'hr', 'createFile', 'error', 'err'), + array('in', 'out', 'hr', 'createFile', 'error', 'err', 'clear'), array(&$this->Dispatcher, $out, $out, $in) ); $collection = new ComponentCollection(); diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php index 5fd0b119c..87731f88c 100644 --- a/cake/tests/cases/console/libs/shell.test.php +++ b/cake/tests/cases/console/libs/shell.test.php @@ -144,7 +144,6 @@ class ShellTest extends CakeTestCase { public function testConstruct() { $this->assertEquals($this->Dispatcher, $this->Shell->Dispatch); $this->assertEqual($this->Shell->name, 'TestShell'); - $this->assertEqual($this->Shell->alias, 'TestShell'); $this->assertType('ConsoleOutput', $this->Shell->stdout); $this->assertType('ConsoleOutput', $this->Shell->stderr); } diff --git a/cake/tests/cases/console/libs/testsuite.test.php b/cake/tests/cases/console/libs/testsuite.test.php index dfeac8634..9d6ae4511 100644 --- a/cake/tests/cases/console/libs/testsuite.test.php +++ b/cake/tests/cases/console/libs/testsuite.test.php @@ -97,12 +97,12 @@ class TestSuiteShellTest extends CakeTestCase { public function testRunnerOptions() { $this->Shell->startup(); $this->Shell->args = array('core', 'Basics'); - $this->Shell->params = array('filter' => 'myFilter', '-colors' => null, '-verbose' => null); + $this->Shell->params = array('filter' => 'myFilter', 'colors' => null, 'verbose' => null); $this->Shell->expects($this->once())->method('run') ->with( array('app' => false, 'plugin' => null, 'output' => 'text', 'case' => 'basics'), - array('--colors', '--verbose', '--filter', 'myFilter') + array('--filter', 'myFilter', '--colors', '--verbose') ); $this->Shell->main(); } From 6774e401fef05e254e4694ff20621dab8314a9b9 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 01:50:40 -0400 Subject: [PATCH 097/207] Correcting error message. --- cake/libs/exceptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cake/libs/exceptions.php b/cake/libs/exceptions.php index 7fdd069a9..6b5d350cc 100644 --- a/cake/libs/exceptions.php +++ b/cake/libs/exceptions.php @@ -319,7 +319,7 @@ class MissingTaskClassException extends CakeException { * @package cake.libs */ class MissingShellMethodException extends CakeException { - protected $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s help`"; + protected $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s --help`"; } /** From 99407e788f352b3d994563fc3dde035aca485c3f Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 01:51:00 -0400 Subject: [PATCH 098/207] Fixing issue where choices would not be correctly validated. --- cake/console/console_option_parser.php | 4 ++-- .../tests/cases/console/console_option_parser.test.php | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 6719f8127..56e8accd4 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -573,8 +573,8 @@ class ConsoleOptionParser { if (!isset($this->_args[$next])) { throw new InvalidArgumentException(__('Too many arguments.')); } - $index = max($next - 1, 0); - if ($this->_args[$index]->validChoice($argument)) { + + if ($this->_args[$next]->validChoice($argument)) { array_push($args, $argument); return $args; } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index 7751a8d01..b7e6c066b 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -282,13 +282,15 @@ class ConsoleOptionParserTest extends CakeTestCase { */ function testPositionalArgWithChoices() { $parser = new ConsoleOptionParser(); - $parser->addArgument('name', array('choices' => array('mark', 'jose'))); + $parser->addArgument('name', array('choices' => array('mark', 'jose'))) + ->addArgument('alias', array('choices' => array('cowboy', 'samurai'))) + ->addArgument('weapon', array('choices' => array('gun', 'sword'))); - $result = $parser->parse(array('mark')); - $expected = array('mark'); + $result = $parser->parse(array('mark', 'samurai', 'sword')); + $expected = array('mark', 'samurai', 'sword'); $this->assertEquals($expected, $result[1], 'Got the correct value.'); - $result = $parser->parse(array('jimmy')); + $result = $parser->parse(array('jose', 'coder')); } /** From c3750d1108159f02dc9c0db68389d1007050c02f Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 01:58:12 -0400 Subject: [PATCH 099/207] Updating AclShell to use features of ConsoleOptionParser. --- cake/console/libs/acl.php | 31 ++++++++----------------------- cake/console/libs/shell.php | 2 ++ 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php index e26aec102..f68fb24e6 100644 --- a/cake/console/libs/acl.php +++ b/cake/console/libs/acl.php @@ -106,20 +106,7 @@ class AclShell extends Shell { * */ public function main() { - $out = __('Available ACL commands:') . "\n"; - $out .= "\t - create\n"; - $out .= "\t - delete\n"; - $out .= "\t - setParent\n"; - $out .= "\t - getPath\n"; - $out .= "\t - check\n"; - $out .= "\t - grant\n"; - $out .= "\t - deny\n"; - $out .= "\t - inherit\n"; - $out .= "\t - view\n"; - $out .= "\t - initdb\n"; - $out .= "\t - help\n\n"; - $out .= __("For help, run the 'help' command. For help on a specific command, run 'help '"); - $this->out($out); + $this->out($this->OptionParser->help()); } /** @@ -127,8 +114,6 @@ class AclShell extends Shell { * */ public function create() { - $this->_checkArgs(3, 'create'); - $this->checkNodeType(); extract($this->__dataVars()); $class = ucfirst($this->args[0]); @@ -144,13 +129,13 @@ class AclShell extends Shell { if (is_string($data) && $data != '/') { $data = array('alias' => $data); } elseif (is_string($data)) { - $this->error(__('/ can not be used as an alias!'), __("\t/ is the root, please supply a sub alias")); + $this->error(__('/ can not be used as an alias!') . __(" / is the root, please supply a sub alias")); } $data['parent_id'] = $parent; $this->Acl->{$class}->create(); if ($this->Acl->{$class}->save($data)) { - $this->out(sprintf(__("New %s '%s' created.\n"), $class, $this->args[2]), true); + $this->out(sprintf(__("New %s '%s' created."), $class, $this->args[2]), 2); } else { $this->err(sprintf(__("There was a problem creating a new %s '%s'."), $class, $this->args[2])); } @@ -161,17 +146,15 @@ class AclShell extends Shell { * */ public function delete() { - $this->_checkArgs(2, 'delete'); - $this->checkNodeType(); extract($this->__dataVars()); $identifier = $this->parseIdentifier($this->args[1]); $nodeId = $this->_getNodeId($class, $identifier); if (!$this->Acl->{$class}->delete($nodeId)) { - $this->error(__('Node Not Deleted'), sprintf(__('There was an error deleting the %s. Check that the node exists'), $class) . ".\n"); + $this->error(__('Node Not Deleted') . sprintf(__('There was an error deleting the %s. Check that the node exists'), $class) . ".\n"); } - $this->out(sprintf(__('%s deleted'), $class) . ".\n", true); + $this->out(sprintf(__('%s deleted.'), $class), 2); } /** @@ -396,9 +379,11 @@ class AclShell extends Shell { 'type' => $type, 'parent' => array( 'help' => __('The node selector for the parent.'), + 'required' => true ), 'alias' => array( - 'help' => __('The alias to use for the newly created node.') + 'help' => __('The alias to use for the newly created node.'), + 'required' => true ) ) ) diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index 77c71267b..beabd4b29 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -326,6 +326,7 @@ class Shell extends Object { $this->OptionParser = $this->getOptionParser(); list($this->params, $this->args) = $this->OptionParser->parse($argv, $command); + $this->command = $command; if (($isTask || $isMethod || $isMain) && $command !== 'execute' ) { $this->startup(); @@ -333,6 +334,7 @@ class Shell extends Object { if (isset($this->params['help'])) { return $this->out($this->OptionParser->help($command)); } + if ($isTask) { $command = Inflector::camelize($command); return $this->{$command}->runCommand('execute', $argv); From 577603fbd969132c817dd76f763ab8dfed1613f0 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 13:04:23 -0400 Subject: [PATCH 100/207] Adding documentation, and improving error messages. --- cake/console/console_input_argument.php | 7 +++--- cake/console/console_input_option.php | 7 +++--- cake/console/console_option_parser.php | 32 ++++++++++++++++++++----- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cake/console/console_input_argument.php b/cake/console/console_input_argument.php index f212d2375..43393c540 100644 --- a/cake/console/console_input_argument.php +++ b/cake/console/console_input_argument.php @@ -115,9 +115,10 @@ class ConsoleInputArgument { return true; } if (!in_array($value, $this->_choices)) { - throw new InvalidArgumentException( - sprintf(__('"%s" is not a valid value for %s'), $value, $this->_name) - ); + throw new InvalidArgumentException(sprintf( + __('"%s" is not a valid value for %s. Please use one of "%s"'), + $value, $this->_name, implode(', ', $this->_choices) + )); } return true; } diff --git a/cake/console/console_input_option.php b/cake/console/console_input_option.php index af66be87b..744546126 100644 --- a/cake/console/console_input_option.php +++ b/cake/console/console_input_option.php @@ -133,9 +133,10 @@ class ConsoleInputOption { return true; } if (!in_array($value, $this->_choices)) { - throw new InvalidArgumentException( - sprintf(__('"%s" is not a valid value for --%s'), $value, $this->_name) - ); + throw new InvalidArgumentException(sprintf( + __('"%s" is not a valid value for --%s. Please use one of "%s"'), + $value, $this->_name, implode(', ', $this->_choices) + )); } return true; } diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 56e8accd4..c500d79ec 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -113,8 +113,10 @@ class ConsoleOptionParser { * ### Providing Help text * * By providing help text for your positional arguments and named arguments, the ConsoleOptionParser - * can generate a help display for you. You can view the help for shells by using the `--help` or `-h` switch. + * can generate a help display for you. You can view the help for shells by using the `--help` or `-h` switch. * + * @param string $command The command name this parser is for. The command name is used for generating help. + * @param boolean $defaultOptions Whether you want the verbose and quiet options set. */ public function __construct($command = null, $defaultOptions = true) { $this->_command = $command; @@ -139,6 +141,8 @@ class ConsoleOptionParser { /** * Static factory method for creating new OptionParsers so you can chain methods off of them. * + * @param string $command The command name this parser is for. The command name is used for generating help. + * @param boolean $defaultOptions Whether you want the verbose and quiet options set. * @return ConsoleOptionParser */ public static function create($command, $defaultOptions = true) { @@ -146,8 +150,22 @@ class ConsoleOptionParser { } /** - * Build a parser from an array. + * Build a parser from an array. Uses an array like * + * {{{ + * $spec = array( + * 'description' => 'text', + * 'epilog' => 'text', + * 'arguments' => array( + * // list of arguments compatible with addArguments. + * ), + * 'options' => array( + * // list of options compatible with addOptions + * ) + * ); + * }}} + * + * @param array $spec The spec to build the OptionParser with. * @return ConsoleOptionParser */ public static function buildFromArray($spec) { @@ -377,7 +395,8 @@ class ConsoleOptionParser { * to parse the $argv * * @param array $argv Array of args (argv) to parse. - * @param string $command The subcommand to use for parsing. + * @param string $command The subcommand to use. If this parameter is a subcommand, that has a parser, + * That parser will be used to parse $argv instead. * @return Array array($params, $args) * @throws InvalidArgumentException When an invalid parameter is encountered. * RuntimeException when required arguments are not supplied. @@ -409,12 +428,12 @@ class ConsoleOptionParser { /** * Gets formatted help for this parser object. - * Generates help text based on the description, options, arguments and epilog + * Generates help text based on the description, options, arguments, subcommands and epilog * in the parser. * * @param string $subcommand If present and a valid subcommand that has a linked parser. * That subcommands help will be shown instead. - * @return string + * @return string Generated help. */ public function help($subcommand = null) { if ( @@ -558,7 +577,8 @@ class ConsoleOptionParser { } /** - * Checks that the argument doesn't exceed the declared arguments. + * Parse an argument, and ensure that the argument doesn't exceed the number of arguments + * and that the argument is a valid choice. * * @param string $argument The argument to append * @param array $args The array of parsed args to append to. From 0fd8d2b8e407496be24c46eb6c28c9fc929bf7b7 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 13:12:23 -0400 Subject: [PATCH 101/207] Adding addSubcommands, and support for subcommands to buildFromArray. This makes the API consistent across all the elements of ConsoleOptionParser. --- cake/console/console_option_parser.php | 19 ++++++++++++++++ .../console/console_option_parser.test.php | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index c500d79ec..80685d9f1 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -161,6 +161,9 @@ class ConsoleOptionParser { * ), * 'options' => array( * // list of options compatible with addOptions + * ), + * 'subcommands' => array( + * // list of subcommands to add. * ) * ); * }}} @@ -176,6 +179,9 @@ class ConsoleOptionParser { if (!empty($spec['options'])) { $parser->addOptions($spec['options']); } + if (!empty($spec['subcommands'])) { + $parser->addSubcommands($spec['subcommands']); + } if (!empty($spec['description'])) { $parser->description($spec['description']); } @@ -362,6 +368,19 @@ class ConsoleOptionParser { return $this; } +/** + * Add multiple subcommands at once. + * + * @param array $commands Array of subcommands. + * @return $this + */ + public function addSubcommands(array $commands) { + foreach ($commands as $name => $params) { + $this->addSubcommand($name, $params); + } + return $this; + } + /** * Gets the arguments defined in the parser. * diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index b7e6c066b..f80092b4e 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -323,6 +323,22 @@ class ConsoleOptionParserTest extends CakeTestCase { $this->assertEquals($parser, $result, 'Adding a subcommand is not chainable'); } +/** + * test adding multiple subcommands + * + * @return void + */ + function testAddSubcommands() { + $parser = new ConsoleOptionParser(); + $result = $parser->addSubcommands(array( + 'initdb' => array('help' => 'Initialize the database'), + 'create' => array('help' => 'Create something') + )); + $this->assertEquals($parser, $result, 'Adding a subcommands is not chainable'); + $result = $parser->subcommands(); + $this->assertEquals(2, count($result), 'Not enough subcommands'); + } + /** * test getting help with defined options. * @@ -523,6 +539,9 @@ TEXT; 'name' => array('help' => 'The name'), 'other' => array('help' => 'The other arg') ), + 'subcommands' => array( + 'initdb' => array('help' => 'make database') + ), 'description' => 'description text', 'epilog' => 'epilog text' ); @@ -537,6 +556,9 @@ TEXT; $args = $parser->arguments(); $this->assertEquals(2, count($args)); + + $commands = $parser->subcommands(); + $this->assertEquals(1, count($commands)); } /** From 43646b937254fc88de790adc4c4d9bc19491d62d Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 13:30:18 -0400 Subject: [PATCH 102/207] Updating AclShell to use new formatting and removing methods that are no longer needed. --- cake/console/libs/acl.php | 43 +++++----------------- cake/tests/cases/console/libs/acl.test.php | 22 +++++------ 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php index f68fb24e6..4f15f8525 100644 --- a/cake/console/libs/acl.php +++ b/cake/console/libs/acl.php @@ -84,7 +84,7 @@ class AclShell extends Shell { $this->_stop(); } - if ($this->command && !in_array($this->command, array('help'))) { + if ($this->command) { if (!config('database')) { $this->out(__('Your database configuration was not found. Take a moment to create one.'), true); $this->args = null; @@ -162,8 +162,6 @@ class AclShell extends Shell { * */ public function setParent() { - $this->_checkArgs(3, 'setParent'); - $this->checkNodeType(); extract($this->__dataVars()); $target = $this->parseIdentifier($this->args[1]); $parent = $this->parseIdentifier($this->args[2]); @@ -187,8 +185,6 @@ class AclShell extends Shell { * */ public function getPath() { - $this->_checkArgs(2, 'getPath'); - $this->checkNodeType(); extract($this->__dataVars()); $identifier = $this->parseIdentifier($this->args[1]); @@ -231,13 +227,12 @@ class AclShell extends Shell { * */ public function check() { - $this->_checkArgs(3, 'check'); extract($this->__getParams()); if ($this->Acl->check($aro, $aco, $action)) { - $this->out(sprintf(__('%s is allowed.'), $aroName), true); + $this->out(sprintf(__('%s is allowed.'), $aroName), true); } else { - $this->out(sprintf(__('%s is not allowed.'), $aroName), true); + $this->out(sprintf(__('%s is not allowed.'), $aroName), true); } } @@ -246,13 +241,12 @@ class AclShell extends Shell { * */ public function grant() { - $this->_checkArgs(3, 'grant'); extract($this->__getParams()); if ($this->Acl->allow($aro, $aco, $action)) { - $this->out(__('Permission granted.'), true); + $this->out(__('Permission granted.'), true); } else { - $this->out(__('Permission was not granted.'), true); + $this->out(__('Permission was not granted.'), true); } } @@ -261,7 +255,6 @@ class AclShell extends Shell { * */ public function deny() { - $this->_checkArgs(3, 'deny'); extract($this->__getParams()); if ($this->Acl->deny($aro, $aco, $action)) { @@ -276,7 +269,6 @@ class AclShell extends Shell { * */ public function inherit() { - $this->_checkArgs(3, 'inherit'); extract($this->__getParams()); if ($this->Acl->inherit($aro, $aco, $action)) { @@ -291,8 +283,6 @@ class AclShell extends Shell { * */ public function view() { - $this->_checkArgs(1, 'view'); - $this->checkNodeType(); extract($this->__dataVars()); if (isset($this->args[1])) { @@ -442,7 +432,7 @@ class AclShell extends Shell { 'arguments' => array( 'aro' => array('help' => __('ARO to check.'), 'required' => true), 'aco' => array('help' => __('ACO to check.'), 'required' => true), - 'action' => array('help' => __('Action to check')) + 'action' => array('help' => __('Action to check'), 'default' => 'all') ) ) ))->addSubcommand('grant', array( @@ -456,7 +446,7 @@ class AclShell extends Shell { 'arguments' => array( 'aro' => array('help' => __('ARO to grant permission to.'), 'required' => true), 'aco' => array('help' => __('ACO to grant access to.'), 'required' => true), - 'action' => array('help' => __('Action to grant')) + 'action' => array('help' => __('Action to grant'), 'default' => 'all') ) ) ))->addSubcommand('deny', array( @@ -470,7 +460,7 @@ class AclShell extends Shell { 'arguments' => array( 'aro' => array('help' => __('ARO to deny.'), 'required' => true), 'aco' => array('help' => __('ACO to deny.'), 'required' => true), - 'action' => array('help' => __('Action to deny')) + 'action' => array('help' => __('Action to deny'), 'default' => 'all') ) ) ))->addSubcommand('inherit', array( @@ -483,7 +473,7 @@ class AclShell extends Shell { 'arguments' => array( 'aro' => array('help' => __('ARO to have permisssions inherit.'), 'required' => true), 'aco' => array('help' => __('ACO to inherit permissions on.'), 'required' => true), - 'action' => array('help' => __('Action to inherit')) + 'action' => array('help' => __('Action to inherit'), 'default' => 'all') ) ) ))->addSubcommand('view', array( @@ -517,19 +507,6 @@ class AclShell extends Shell { return $parser; } -/** - * Check that first argument specifies a valid Node type (ARO/ACO) - * - */ - public function checkNodeType() { - if (!isset($this->args[0])) { - return false; - } - if ($this->args[0] != 'aco' && $this->args[0] != 'aro') { - $this->error(sprintf(__("Missing/Unknown node type: '%s'"), $this->args[0]), __('Please specify which ACL object type you wish to create. Either "aro" or "aco"')); - } - } - /** * Checks that given node exists * @@ -538,7 +515,7 @@ class AclShell extends Shell { * @return boolean Success */ public function nodeExists() { - if (!$this->checkNodeType() && !isset($this->args[1])) { + if (!isset($this->args[0]) || !isset($this->args[1])) { return false; } extract($this->__dataVars($this->args[0])); diff --git a/cake/tests/cases/console/libs/acl.test.php b/cake/tests/cases/console/libs/acl.test.php index 3bfd9dcfe..9af1eceb0 100644 --- a/cake/tests/cases/console/libs/acl.test.php +++ b/cake/tests/cases/console/libs/acl.test.php @@ -136,9 +136,9 @@ class AclShellTest extends CakeTestCase { */ public function testCreate() { $this->Task->args = array('aro', 'root', 'User.1'); - $this->Task->expects($this->at(0))->method('out')->with("New Aro 'User.1' created.\n", true); - $this->Task->expects($this->at(1))->method('out')->with("New Aro 'User.3' created.\n", true); - $this->Task->expects($this->at(2))->method('out')->with("New Aro 'somealias' created.\n", true); + $this->Task->expects($this->at(0))->method('out')->with("New Aro 'User.1' created.", 2); + $this->Task->expects($this->at(1))->method('out')->with("New Aro 'User.3' created.", 2); + $this->Task->expects($this->at(2))->method('out')->with("New Aro 'somealias' created.", 2); $this->Task->create(); @@ -178,7 +178,7 @@ class AclShellTest extends CakeTestCase { public function testDelete() { $this->Task->args = array('aro', 'AuthUser.1'); $this->Task->expects($this->at(0))->method('out') - ->with("Aro deleted.\n", true); + ->with("Aro deleted.", 2); $this->Task->delete(); $Aro = ClassRegistry::init('Aro'); @@ -208,7 +208,7 @@ class AclShellTest extends CakeTestCase { public function testGrant() { $this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create'); $this->Task->expects($this->at(0))->method('out') - ->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission granted/'), true); + ->with($this->matchesRegularExpression('/granted/'), true); $this->Task->grant(); $node = $this->Task->Acl->Aro->read(null, 4); @@ -240,13 +240,13 @@ class AclShellTest extends CakeTestCase { */ public function testCheck() { $this->Task->expects($this->at(0))->method('out') - ->with(new PHPUnit_Framework_Constraint_PCREMatch('/not allowed/'), true); + ->with($this->matchesRegularExpression('/not allowed/'), true); $this->Task->expects($this->at(1))->method('out') - ->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission granted/'), true); + ->with($this->matchesRegularExpression('/granted/'), true); $this->Task->expects($this->at(2))->method('out') - ->with(new PHPUnit_Framework_Constraint_PCREMatch('/is allowed/'), true); + ->with($this->matchesRegularExpression('/is.*allowed/'), true); $this->Task->expects($this->at(3))->method('out') - ->with(new PHPUnit_Framework_Constraint_PCREMatch('/not allowed/'), true); + ->with($this->matchesRegularExpression('/not.*allowed/'), true); $this->Task->args = array('AuthUser.2', 'ROOT/Controller1', '*'); $this->Task->check(); @@ -268,9 +268,9 @@ class AclShellTest extends CakeTestCase { */ public function testInherit() { $this->Task->expects($this->at(0))->method('out') - ->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission granted/'), true); + ->with($this->matchesRegularExpression('/Permission .*granted/'), true); $this->Task->expects($this->at(1))->method('out') - ->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission inherited/'), true); + ->with($this->matchesRegularExpression('/Permission .*inherited/'), true); $this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create'); $this->Task->grant(); From 2af684cbec38d752315b701181012fd0cf68ae4e Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 22:53:23 -0400 Subject: [PATCH 103/207] Fixing issue where exceptions for missing required arguments would be raised when trying to get help. --- cake/console/console_option_parser.php | 2 +- .../cases/console/console_option_parser.test.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 80685d9f1..5360c8fa2 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -436,7 +436,7 @@ class ConsoleOptionParser { } } foreach ($this->_args as $i => $arg) { - if ($arg->isRequired() && !isset($args[$i])) { + if ($arg->isRequired() && !isset($args[$i]) && empty($params['help'])) { throw new RuntimeException( sprintf(__('Missing required arguments. %s is required.'), $arg->name()) ); diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index f80092b4e..c98d25d87 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -396,6 +396,20 @@ TEXT; $this->assertEquals($expected, $result, 'Help does not match'); } +/** + * test that no exception is triggered when help is being generated + * + * @return void + */ + function testHelpNoExceptionWhenGettingHelp() { + $parser = new ConsoleOptionParser('mycommand', false); + $parser->addOption('test', array('help' => 'A test option.')) + ->addArgument('model', array('help' => 'The model to make.', 'required' => true)); + + $result = $parser->parse(array('--help')); + $this->assertTrue($result[0]['help']); + } + /** * test help() with options and arguments that have choices. * From 3585620470014d4e79bb36c85d44b608f2c1fcaa Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 23:06:50 -0400 Subject: [PATCH 104/207] Adding a ghetto option parser to the testsuite shell. Still need to add in all the options from phpunit, and the other cake options. --- cake/console/libs/testsuite.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cake/console/libs/testsuite.php b/cake/console/libs/testsuite.php index 58e36026f..b445170c4 100644 --- a/cake/console/libs/testsuite.php +++ b/cake/console/libs/testsuite.php @@ -28,6 +28,26 @@ class TestSuiteShell extends Shell { */ protected $_dispatcher = null; +/** + * get the option parser for the test suite. + * + * @return void + */ + public function getOptionParser() { + $parser = new ConsoleOptionParser($this->name, false); + $parser->description(array( + 'The CakPHP Testsuite allows you to run test cases from the command line', + 'If run with no command line arguments, a list of available core test cases will be shown' + ))->addArgument('category', array( + 'help' => __('app, core or name of a plugin.'), + 'required' => true + ))->addArgument('file', array( + 'help' => __('file name with folder prefix and without the test.php suffix.'), + 'required' => true, + )); + return $parser; + } + /** * Initialization method installs Simpletest and loads all plugins * From b328276289ce9aae4c09918a6708e9a714516a55 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 23:12:38 -0400 Subject: [PATCH 105/207] Adding default values into the parsed output. This makes it so options with default values are always part of the parsed options regardless of whether or not they were included. This will allow shells to include less logic checking for existence of parameters. --- cake/console/console_option_parser.php | 13 ++++++++++--- .../cases/console/console_option_parser.test.php | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index 5360c8fa2..ef2ddc7ba 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -130,10 +130,12 @@ class ConsoleOptionParser { if ($defaultOptions) { $this->addOption('verbose', array( 'short' => 'v', - 'help' => __('Enable verbose output.') + 'help' => __('Enable verbose output.'), + 'boolean' => true ))->addOption('quiet', array( 'short' => 'q', - 'help' => __('Enable quiet output.') + 'help' => __('Enable quiet output.'), + 'boolean' => true )); } } @@ -249,7 +251,7 @@ class ConsoleOptionParser { * - `short` - The single letter variant for this option, leave undefined for none. * - `help` - Help text for this option. Used when generating help for the option. * - `default` - The default value for this option. Defaults are added into the parsed params when the - * attached option is not provided. Using default and boolean together will not work. + * attached option is not provided or has no value. Using default and boolean together will not work. * are added into the parsed parameters when the option is undefined. * - `boolean` - The option uses no value, its just a boolean switch. Defaults to false. * If an option is defined as boolean, it will always be added to the parsed params. If no present @@ -442,6 +444,11 @@ class ConsoleOptionParser { ); } } + foreach ($this->_options as $option) { + if ($option->defaultValue() !== null && !isset($params[$option->name()]) && !$option->isBoolean()) { + $params[$option->name()] = $option->defaultValue(); + } + } return array($params, $args); } diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index c98d25d87..bc438c36e 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -105,6 +105,13 @@ class ConsoleOptionParserTest extends CakeTestCase { )); $result = $parser->parse(array('--test')); $this->assertEquals(array('test' => 'default value'), $result[0], 'Default value did not parse out'); + + $parser = new ConsoleOptionParser(); + $parser->addOption('test', array( + 'default' => 'default value', + )); + $result = $parser->parse(array()); + $this->assertEquals(array('test' => 'default value'), $result[0], 'Default value did not parse out'); } /** From 10090696a019d4cf27493c3b110c028e092ed8d4 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 11 Oct 2010 23:43:13 -0400 Subject: [PATCH 106/207] Making boolean switches behave. Boolean switches always show up in the parsed options. When left undefined they insert a false, and when included they insert a true. This makes working with them require less checks. --- cake/console/console_option_parser.php | 11 ++- cake/console/libs/shell.php | 2 +- cake/console/libs/testsuite.php | 4 +- .../console/console_option_parser.test.php | 89 ++++++++++++------- 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/cake/console/console_option_parser.php b/cake/console/console_option_parser.php index ef2ddc7ba..ff487583b 100644 --- a/cake/console/console_option_parser.php +++ b/cake/console/console_option_parser.php @@ -445,8 +445,15 @@ class ConsoleOptionParser { } } foreach ($this->_options as $option) { - if ($option->defaultValue() !== null && !isset($params[$option->name()]) && !$option->isBoolean()) { - $params[$option->name()] = $option->defaultValue(); + $name = $option->name(); + $isBoolean = $option->isBoolean(); + $default = $option->defaultValue(); + + if ($default !== null && !isset($params[$name]) && !$isBoolean) { + $params[$name] = $default; + } + if ($isBoolean && !isset($params[$name])) { + $params[$name] = false; } } return array($params, $args); diff --git a/cake/console/libs/shell.php b/cake/console/libs/shell.php index beabd4b29..4006868d4 100644 --- a/cake/console/libs/shell.php +++ b/cake/console/libs/shell.php @@ -331,7 +331,7 @@ class Shell extends Object { if (($isTask || $isMethod || $isMain) && $command !== 'execute' ) { $this->startup(); } - if (isset($this->params['help'])) { + if (!empty($this->params['help'])) { return $this->out($this->OptionParser->help($command)); } diff --git a/cake/console/libs/testsuite.php b/cake/console/libs/testsuite.php index b445170c4..22277f144 100644 --- a/cake/console/libs/testsuite.php +++ b/cake/console/libs/testsuite.php @@ -104,7 +104,9 @@ class TestSuiteShell extends Shell { */ protected function runnerOptions() { $options = array(); - foreach ($this->params as $param => $value) { + $params = $this->params; + unset($params['help']); + foreach ($params as $param => $value) { $options[] = '--' . $param; if (is_string($value)) { $options[] = $value; diff --git a/cake/tests/cases/console/console_option_parser.test.php b/cake/tests/cases/console/console_option_parser.test.php index bc438c36e..aeda66efe 100644 --- a/cake/tests/cases/console/console_option_parser.test.php +++ b/cake/tests/cases/console/console_option_parser.test.php @@ -28,7 +28,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testDescription() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $result = $parser->description('A test'); $this->assertEquals($parser, $result, 'Setting description is not chainable'); @@ -44,7 +44,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testEpilog() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $result = $parser->epilog('A test'); $this->assertEquals($parser, $result, 'Setting epilog is not chainable'); @@ -60,7 +60,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddOptionReturnSelf() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $result = $parser->addOption('test'); $this->assertEquals($parser, $result, 'Did not return $this from addOption'); } @@ -71,12 +71,12 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddOptionLong() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('test', array( 'short' => 't' )); $result = $parser->parse(array('--test', 'value')); - $this->assertEquals(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); + $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out'); } /** @@ -85,12 +85,12 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddOptionLongEquals() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('test', array( 'short' => 't' )); $result = $parser->parse(array('--test=value')); - $this->assertEquals(array('test' => 'value'), $result[0], 'Long parameter did not parse out'); + $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out'); } /** @@ -99,19 +99,19 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddOptionDefault() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('test', array( 'default' => 'default value', )); $result = $parser->parse(array('--test')); - $this->assertEquals(array('test' => 'default value'), $result[0], 'Default value did not parse out'); + $this->assertEquals(array('test' => 'default value', 'help' => false), $result[0], 'Default value did not parse out'); - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('test', array( 'default' => 'default value', )); $result = $parser->parse(array()); - $this->assertEquals(array('test' => 'default value'), $result[0], 'Default value did not parse out'); + $this->assertEquals(array('test' => 'default value', 'help' => false), $result[0], 'Default value did not parse out'); } /** @@ -120,12 +120,32 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddOptionShort() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('test', array( 'short' => 't' )); $result = $parser->parse(array('-t', 'value')); - $this->assertEquals(array('test' => 'value'), $result[0], 'Short parameter did not parse out'); + $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Short parameter did not parse out'); + } + +/** + * test adding and using boolean options. + * + * @return void + */ + function testAddOptionBoolean() { + $parser = new ConsoleOptionParser('test', false); + $parser->addOption('test', array( + 'boolean' => true, + )); + + $result = $parser->parse(array('--test', 'value')); + $expected = array(array('test' => true, 'help' => false), array('value')); + $this->assertEquals($expected, $result); + + $result = $parser->parse(array('value')); + $expected = array(array('test' => false, 'help' => false), array('value')); + $this->assertEquals($expected, $result); } /** @@ -134,13 +154,13 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddOptionMultipleShort() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('test', array('short' => 't')) ->addOption('file', array('short' => 'f')) ->addOption('output', array('short' => 'o')); $result = $parser->parse(array('-o', '-t', '-f')); - $expected = array('file' => true, 'test' => true, 'output' => true); + $expected = array('file' => true, 'test' => true, 'output' => true, 'help' => false); $this->assertEquals($expected, $result[0], 'Short parameter did not parse out'); $result = $parser->parse(array('-otf')); @@ -153,13 +173,13 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testMultipleOptions() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('test') ->addOption('connection') ->addOption('table', array('short' => 't')); $result = $parser->parse(array('--test', 'value', '-t', '--connection', 'postgres')); - $expected = array('test' => 'value', 'table' => true, 'connection' => 'postgres'); + $expected = array('test' => 'value', 'table' => true, 'connection' => 'postgres', 'help' => false); $this->assertEquals($expected, $result[0], 'multiple options did not parse'); } @@ -186,12 +206,12 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testOptionWithBooleanParam() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('no-commit', array('boolean' => true)) ->addOption('table', array('short' => 't')); $result = $parser->parse(array('--table', 'posts', '--no-commit', 'arg1', 'arg2')); - $expected = array(array('table' => 'posts', 'no-commit' => true), array('arg1', 'arg2')); + $expected = array(array('table' => 'posts', 'no-commit' => true, 'help' => false), array('arg1', 'arg2')); $this->assertEquals($expected, $result, 'Boolean option did not parse correctly.'); } @@ -201,7 +221,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @expectedException InvalidArgumentException */ function testOptionThatDoesNotExist() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('no-commit', array('boolean' => true)); $result = $parser->parse(array('--fail', 'other')); @@ -214,11 +234,11 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testOptionWithChoices() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('name', array('choices' => array('mark', 'jose'))); $result = $parser->parse(array('--name', 'mark')); - $expected = array('name' => 'mark'); + $expected = array('name' => 'mark', 'help' => false); $this->assertEquals($expected, $result[0], 'Got the correct value.'); $result = $parser->parse(array('--name', 'jimmy')); @@ -230,7 +250,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testPositionalArgument() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $result = $parser->addArgument('name', array('help' => 'An argument')); $this->assertEquals($parser, $result, 'Should returnn this'); } @@ -241,7 +261,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testPositionalArgOverwrite() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addArgument('name', array('help' => 'An argument')) ->addArgument('other', array('index' => 0)); @@ -256,7 +276,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testParseArgumentTooMany() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addArgument('name', array('help' => 'An argument')) ->addArgument('other'); @@ -274,7 +294,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testPositionalArgNotEnough() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addArgument('name', array('required' => true)) ->addArgument('other', array('required' => true)); @@ -288,7 +308,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testPositionalArgWithChoices() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addArgument('name', array('choices' => array('mark', 'jose'))) ->addArgument('alias', array('choices' => array('cowboy', 'samurai'))) ->addArgument('weapon', array('choices' => array('gun', 'sword'))); @@ -306,7 +326,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddArguments() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $result = $parser->addArguments(array( 'name' => array('help' => 'The name'), 'other' => array('help' => 'The other arg') @@ -323,7 +343,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testSubcommand() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $result = $parser->addSubcommand('initdb', array( 'help' => 'Initialize the database' )); @@ -336,7 +356,7 @@ class ConsoleOptionParserTest extends CakeTestCase { * @return void */ function testAddSubcommands() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $result = $parser->addSubcommands(array( 'initdb' => array('help' => 'Initialize the database'), 'create' => array('help' => 'Create something') @@ -589,7 +609,7 @@ TEXT; * @return void */ function testParsingWithSubParser() { - $parser = new ConsoleOptionParser(); + $parser = new ConsoleOptionParser('test', false); $parser->addOption('primary') ->addArgument('one', array('required' => true, 'choices' => array('a', 'b'))) ->addArgument('two', array('required' => true)) @@ -606,7 +626,12 @@ TEXT; )); $result = $parser->parse(array('--secondary', '--fourth', '4', 'c'), 'sub'); - $expected = array(array('secondary' => true, 'fourth' => '4'), array('c')); + $expected = array(array( + 'secondary' => true, + 'fourth' => '4', + 'help' => false, + 'verbose' => false, + 'quiet' => false), array('c')); $this->assertEquals($expected, $result, 'Sub parser did not parse request.'); } } \ No newline at end of file From 52c1c71e4ab8fc25503b12d32d650bfe15c81a52 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 12 Oct 2010 00:04:10 -0400 Subject: [PATCH 107/207] Updating fixture task to use the OptionParser Fixing failing tests caused by changing tasks to lazy loading. --- cake/console/libs/tasks/fixture.php | 56 ++++++++++--------- .../console/libs/tasks/controller.test.php | 2 +- .../cases/console/libs/tasks/fixture.test.php | 1 + .../cases/console/libs/tasks/model.test.php | 2 +- .../cases/console/libs/tasks/plugin.test.php | 2 +- .../cases/console/libs/tasks/view.test.php | 1 + 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/cake/console/libs/tasks/fixture.php b/cake/console/libs/tasks/fixture.php index 49455459a..8d5a5644c 100644 --- a/cake/console/libs/tasks/fixture.php +++ b/cake/console/libs/tasks/fixture.php @@ -59,6 +59,35 @@ class FixtureTask extends BakeTask { $this->path = $this->Dispatch->params['working'] . DS . 'tests' . DS . 'fixtures' . DS; } +/** + * get the option parser. + * + * @return void + */ + public function getOptionParser() { + $parser = parent::getOptionParser(); + return $parser->description( + __('Generate fixtures for use with the test suite. You can use `bake fixture all` to bake all fixtures.') + )->addArgument('name', array( + 'help' => __('Name of the fixture to bake. Can use Plugin.name to bake plugin fixtures.') + ))->addOption('count', array( + 'help' => __('When using generated data, the number of records to include in the fixture(s).'), + 'short' => 'n', + 'default' => 10 + ))->addOption('connection', array( + 'help' => __('Which database configuration to use for baking.'), + 'short' => 'c', + 'default' => 'default' + ))->addOption('plugin', array( + 'help' => __('CamelCased name of the plugin to bake fixtures for.'), + 'short' => 'p', + ))->addOption('records', array( + 'help' => 'Used with --count and /all commands to pull [n] records from the live tables, where [n] is either --count or the default of 10', + 'short' => 'r', + 'boolean' => true + )); + } + /** * Execution method always used for tasks * Handles dispatching to interactive, named, or all processess. @@ -382,31 +411,4 @@ class FixtureTask extends BakeTask { return $out; } -/** - * Displays help contents - * - */ - public function help() { - $this->hr(); - $this->out("Usage: cake bake fixture "); - $this->hr(); - $this->out('Arguments:'); - $this->out(); - $this->out(""); - $this->out("\tName of the fixture to bake. Can use Plugin.name"); - $this->out("\tas a shortcut for plugin baking."); - $this->out(); - $this->out('Commands:'); - $this->out("\nfixture \n\tbakes fixture with specified name."); - $this->out("\nfixture all\n\tbakes all fixtures."); - $this->out(); - $this->out('Parameters:'); - $this->out("\t-count When using generated data, the number of records to include in the fixture(s)."); - $this->out("\t-connection Which database configuration to use for baking."); - $this->out("\t-plugin CamelCased name of plugin to bake fixtures for."); - $this->out("\t-records Used with -count and /all commands to pull [n] records from the live tables"); - $this->out("\t Where [n] is either -count or the default of 10."); - $this->out(); - $this->_stop(); - } } diff --git a/cake/tests/cases/console/libs/tasks/controller.test.php b/cake/tests/cases/console/libs/tasks/controller.test.php index 1e68acbd0..43c758291 100644 --- a/cake/tests/cases/console/libs/tasks/controller.test.php +++ b/cake/tests/cases/console/libs/tasks/controller.test.php @@ -76,7 +76,7 @@ class ControllerTaskTest extends CakeTestCase { ); $this->Task->name = 'ControllerTask'; $this->Task->Dispatch->shellPaths = App::path('shells'); - $this->Task->Template =& new TemplateTask($this->Task->Dispatch); + $this->Task->Template = new TemplateTask($this->Dispatcher, $out, $out, $in); $this->Task->Template->params['theme'] = 'default'; $this->Task->Model = $this->getMock('ModelTask', diff --git a/cake/tests/cases/console/libs/tasks/fixture.test.php b/cake/tests/cases/console/libs/tasks/fixture.test.php index 7b8e82c01..656cf7d46 100644 --- a/cake/tests/cases/console/libs/tasks/fixture.test.php +++ b/cake/tests/cases/console/libs/tasks/fixture.test.php @@ -59,6 +59,7 @@ class FixtureTaskTest extends CakeTestCase { array(&$this->Dispatcher, $out, $out, $in) ); $this->Task->Template = new TemplateTask($this->Dispatcher, $out, $out, $in); + $this->Task->DbConfig = $this->getMock('DbConfigTask', array(), array(&$this->Dispatcher, $out, $out, $in)); $this->Task->Dispatch->shellPaths = App::path('shells'); $this->Task->Template->initialize(); } diff --git a/cake/tests/cases/console/libs/tasks/model.test.php b/cake/tests/cases/console/libs/tasks/model.test.php index 33dd42a37..0d7c4c12f 100644 --- a/cake/tests/cases/console/libs/tasks/model.test.php +++ b/cake/tests/cases/console/libs/tasks/model.test.php @@ -87,7 +87,7 @@ class ModelTaskTest extends CakeTestCase { $this->Task->Fixture = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher, $out, $out, $in)); $this->Task->Test = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher, $out, $out, $in)); - $this->Task->Template =& new TemplateTask($this->Task->Dispatch, $out, $out, $in); + $this->Task->Template = new TemplateTask($this->Task->Dispatch, $out, $out, $in); $this->Task->name = 'ModelTask'; $this->Task->interactive = true; diff --git a/cake/tests/cases/console/libs/tasks/plugin.test.php b/cake/tests/cases/console/libs/tasks/plugin.test.php index 5fda7fda2..03ba4c992 100644 --- a/cake/tests/cases/console/libs/tasks/plugin.test.php +++ b/cake/tests/cases/console/libs/tasks/plugin.test.php @@ -226,7 +226,7 @@ class PluginTaskTest extends CakeTestCase { $this->Task->Model = $this->getMock('ModelTask', array(), array(&$this->Dispatcher, $out, $out, $in)); - $this->Task->expects($this->at(0))->method('in')->will($this->returnValue($this->_testPath)); + $this->Task->expects($this->once())->method('in')->will($this->returnValue($this->_testPath)); $this->Task->Model->expects($this->once())->method('loadTasks'); $this->Task->Model->expects($this->once())->method('execute'); diff --git a/cake/tests/cases/console/libs/tasks/view.test.php b/cake/tests/cases/console/libs/tasks/view.test.php index c43b55a4e..759239ed6 100644 --- a/cake/tests/cases/console/libs/tasks/view.test.php +++ b/cake/tests/cases/console/libs/tasks/view.test.php @@ -233,6 +233,7 @@ class ViewTaskTest extends CakeTestCase { $this->Task->Template = new TemplateTask($this->Dispatcher, $out, $out, $in); $this->Task->Controller = $this->getMock('ControllerTask', array(), array(&$this->Dispatcher, $out, $out, $in)); $this->Task->Project = $this->getMock('ProjectTask', array(), array(&$this->Dispatcher, $out, $out, $in)); + $this->Task->DbConfig = $this->getMock('DbConfigTask', array(), array(&$this->Dispatcher, $out, $out, $in)); $this->Dispatcher->shellPaths = App::path('shells'); $this->Task->path = TMP; From d3e4cedf12633d54253aed3f5c2f7417e9a99719 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 12 Oct 2010 22:25:04 -0400 Subject: [PATCH 108/207] Updating test task to use the option parser. Removing dead help method. --- cake/console/libs/tasks/test.php | 35 +++++++++++++------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/cake/console/libs/tasks/test.php b/cake/console/libs/tasks/test.php index f64e9bdc1..5e1d353bb 100644 --- a/cake/console/libs/tasks/test.php +++ b/cake/console/libs/tasks/test.php @@ -60,7 +60,6 @@ class TestTask extends BakeTask { */ protected $_fixtures = array(); - /** * Execution method always used for tasks * @@ -77,7 +76,7 @@ class TestTask extends BakeTask { if (count($this->args) > 1) { $type = Inflector::underscore($this->args[0]); if ($this->bake($type, $this->args[1])) { - $this->out('done'); + $this->out('Done'); } } } @@ -412,27 +411,21 @@ class TestTask extends BakeTask { } /** - * Show help file. + * get the option parser. * * @return void */ - public function help() { - $this->hr(); - $this->out("Usage: cake bake test "); - $this->hr(); - $this->out('Commands:'); - $this->out(""); - $this->out("test model post\n\tbakes a test case for the post model."); - $this->out(""); - $this->out("test controller comments\n\tbakes a test case for the comments controller."); - $this->out(""); - $this->out('Arguments:'); - $this->out("\t Can be any of the following 'controller', 'model', 'helper',\n\t'component', 'behavior'."); - $this->out("\t Any existing class for the chosen type."); - $this->out(""); - $this->out("Parameters:"); - $this->out("\t-plugin CamelCased name of plugin to bake tests for."); - $this->out(""); - $this->_stop(); + public function getOptionParser() { + $parser = parent::getOptionParser(); + return $parser->description(__('Bake test case skeletons for classes.')) + ->addArgument('type', array( + 'help' => __('Type of class to bake, can be any of the following: controller, model, helper, component or behavior.'), + 'choices' => array('controller', 'model', 'helper', 'component', 'behavior') + ))->addArgument('name', array( + 'help' => __('An existing class to bake tests for.') + ))->addOption('plugin', array( + 'short' => 'p', + 'help' => __('CamelCased name of the plugin to bake tests for.') + )); } } From 50139b153cc99d7bb076072b544ac4235cdccdd2 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 12 Oct 2010 22:36:51 -0400 Subject: [PATCH 109/207] Adding OptionParser to view task. Adding epilog information about interactive mode to view and fixture tasks. --- cake/console/libs/tasks/fixture.php | 2 +- cake/console/libs/tasks/test.php | 2 +- cake/console/libs/tasks/view.php | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/cake/console/libs/tasks/fixture.php b/cake/console/libs/tasks/fixture.php index 8d5a5644c..19fe0d76f 100644 --- a/cake/console/libs/tasks/fixture.php +++ b/cake/console/libs/tasks/fixture.php @@ -85,7 +85,7 @@ class FixtureTask extends BakeTask { 'help' => 'Used with --count and /all commands to pull [n] records from the live tables, where [n] is either --count or the default of 10', 'short' => 'r', 'boolean' => true - )); + ))->epilog(__('Omitting all arguments and options will enter into an interactive mode.'));; } /** diff --git a/cake/console/libs/tasks/test.php b/cake/console/libs/tasks/test.php index 5e1d353bb..52d8cf9f1 100644 --- a/cake/console/libs/tasks/test.php +++ b/cake/console/libs/tasks/test.php @@ -426,6 +426,6 @@ class TestTask extends BakeTask { ))->addOption('plugin', array( 'short' => 'p', 'help' => __('CamelCased name of the plugin to bake tests for.') - )); + ))->epilog(__('Omitting all arguments and options will enter into an interactive mode.')); } } diff --git a/cake/console/libs/tasks/view.php b/cake/console/libs/tasks/view.php index f4f4e95bd..8ae7dd51a 100644 --- a/cake/console/libs/tasks/view.php +++ b/cake/console/libs/tasks/view.php @@ -460,6 +460,33 @@ class ViewTask extends BakeTask { $this->_stop(); } +/** + * get the option parser for this task + * + * @return ConsoleOptionParser + */ + public function getOptionParser() { + $parser = parent::getOptionParser(); + return $parser->description( + __('Bake views for a controller, using built-in or custom templates.') + )->addArgument('controller', array( + 'help' => __('Name of the controller views to bake. Can be Plugin.name as a shortcut for plugin baking.') + ))->addArgument('action', array( + 'help' => __("Will bake a single action's file. core templates are (index, add, edit, view)") + ))->addArgument('alias', array( + 'help' => __('Will bake the template in but create the filename after .') + ))->addOption('plugin', array( + 'help' => __('Plugin to bake the view into.') + ))->addOption('admin', array( + 'help' => __('Set to only bake views for a prefix in Routing.prefixes'), + 'boolean' => true + ))->addOption('connection', array( + 'help' => __('The connection the connected model is on.') + ))->addSubcommand('all', array( + 'help' => __('Bake all CRUD action views for all controllers. Requires models and controllers to exist.') + ))->epilog(__('Omitting all arguments and options will enter into an interactive mode.')); + } + /** * Returns associations for controllers models. * From 4985572f9bd017d1a93f1fff2f9ff2950f760b99 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 12 Oct 2010 22:55:19 -0400 Subject: [PATCH 110/207] Adding option parser to the project task. --- cake/console/libs/tasks/project.php | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/cake/console/libs/tasks/project.php b/cake/console/libs/tasks/project.php index f5ca174d5..9813527e6 100644 --- a/cake/console/libs/tasks/project.php +++ b/cake/console/libs/tasks/project.php @@ -343,21 +343,17 @@ class ProjectTask extends Shell { } /** - * Help + * get the option parser. * - * @return void + * @return ConsoleOptionParser */ - public function help() { - $this->hr(); - $this->out("Usage: cake bake project "); - $this->hr(); - $this->out('Commands:'); - $this->out(); - $this->out("project "); - $this->out("\tbakes app directory structure."); - $this->out("\tif begins with '/' path is absolute."); - $this->out(); - $this->_stop(); + public function getOptionParser() { + $parser = parent::getOptionParser(); + return $parser->description( + __('Generate a new CakePHP project skeleton.') + )->addArgument('name', array( + 'help' => __('Application directory to make, if it starts with "/" the path is absolute.') + )); } } From 6b6d9e9b30306af3ce9b3db92945974aed0fd010 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 12 Oct 2010 23:06:07 -0400 Subject: [PATCH 111/207] Adding option parser to model task. Adding omitted options to view task. --- cake/console/libs/tasks/model.php | 40 +++++++++++++---------------- cake/console/libs/tasks/view.php | 42 ++----------------------------- 2 files changed, 19 insertions(+), 63 deletions(-) diff --git a/cake/console/libs/tasks/model.php b/cake/console/libs/tasks/model.php index 80f5c3717..d44f1aa3c 100644 --- a/cake/console/libs/tasks/model.php +++ b/cake/console/libs/tasks/model.php @@ -873,31 +873,25 @@ class ModelTask extends BakeTask { } /** - * Displays help contents + * get the option parser. * + * @return void */ - public function help() { - $this->hr(); - $this->out("Usage: cake bake model "); - $this->hr(); - $this->out('Arguments:'); - $this->out(); - $this->out(""); - $this->out("\tName of the model to bake. Can use Plugin.name"); - $this->out("\tas a shortcut for plugin baking."); - $this->out(); - $this->out('Commands:'); - $this->out(); - $this->out("model"); - $this->out("\tbakes model in interactive mode."); - $this->out(); - $this->out("model "); - $this->out("\tbakes model file with no associations or validation"); - $this->out(); - $this->out("model all"); - $this->out("\tbakes all model files with associations and validation"); - $this->out(); - $this->_stop(); + public function getOptionParser() { + $parser = parent::getOptionParser(); + return $parser->description( + __('Bake models.') + )->addArgument('name', array( + 'help' => __('Name of the model to bake. Can use Plugin.name to bake plugin models.') + ))->addSubcommand('all', array( + 'help' => __('Bake all model files with associations and validation.') + ))->addOption('plugin', array( + 'short' => 'p', + 'help' => __('Plugin to bake the model into.') + ))->addOption('connection', array( + 'short' => 'c', + 'help' => __('The connection the model table is on.') + ))->epilog(__('Omitting all arguments and options will enter into an interactive mode.')); } /** diff --git a/cake/console/libs/tasks/view.php b/cake/console/libs/tasks/view.php index 8ae7dd51a..65ee1f0da 100644 --- a/cake/console/libs/tasks/view.php +++ b/cake/console/libs/tasks/view.php @@ -420,46 +420,6 @@ class ViewTask extends BakeTask { return $template; } -/** - * Displays help contents - * - */ - public function help() { - $this->hr(); - $this->out("Usage: cake bake view ..."); - $this->hr(); - $this->out('Arguments:'); - $this->out(); - $this->out(""); - $this->out("\tName of the controller views to bake. Can use Plugin.name"); - $this->out("\tas a shortcut for plugin baking."); - $this->out(); - $this->out(""); - $this->out("\tName of the action view to bake"); - $this->out(); - $this->out('Commands:'); - $this->out(); - $this->out("view "); - $this->out("\tWill read the given controller for methods"); - $this->out("\tand bake corresponding views."); - $this->out("\tUsing the -admin flag will only bake views for actions"); - $this->out("\tthat begin with Routing.prefixes."); - $this->out("\tIf var scaffold is found it will bake the CRUD actions"); - $this->out("\t(index,view,add,edit)"); - $this->out(); - $this->out("view "); - $this->out("\tWill bake a template. core templates: (index, add, edit, view)"); - $this->out(); - $this->out("view