diff --git a/composer.json b/composer.json index 036d8551..4f414766 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,8 @@ "pnz/messenger-filesystem-transport": "^0.1.2", "guzzlehttp/guzzle": "~6.0", "symfony/security-bundle": "^4.1", - "symfony/monolog-bundle": "^3.3" + "symfony/monolog-bundle": "^3.3", + "symfony/maker-bundle": "^1.8" }, "autoload": { "psr-4": { @@ -49,7 +50,8 @@ }, "autoload-dev": { "psr-4": { - "Tienvx\\Bundle\\MbtBundle\\Tests\\": ["tests", "tests/app/src"] + "Tienvx\\Bundle\\MbtBundle\\Tests\\": "tests/", + "App\\": "tests/app/src/" } }, "scripts": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1d10c6b5..f61c1632 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,7 @@ - + @@ -18,6 +18,7 @@ ./src ./src/Resources + ./src/Maker diff --git a/src/Command/ExecuteTaskCommand.php b/src/Command/ExecuteTaskCommand.php index da8200a5..9601b68a 100644 --- a/src/Command/ExecuteTaskCommand.php +++ b/src/Command/ExecuteTaskCommand.php @@ -97,7 +97,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->setAnonymousToken(); - $subject = $this->subjectManager->createSubjectForModel($task->getModel()); + $subject = $this->subjectManager->createSubject($task->getModel()); $subject->setUp(); $generator = $this->generatorManager->getGenerator($task->getGenerator()); $workflow = $this->workflowRegistry->get($subject, $task->getModel()); diff --git a/src/Command/GeneratePathCommand.php b/src/Command/GeneratePathCommand.php index 002f0e25..495323bb 100644 --- a/src/Command/GeneratePathCommand.php +++ b/src/Command/GeneratePathCommand.php @@ -71,7 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $model = $input->getArgument('model'); $generatorName = $input->getOption('generator'); $generator = $this->generatorManager->getGenerator($generatorName); - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = $this->subjectManager->createSubject($model); $subject->setTesting(true); $subject->setUp(); $workflow = $this->workflowRegistry->get($subject, $model); diff --git a/src/DependencyInjection/Compiler/WorkflowRegistryPass.php b/src/DependencyInjection/Compiler/WorkflowRegistryPass.php index 4a40befe..3a17b577 100644 --- a/src/DependencyInjection/Compiler/WorkflowRegistryPass.php +++ b/src/DependencyInjection/Compiler/WorkflowRegistryPass.php @@ -8,6 +8,7 @@ use Symfony\Component\DependencyInjection\Reference; use Tienvx\Bundle\MbtBundle\Command\ExecuteTaskCommand; use Tienvx\Bundle\MbtBundle\Command\GeneratePathCommand; +use Tienvx\Bundle\MbtBundle\Maker\MakeSubject; use Tienvx\Bundle\MbtBundle\Validator\Constraints\ModelValidator; class WorkflowRegistryPass implements CompilerPassInterface @@ -45,6 +46,11 @@ public function process(ContainerBuilder $container) $modelValidatorDefinition = $container->getDefinition(ModelValidator::class); $modelValidatorDefinition->addMethodCall('setWorkflowRegistry', [$workflowRegistry]); + + if ($container->hasDefinition(MakeSubject::class)) { + $makeSubjectDefinition = $container->getDefinition(MakeSubject::class); + $makeSubjectDefinition->addMethodCall('setWorkflowRegistry', [$workflowRegistry]); + } } } } diff --git a/src/Maker/MakeSubject.php b/src/Maker/MakeSubject.php new file mode 100644 index 00000000..430c5cd9 --- /dev/null +++ b/src/Maker/MakeSubject.php @@ -0,0 +1,121 @@ +subjectManager = $subjectManager; + } + + public function setWorkflowRegistry(Registry $workflowRegistry) + { + $this->workflowRegistry = $workflowRegistry; + } + + public static function getCommandName(): string + { + return 'make:subject'; + } + + public function configureCommand(Command $command, InputConfiguration $inputConf) + { + $command + ->setDescription('Creates a new subject class for a model') + ->addArgument('model', InputArgument::OPTIONAL, 'The model to generate subject.') + ->addArgument('subject-class', InputArgument::OPTIONAL, sprintf('Choose a name for your subject class (e.g. %s)', Str::asClassName(Str::getRandomTerm()))) + ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeSubject.txt')) + ; + } + + /** + * @param InputInterface $input + * @param ConsoleStyle $io + * @param Generator $generator + * @throws \Exception + */ + public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator) + { + $model = $input->getArgument('model'); + $subject = new class extends Subject { + }; + $workflow = $this->workflowRegistry->get($subject, $model); + + if ($this->subjectManager->hasSubject($model)) { + $subject = $this->subjectManager->getSubject($model); + if (class_exists($subject)) { + $io->text(sprintf('The subject for model %s has been already defined: %s!', $model, $subject)); + return; + } + } else { + $subject = $input->getArgument('subject-class'); + } + + $subjectClassNameDetails = $generator->createClassNameDetails( + $subject, + 'Subject\\' + ); + + $methods = []; + foreach ($workflow->getDefinition()->getPlaces() as $place => $status) { + if ($status) { + $methods[] = $place; + } + } + foreach ($workflow->getDefinition()->getTransitions() as $transition) { + $methods[] = $transition->getName(); + } + + $generator->generateClass( + $subjectClassNameDetails->getFullName(), + __DIR__.'/../Resources/skeleton/subject/Subject.tpl.php', + [ + 'methods' => array_unique($methods), + ] + ); + + $generator->writeChanges(); + + $this->writeSuccessMessage($io); + $io->text([ + 'Next: Update configuration file at tienvx_mbt.subjects, add this line:', + sprintf('%s: %s', $model, $subjectClassNameDetails->getFullName()), + 'Then: Open your new subject class and implement places and transitions!' + ]); + } + + public function configureDependencies(DependencyBuilder $dependencies) + { + $dependencies->addClassDependency( + // we only need doctrine/annotations, which contains + // the recipe that loads annotation data providers + Annotation::class, + 'annotations' + ); + } +} diff --git a/src/PathReducer/BinaryPathReducer.php b/src/PathReducer/BinaryPathReducer.php index 52dadaec..edee5817 100644 --- a/src/PathReducer/BinaryPathReducer.php +++ b/src/PathReducer/BinaryPathReducer.php @@ -9,6 +9,7 @@ use Tienvx\Bundle\MbtBundle\Helper\GraphBuilder; use Tienvx\Bundle\MbtBundle\Helper\PathBuilder; use Tienvx\Bundle\MbtBundle\Helper\PathRunner; +use Tienvx\Bundle\MbtBundle\Subject\Subject; class BinaryPathReducer extends AbstractPathReducer { @@ -21,7 +22,8 @@ public function reduce(Bug $bug) parent::reduce($bug); $model = $bug->getTask()->getModel(); - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = new class extends Subject { + }; $workflow = $this->workflowRegistry->get($subject, $model); if (!$workflow instanceof StateMachine) { @@ -47,7 +49,7 @@ public function reduce(Bug $bug) // Make sure new path shorter than old path. if ($newPath->countPlaces() < $path->countPlaces()) { try { - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = $this->subjectManager->createSubject($model); PathRunner::run($newPath, $workflow, $subject); } catch (Throwable $newThrowable) { if ($newThrowable->getMessage() === $bug->getBugMessage()) { diff --git a/src/PathReducer/GreedyPathReducer.php b/src/PathReducer/GreedyPathReducer.php index 89ac96b5..efc69fc1 100644 --- a/src/PathReducer/GreedyPathReducer.php +++ b/src/PathReducer/GreedyPathReducer.php @@ -9,6 +9,7 @@ use Tienvx\Bundle\MbtBundle\Helper\GraphBuilder; use Tienvx\Bundle\MbtBundle\Helper\PathBuilder; use Tienvx\Bundle\MbtBundle\Helper\PathRunner; +use Tienvx\Bundle\MbtBundle\Subject\Subject; class GreedyPathReducer extends AbstractPathReducer { @@ -21,7 +22,8 @@ public function reduce(Bug $bug) parent::reduce($bug); $model = $bug->getTask()->getModel(); - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = new class extends Subject { + }; $workflow = $this->workflowRegistry->get($subject, $model); if (!$workflow instanceof StateMachine) { @@ -47,7 +49,7 @@ public function reduce(Bug $bug) // Make sure new path shorter than old path. if ($newPath->countPlaces() < $path->countPlaces()) { try { - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = $this->subjectManager->createSubject($model); PathRunner::run($newPath, $workflow, $subject); } catch (Throwable $newThrowable) { if ($newThrowable->getMessage() === $bug->getBugMessage()) { diff --git a/src/PathReducer/LoopPathReducer.php b/src/PathReducer/LoopPathReducer.php index a0ca4918..8275fd70 100644 --- a/src/PathReducer/LoopPathReducer.php +++ b/src/PathReducer/LoopPathReducer.php @@ -30,7 +30,7 @@ public function reduce(Bug $bug) // Make sure new path shorter than old path. if ($newPath->countPlaces() < $path->countPlaces()) { try { - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = $this->subjectManager->createSubject($model); $workflow = $this->workflowRegistry->get($subject, $model); PathRunner::run($newPath, $workflow, $subject); } catch (Throwable $newThrowable) { diff --git a/src/PathReducer/QueuedLoopPathReducer.php b/src/PathReducer/QueuedLoopPathReducer.php index 9d2cd084..e30345d4 100644 --- a/src/PathReducer/QueuedLoopPathReducer.php +++ b/src/PathReducer/QueuedLoopPathReducer.php @@ -76,7 +76,7 @@ public function handle(string $message) // Make sure new path shorter than old path. if ($newPath->countPlaces() < $path->countPlaces()) { try { - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = $this->subjectManager->createSubject($model); $workflow = $this->workflowRegistry->get($subject, $model); PathRunner::run($newPath, $workflow, $subject); } catch (Throwable $newThrowable) { diff --git a/src/PathReducer/RandomPathReducer.php b/src/PathReducer/RandomPathReducer.php index 06f61813..b1b1baac 100644 --- a/src/PathReducer/RandomPathReducer.php +++ b/src/PathReducer/RandomPathReducer.php @@ -10,6 +10,7 @@ use Tienvx\Bundle\MbtBundle\Helper\PathBuilder; use Tienvx\Bundle\MbtBundle\Helper\Randomizer; use Tienvx\Bundle\MbtBundle\Helper\PathRunner; +use Tienvx\Bundle\MbtBundle\Subject\Subject; class RandomPathReducer extends AbstractPathReducer { @@ -22,7 +23,8 @@ public function reduce(Bug $bug) parent::reduce($bug); $model = $bug->getTask()->getModel(); - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = new class extends Subject { + }; $workflow = $this->workflowRegistry->get($subject, $model); if (!$workflow instanceof StateMachine) { @@ -41,7 +43,7 @@ public function reduce(Bug $bug) // Make sure new path shorter than old path. if ($newPath->countPlaces() < $path->countPlaces()) { try { - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = $this->subjectManager->createSubject($model); PathRunner::run($newPath, $workflow, $subject); } catch (Throwable $newThrowable) { if ($newThrowable->getMessage() === $bug->getBugMessage()) { diff --git a/src/PathReducer/WeightedRandomPathReducer.php b/src/PathReducer/WeightedRandomPathReducer.php index 6dcc9a48..fe0b0801 100644 --- a/src/PathReducer/WeightedRandomPathReducer.php +++ b/src/PathReducer/WeightedRandomPathReducer.php @@ -11,6 +11,7 @@ use Tienvx\Bundle\MbtBundle\Helper\PathBuilder; use Tienvx\Bundle\MbtBundle\Helper\Randomizer; use Tienvx\Bundle\MbtBundle\Helper\PathRunner; +use Tienvx\Bundle\MbtBundle\Subject\Subject; class WeightedRandomPathReducer extends AbstractPathReducer { @@ -23,7 +24,8 @@ public function reduce(Bug $bug) parent::reduce($bug); $model = $bug->getTask()->getModel(); - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = new class extends Subject { + }; $workflow = $this->workflowRegistry->get($subject, $model); if (!$workflow instanceof StateMachine) { @@ -46,7 +48,7 @@ public function reduce(Bug $bug) // Make sure new path shorter than old path. if ($newPath->countPlaces() < $path->countPlaces()) { try { - $subject = $this->subjectManager->createSubjectForModel($model); + $subject = $this->subjectManager->createSubject($model); PathRunner::run($newPath, $workflow, $subject); } catch (Throwable $newThrowable) { if ($newThrowable->getMessage() === $bug->getBugMessage()) { diff --git a/src/Resources/help/MakeSubject.txt b/src/Resources/help/MakeSubject.txt new file mode 100644 index 00000000..7df22893 --- /dev/null +++ b/src/Resources/help/MakeSubject.txt @@ -0,0 +1,5 @@ +The %command.name% command generates a new subject class for a model. + +php %command.full_name% model-name CoolStuffSubject + +If the argument is missing, the command will ask for the model name and subject class name interactively. diff --git a/src/Resources/skeleton/subject/Subject.tpl.php b/src/Resources/skeleton/subject/Subject.tpl.php new file mode 100644 index 00000000..dfb7486b --- /dev/null +++ b/src/Resources/skeleton/subject/Subject.tpl.php @@ -0,0 +1,15 @@ + + +namespace ; + +use Tienvx\Bundle\MbtBundle\Subject\Subject; + +class extends Subject +{ + $method): ?> + public function () + { + } + + +} diff --git a/src/Subject/SubjectManager.php b/src/Subject/SubjectManager.php index 7a64cf05..3582c475 100644 --- a/src/Subject/SubjectManager.php +++ b/src/Subject/SubjectManager.php @@ -30,17 +30,22 @@ public function addSubjects(array $subjects) } } - public function hasSubjectForModel(string $model) + public function hasSubject(string $model) { return isset($this->subjects[$model]); } + public function getSubject(string $model) + { + return $this->subjects[$model]; + } + /** * @param string $model * @return Subject * @throws Exception */ - public function createSubjectForModel(string $model): Subject + public function createSubject(string $model): Subject { if (!isset($this->subjects[$model])) { throw new Exception(sprintf('Subject for model %s is not specified.', $model)); diff --git a/src/Validator/Constraints/ModelValidator.php b/src/Validator/Constraints/ModelValidator.php index f450b0fb..bfccf7af 100644 --- a/src/Validator/Constraints/ModelValidator.php +++ b/src/Validator/Constraints/ModelValidator.php @@ -7,7 +7,7 @@ use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Workflow\Registry; -use Tienvx\Bundle\MbtBundle\Subject\SubjectManager; +use Tienvx\Bundle\MbtBundle\Subject\Subject; /** * @Annotation @@ -19,17 +19,6 @@ class ModelValidator extends ConstraintValidator */ protected $workflowRegistry; - /** - * @var SubjectManager - */ - protected $subjectManager; - - public function __construct( - SubjectManager $subjectManager - ) { - $this->subjectManager = $subjectManager; - } - public function setWorkflowRegistry(Registry $workflowRegistry) { $this->workflowRegistry = $workflowRegistry; @@ -58,7 +47,8 @@ public function validate($value, Constraint $constraint) throw new UnexpectedTypeException($value, 'string'); } - $subject = $this->subjectManager->createSubjectForModel($value); + $subject = new class extends Subject { + }; if (!$this->workflowRegistry->get($subject, $value)) { $this->context->buildViolation($constraint->message) ->setParameter('{{ string }}', $value) diff --git a/tests/app/bin/console b/tests/app/bin/console index ea6170cf..cfc3e56f 100755 --- a/tests/app/bin/console +++ b/tests/app/bin/console @@ -1,7 +1,7 @@ #!/usr/bin/env php