From 57e25b94d2c6ceba946bad7f58a4f08fbc954223 Mon Sep 17 00:00:00 2001 From: Cherif BOUCHELAGHEM Date: Sun, 13 Sep 2015 21:08:01 +0100 Subject: [PATCH] 0.1 version --- .gitignore | 1 + ContainerLocator.php | 45 ++ README.md | 31 ++ Tactician.php | 100 ++++ composer.json | 22 + composer.lock | 465 ++++++++++++++++++ phpunit.xml.dist | 27 + tests/ContainerLocatorTest.php | 63 +++ tests/TacticianTest.php | 73 +++ tests/TestCase.php | 57 +++ tests/bootstrap.php | 7 + .../fixtures/commands/CompleteTaskCommand.php | 7 + tests/fixtures/commands/DeleteTaskCommand.php | 7 + tests/fixtures/container/Mailer.php | 7 + .../handlers/CompleteTaskCommandHandler.php | 20 + 15 files changed, 932 insertions(+) create mode 100644 ContainerLocator.php create mode 100644 README.md create mode 100644 Tactician.php create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 phpunit.xml.dist create mode 100644 tests/ContainerLocatorTest.php create mode 100644 tests/TacticianTest.php create mode 100644 tests/TestCase.php create mode 100644 tests/bootstrap.php create mode 100644 tests/fixtures/commands/CompleteTaskCommand.php create mode 100644 tests/fixtures/commands/DeleteTaskCommand.php create mode 100644 tests/fixtures/container/Mailer.php create mode 100644 tests/fixtures/handlers/CompleteTaskCommandHandler.php diff --git a/.gitignore b/.gitignore index 3a693c9..3c77bbd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ vendor/ # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file # composer.lock +phpunit.xml \ No newline at end of file diff --git a/ContainerLocator.php b/ContainerLocator.php new file mode 100644 index 0000000..75b3a6d --- /dev/null +++ b/ContainerLocator.php @@ -0,0 +1,45 @@ +container = Yii::$container; + + $this->addHandlers($commandNameToHandlerMap); + } + + public function addHandler($handler,$commandName) + { + $this->commandNameToHandlerMap[$commandName] = $handler; + } + + public function addHandlers(array $commandNameToHandlerMap) + { + foreach ($commandNameToHandlerMap as $commandName => $handler) { + $this->addHandler($handler,$commandName); + } + } + + public function getHandlerForCommand($commandName) + { + if (!isset($this->commandNameToHandlerMap[$commandName])) { + throw MissingHandlerException::forCommand($commandName); + } + + $serviceId = $this->commandNameToHandlerMap[$commandName]; + return $this->container->get($serviceId); + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b48e9b --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +Yii2 Tactician +============== +Tactician command bus library wrapper for Yii2 + +Installation +------------ + +The preferred way to install this extension is through [composer](http://getcomposer.org/download/). + +Either run + +``` +php composer.phar require --prefer-dist cherif/yii2-tactician "*" +``` + +or add + +``` +"cherif/yii2-tactician": "*" +``` + +to the require section of your `composer.json` file. + + +Usage +----- + +Once the extension is installed, simply use it in your code by : + +```php +``` \ No newline at end of file diff --git a/Tactician.php b/Tactician.php new file mode 100644 index 0000000..77b9560 --- /dev/null +++ b/Tactician.php @@ -0,0 +1,100 @@ +buildTactician(); + } + + public function init() + { + } + + protected function buildTactician() + { + $this->createExtractor($this->extractor); + $this->createContainerLocator($this->commandHandlerMap); + $this->createInflector($this->inflector); + $this->createHandlerMiddleware(); + $this->createCommandBus(); + } + + protected function createInflector($inflectorClass) + { + return Yii::$container->set('League\Tactician\Handler\MethodNameInflector\MethodNameInflector',[ + 'class'=>$inflectorClass + ]); + } + + protected function createExtractor($extractorClass) + { + return Yii::$container->set('League\Tactician\Handler\CommandNameExtractor\CommandNameExtractor',[ + 'class'=>$extractorClass + ]); + } + + protected function createContainerLocator($mapping = []) + { + return Yii::$container->set('League\Tactician\Handler\Locator\HandlerLocator',[ + 'class'=>'cherif\tactician\ContainerLocator', + ],[$mapping]); + } + + /** + * + */ + + protected function createHandlerMiddleware() + { + return Yii::$container->set('commandHandlerMiddleware', + [ + 'class'=>'League\Tactician\Handler\CommandHandlerMiddleware' + ], + [ + Yii::$container->get('League\Tactician\Handler\CommandNameExtractor\CommandNameExtractor'), + Yii::$container->get('League\Tactician\Handler\Locator\HandlerLocator'), + Yii::$container->get('League\Tactician\Handler\MethodNameInflector\MethodNameInflector') + ] + ); + } + + + /** + * + */ + protected function createCommandBus() + { + $handlerMiddleware = Yii::$container->get('commandHandlerMiddleware'); + Yii::$container->set('commandBus',[ + 'class'=>'League\Tactician\CommandBus' + ], + [[$handlerMiddleware]] + ); + } + + + + public function __call($name,$args) + { + try { + $commandBus = Yii::$container->get('commandBus'); + return call_user_func_array([$commandBus,$name], $args); + } catch (Exception $e) { + parent::__call($name,$args); + } + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..64e0ab3 --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "cherif/yii2-tactician", + "description": "Yii2 component for Tactician command bus library", + "type": "yii2-extension", + "keywords": ["yii2","extension","tactician","command","cqs"], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Cherif BOUCHELAGHEM", + "email": "cherif.bouchelaghem@gmail.com" + } + ], + "require": { + "yiisoft/yii2": "*", + "league/tactician": "^0.6.1" + }, + "autoload": { + "psr-4": { + "cherif\\tactician\\": "" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..a88682d --- /dev/null +++ b/composer.lock @@ -0,0 +1,465 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "9bf0e49685f5671aeec8537e9a2acd0b", + "packages": [ + { + "name": "bower-asset/jquery", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/jquery/jquery.git", + "reference": "7751e69b615c6eca6f783a81e292a55725af6b85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jquery/jquery/zipball/7751e69b615c6eca6f783a81e292a55725af6b85", + "reference": "7751e69b615c6eca6f783a81e292a55725af6b85", + "shasum": "" + }, + "require-dev": { + "bower-asset/qunit": "1.14.0", + "bower-asset/requirejs": "2.1.10", + "bower-asset/sinon": "1.8.1", + "bower-asset/sizzle": "2.1.1-patch2" + }, + "type": "bower-asset-library", + "extra": { + "bower-asset-main": "dist/jquery.js", + "bower-asset-ignore": [ + "**/.*", + "build", + "dist/cdn", + "speed", + "test", + "*.md", + "AUTHORS.txt", + "Gruntfile.js", + "package.json" + ] + }, + "license": [ + "MIT" + ], + "keywords": [ + "javascript", + "jquery", + "library" + ] + }, + { + "name": "bower-asset/jquery.inputmask", + "version": "3.1.63", + "source": { + "type": "git", + "url": "https://github.com/RobinHerbots/jquery.inputmask.git", + "reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/RobinHerbots/jquery.inputmask/zipball/c40c7287eadc31e341ebbf0c02352eb55b9cbc48", + "reference": "c40c7287eadc31e341ebbf0c02352eb55b9cbc48", + "shasum": "" + }, + "require": { + "bower-asset/jquery": ">=1.7" + }, + "type": "bower-asset-library", + "extra": { + "bower-asset-main": [ + "./dist/inputmask/jquery.inputmask.js", + "./dist/inputmask/jquery.inputmask.extensions.js", + "./dist/inputmask/jquery.inputmask.date.extensions.js", + "./dist/inputmask/jquery.inputmask.numeric.extensions.js", + "./dist/inputmask/jquery.inputmask.phone.extensions.js", + "./dist/inputmask/jquery.inputmask.regex.extensions.js" + ], + "bower-asset-ignore": [ + "**/.*", + "qunit/", + "nuget/", + "tools/", + "js/", + "*.md", + "build.properties", + "build.xml", + "jquery.inputmask.jquery.json" + ] + }, + "license": [ + "http://opensource.org/licenses/mit-license.php" + ], + "description": "jquery.inputmask is a jquery plugin which create an input mask.", + "keywords": [ + "form", + "input", + "inputmask", + "jquery", + "mask", + "plugins" + ] + }, + { + "name": "bower-asset/punycode", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/bestiejs/punycode.js.git", + "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bestiejs/punycode.js/zipball/38c8d3131a82567bfef18da09f7f4db68c84f8a3", + "reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3", + "shasum": "" + }, + "type": "bower-asset-library", + "extra": { + "bower-asset-main": "punycode.js", + "bower-asset-ignore": [ + "coverage", + "tests", + ".*", + "component.json", + "Gruntfile.js", + "node_modules", + "package.json" + ] + } + }, + { + "name": "bower-asset/yii2-pjax", + "version": "v2.0.4", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/jquery-pjax.git", + "reference": "3f20897307cca046fca5323b318475ae9dac0ca0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/jquery-pjax/zipball/3f20897307cca046fca5323b318475ae9dac0ca0", + "reference": "3f20897307cca046fca5323b318475ae9dac0ca0", + "shasum": "" + }, + "require": { + "bower-asset/jquery": ">=1.8" + }, + "type": "bower-asset-library", + "extra": { + "bower-asset-main": "./jquery.pjax.js", + "bower-asset-ignore": [ + ".travis.yml", + "Gemfile", + "Gemfile.lock", + "vendor/", + "script/", + "test/" + ] + }, + "license": [ + "MIT" + ] + }, + { + "name": "cebe/markdown", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/cebe/markdown.git", + "reference": "54a2c49de31cc44e864ebf0500a35ef21d0010b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cebe/markdown/zipball/54a2c49de31cc44e864ebf0500a35ef21d0010b2", + "reference": "54a2c49de31cc44e864ebf0500a35ef21d0010b2", + "shasum": "" + }, + "require": { + "lib-pcre": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "cebe/indent": "*", + "facebook/xhprof": "*@dev", + "phpunit/phpunit": "4.1.*" + }, + "bin": [ + "bin/markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "cebe\\markdown\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Creator" + } + ], + "description": "A super fast, highly extensible markdown parser for PHP", + "homepage": "https://github.com/cebe/markdown#readme", + "keywords": [ + "extensible", + "fast", + "gfm", + "markdown", + "markdown-extra" + ], + "time": "2015-03-06 05:28:07" + }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.6.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "6f389f0f25b90d0b495308efcfa073981177f0fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/6f389f0f25b90d0b495308efcfa073981177f0fd", + "reference": "6f389f0f25b90d0b495308efcfa073981177f0fd", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "HTMLPurifier": "library/" + }, + "files": [ + "library/HTMLPurifier.composer.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "time": "2013-11-30 08:25:19" + }, + { + "name": "league/tactician", + "version": "v0.6.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/tactician.git", + "reference": "5313db226bf4e236faf8e5bb2a79203b78a9ae41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/tactician/zipball/5313db226bf4e236faf8e5bb2a79203b78a9ae41", + "reference": "5313db226bf4e236faf8e5bb2a79203b78a9ae41", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "mockery/mockery": "~0.9", + "phpunit/phpunit": "~4.3", + "squizlabs/php_codesniffer": "~2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.7-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Tactician\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ross Tuck", + "homepage": "http://tactician.thephpleague.com" + } + ], + "description": "A small, flexible command bus. Handy for building service layers.", + "keywords": [ + "command", + "command bus", + "service layer" + ], + "time": "2015-08-02 09:13:42" + }, + { + "name": "yiisoft/yii2", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "f42b2eb80f61992438661b01d0d74c6738e2ff38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/f42b2eb80f61992438661b01d0d74c6738e2ff38", + "reference": "f42b2eb80f61992438661b01d0d74c6738e2ff38", + "shasum": "" + }, + "require": { + "bower-asset/jquery": "2.1.*@stable | 1.11.*@stable", + "bower-asset/jquery.inputmask": "3.1.*", + "bower-asset/punycode": "1.3.*", + "bower-asset/yii2-pjax": ">=2.0.1", + "cebe/markdown": "~1.0.0 | ~1.1.0", + "ext-mbstring": "*", + "ezyang/htmlpurifier": "4.6.*", + "lib-pcre": "*", + "php": ">=5.4.0", + "yiisoft/yii2-composer": "*" + }, + "bin": [ + "yii" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "yii\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + }, + { + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru", + "homepage": "http://rmcreative.ru/", + "role": "Core framework development" + }, + { + "name": "Maurizio Domba", + "homepage": "http://mdomba.info/", + "role": "Core framework development" + }, + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Core framework development" + }, + { + "name": "Timur Ruziev", + "email": "resurtm@gmail.com", + "homepage": "http://resurtm.com/", + "role": "Core framework development" + }, + { + "name": "Paul Klimov", + "email": "klimov.paul@gmail.com", + "role": "Core framework development" + } + ], + "description": "Yii PHP Framework Version 2", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "framework", + "yii2" + ], + "time": "2015-08-05 22:00:30" + }, + { + "name": "yiisoft/yii2-composer", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-composer.git", + "reference": "ca8d23707ae47d20b0454e4b135c156f6da6d7be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/ca8d23707ae47d20b0454e4b135c156f6da6d7be", + "reference": "ca8d23707ae47d20b0454e4b135c156f6da6d7be", + "shasum": "" + }, + "require": { + "composer-plugin-api": "1.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "yii\\composer\\Plugin", + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "yii\\composer\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + } + ], + "description": "The composer plugin for Yii extension installer", + "keywords": [ + "composer", + "extension installer", + "yii2" + ], + "time": "2015-03-01 06:22:44" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..3098017 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,27 @@ + + + + + + + ./tests/ + + + + + + . + + + + diff --git a/tests/ContainerLocatorTest.php b/tests/ContainerLocatorTest.php new file mode 100644 index 0000000..9ec2c24 --- /dev/null +++ b/tests/ContainerLocatorTest.php @@ -0,0 +1,63 @@ + 'cherif\tactician\tests\fixtures\handlers\CompleteTaskCommandHandler', + ); + $this->containerLocator = new ContainerLocator( + $mapping + ); + } + + public function testHandlerIsReturnedForSpecificClass() + { + $this->assertInstanceOf( + CompleteTaskCommandHandler::class, + $this->containerLocator->getHandlerForCommand(CompleteTaskCommand::class) + ); + } + + public function testHandlerIsReturnedForAddedClass() + { + $this->containerLocator->addHandler('stdClass', CompleteTaskCommand::class); + $this->assertInstanceOf( + 'stdClass', + $this->containerLocator->getHandlerForCommand(CompleteTaskCommand::class) + ); + } + + public function testHandlerIsReturnedForAddedClasses() + { + $this->containerLocator->addHandlers([CompleteTaskCommand::class => 'stdClass']); + $this->assertInstanceOf( + 'stdClass', + $this->containerLocator->getHandlerForCommand(CompleteTaskCommand::class) + ); + } + + /** + * @expectedException League\Tactician\Exception\MissingHandlerException + */ + public function testMissingCommandException() + { + $this->containerLocator->getHandlerForCommand(DeleteTaskCommand::class); + } +} \ No newline at end of file diff --git a/tests/TacticianTest.php b/tests/TacticianTest.php new file mode 100644 index 0000000..fc2f6b6 --- /dev/null +++ b/tests/TacticianTest.php @@ -0,0 +1,73 @@ +assertInstanceOf('cherif\tactician\Tactician',Yii::$app->tactician); + } + + /** + * @test + */ + public function should_return_instance_of_container_locator() + { + $containerLocator = Yii::$container->get('League\Tactician\Handler\Locator\HandlerLocator'); + $extractor = Yii::$container->get('League\Tactician\Handler\CommandNameExtractor\CommandNameExtractor'); + $inflector = Yii::$container->get('League\Tactician\Handler\MethodNameInflector\MethodNameInflector'); + + + $this->assertInstanceOf('cherif\tactician\containerLocator',$containerLocator); + $this->assertInstanceOf('League\Tactician\Handler\CommandNameExtractor\CommandNameExtractor',$extractor); + $this->assertInstanceOf('League\Tactician\Handler\MethodNameInflector\MethodNameInflector',$inflector); + } + + /** + * @test + */ + + public function should_return_insrance_of_command_bus() + { + $commandBus = Yii::$container->get('commandBus'); + $this->assertInstanceOf('League\Tactician\CommandBus',$commandBus); + } + + /** + * @test + */ + + public function should_handle_command() + { + $commandBusComponent = Yii::$app->tactician; + $commandBusContainer = Yii::$container->get('commandBus'); + $this->assertEquals( + 'foobar', + $commandBusComponent->handle(new CompleteTaskCommand()) + ); + + $this->assertEquals('foobar', + $commandBusContainer->handle(new CompleteTaskCommand()) + ); + } + + + /** + * @test + * @expectedException League\Tactician\Exception\MissingHandlerException + */ + + public function should_throw_exception_on_missing_handler() + { + $commandBus = Yii::$container->get('commandBus'); + $commandBus->handle(new DeleteTaskCommand()); + } + +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..2a3d9a6 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,57 @@ +mockApplication(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->destroyApplication(); + parent::tearDown(); + } + protected function mockApplication($config = null) + { + Yii::$container = new Container; + $config = [ + 'id'=>'unit', + 'class'=>'\yii\console\Application', + 'bootstrap'=>['tactician'], + 'name'=>'Tactician Yii2 container unit tests', + 'basePath'=>__DIR__, + 'components'=>[ + 'tactician'=> [ + 'class'=>'cherif\tactician\Tactician', + 'inflector' => 'League\Tactician\Handler\MethodNameInflector\HandleClassNameInflector', + 'extractor' => 'League\Tactician\Handler\CommandNameExtractor\ClassNameExtractor', + 'commandHandlerMap'=> [ + 'cherif\tactician\tests\fixtures\commands\CompleteTaskCommand' => 'cherif\tactician\tests\fixtures\handlers\CompleteTaskCommandHandler', + ], + 'middlewares'=> [], + ] + ] + ]; + + Yii::createObject($config); + } + + protected function destroyApplication() + { + Yii::$app= null; + Yii::$container = new Container(); + } +} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..5ea9094 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,7 @@ +mailer = $mailer; + } + + public function handleCompleteTaskCommand($command) + { + return 'foobar'; + } +}