Browse Source

Merge pull request #3358 from cakephp/shelldispatcher-alias

3.0 - Add Shelldispatcher::alias()
José Lorenzo Rodríguez 12 years ago
parent
commit
5f2261837d
2 changed files with 147 additions and 198 deletions
  1. 65 39
      src/Console/ShellDispatcher.php
  2. 82 159
      tests/TestCase/Console/ShellDispatcherTest.php

+ 65 - 39
src/Console/ShellDispatcher.php

@@ -35,6 +35,13 @@ class ShellDispatcher {
 	public $args = [];
 
 /**
+ * List of connected aliases.
+ *
+ * @var array
+ */
+	protected static $_aliases = [];
+
+/**
  * Constructor
  *
  * The execution of the script is stopped after dispatching the request with
@@ -53,6 +60,32 @@ class ShellDispatcher {
 	}
 
 /**
+ * Add an alias for a shell command.
+ *
+ * Aliases allow you to call shells by alternate names. This is most
+ * useful when dealing with plugin shells that you want to have shorter
+ * names for.
+ *
+ * If you re-use an alias the last alias set will be the one available.
+ *
+ * @param string $short The new short name for the shell.
+ * @param string $original The original full name for the shell.
+ * @return void
+ */
+	public static function alias($short, $original) {
+		static::$_aliases[$short] = $original;
+	}
+
+/**
+ * Clear any aliases that have been set.
+ *
+ * @return void
+ */
+	public static function resetAliases() {
+		static::$_aliases = [];
+	}
+
+/**
  * Run the dispatcher
  *
  * @param array $argv The argv from PHP
@@ -124,35 +157,15 @@ class ShellDispatcher {
 			return true;
 		}
 
-		$Shell = $this->_getShell($shell);
+		$Shell = $this->findShell($shell);
 
 		$command = null;
 		if (isset($this->args[0])) {
 			$command = $this->args[0];
 		}
 
-		if ($Shell instanceof Shell) {
-			$Shell->initialize();
-			return $Shell->runCommand($command, $this->args);
-		}
-
-		$methods = array_diff(get_class_methods($Shell), get_class_methods('Cake\Console\Shell'));
-		$added = in_array($command, $methods);
-		$private = $command[0] === '_' && method_exists($Shell, $command);
-
-		if (!$private) {
-			if ($added) {
-				$this->shiftArgs();
-				$Shell->startup();
-				return $Shell->{$command}();
-			}
-			if (method_exists($Shell, 'main')) {
-				$Shell->startup();
-				return $Shell->main();
-			}
-		}
-
-		throw new Error\MissingShellMethodException(['shell' => $shell, 'method' => $command]);
+		$Shell->initialize();
+		return $Shell->runCommand($command, $this->args);
 	}
 
 /**
@@ -161,27 +174,40 @@ class ShellDispatcher {
  * All paths in the loaded shell paths are searched.
  *
  * @param string $shell Optionally the name of a plugin
- * @return mixed An object
+ * @return \Cake\Console\Shell A shell instance.
  * @throws \Cake\Console\Error\MissingShellException when errors are encountered.
  */
-	protected function _getShell($shell) {
-		list($plugin, $shell) = pluginSplit($shell);
-
-		$plugin = Inflector::camelize($plugin);
-		$class = Inflector::camelize($shell);
-		if ($plugin) {
-			$class = $plugin . '.' . $class;
+	public function findShell($shell) {
+		$classname = $this->_shellExists($shell);
+		if (!$classname && isset(static::$_aliases[$shell])) {
+			$shell = static::$_aliases[$shell];
+			$classname = $this->_shellExists($shell);
 		}
-		$class = App::classname($class, 'Console/Command', 'Shell');
+		if ($classname) {
+			list($plugin) = pluginSplit($shell);
+			$instance = new $classname();
+			$instance->plugin = Inflector::camelize(trim($plugin, '.'));
+			return $instance;
+		}
+		throw new Error\MissingShellException([
+			'class' => $shell,
+		]);
+	}
 
-		if (!class_exists($class)) {
-			throw new Error\MissingShellException([
-				'class' => $shell,
-			]);
+/**
+ * Check if a shell class exists for the given name.
+ *
+ * @param string $shell The shell name to look for.
+ * @return string|boolean Either the classname or false.
+ */
+	protected function _shellExists($shell) {
+		$class = array_map('Cake\Utility\Inflector::camelize', explode('.', $shell));
+		$class = implode('.', $class);
+		$class = App::classname($class, 'Console/Command', 'Shell');
+		if (class_exists($class)) {
+			return $class;
 		}
-		$Shell = new $class();
-		$Shell->plugin = trim($plugin, '.');
-		return $Shell;
+		return false;
 	}
 
 /**

+ 82 - 159
tests/TestCase/Console/ShellDispatcherTest.php

@@ -21,123 +21,102 @@ use Cake\Core\Plugin;
 use Cake\TestSuite\TestCase;
 
 /**
- * TestShellDispatcher class
- *
- */
-class TestShellDispatcher extends ShellDispatcher {
-
-/**
- * params property
- *
- * @var array
- */
-	public $params = array();
-
-/**
- * stopped property
- *
- * @var string
- */
-	public $stopped = null;
-
-/**
- * TestShell
+ * ShellDispatcherTest
  *
- * @var mixed
  */
-	public $TestShell;
+class ShellDispatcherTest extends TestCase {
 
 /**
- * _initEnvironment method
+ * setUp method
  *
  * @return void
  */
-	protected function _initEnvironment() {
+	public function setUp() {
+		parent::setUp();
+		Plugin::load('TestPlugin');
+		Configure::write('App.namespace', 'TestApp');
+		$this->dispatcher = $this->getMock('Cake\Console\ShellDispatcher', ['_stop']);
 	}
 
 /**
- * clear method
+ * teardown
  *
  * @return void
  */
-	public function clear() {
+	public function tearDown() {
+		parent::tearDown();
+		ShellDispatcher::resetAliases();
 	}
 
 /**
- * _stop method
+ * Test error on missing shell
  *
+ * @expectedException Cake\Console\Error\MissingShellException
  * @return void
  */
-	protected function _stop($status = 0) {
-		$this->stopped = 'Stopped with status: ' . $status;
-		return $status;
+	public function testFindShellMissing() {
+		$this->dispatcher->findShell('nope');
 	}
 
 /**
- * getShell
+ * Test error on missing plugin shell
  *
- * @param string $shell
- * @return mixed
+ * @expectedException Cake\Console\Error\MissingShellException
+ * @return void
  */
-	public function getShell($shell) {
-		return $this->_getShell($shell);
+	public function testFindShellMissingPlugin() {
+		$this->dispatcher->findShell('test_plugin.nope');
 	}
 
 /**
- * _getShell
+ * Verify loading of (plugin-) shells
  *
- * @param string $shell
- * @return mixed
+ * @return void
  */
-	protected function _getShell($shell) {
-		if (isset($this->TestShell)) {
-			return $this->TestShell;
-		}
-		return parent::_getShell($shell);
-	}
+	public function testFindShell() {
+		$result = $this->dispatcher->findShell('sample');
+		$this->assertInstanceOf('TestApp\Console\Command\SampleShell', $result);
 
-}
+		$result = $this->dispatcher->findShell('test_plugin.example');
+		$this->assertInstanceOf('TestPlugin\Console\Command\ExampleShell', $result);
+		$this->assertEquals('TestPlugin', $result->plugin);
+		$this->assertEquals('Example', $result->name);
 
-/**
- * ShellDispatcherTest
- *
- */
-class ShellDispatcherTest extends TestCase {
+		$result = $this->dispatcher->findShell('TestPlugin.example');
+		$this->assertInstanceOf('TestPlugin\Console\Command\ExampleShell', $result);
+
+		$result = $this->dispatcher->findShell('TestPlugin.Example');
+		$this->assertInstanceOf('TestPlugin\Console\Command\ExampleShell', $result);
+	}
 
 /**
- * setUp method
+ * Test getting shells with aliases.
  *
  * @return void
  */
-	public function setUp() {
-		parent::setUp();
-		Plugin::load('TestPlugin');
+	public function testFindShellAliased() {
+		ShellDispatcher::alias('short', 'test_plugin.example');
+
+		$result = $this->dispatcher->findShell('short');
+		$this->assertInstanceOf('TestPlugin\Console\Command\ExampleShell', $result);
+		$this->assertEquals('TestPlugin', $result->plugin);
+		$this->assertEquals('Example', $result->name);
 	}
 
 /**
- * Verify loading of (plugin-) shells
+ * Test finding a shell that has a matching alias.
+ *
+ * Aliases should not overload concrete shells.
  *
  * @return void
  */
-	public function testGetShell() {
-		$this->skipIf(class_exists('SampleShell'), 'SampleShell Class already loaded.');
-		$this->skipIf(class_exists('ExampleShell'), 'ExampleShell Class already loaded.');
+	public function testFindShellAliasedAppShadow() {
+		ShellDispatcher::alias('sample', 'test_plugin.example');
 
-		Configure::write('App.namespace', 'TestApp');
-		$Dispatcher = new TestShellDispatcher();
-
-		$result = $Dispatcher->getShell('sample');
+		$result = $this->dispatcher->findShell('sample');
 		$this->assertInstanceOf('TestApp\Console\Command\SampleShell', $result);
-
-		$Dispatcher = new TestShellDispatcher();
-		$result = $Dispatcher->getShell('test_plugin.example');
-		$this->assertInstanceOf('TestPlugin\Console\Command\ExampleShell', $result);
-		$this->assertEquals('TestPlugin', $result->plugin);
-		$this->assertEquals('Example', $result->name);
-
-		$Dispatcher = new TestShellDispatcher();
-		$result = $Dispatcher->getShell('TestPlugin.example');
-		$this->assertInstanceOf('TestPlugin\Console\Command\ExampleShell', $result);
+		$this->assertEmpty($result->plugin);
+		$this->assertEquals('Sample', $result->name);
 	}
 
 /**
@@ -146,7 +125,7 @@ class ShellDispatcherTest extends TestCase {
  * @return void
  */
 	public function testDispatchShellWithMain() {
-		$Dispatcher = new TestShellDispatcher();
+		$dispatcher = $this->getMock('Cake\Console\ShellDispatcher', ['findShell']);
 		$Shell = $this->getMock('Cake\Console\Shell');
 
 		$Shell->expects($this->once())->method('initialize');
@@ -154,12 +133,15 @@ class ShellDispatcherTest extends TestCase {
 			->with(null, array())
 			->will($this->returnValue(true));
 
-		$Dispatcher->TestShell = $Shell;
+		$dispatcher->expects($this->any())
+			->method('findShell')
+			->with('mock_with_main')
+			->will($this->returnValue($Shell));
 
-		$Dispatcher->args = array('mock_with_main');
-		$result = $Dispatcher->dispatch();
+		$dispatcher->args = array('mock_with_main');
+		$result = $dispatcher->dispatch();
 		$this->assertEquals(0, $result);
-		$this->assertEquals(array(), $Dispatcher->args);
+		$this->assertEquals(array(), $dispatcher->args);
 	}
 
 /**
@@ -168,7 +150,7 @@ class ShellDispatcherTest extends TestCase {
  * @return void
  */
 	public function testDispatchShellWithoutMain() {
-		$Dispatcher = new TestShellDispatcher();
+		$dispatcher = $this->getMock('Cake\Console\ShellDispatcher', ['findShell']);
 		$Shell = $this->getMock('Cake\Console\Shell');
 
 		$Shell->expects($this->once())->method('initialize');
@@ -176,70 +158,13 @@ class ShellDispatcherTest extends TestCase {
 			->with('initdb', array('initdb'))
 			->will($this->returnValue(true));
 
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main', 'initdb');
-		$result = $Dispatcher->dispatch();
-		$this->assertEquals(0, $result);
-	}
-
-/**
- * Verify correct dispatch of custom classes with a main method
- *
- * @return void
- */
-	public function testDispatchNotAShellWithMain() {
-		$Dispatcher = new TestShellDispatcher();
-		$methods = ['main', 'initdb', 'initialize', 'loadTasks', 'startup', '_secret'];
-		$Shell = $this->getMock('stdClass', $methods);
-
-		$Shell->expects($this->never())->method('initialize');
-		$Shell->expects($this->once())->method('startup');
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a');
-		$result = $Dispatcher->dispatch();
-		$this->assertEquals(0, $result);
-		$this->assertEquals(array(), $Dispatcher->args);
-
-		$Shell = $this->getMock('stdClass', $methods);
-		$Shell->expects($this->once())->method('initdb')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a', 'initdb');
-		$result = $Dispatcher->dispatch();
-		$this->assertEquals(0, $result);
-	}
-
-/**
- * Verify correct dispatch of custom classes without a main method
- *
- * @return void
- */
-	public function testDispatchNotAShellWithoutMain() {
-		$Dispatcher = new TestShellDispatcher();
-		$methods = ['main', 'initdb', 'initialize', 'loadTasks', 'startup', '_secret'];
-		$Shell = $this->getMock('stdClass', $methods);
-
-		$Shell->expects($this->never())->method('initialize');
-		$Shell->expects($this->once())->method('startup');
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Dispatcher->TestShell = $Shell;
+		$dispatcher->expects($this->any())
+			->method('findShell')
+			->with('mock_without_main')
+			->will($this->returnValue($Shell));
 
-		$Dispatcher->args = array('mock_without_main_not_a');
-		$result = $Dispatcher->dispatch();
-		$this->assertEquals(0, $result);
-		$this->assertEquals(array(), $Dispatcher->args);
-
-		$Shell = $this->getMock('stdClass', $methods);
-		$Shell->expects($this->once())->method('initdb')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main_not_a', 'initdb');
-		$result = $Dispatcher->dispatch();
+		$dispatcher->args = array('mock_without_main', 'initdb');
+		$result = $dispatcher->dispatch();
 		$this->assertEquals(0, $result);
 	}
 
@@ -249,27 +174,25 @@ class ShellDispatcherTest extends TestCase {
  * @return void
  */
 	public function testShiftArgs() {
-		$Dispatcher = new TestShellDispatcher();
-
-		$Dispatcher->args = array('a', 'b', 'c');
-		$this->assertEquals('a', $Dispatcher->shiftArgs());
-		$this->assertSame($Dispatcher->args, array('b', 'c'));
+		$this->dispatcher->args = array('a', 'b', 'c');
+		$this->assertEquals('a', $this->dispatcher->shiftArgs());
+		$this->assertSame($this->dispatcher->args, array('b', 'c'));
 
-		$Dispatcher->args = array('a' => 'b', 'c', 'd');
-		$this->assertEquals('b', $Dispatcher->shiftArgs());
-		$this->assertSame($Dispatcher->args, array('c', 'd'));
+		$this->dispatcher->args = array('a' => 'b', 'c', 'd');
+		$this->assertEquals('b', $this->dispatcher->shiftArgs());
+		$this->assertSame($this->dispatcher->args, array('c', 'd'));
 
-		$Dispatcher->args = array('a', 'b' => 'c', 'd');
-		$this->assertEquals('a', $Dispatcher->shiftArgs());
-		$this->assertSame($Dispatcher->args, array('b' => 'c', 'd'));
+		$this->dispatcher->args = array('a', 'b' => 'c', 'd');
+		$this->assertEquals('a', $this->dispatcher->shiftArgs());
+		$this->assertSame($this->dispatcher->args, array('b' => 'c', 'd'));
 
-		$Dispatcher->args = array(0 => 'a', 2 => 'b', 30 => 'c');
-		$this->assertEquals('a', $Dispatcher->shiftArgs());
-		$this->assertSame($Dispatcher->args, array(0 => 'b', 1 => 'c'));
+		$this->dispatcher->args = array(0 => 'a', 2 => 'b', 30 => 'c');
+		$this->assertEquals('a', $this->dispatcher->shiftArgs());
+		$this->assertSame($this->dispatcher->args, array(0 => 'b', 1 => 'c'));
 
-		$Dispatcher->args = array();
-		$this->assertNull($Dispatcher->shiftArgs());
-		$this->assertSame(array(), $Dispatcher->args);
+		$this->dispatcher->args = array();
+		$this->assertNull($this->dispatcher->shiftArgs());
+		$this->assertSame(array(), $this->dispatcher->args);
 	}
 
 }