From e7a23f54b071fca69837b95c5c1f0b0a882502c3 Mon Sep 17 00:00:00 2001 From: Edouard Tack Date: Fri, 4 Aug 2017 17:51:21 +0200 Subject: [PATCH 01/12] Add Shell to create action-class file --- README.md | 20 +++++++ src/Shell/ActionShell.php | 91 +++++++++++++++++++++++++++++ src/Template/Bake/Action/action.ctp | 30 ++++++++++ 3 files changed, 141 insertions(+) create mode 100644 src/Shell/ActionShell.php create mode 100644 src/Template/Bake/Action/action.ctp diff --git a/README.md b/README.md index 5028ab4..4782c76 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,26 @@ class EditAction extends Action } ``` +#### Using shell to create actions-class ingredients + +You have to activate plugin in `config/bootstrap_cli.php` with adding `Plugin::load('HavokInspiration/ActionsClass');` + +``` +php bin/cake.php HavokInspiration/ActionsClass.action +``` + +Differents options are available: +* --controller or -c : it's the name of directory (eg: src/Controller/{CONTROLLER_NAME}/EditAction.php) +* --action or -a : it's the name of action file (eg: src/Controller/{CONTROLLER_NAME}/{ACTION_NAME}Action.php) +* --prefix : use the prefix CakePHP convention +* --plugin : use the plugin CakePHP convention + +So, we can write + +``` +php bin/cake.php HavokInspiration/ActionsClass.action -c controller_name -a action_name +``` + ### Integration testing The plugin is compatible with the [Integration testing](https://book.cakephp.org/3.0/en/development/testing.html#controller-integration-testing) feature of CakePHP. diff --git a/src/Shell/ActionShell.php b/src/Shell/ActionShell.php new file mode 100644 index 0000000..4a01284 --- /dev/null +++ b/src/Shell/ActionShell.php @@ -0,0 +1,91 @@ +addOptions([ + 'controller' => [ + 'short' => 'c', + 'help' => 'The controller folder' + ], + 'action' => [ + 'short' => 'a', + 'help' => 'The action file' + ], + 'prefix' => [ + 'help' => 'The prefix name' + ], + 'plugin' => [ + 'help' => 'The plugin folder' + ] + ]); + + return $parser; + } + + /** + * main() method. + * + * @return bool|int Success or error code. + */ + public function main() + { + if (!$this->param('controller')) { + $this->out('The controller name (-c Controller) is needed'); + + return false; + } + + $directory = APP; + $data['{{namespace}}'] = 'App'; + if ($this->param('plugin')) { + $data['{{namespace}}'] = $this->_camelize($this->param('plugin')); + $directory = ROOT . DS . 'plugins' . DS . $data['{{namespace}}'] . 'src' . DS; + } + $directory .= 'Controller' . DS; + + if ($this->param('prefix')) { + $data['{{namespace}}'] .= '\\' . $this->_camelize($this->param('prefix')); + $directory .= $this->_camelize($this->param('prefix')) . DS; + } + + $controllerName = $this->_camelize($this->param('controller')); + $data['{{controller}}'] = $controllerName; + + $folder = new Folder($directory . $controllerName, true); + + $data['{{name}}'] = 'Index'; + if ($this->param('action')) + $data['{{name}}'] = $this->_camelize($this->param('action')); + + $templateFile = dirname(__DIR__) . DS . 'Template' . DS . 'Bake' . DS . 'Action' . DS . 'action.ctp'; + $file = new File($templateFile); + $contents = str_replace(array_keys($data), $data, $file->read()); + + $this->createFile($folder->pwd() . DS . $data['{{name}}'] . 'Action.php', $contents); + } +} diff --git a/src/Template/Bake/Action/action.ctp b/src/Template/Bake/Action/action.ctp new file mode 100644 index 0000000..cc2f52d --- /dev/null +++ b/src/Template/Bake/Action/action.ctp @@ -0,0 +1,30 @@ + Date: Fri, 4 Aug 2017 18:03:08 +0200 Subject: [PATCH 02/12] Change README and comment/refactoring code --- README.md | 40 +++++++++++++++++++-------------------- src/Shell/ActionShell.php | 25 ++++++++++++++++++------ 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 4782c76..323dbd0 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,26 @@ src/ Your `Action` classes are only expected to hold an `execute()` method. It can receive passed parameters as in regular controller actions (meaning the URL `/posts/edit/5` will pass `5` to the argument `$id` in the `execute()` method of the `EditAction` class in our previous example). +#### Using shell to create actions-class ingredients + +You have to activate plugin in `config/bootstrap_cli.php` with adding `Plugin::load('HavokInspiration/ActionsClass');` + +``` +php bin/cake.php HavokInspiration/ActionsClass.action +``` + +Differents options are available: +* --controller or -c : it's the name of directory (eg: src/Controller/{CONTROLLER_NAME}/EditAction.php) +* --action or -a : it's the name of action file (eg: src/Controller/{CONTROLLER_NAME}/{ACTION_NAME}Action.php) +* --prefix : use the prefix CakePHP convention +* --plugin : use the plugin CakePHP convention + +So, we can write + +``` +php bin/cake.php HavokInspiration/ActionsClass.action -c controller_name -a action_name +``` + ## Compatibility This plugin was designed to have a maximum compatibility with the regular CakePHP behavior. @@ -177,26 +197,6 @@ class EditAction extends Action } ``` -#### Using shell to create actions-class ingredients - -You have to activate plugin in `config/bootstrap_cli.php` with adding `Plugin::load('HavokInspiration/ActionsClass');` - -``` -php bin/cake.php HavokInspiration/ActionsClass.action -``` - -Differents options are available: -* --controller or -c : it's the name of directory (eg: src/Controller/{CONTROLLER_NAME}/EditAction.php) -* --action or -a : it's the name of action file (eg: src/Controller/{CONTROLLER_NAME}/{ACTION_NAME}Action.php) -* --prefix : use the prefix CakePHP convention -* --plugin : use the plugin CakePHP convention - -So, we can write - -``` -php bin/cake.php HavokInspiration/ActionsClass.action -c controller_name -a action_name -``` - ### Integration testing The plugin is compatible with the [Integration testing](https://book.cakephp.org/3.0/en/development/testing.html#controller-integration-testing) feature of CakePHP. diff --git a/src/Shell/ActionShell.php b/src/Shell/ActionShell.php index 4a01284..cbd3be6 100644 --- a/src/Shell/ActionShell.php +++ b/src/Shell/ActionShell.php @@ -62,30 +62,43 @@ public function main() $directory = APP; $data['{{namespace}}'] = 'App'; + + // Go to the plugin directory if ($this->param('plugin')) { $data['{{namespace}}'] = $this->_camelize($this->param('plugin')); $directory = ROOT . DS . 'plugins' . DS . $data['{{namespace}}'] . 'src' . DS; } + + // It's always in Controller directory $directory .= 'Controller' . DS; + // Add prefix name and directory if ($this->param('prefix')) { - $data['{{namespace}}'] .= '\\' . $this->_camelize($this->param('prefix')); - $directory .= $this->_camelize($this->param('prefix')) . DS; + $prefixname .= '\\' . $this->_camelize($this->param('prefix')); + $data['{{namespace}}'] = $prefixname; + $directory .= $prefixname . DS; } + // Controller name use CakePHP Convention $controllerName = $this->_camelize($this->param('controller')); $data['{{controller}}'] = $controllerName; + // Find and create folder $folder = new Folder($directory . $controllerName, true); + // Index is the default action if -a is not define $data['{{name}}'] = 'Index'; - if ($this->param('action')) - $data['{{name}}'] = $this->_camelize($this->param('action')); + if ($this->param('action')) { + $actionName = $this->_camelize($this->param('action')); + $data['{{name}}'] = $actionName; + } + // The action Template available on HavokInspiration/cakephp-actions-class plugin $templateFile = dirname(__DIR__) . DS . 'Template' . DS . 'Bake' . DS . 'Action' . DS . 'action.ctp'; $file = new File($templateFile); - $contents = str_replace(array_keys($data), $data, $file->read()); + // Assign data + $contents = str_replace(array_keys($data), array_values($data), $file->read()); - $this->createFile($folder->pwd() . DS . $data['{{name}}'] . 'Action.php', $contents); + $this->createFile($folder->pwd() . DS . $actionName . 'Action.php', $contents); } } From 7bc6758311da228527b6ff10f4a968e8cc5bede2 Mon Sep 17 00:00:00 2001 From: Edouard Tack Date: Wed, 9 Aug 2017 17:48:07 +0200 Subject: [PATCH 03/12] Refactoring --- src/Shell/ActionShell.php | 113 +++++++++++++++++++++------- src/Template/Bake/Action/action.ctp | 2 +- src/Template/Bake/Tests/action.ctp | 31 ++++++++ 3 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 src/Template/Bake/Tests/action.ctp diff --git a/src/Shell/ActionShell.php b/src/Shell/ActionShell.php index cbd3be6..27a2895 100644 --- a/src/Shell/ActionShell.php +++ b/src/Shell/ActionShell.php @@ -4,10 +4,8 @@ use Cake\Console\Shell; use Cake\Core\ConventionsTrait; -use Cake\Database\Exception; -use Cake\Filesystem\Folder; use Cake\Filesystem\File; -use Cake\Utility\Inflector; +use Cake\Utility\Text; /** * Fixtures shell command. @@ -16,6 +14,16 @@ class ActionShell extends Shell { use ConventionsTrait; + /** + * @var string + */ + protected $templateFile = ''; + + /** + * @var array + */ + protected $data = []; + /** * Manage the available sub-commands along with their arguments and help * @@ -50,7 +58,7 @@ public function getOptionParser() /** * main() method. * - * @return bool|int Success or error code. + * @return bool Success or not. */ public function main() { @@ -60,45 +68,98 @@ public function main() return false; } + // The action Template available on HavokInspiration/cakephp-actions-class plugin + $this->templateFile = dirname(__DIR__) . DS . 'Template' . DS . 'Bake' . DS; + + // Controller name use CakePHP Convention + $controllerName = $this->_inflected($this->param('controller')); + if (empty($controllerName)) { + $this->out('The controller name is empty.'); + $this->out('Respect the CakePHP convention.'); + + return false; + } + $this->data['{{controller}}'] = $controllerName; + $this->data['{{controller.lower}}'] = strtolower($controllerName); + $directory = APP; - $data['{{namespace}}'] = 'App'; + $nameSpace = 'App'; // Go to the plugin directory if ($this->param('plugin')) { - $data['{{namespace}}'] = $this->_camelize($this->param('plugin')); - $directory = ROOT . DS . 'plugins' . DS . $data['{{namespace}}'] . 'src' . DS; + $pluginName = $this->_inflected($this->param('plugin')); + if (!empty($pluginName)) { + $nameSpace = $pluginName; + $directory = ROOT . DS . 'plugins' . DS . $pluginName . DS . 'src' . DS; + } } // It's always in Controller directory - $directory .= 'Controller' . DS; + $directory .= 'Controller' . DS; // Add prefix name and directory if ($this->param('prefix')) { - $prefixname .= '\\' . $this->_camelize($this->param('prefix')); - $data['{{namespace}}'] = $prefixname; - $directory .= $prefixname . DS; + $prefixname = $this->_inflected($this->param('prefix')); + if (!empty($prefixname)) { + $nameSpace .= '\\' . $prefixname; + $directory .= $prefixname . DS; + } } - // Controller name use CakePHP Convention - $controllerName = $this->_camelize($this->param('controller')); - $data['{{controller}}'] = $controllerName; - - // Find and create folder - $folder = new Folder($directory . $controllerName, true); - // Index is the default action if -a is not define - $data['{{name}}'] = 'Index'; if ($this->param('action')) { - $actionName = $this->_camelize($this->param('action')); - $data['{{name}}'] = $actionName; + $actionName = $this->_inflected($this->param('action')); } + if (empty($actionName)) { + $actionName = 'Index'; + } + $this->data['{{name}}'] = $actionName; - // The action Template available on HavokInspiration/cakephp-actions-class plugin - $templateFile = dirname(__DIR__) . DS . 'Template' . DS . 'Bake' . DS . 'Action' . DS . 'action.ctp'; - $file = new File($templateFile); + $this->data['{{namespace}}'] = $nameSpace; + + $this->_createActionFile($directory); + $this->_createTestFile(); + + return true; + } + + /** + * Create Controller Action file + * + * @param string $directory The directory to controller + */ + protected function _createActionFile($directory) + { + $file = new File($this->templateFile . 'Action' . DS . 'action.ctp'); // Assign data - $contents = str_replace(array_keys($data), array_values($data), $file->read()); + $contents = str_replace(array_keys($this->data), array_values($this->data), $file->read()); - $this->createFile($folder->pwd() . DS . $actionName . 'Action.php', $contents); + $this->createFile($directory . $this->data['{{controller}}'] . DS . $this->data['{{name}}'] . 'Action.php', $contents); + } + + /** + * Create TestCase file + */ + protected function _createTestFile() + { + $file = new File($this->templateFile . 'Tests' . DS . 'action.ctp'); + // Assign data + $contents = str_replace(array_keys($this->data), array_values($this->data), $file->read()); + + $this->createFile(TESTS . 'TestCase' . DS . $this->data['{{controller}}'] . DS . $this->data['{{name}}'] . 'Action.php', $contents); + } + + /** + * Correct string with Text::slug and use CakePHP convention + * + * @param string $string + * @see https://book.cakephp.org/3.0/en/core-libraries/text.html#Cake\Utility\Text::slug + * @see https://book.cakephp.org/3.0/en/intro/conventions.html + * + * @return string The string after Text::slug and + */ + protected function _inflected($string) + { + return $this->_camelize(Text::slug($string, ['replacement' => '_'])); } } diff --git a/src/Template/Bake/Action/action.ctp b/src/Template/Bake/Action/action.ctp index cc2f52d..0a9a2f1 100644 --- a/src/Template/Bake/Action/action.ctp +++ b/src/Template/Bake/Action/action.ctp @@ -21,7 +21,7 @@ use HavokInspiration\ActionsClass\Controller\Action; class {{name}}Action extends Action { /** - * + * It's like {{controller}}Controller::{{name}} method */ public function execute() { diff --git a/src/Template/Bake/Tests/action.ctp b/src/Template/Bake/Tests/action.ctp new file mode 100644 index 0000000..409f4b9 --- /dev/null +++ b/src/Template/Bake/Tests/action.ctp @@ -0,0 +1,31 @@ +get('/{{controller.lower}}'); + $this->assertResponseOk(); + } +} From 468e889ef6df58dab0e3957e6189b25d39bfc317 Mon Sep 17 00:00:00 2001 From: Edouard Tack Date: Fri, 11 Aug 2017 16:41:52 +0200 Subject: [PATCH 04/12] Create task rather than Shell --- README.md | 17 +- src/Shell/ActionShell.php | 165 ------------------ src/Shell/Task/ActionTask.php | 134 ++++++++++++++ src/Template/Bake/{Action => }/action.ctp | 12 +- .../Bake/{Tests/action.ctp => tests.ctp} | 14 +- 5 files changed, 153 insertions(+), 189 deletions(-) delete mode 100644 src/Shell/ActionShell.php create mode 100644 src/Shell/Task/ActionTask.php rename src/Template/Bake/{Action => }/action.ctp (62%) rename src/Template/Bake/{Tests/action.ctp => tests.ctp} (59%) diff --git a/README.md b/README.md index 323dbd0..2dc0e40 100644 --- a/README.md +++ b/README.md @@ -119,25 +119,20 @@ src/ Your `Action` classes are only expected to hold an `execute()` method. It can receive passed parameters as in regular controller actions (meaning the URL `/posts/edit/5` will pass `5` to the argument `$id` in the `execute()` method of the `EditAction` class in our previous example). -#### Using shell to create actions-class ingredients +#### Using command-line to create actions-class ingredients You have to activate plugin in `config/bootstrap_cli.php` with adding `Plugin::load('HavokInspiration/ActionsClass');` +Create an Action File with this following command + ``` -php bin/cake.php HavokInspiration/ActionsClass.action +php bin/cake.php bake action Posts/Index ``` Differents options are available: -* --controller or -c : it's the name of directory (eg: src/Controller/{CONTROLLER_NAME}/EditAction.php) -* --action or -a : it's the name of action file (eg: src/Controller/{CONTROLLER_NAME}/{ACTION_NAME}Action.php) +* Controller/action or only Controller (Index is the default action) * --prefix : use the prefix CakePHP convention -* --plugin : use the plugin CakePHP convention - -So, we can write - -``` -php bin/cake.php HavokInspiration/ActionsClass.action -c controller_name -a action_name -``` +* -p/--plugin : use the plugin CakePHP convention ## Compatibility diff --git a/src/Shell/ActionShell.php b/src/Shell/ActionShell.php deleted file mode 100644 index 27a2895..0000000 --- a/src/Shell/ActionShell.php +++ /dev/null @@ -1,165 +0,0 @@ -addOptions([ - 'controller' => [ - 'short' => 'c', - 'help' => 'The controller folder' - ], - 'action' => [ - 'short' => 'a', - 'help' => 'The action file' - ], - 'prefix' => [ - 'help' => 'The prefix name' - ], - 'plugin' => [ - 'help' => 'The plugin folder' - ] - ]); - - return $parser; - } - - /** - * main() method. - * - * @return bool Success or not. - */ - public function main() - { - if (!$this->param('controller')) { - $this->out('The controller name (-c Controller) is needed'); - - return false; - } - - // The action Template available on HavokInspiration/cakephp-actions-class plugin - $this->templateFile = dirname(__DIR__) . DS . 'Template' . DS . 'Bake' . DS; - - // Controller name use CakePHP Convention - $controllerName = $this->_inflected($this->param('controller')); - if (empty($controllerName)) { - $this->out('The controller name is empty.'); - $this->out('Respect the CakePHP convention.'); - - return false; - } - $this->data['{{controller}}'] = $controllerName; - $this->data['{{controller.lower}}'] = strtolower($controllerName); - - $directory = APP; - $nameSpace = 'App'; - - // Go to the plugin directory - if ($this->param('plugin')) { - $pluginName = $this->_inflected($this->param('plugin')); - if (!empty($pluginName)) { - $nameSpace = $pluginName; - $directory = ROOT . DS . 'plugins' . DS . $pluginName . DS . 'src' . DS; - } - } - - // It's always in Controller directory - $directory .= 'Controller' . DS; - - // Add prefix name and directory - if ($this->param('prefix')) { - $prefixname = $this->_inflected($this->param('prefix')); - if (!empty($prefixname)) { - $nameSpace .= '\\' . $prefixname; - $directory .= $prefixname . DS; - } - } - - // Index is the default action if -a is not define - if ($this->param('action')) { - $actionName = $this->_inflected($this->param('action')); - } - if (empty($actionName)) { - $actionName = 'Index'; - } - $this->data['{{name}}'] = $actionName; - - $this->data['{{namespace}}'] = $nameSpace; - - $this->_createActionFile($directory); - $this->_createTestFile(); - - return true; - } - - /** - * Create Controller Action file - * - * @param string $directory The directory to controller - */ - protected function _createActionFile($directory) - { - $file = new File($this->templateFile . 'Action' . DS . 'action.ctp'); - // Assign data - $contents = str_replace(array_keys($this->data), array_values($this->data), $file->read()); - - $this->createFile($directory . $this->data['{{controller}}'] . DS . $this->data['{{name}}'] . 'Action.php', $contents); - } - - /** - * Create TestCase file - */ - protected function _createTestFile() - { - $file = new File($this->templateFile . 'Tests' . DS . 'action.ctp'); - // Assign data - $contents = str_replace(array_keys($this->data), array_values($this->data), $file->read()); - - $this->createFile(TESTS . 'TestCase' . DS . $this->data['{{controller}}'] . DS . $this->data['{{name}}'] . 'Action.php', $contents); - } - - /** - * Correct string with Text::slug and use CakePHP convention - * - * @param string $string - * @see https://book.cakephp.org/3.0/en/core-libraries/text.html#Cake\Utility\Text::slug - * @see https://book.cakephp.org/3.0/en/intro/conventions.html - * - * @return string The string after Text::slug and - */ - protected function _inflected($string) - { - return $this->_camelize(Text::slug($string, ['replacement' => '_'])); - } -} diff --git a/src/Shell/Task/ActionTask.php b/src/Shell/Task/ActionTask.php new file mode 100644 index 0000000..4e89248 --- /dev/null +++ b/src/Shell/Task/ActionTask.php @@ -0,0 +1,134 @@ +out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); + + if (strpos($name, '/') !== false) { + list($controller, $action) = explode('/', $name); + } else { + $controller = $name; + $action = 'index'; + } + $controller = $this->_camelize($controller); + $action = $this->_camelize($action); + + $path = APP . 'Controller'; + $namespace = Configure::read('App.namespace'); + + $prefix = $this->_getPrefix(); + if ($prefix) { + $path .= DS . $prefix; + $prefix = '\\' . str_replace('/', '\\', $prefix); + } + + if ($this->plugin) { + $path = $this->getPath(). 'Controller'; + $namespace = $this->_pluginNamespace($this->plugin); + } + + $data = [ + 'action' => $action, + 'controller' => $controller, + 'namespace' => $namespace, + 'prefix' => $prefix + ]; + + $this->BakeTemplate->set($data); + + $filename = $path . DS . $controller . DS . $action . 'Action.php'; + + $this->_createActionFile($filename); + $this->_createTestFile(TESTS . 'TestCase' . DS . 'Controller' . DS . $controller . DS . $action . 'Action.php'); + } + + /** + * Gets the option parser instance and configures it. + * + * @return \Cake\Console\ConsoleOptionParser + */ + public function getOptionParser() + { + $parser = parent::getOptionParser(); + $name = $this->name(); + $parser->setDescription( + sprintf('Bake a %s.', $name) + )->addOption('prefix', [ + 'help' => 'The namespace/routing prefix to use.' + ]); + + return $parser; + } + + /** + * Create Controller Action file + * + * @param string $filename The filename path to create file + */ + protected function _createActionFile($filename) + { + $contents = $this->BakeTemplate->generate($this->template()); + $this->createFile($filename, $contents); + } + + /** + * Create TestCase file + * + * @param string $filename The filename path to create file + */ + protected function _createTestFile($filename) + { + $contents = $this->BakeTemplate->generate('HavokInspiration/ActionsClass.tests'); + $this->createFile($filename, $contents); + } +} diff --git a/src/Template/Bake/Action/action.ctp b/src/Template/Bake/action.ctp similarity index 62% rename from src/Template/Bake/Action/action.ctp rename to src/Template/Bake/action.ctp index 0a9a2f1..5d48895 100644 --- a/src/Template/Bake/Action/action.ctp +++ b/src/Template/Bake/action.ctp @@ -8,20 +8,20 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -namespace {{namespace}}\Controller\{{controller}}; +namespace <%= $namespace %>\Controller<%= $prefix %>\<%= $controller %>; use HavokInspiration\ActionsClass\Controller\Action; /** - * {{controller}} Controller - * {{name}} Action + * <%= $controller %> Controller + * <%= $action %> Action * - * @package {{namespace}}\Controller + * @package <%= $namespace %>\Controller */ -class {{name}}Action extends Action +class <%= $action %>Action extends Action { /** - * It's like {{controller}}Controller::{{name}} method + * It's like <%= $controller %>Controller::<%= $action %> method */ public function execute() { diff --git a/src/Template/Bake/Tests/action.ctp b/src/Template/Bake/tests.ctp similarity index 59% rename from src/Template/Bake/Tests/action.ctp rename to src/Template/Bake/tests.ctp index 409f4b9..7facd36 100644 --- a/src/Template/Bake/Tests/action.ctp +++ b/src/Template/Bake/tests.ctp @@ -13,19 +13,19 @@ namespace App\Test\TestCase\Controller; use Cake\TestSuite\IntegrationTestCase; /** - * {{controller}} Controller - * {{name}} Action + * <%= $controller %> Controller + * <%= $action %> Action * - * @package {{namespace}}\Controller + * @package <%= $namespace %>\Controller */ -class {{name}}ActionTest extends IntegrationTestCase +class <%= $action %>ActionTest extends IntegrationTestCase { /** - * TestCase for \{{namespace}}\Controller\{{controller}}\{{name}}Action + * TestCase for \<%= $namespace %>\Controller\<%= $controller %>\<%= $action %>Action */ - public function test{{name}}Action() + public function test<%= $action %>Action() { - $this->get('/{{controller.lower}}'); + $this->get('/<%= strtolower($controller) %>'); $this->assertResponseOk(); } } From a383093303d72fac3d6e81c0e24e458c06325b9b Mon Sep 17 00:00:00 2001 From: Edouard Tack Date: Mon, 14 Aug 2017 11:28:05 +0200 Subject: [PATCH 05/12] Fix indentations --- src/Shell/Task/ActionTask.php | 40 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Shell/Task/ActionTask.php b/src/Shell/Task/ActionTask.php index 4e89248..bc31989 100644 --- a/src/Shell/Task/ActionTask.php +++ b/src/Shell/Task/ActionTask.php @@ -3,22 +3,22 @@ namespace HavokInspiration\ActionsClass\Shell\Task; use Cake\Console\Shell; -use Bake\Shell\Task\SimpleBakeTask; use Cake\Core\Configure; +use Bake\Shell\Task\SimpleBakeTask; /** * Command line to create HavokInspiration\ActionsClass action file */ class ActionTask extends SimpleBakeTask { - /** + /** * Tasks to be loaded by this Task * * @var array */ - public $tasks = [ - 'Bake.BakeTemplate' - ]; + public $tasks = [ + 'Bake.BakeTemplate' + ]; /** * {@inheritDoc} @@ -52,22 +52,22 @@ public function template() */ public function bake($name) { - $this->out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); + $this->out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); - if (strpos($name, '/') !== false) { - list($controller, $action) = explode('/', $name); - } else { - $controller = $name; - $action = 'index'; - } - $controller = $this->_camelize($controller); - $action = $this->_camelize($action); + if (strpos($name, '/') !== false) { + list($controller, $action) = explode('/', $name); + } else { + $controller = $name; + $action = 'index'; + } + $controller = $this->_camelize($controller); + $action = $this->_camelize($action); $path = APP . 'Controller'; $namespace = Configure::read('App.namespace'); - $prefix = $this->_getPrefix(); - if ($prefix) { + $prefix = $this->_getPrefix(); + if ($prefix) { $path .= DS . $prefix; $prefix = '\\' . str_replace('/', '\\', $prefix); } @@ -78,10 +78,10 @@ public function bake($name) } $data = [ - 'action' => $action, - 'controller' => $controller, - 'namespace' => $namespace, - 'prefix' => $prefix + 'action' => $action, + 'controller' => $controller, + 'namespace' => $namespace, + 'prefix' => $prefix ]; $this->BakeTemplate->set($data); From c5ab11cc8ea0d5b8d6f85c5e7c3a1258644483b1 Mon Sep 17 00:00:00 2001 From: Edouard Tack Date: Mon, 14 Aug 2017 15:52:34 +0200 Subject: [PATCH 06/12] Change TestCase creation file --- src/Shell/Task/ActionTask.php | 83 +++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/src/Shell/Task/ActionTask.php b/src/Shell/Task/ActionTask.php index bc31989..2396b35 100644 --- a/src/Shell/Task/ActionTask.php +++ b/src/Shell/Task/ActionTask.php @@ -3,8 +3,8 @@ namespace HavokInspiration\ActionsClass\Shell\Task; use Cake\Console\Shell; -use Cake\Core\Configure; use Bake\Shell\Task\SimpleBakeTask; +use Cake\Core\Configure; /** * Command line to create HavokInspiration\ActionsClass action file @@ -25,7 +25,7 @@ class ActionTask extends SimpleBakeTask */ public function name() { - return 'file for HavokInspiration/ActionsClass'; + return 'file for action'; } /** @@ -54,14 +54,7 @@ public function bake($name) { $this->out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); - if (strpos($name, '/') !== false) { - list($controller, $action) = explode('/', $name); - } else { - $controller = $name; - $action = 'index'; - } - $controller = $this->_camelize($controller); - $action = $this->_camelize($action); + list($controller, $action) = $this->setName($name); $path = APP . 'Controller'; $namespace = Configure::read('App.namespace'); @@ -73,7 +66,7 @@ public function bake($name) } if ($this->plugin) { - $path = $this->getPath(). 'Controller'; + $path = $this->getPath() . 'Controller'; $namespace = $this->_pluginNamespace($this->plugin); } @@ -87,9 +80,7 @@ public function bake($name) $this->BakeTemplate->set($data); $filename = $path . DS . $controller . DS . $action . 'Action.php'; - $this->_createActionFile($filename); - $this->_createTestFile(TESTS . 'TestCase' . DS . 'Controller' . DS . $controller . DS . $action . 'Action.php'); } /** @@ -102,7 +93,7 @@ public function getOptionParser() $parser = parent::getOptionParser(); $name = $this->name(); $parser->setDescription( - sprintf('Bake a %s.', $name) + 'Bake an Action class' )->addOption('prefix', [ 'help' => 'The namespace/routing prefix to use.' ]); @@ -110,6 +101,70 @@ public function getOptionParser() return $parser; } + + /** + * Assembles and writes a unit test file + * + * @param string $name Name parameter + * @return string|null Baked test + */ + public function bakeTest($name) + { + if (!empty($this->params['no-test'])) { + return null; + } + + list($controller, $action) = $this->setName($name); + $namespace = Configure::read('App.namespace'); + $path = TESTS . 'TestCase' . DS . 'Controller'; + + $plugin = $this->plugin; + if ($plugin) { + $path = $this->getPath() . 'Controller'; + $namespace = $this->_pluginNamespace($this->plugin); + } + + $prefix = $this->_getPrefix(); + if ($prefix) { + $path .= DS . $prefix; + $prefix = '\\' . str_replace('/', '\\', $prefix); + } + + $data = [ + 'action' => $action, + 'controller' => $controller, + 'namespace' => $namespace, + 'prefix' => $prefix + ]; + + $this->BakeTemplate->set($data); + + $path .= DS . $controller . DS . $action . 'ActionTest.php'; + $this->_createTestFile($path); + + return true; + } + + /** + * Transform the name parameter into Controller & Action name + * + * @param string $name + * @return array + */ + protected function setName($name) + { + if (strpos($name, '/') !== false) { + list($controller, $action) = explode('/', $name); + } else { + $controller = $name; + $action = 'index'; + } + $controller = $this->_camelize($controller); + $action = $this->_camelize($action); + + return [$controller, $action]; + } + /** * Create Controller Action file * From 64f80edc38eb09bd92024f8a891db176c3fbf86c Mon Sep 17 00:00:00 2001 From: Edouard Tack Date: Fri, 18 Aug 2017 10:50:00 +0200 Subject: [PATCH 07/12] Fix problem with plugin path + refactoring bake and bakeTest methods --- src/Shell/Task/ActionTask.php | 87 +++++++++++++++++------------------ 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/src/Shell/Task/ActionTask.php b/src/Shell/Task/ActionTask.php index 2396b35..f3c0a6f 100644 --- a/src/Shell/Task/ActionTask.php +++ b/src/Shell/Task/ActionTask.php @@ -11,6 +11,11 @@ */ class ActionTask extends SimpleBakeTask { + /** + * {@inheritDoc} + */ + public $pathFragment = ''; + /** * Tasks to be loaded by this Task * @@ -45,48 +50,32 @@ public function template() } /** - * Generate a class stub - * - * @param string $name The classname to generate. - * @return string + * {@inheritDoc} */ public function bake($name) { $this->out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); - list($controller, $action) = $this->setName($name); - - $path = APP . 'Controller'; - $namespace = Configure::read('App.namespace'); + $path = APP; - $prefix = $this->_getPrefix(); - if ($prefix) { - $path .= DS . $prefix; - $prefix = '\\' . str_replace('/', '\\', $prefix); - } + return $this->bakeAction($name, $path); + } - if ($this->plugin) { - $path = $this->getPath() . 'Controller'; - $namespace = $this->_pluginNamespace($this->plugin); + /** + * {@inheritDoc} + */ + public function bakeTest($name) + { + if (!empty($this->params['no-test'])) { + return null; } + $path = TESTS . 'TestCase' . DS; - $data = [ - 'action' => $action, - 'controller' => $controller, - 'namespace' => $namespace, - 'prefix' => $prefix - ]; - - $this->BakeTemplate->set($data); - - $filename = $path . DS . $controller . DS . $action . 'Action.php'; - $this->_createActionFile($filename); + return $this->bakeAction($name, $path, 'Test'); } /** - * Gets the option parser instance and configures it. - * - * @return \Cake\Console\ConsoleOptionParser + * {@inheritDoc} */ public function getOptionParser() { @@ -101,28 +90,30 @@ public function getOptionParser() return $parser; } - /** - * Assembles and writes a unit test file + * Do the bake action with the parameters in command line + * + * @param string $name The name of the action + * @param string $path Where the file will be create + * @param string $type if is Test or other * - * @param string $name Name parameter - * @return string|null Baked test + * @return bool */ - public function bakeTest($name) + protected function bakeAction($name, $path, $type = '') { - if (!empty($this->params['no-test'])) { - return null; - } - list($controller, $action) = $this->setName($name); + $namespace = Configure::read('App.namespace'); - $path = TESTS . 'TestCase' . DS . 'Controller'; $plugin = $this->plugin; if ($plugin) { - $path = $this->getPath() . 'Controller'; + $path = $this->_pluginPath($plugin) . 'src' . DS; + if ($type == 'Test') { + $path = $this->_pluginPath($plugin) . 'tests' . DS . 'TestCase' . DS; + } $namespace = $this->_pluginNamespace($this->plugin); } + $path .= 'Controller'; $prefix = $this->_getPrefix(); if ($prefix) { @@ -138,9 +129,13 @@ public function bakeTest($name) ]; $this->BakeTemplate->set($data); - - $path .= DS . $controller . DS . $action . 'ActionTest.php'; - $this->_createTestFile($path); + $filename = $path . DS . $controller . DS . $action . 'Action' . $type . '.php'; + + if ($type == 'Test') { + $this->createTestFile($filename); + } else { + $this->createActionFile($filename); + } return true; } @@ -170,7 +165,7 @@ protected function setName($name) * * @param string $filename The filename path to create file */ - protected function _createActionFile($filename) + protected function createActionFile($filename) { $contents = $this->BakeTemplate->generate($this->template()); $this->createFile($filename, $contents); @@ -181,7 +176,7 @@ protected function _createActionFile($filename) * * @param string $filename The filename path to create file */ - protected function _createTestFile($filename) + protected function createTestFile($filename) { $contents = $this->BakeTemplate->generate('HavokInspiration/ActionsClass.tests'); $this->createFile($filename, $contents); From 128fc553fe2d9e6e96a86f0a9e859f0f99511298 Mon Sep 17 00:00:00 2001 From: Yves P Date: Mon, 28 Aug 2017 22:08:02 +0200 Subject: [PATCH 08/12] Refactor to be more aligned with how bake task are normally implemented --- src/Shell/Task/ActionTask.php | 195 +++++++++++++++++++--------------- src/Template/Bake/action.ctp | 28 ++--- src/Template/Bake/test.ctp | 22 ++++ src/Template/Bake/tests.ctp | 31 ------ 4 files changed, 144 insertions(+), 132 deletions(-) create mode 100644 src/Template/Bake/test.ctp delete mode 100644 src/Template/Bake/tests.ctp diff --git a/src/Shell/Task/ActionTask.php b/src/Shell/Task/ActionTask.php index f3c0a6f..c03d041 100644 --- a/src/Shell/Task/ActionTask.php +++ b/src/Shell/Task/ActionTask.php @@ -1,5 +1,15 @@ out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); - - $path = APP; - - return $this->bakeAction($name, $path); + return 'HavokInspiration/ActionsClass.test'; } /** * {@inheritDoc} */ - public function bakeTest($name) + public function bake($name) { - if (!empty($this->params['no-test'])) { - return null; - } - $path = TESTS . 'TestCase' . DS; - - return $this->bakeAction($name, $path, 'Test'); - } + if (strpos($name, '/') === false) { + $this->err('You must pass a Controller name for your action in the format `ControllerName/ActionName`'); - /** - * {@inheritDoc} - */ - public function getOptionParser() - { - $parser = parent::getOptionParser(); - $name = $this->name(); - $parser->setDescription( - 'Bake an Action class' - )->addOption('prefix', [ - 'help' => 'The namespace/routing prefix to use.' - ]); + return Shell::CODE_ERROR; + } - return $parser; - } + $this->out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); - /** - * Do the bake action with the parameters in command line - * - * @param string $name The name of the action - * @param string $path Where the file will be create - * @param string $type if is Test or other - * - * @return bool - */ - protected function bakeAction($name, $path, $type = '') - { - list($controller, $action) = $this->setName($name); + list($controller, $action) = $this->getName($name); $namespace = Configure::read('App.namespace'); - - $plugin = $this->plugin; - if ($plugin) { - $path = $this->_pluginPath($plugin) . 'src' . DS; - if ($type == 'Test') { - $path = $this->_pluginPath($plugin) . 'tests' . DS . 'TestCase' . DS; - } + if ($this->plugin) { $namespace = $this->_pluginNamespace($this->plugin); } - $path .= 'Controller'; $prefix = $this->_getPrefix(); if ($prefix) { - $path .= DS . $prefix; $prefix = '\\' . str_replace('/', '\\', $prefix); } @@ -128,57 +106,106 @@ protected function bakeAction($name, $path, $type = '') 'prefix' => $prefix ]; - $this->BakeTemplate->set($data); - $filename = $path . DS . $controller . DS . $action . 'Action' . $type . '.php'; + $out = $this->bakeAction($action, $data); - if ($type == 'Test') { - $this->createTestFile($filename); - } else { - $this->createActionFile($filename); + if ($this->params['no-test'] !== true) { + $this->bakeActionTest($action, $data); } - return true; + return $out; } /** - * Transform the name parameter into Controller & Action name + * Generate the action code * - * @param string $name - * @return array + * @param string $actionName The name of the action. + * @param array $data The data to turn into code. + * @return string The generated action file. */ - protected function setName($name) + public function bakeAction($actionName, array $data) { - if (strpos($name, '/') !== false) { - list($controller, $action) = explode('/', $name); - } else { - $controller = $name; - $action = 'index'; - } - $controller = $this->_camelize($controller); - $action = $this->_camelize($action); + $data += [ + 'namespace' => null, + 'controller' => null, + 'prefix' => null, + 'actions' => null, + ]; + $this->BakeTemplate->set($data); + $contents = $this->BakeTemplate->generate($this->template()); + $path = $this->getPath(); + $filename = $path . $data['controller'] . DS . $this->fileName($actionName); + $this->createFile($filename, $contents); - return [$controller, $action]; + return $contents; } /** - * Create Controller Action file + * Assembles and writes a unit test file * - * @param string $filename The filename path to create file + * @param string $className Controller class name + * @return string|null Baked test */ - protected function createActionFile($filename) + public function bakeActionTest($actionName, $data) { - $contents = $this->BakeTemplate->generate($this->template()); + $data += [ + 'namespace' => null, + 'controller' => null, + 'prefix' => null, + 'actions' => null, + ]; + $this->Test->plugin = $this->plugin; + $this->BakeTemplate->set($data); + + $prefix = $this->_getPrefix(); + $contents = $this->BakeTemplate->generate($this->templateTest()); + + $path = $this->Test->getPath() . 'Controller' . DS; + if ($prefix) { + $path .= $prefix . DS; + } + $path .= $data['controller']; + + $filename = $path . DS . $this->fileName($actionName, true); $this->createFile($filename, $contents); + + return $contents; } /** - * Create TestCase file + * Transform the name parameter into Controller & Action name. * - * @param string $filename The filename path to create file + * @param string $name Name passed to the CLI. + * @return array First key is the controller name, second key the action name. */ - protected function createTestFile($filename) + protected function getName($name) { - $contents = $this->BakeTemplate->generate('HavokInspiration/ActionsClass.tests'); - $this->createFile($filename, $contents); + list($controller, $action) = explode('/', $name); + + $controller = $this->_camelize($controller); + $action = $this->_camelize($action); + + return [$controller, $action]; + } + + /** + * {@inheritDoc} + */ + public function getOptionParser() + { + $parser = parent::getOptionParser(); + + $parser + ->setDescription( + 'Bake an Action class file skeleton' + ) + ->addOption('prefix', [ + 'help' => 'The namespace/routing prefix to use.' + ]) + ->addOption('no-test', [ + 'boolean' => true, + 'help' => 'Do not generate a test skeleton.' + ]); + + return $parser; } } diff --git a/src/Template/Bake/action.ctp b/src/Template/Bake/action.ctp index 5d48895..4418d5f 100644 --- a/src/Template/Bake/action.ctp +++ b/src/Template/Bake/action.ctp @@ -1,30 +1,24 @@ \Controller<%= $prefix %>\<%= $controller %>; use HavokInspiration\ActionsClass\Controller\Action; /** - * <%= $controller %> Controller - * <%= $action %> Action + * Controller : <%= $controller %> + * Action : <%= $action %> * * @package <%= $namespace %>\Controller */ class <%= $action %>Action extends Action { - /** - * It's like <%= $controller %>Controller::<%= $action %> method - */ - public function execute() - { + /** + * This method will be executed when the `<%= $controller %>` Controller action `<%= $action %>` will be invoked. + * It is the equivalent of the `<%= $controller %>Controller::<%= $action %>()` method. + * + * @return void|\Cake\Network\Response + */ + public function execute() +{ - } +} } diff --git a/src/Template/Bake/test.ctp b/src/Template/Bake/test.ctp new file mode 100644 index 0000000..0d4bea6 --- /dev/null +++ b/src/Template/Bake/test.ctp @@ -0,0 +1,22 @@ +\Test\TestCase\Controller<%= $prefix %>\<%= $controller %>; + +use Cake\TestSuite\IntegrationTestCase; + +/** + * Controller <%= $controller %> + * Action <%= $action %> + * + * @package <%= $namespace %>\Controller + */ +class <%= $action %>ActionTest extends IntegrationTestCase +{ + /** + * TestCase for \<%= $namespace %>\Controller\<%= $controller %>\<%= $action %>Action + */ + public function test<%= $action %>Action() + { + $this->get('<%= str_replace('\\', '/', strtolower($prefix)) %>/<%= strtolower($controller) %>/<%= strtolower($action) %>'); + $this->assertResponseOk(); + } +} diff --git a/src/Template/Bake/tests.ctp b/src/Template/Bake/tests.ctp deleted file mode 100644 index 7facd36..0000000 --- a/src/Template/Bake/tests.ctp +++ /dev/null @@ -1,31 +0,0 @@ - Controller - * <%= $action %> Action - * - * @package <%= $namespace %>\Controller - */ -class <%= $action %>ActionTest extends IntegrationTestCase -{ - /** - * TestCase for \<%= $namespace %>\Controller\<%= $controller %>\<%= $action %>Action - */ - public function test<%= $action %>Action() - { - $this->get('/<%= strtolower($controller) %>'); - $this->assertResponseOk(); - } -} From 1c614ecda70e0d4a354224c88dabb95a8a76e152 Mon Sep 17 00:00:00 2001 From: Yves P Date: Mon, 28 Aug 2017 22:20:47 +0200 Subject: [PATCH 09/12] Fix the PHPStan check --- .travis.yml | 2 +- composer.json | 4 +- phpstan.neon | 5 ++ src/Shell/Task/ActionTask.php | 2 +- .../BakeTemplatePropertyReflection.php | 57 +++++++++++++++++++ ...hellPropertiesClassReflectionExtension.php | 42 ++++++++++++++ 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 phpstan.neon create mode 100644 tests/PHPStan/BakeTemplatePropertyReflection.php create mode 100644 tests/PHPStan/ShellPropertiesClassReflectionExtension.php diff --git a/.travis.yml b/.travis.yml index 0c67f83..3734e1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ before_script: script: - sh -c "if [ '$DEFAULT' = '1' ]; then vendor/bin/phpunit --stderr; fi" - - sh -c "if [ '$PHPSTAN' = '1' ]; then composer require --dev phpstan/phpstan:^0.7 && vendor/bin/phpstan analyse -l 5 src; fi" + - sh -c "if [ '$PHPSTAN' = '1' ]; then composer require --dev phpstan/phpstan:^0.8 && vendor/bin/phpstan analyse -c phpstan.neon -l 5 src; fi" - sh -c "if [ '$CODECOVERAGE' = '1' ]; then vendor/bin/phpunit --coverage-clover=clover.xml || true; fi" after_success: diff --git a/composer.json b/composer.json index 88da36f..0207581 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "cakephp/cakephp": "^3.4" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^6.0", + "cakephp/bake": "@stable" }, "autoload": { "psr-4": { @@ -24,6 +25,7 @@ "autoload-dev": { "psr-4": { "HavokInspiration\\ActionsClass\\Test\\": "tests", + "HavokInspiration\\ActionsClass\\PHPStan\\": "tests/PHPStan", "Cake\\Test\\": "./vendor/cakephp/cakephp/tests", "TestApp\\": "tests/test_app/TestApp", "TestPlugin\\": "tests/test_app/Plugin/TestPlugin/src", diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..41b499b --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +services: + - + class: HavokInspiration\ActionsClass\PHPStan\ShellPropertiesClassReflectionExtension + tags: + - phpstan.broker.propertiesClassReflectionExtension \ No newline at end of file diff --git a/src/Shell/Task/ActionTask.php b/src/Shell/Task/ActionTask.php index c03d041..f24e942 100644 --- a/src/Shell/Task/ActionTask.php +++ b/src/Shell/Task/ActionTask.php @@ -82,7 +82,7 @@ public function bake($name) if (strpos($name, '/') === false) { $this->err('You must pass a Controller name for your action in the format `ControllerName/ActionName`'); - return Shell::CODE_ERROR; + return (string)Shell::CODE_ERROR; } $this->out("\n" . sprintf('Baking action class for %s...', $name), 1, Shell::QUIET); diff --git a/tests/PHPStan/BakeTemplatePropertyReflection.php b/tests/PHPStan/BakeTemplatePropertyReflection.php new file mode 100644 index 0000000..a64a896 --- /dev/null +++ b/tests/PHPStan/BakeTemplatePropertyReflection.php @@ -0,0 +1,57 @@ +declaringClass = $declaringClass; + $this->type = $type; + } + public function getDeclaringClass(): ClassReflection + { + return $this->declaringClass; + } + + public function isStatic(): bool + { + return false; + } + + public function isPrivate(): bool + { + return false; + } + + public function isPublic(): bool + { + return true; + } + + public function getType(): Type + { + return $this->type; + } + + public function isReadable(): bool + { + return true; + } + + public function isWritable(): bool + { + return true; + } +} diff --git a/tests/PHPStan/ShellPropertiesClassReflectionExtension.php b/tests/PHPStan/ShellPropertiesClassReflectionExtension.php new file mode 100644 index 0000000..0848350 --- /dev/null +++ b/tests/PHPStan/ShellPropertiesClassReflectionExtension.php @@ -0,0 +1,42 @@ +isSubclassOf(SimpleBakeTask::class) && ($propertyName === 'BakeTemplate' || $propertyName === 'Test'); + } + + /** + * @param ClassReflection $classReflection Class reflection + * @param string $propertyName Method name + * @return PropertyReflection + */ + public function getProperty(ClassReflection $classReflection, string $propertyName): PropertyReflection + { + $object = 'Bake\Shell\Task\BakeTemplateTask'; + + if ($propertyName === 'Test') { + $object = 'Bake\Shell\Task\TestTask'; + } + + return new BakeTemplatePropertyReflection( + $classReflection, + new ObjectType($object, false) + ); + } +} From 7d3a4009b321c373571a917c026df1a21937962e Mon Sep 17 00:00:00 2001 From: Yves P Date: Mon, 28 Aug 2017 23:03:19 +0200 Subject: [PATCH 10/12] Add tests for the bake feature --- src/Shell/Task/ActionTask.php | 2 +- src/Template/Bake/action.ctp | 4 +- tests/TestCase/Shell/Task/ActionTaskTest.php | 216 ++++++++++++++++++ tests/bootstrap.php | 25 +- .../Controller/Admin/Posts/IndexAction.php | 24 ++ .../Plugin/Admin/Posts/IndexAction.php | 24 ++ .../Controller/Plugin/Posts/IndexAction.php | 24 ++ .../Controller/Posts/IndexAction.php | 24 ++ 8 files changed, 334 insertions(+), 9 deletions(-) create mode 100644 tests/TestCase/Shell/Task/ActionTaskTest.php create mode 100644 tests/comparisons/Controller/Admin/Posts/IndexAction.php create mode 100644 tests/comparisons/Controller/Plugin/Admin/Posts/IndexAction.php create mode 100644 tests/comparisons/Controller/Plugin/Posts/IndexAction.php create mode 100644 tests/comparisons/Controller/Posts/IndexAction.php diff --git a/src/Shell/Task/ActionTask.php b/src/Shell/Task/ActionTask.php index f24e942..91644aa 100644 --- a/src/Shell/Task/ActionTask.php +++ b/src/Shell/Task/ActionTask.php @@ -108,7 +108,7 @@ public function bake($name) $out = $this->bakeAction($action, $data); - if ($this->params['no-test'] !== true) { + if (!isset($this->params['no-test']) || $this->params['no-test'] !== true) { $this->bakeActionTest($action, $data); } diff --git a/src/Template/Bake/action.ctp b/src/Template/Bake/action.ctp index 4418d5f..552860c 100644 --- a/src/Template/Bake/action.ctp +++ b/src/Template/Bake/action.ctp @@ -18,7 +18,7 @@ class <%= $action %>Action extends Action * @return void|\Cake\Network\Response */ public function execute() -{ + { -} + } } diff --git a/tests/TestCase/Shell/Task/ActionTaskTest.php b/tests/TestCase/Shell/Task/ActionTaskTest.php new file mode 100644 index 0000000..bc3b26d --- /dev/null +++ b/tests/TestCase/Shell/Task/ActionTaskTest.php @@ -0,0 +1,216 @@ +_compareBasePath = Plugin::path('HavokInspiration/ActionsClass') . 'tests' . DS . 'comparisons' . DS . 'Controller' . DS; + + $io = $this->getMockBuilder('Cake\Console\ConsoleIo') + ->disableOriginalConstructor() + ->getMock(); + + $this->Task = $this->getMockBuilder('HavokInspiration\ActionsClass\Shell\Task\ActionTask') + ->setMethods(['in', 'err', 'createFile', '_stop']) + ->setConstructorArgs([$io]) + ->getMock(); + + $this->Task->Test = $this->getMockBuilder('Bake\Shell\Task\TestTask') + ->setMethods(['in', 'err', 'createFile', '_stop']) + ->setConstructorArgs([$io]) + ->getMock(); + + $this->Task->BakeTemplate = new BakeTemplateTask($io); + $this->Task->BakeTemplate->initialize(); + $this->Task->BakeTemplate->interactive = false; + $this->Task->Test->BakeTemplate = new BakeTemplateTask($io); + $this->Task->Test->BakeTemplate->initialize(); + $this->Task->Test->BakeTemplate->interactive = false; + } + + /** + * Load a plugin from the tests folder, and add to the autoloader + * + * @param string $name plugin name to load + * @return void + */ + protected function _loadTestPlugin($name) + { + $path = TESTS . 'test_app' . DS . 'Plugin' . DS . $name . DS; + + Plugin::load($name, [ + 'path' => $path, + 'autoload' => true + ]); + } + + /** + * Test the main method. + * + * @return void + */ + public function testMain() + { + $this->Task->expects($this->at(0)) + ->method('createFile') + ->with( + $this->_normalizePath(APP . 'Controller/Posts/IndexAction.php'), + $this->stringContains('class IndexAction extends Action') + ); + + $this->Task->expects($this->at(1)) + ->method('createFile') + ->with( + $this->_normalizePath(TESTS . 'TestCase/Controller/Posts/IndexActionTest.php'), + $this->stringContains('class IndexActionTest extends IntegrationTestCase') + ); + + $this->Task->main('Posts/Index'); + } + + /** + * Test the main method with the no-test parameter will only call createFile once. + * + * @return void + */ + public function testMainNoTest() + { + $this->Task->expects($this->once()) + ->method('createFile'); + + $this->Task->params['no-test'] = true; + $this->Task->main('Posts/Index'); + } + + /** + * Test the main method with a plugin. + * + * @return void + */ + public function testMainPlugin() + { + $this->_loadTestPlugin('MaintenanceTest'); + $path = Plugin::path('MaintenanceTest'); + + $this->Task->expects($this->at(0)) + ->method('createFile') + ->with( + $this->_normalizePath($path . 'src/Controller/Posts/IndexAction.php'), + $this->stringContains('class IndexAction extends Action') + ); + + $this->Task->expects($this->at(1)) + ->method('createFile') + ->with( + $this->_normalizePath($path . 'tests/TestCase/Controller/Posts/IndexActionTest.php'), + $this->stringContains('class IndexActionTest extends IntegrationTestCase') + ); + + $this->Task->main('MaintenanceTest.Posts/Index'); + } + + /** + * Test bake. + * + * @return void + */ + public function testBake() + { + $this->Task->expects($this->at(0)) + ->method('createFile') + ->with( + $this->_normalizePath(APP . 'Controller/Posts/IndexAction.php') + ); + + $result = $this->Task->bake('Posts/Index'); + $this->assertSameAsFile('Posts/IndexAction.php', $result); + } + + /** + * Test bake with a plugin. + * + * @return void + */ + public function testBakePlugin() + { + $this->_loadTestPlugin('MaintenanceTest'); + $path = Plugin::path('MaintenanceTest'); + + $this->Task->expects($this->at(0)) + ->method('createFile') + ->with( + $this->_normalizePath($path . 'src/Controller/Posts/IndexAction.php') + ); + + $this->Task->plugin = 'MaintenanceTest'; + $result = $this->Task->bake('Posts/Index'); + $this->assertSameAsFile('Plugin/Posts/IndexAction.php', $result); + } + + /** + * Test bake with a routing prefix. + * + * @return void + */ + public function testBakePrefix() + { + $this->Task->expects($this->at(0)) + ->method('createFile') + ->with( + $this->_normalizePath(APP . 'Controller/Admin/Posts/IndexAction.php') + ); + + $this->Task->params['prefix'] = 'Admin'; + $result = $this->Task->bake('Posts/Index'); + $this->assertSameAsFile('Admin/Posts/IndexAction.php', $result); + } + + /** + * Test bake with a plugin and a routing prefix. + * + * @return void + */ + public function testBakePluginPrefix() + { + $this->_loadTestPlugin('MaintenanceTest'); + $path = Plugin::path('MaintenanceTest'); + + $this->Task->expects($this->at(0)) + ->method('createFile') + ->with( + $this->_normalizePath($path . 'src/Controller/Admin/Posts/IndexAction.php') + ); + + $this->Task->params['prefix'] = 'Admin'; + $this->Task->plugin = 'MaintenanceTest'; + $result = $this->Task->bake('Posts/Index'); + $this->assertSameAsFile('Plugin/Admin/Posts/IndexAction.php', $result); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8c7d5ca..6044795 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -9,6 +9,9 @@ * @link http://github.com/HavokInspiration/cakephp-actions-class * @license http://www.opensource.org/licenses/mit-license.php MIT License */ +use Cake\Core\Configure; +use Cake\Core\Plugin; + $findRoot = function ($root) { do { $lastRoot = $root; @@ -22,12 +25,22 @@ }; $root = $findRoot(__FILE__); unset($findRoot); - chdir($root); -if (file_exists($root . '/config/bootstrap.php')) { - require $root . '/config/bootstrap.php'; +define('CORE_PATH', $root . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp' . DS); +define('ROOT', $root . DS . 'tests' . DS . 'test_app'); +define('APP', ROOT . DS . 'App' . DS); +define('TESTS', ROOT . DS . 'tests' . DS); +define('TMP', sys_get_temp_dir() . DS); + +Configure::write('App', [ + 'namespace' => 'App', + 'paths' => [ + 'plugins' => [APP . 'Plugin' . DS], + 'templates' => [APP . 'Template' . DS] + ] +]); - return; -} -require $root . '/vendor/cakephp/cakephp/tests/bootstrap.php'; +Plugin::load('HavokInspiration/ActionsClass', [ + 'path' => dirname(dirname(__FILE__)) . DS, +]); \ No newline at end of file diff --git a/tests/comparisons/Controller/Admin/Posts/IndexAction.php b/tests/comparisons/Controller/Admin/Posts/IndexAction.php new file mode 100644 index 0000000..2bb44af --- /dev/null +++ b/tests/comparisons/Controller/Admin/Posts/IndexAction.php @@ -0,0 +1,24 @@ + Date: Mon, 28 Aug 2017 23:12:36 +0200 Subject: [PATCH 11/12] Rephrase a part of the README and add more details about the options --- README.md | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2dc0e40..dd1a4bd 100644 --- a/README.md +++ b/README.md @@ -119,20 +119,45 @@ src/ Your `Action` classes are only expected to hold an `execute()` method. It can receive passed parameters as in regular controller actions (meaning the URL `/posts/edit/5` will pass `5` to the argument `$id` in the `execute()` method of the `EditAction` class in our previous example). -#### Using command-line to create actions-class ingredients +#### Using the `bake` command-line to create Action classes -You have to activate plugin in `config/bootstrap_cli.php` with adding `Plugin::load('HavokInspiration/ActionsClass');` +You first need to load the plugin in your **config/bootstrap_cli.php** : + +```php +Plugin::load('HavokInspiration/ActionsClass'); +``` + +You can then create an Action class file with the following command : + +``` +bin/cake bake action Posts/Index +``` -Create an Action File with this following command +The command expects the name to get the controller name and the action name separated by a forward slash. For instance, the above example would create a `IndexAction` file for the `Posts` controller. +You can also specify the routing prefix your controller action lives under by using the `--prefix` option : + ``` -php bin/cake.php bake action Posts/Index +bin/cake bake action Posts/Index --prefix Admin ``` -Differents options are available: -* Controller/action or only Controller (Index is the default action) -* --prefix : use the prefix CakePHP convention -* -p/--plugin : use the plugin CakePHP convention +If you want to create an action file for a plugin, you can use the `--plugin` option : + +``` +bin/cake bake action Posts/Index --plugin MyPlugin +``` + +You can of course use both together : + +``` +bin/cake bake action Posts/Index --plugin MyPlugin --prefix Admin +``` + +By default, baking an action class will generate the corresponding test file. You can skip the test file generation by using the `--no-test` boolean flag : + +``` +bin/cake bake action Posts/Index --no-test +``` ## Compatibility From 6a4301b216f60da77263bc2952d790b11bf61b47 Mon Sep 17 00:00:00 2001 From: Yves P Date: Mon, 28 Aug 2017 23:16:54 +0200 Subject: [PATCH 12/12] Add a test for the ControllerName/ActionName pattern expected --- tests/TestCase/Shell/Task/ActionTaskTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/TestCase/Shell/Task/ActionTaskTest.php b/tests/TestCase/Shell/Task/ActionTaskTest.php index bc3b26d..fc1c6cd 100644 --- a/tests/TestCase/Shell/Task/ActionTaskTest.php +++ b/tests/TestCase/Shell/Task/ActionTaskTest.php @@ -71,6 +71,22 @@ protected function _loadTestPlugin($name) ]); } + /** + * Test the main method with a wrong name passed (does not follow the ControllerName/ActionName pattern). + * + * @return void + */ + public function testWrongName() + { + $this->Task->expects($this->once()) + ->method('err') + ->with( + 'You must pass a Controller name for your action in the format `ControllerName/ActionName`' + ); + + $this->Task->main('Posts'); + } + /** * Test the main method. *