diff --git a/gacela b/gacela index 14f427fe..a379042a 100755 --- a/gacela +++ b/gacela @@ -19,6 +19,7 @@ Config::setApplicationRootDir($cwd); $codeGeneratorFactory = new CodeGeneratorFactory(); $application = new Application(); -$application->add($codeGeneratorFactory->createMakerCommand()); +$application->add($codeGeneratorFactory->createMakerFileCommand()); +$application->add($codeGeneratorFactory->createMakerModuleCommand()); $application->run(); diff --git a/src/CodeGenerator/CodeGeneratorFactory.php b/src/CodeGenerator/CodeGeneratorFactory.php index 0f8e1991..5b9d66f2 100644 --- a/src/CodeGenerator/CodeGeneratorFactory.php +++ b/src/CodeGenerator/CodeGeneratorFactory.php @@ -6,7 +6,9 @@ use Gacela\CodeGenerator\Domain\CommandArgumentsParser; use Gacela\CodeGenerator\Domain\FileContentGenerator; +use Gacela\CodeGenerator\Domain\FilenameSanitizer; use Gacela\CodeGenerator\Infrastructure\Command\MakeFileCommand; +use Gacela\CodeGenerator\Infrastructure\Command\MakeModuleCommand; use Gacela\Framework\AbstractFactory; /** @@ -14,10 +16,19 @@ */ final class CodeGeneratorFactory extends AbstractFactory { - public function createMakerCommand(): MakeFileCommand + public function createMakerModuleCommand(): MakeModuleCommand + { + return new MakeModuleCommand( + $this->createCommandArgumentsParser(), + $this->createFileContentGenerator() + ); + } + + public function createMakerFileCommand(): MakeFileCommand { return new MakeFileCommand( $this->createCommandArgumentsParser(), + $this->createFilenameSanitizer(), $this->createFileContentGenerator() ); } @@ -29,6 +40,11 @@ private function createCommandArgumentsParser(): CommandArgumentsParser ); } + private function createFilenameSanitizer(): FilenameSanitizer + { + return new FilenameSanitizer(); + } + private function createFileContentGenerator(): FileContentGenerator { return new FileContentGenerator($this->getConfig()); diff --git a/src/CodeGenerator/Domain/FileContentGenerator.php b/src/CodeGenerator/Domain/FileContentGenerator.php index 0f729ea0..d3987557 100644 --- a/src/CodeGenerator/Domain/FileContentGenerator.php +++ b/src/CodeGenerator/Domain/FileContentGenerator.php @@ -6,6 +6,7 @@ use Gacela\CodeGenerator\Domain\ReadModel\CommandArguments; use Gacela\CodeGenerator\Infrastructure\Template\CodeTemplateInterface; +use RuntimeException; final class FileContentGenerator { @@ -16,17 +17,15 @@ public function __construct(CodeTemplateInterface $codeTemplate) $this->codeTemplate = $codeTemplate; } - public function generate( - CommandArguments $commandArguments, - string $moduleName - ): void { + public function generate(CommandArguments $commandArguments, string $filename): void + { $this->mkdir($commandArguments->directory()); - $path = sprintf('%s/%s.php', $commandArguments->directory(), $moduleName); + $path = sprintf('%s/%s.php', $commandArguments->directory(), $filename); $search = ['$NAMESPACE$', '$CLASS_NAME$']; - $replace = [$commandArguments->namespace(), $moduleName]; + $replace = [$commandArguments->namespace(), $filename]; - $template = $this->findTemplate($moduleName); + $template = $this->findTemplate($filename); $fileContent = str_replace($search, $replace, $template); file_put_contents($path, $fileContent); @@ -38,23 +37,26 @@ private function mkdir(string $directory): void return; } if (!mkdir($directory) && !is_dir($directory)) { - throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory)); + throw new RuntimeException(sprintf('Directory "%s" was not created', $directory)); } } - private function findTemplate(string $moduleName): string + private function findTemplate(string $filename): string { - switch (strtolower($moduleName)) { - case 'facade': + switch ($filename) { + case FilenameSanitizer::FACADE: return $this->codeTemplate->getFacadeMakerTemplate(); - case 'factory': + case FilenameSanitizer::FACTORY: return $this->codeTemplate->getFactoryMakerTemplate(); - case 'config': + case FilenameSanitizer::CONFIG: return $this->codeTemplate->getConfigMakerTemplate(); - case 'dependencyprovider': + case FilenameSanitizer::DEPENDENCY_PROVIDER: return $this->codeTemplate->getDependencyProviderMakerTemplate(); - + default: + throw new RuntimeException(sprintf( + 'Unknown template for "%s"?', + $filename + )); } - throw new \RuntimeException('Unknown template for module ' . $moduleName); } } diff --git a/src/CodeGenerator/Domain/FilenameSanitizer.php b/src/CodeGenerator/Domain/FilenameSanitizer.php new file mode 100644 index 00000000..73875cc3 --- /dev/null +++ b/src/CodeGenerator/Domain/FilenameSanitizer.php @@ -0,0 +1,42 @@ + 1) { + throw new RuntimeException(sprintf( + 'Which filename do you mean [%s]?', + implode(' or ', $maxValKeys) + )); + } + + return reset($maxValKeys); + } +} diff --git a/src/CodeGenerator/Infrastructure/Command/MakeFileCommand.php b/src/CodeGenerator/Infrastructure/Command/MakeFileCommand.php index 493b440c..de2803b9 100644 --- a/src/CodeGenerator/Infrastructure/Command/MakeFileCommand.php +++ b/src/CodeGenerator/Infrastructure/Command/MakeFileCommand.php @@ -6,27 +6,26 @@ use Gacela\CodeGenerator\Domain\FileContentGenerator; use Gacela\CodeGenerator\Domain\CommandArgumentsParser; -use RuntimeException; +use Gacela\CodeGenerator\Domain\FilenameSanitizer; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use function in_array; -use function json_encode; final class MakeFileCommand extends Command { - private const EXPECTED_FILENAMES = ['Facade', 'Factory', 'Config', 'DependencyProvider']; - private CommandArgumentsParser $argumentsParser; + private FilenameSanitizer $filenameSanitizer; private FileContentGenerator $fileContentGenerator; public function __construct( CommandArgumentsParser $argumentsParser, + FilenameSanitizer $filenameSanitizer, FileContentGenerator $fileContentGenerator ) { parent::__construct('make:file'); $this->argumentsParser = $argumentsParser; + $this->filenameSanitizer = $filenameSanitizer; $this->fileContentGenerator = $fileContentGenerator; } @@ -43,24 +42,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int $path = $input->getArgument('path'); $commandArguments = $this->argumentsParser->parse($path); - /** @var string $filename */ - $filename = $input->getArgument('filename'); - $filename = ucfirst($filename); - $this->verifyFilename($filename); - $this->fileContentGenerator->generate($commandArguments, $filename); + /** @var string $rawFilename */ + $rawFilename = $input->getArgument('filename'); + $filename = $this->filenameSanitizer->sanitize($rawFilename); + $this->fileContentGenerator->generate($commandArguments, $filename); $output->writeln("> Path '$path/$filename' created successfully"); return 0; } - - private function verifyFilename(string $filename): void - { - if (!in_array($filename, self::EXPECTED_FILENAMES)) { - throw new RuntimeException(sprintf( - 'Filename must be one of these values: %s', - json_encode(self::EXPECTED_FILENAMES, JSON_THROW_ON_ERROR) - )); - } - } } diff --git a/src/CodeGenerator/Infrastructure/Command/MakeModuleCommand.php b/src/CodeGenerator/Infrastructure/Command/MakeModuleCommand.php new file mode 100644 index 00000000..f3ec3300 --- /dev/null +++ b/src/CodeGenerator/Infrastructure/Command/MakeModuleCommand.php @@ -0,0 +1,66 @@ +argumentsParser = $argumentsParser; + $this->fileContentGenerator = $fileContentGenerator; + } + + protected function configure(): void + { + $this->setDescription('Generate a basic module with an empty Facade|Factory|Config|DependencyProvider.') + ->addArgument('path', InputArgument::REQUIRED, 'The file path. For example "App/TestModule/TestSubModule"'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + /** @var string $path */ + $path = $input->getArgument('path'); + $commandArguments = $this->argumentsParser->parse($path); + + foreach (self::FILENAMES as $filename) { + $this->fileContentGenerator->generate($commandArguments, $filename); + $output->writeln("> Path '$path/$filename' created successfully"); + } + + $pieces = explode('/', $commandArguments->directory()); + $moduleName = end($pieces); + $output->writeln("Module '$moduleName' created successfully"); + + return 0; + } + + private function verifyFilename(string $filename): void + { + if (!in_array($filename, self::FILENAMES)) { + throw new RuntimeException(sprintf( + 'Filename must be one of these values: %s', + json_encode(self::FILENAMES, JSON_THROW_ON_ERROR) + )); + } + } +} diff --git a/tests/Unit/CodeGenerator/CommandArgumentsParserTest.php b/tests/Unit/CodeGenerator/Domain/CommandArgumentsParserTest.php similarity index 98% rename from tests/Unit/CodeGenerator/CommandArgumentsParserTest.php rename to tests/Unit/CodeGenerator/Domain/CommandArgumentsParserTest.php index 691e7323..72f4a17d 100644 --- a/tests/Unit/CodeGenerator/CommandArgumentsParserTest.php +++ b/tests/Unit/CodeGenerator/Domain/CommandArgumentsParserTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace GacelaTest\Unit\CodeGenerator; +namespace GacelaTest\Unit\CodeGenerator\Domain; use Gacela\CodeGenerator\Domain\CommandArgumentsParser; use Gacela\CodeGenerator\Domain\Exception\CommandArgumentsException; diff --git a/tests/Unit/CodeGenerator/Domain/FilenameSanitizerTest.php b/tests/Unit/CodeGenerator/Domain/FilenameSanitizerTest.php new file mode 100644 index 00000000..0318d86e --- /dev/null +++ b/tests/Unit/CodeGenerator/Domain/FilenameSanitizerTest.php @@ -0,0 +1,107 @@ +filenameSanitizer = new FilenameSanitizer(); + } + + public function test_facade_or_factory_problem(): void + { + $this->expectExceptionMessageMatches('/Which filename do you mean/'); + $this->filenameSanitizer->sanitize('fac'); + } + + /** + * @dataProvider providerFacade + */ + public function test_facade(string $filename): void + { + self::assertSame( + FilenameSanitizer::FACADE, + $this->filenameSanitizer->sanitize($filename) + ); + } + + public function providerFacade(): iterable + { + yield ['faca']; + yield ['facad']; + yield ['facade']; + yield ['Facade']; + yield ['cade']; + } + + /** + * @dataProvider providerFactory + */ + public function test_factory(string $filename): void + { + self::assertSame( + FilenameSanitizer::FACTORY, + $this->filenameSanitizer->sanitize($filename) + ); + } + + public function providerFactory(): iterable + { + yield ['fact']; + yield ['facto']; + yield ['factor']; + yield ['factory']; + yield ['Factory']; + yield ['tory']; + } + + /** + * @dataProvider providerConfig + */ + public function test_config(string $filename): void + { + self::assertSame( + FilenameSanitizer::CONFIG, + $this->filenameSanitizer->sanitize($filename) + ); + } + + public function providerConfig(): iterable + { + yield ['conf']; + yield ['confi']; + yield ['config']; + yield ['Config']; + yield ['fig']; + } + + /** + * @dataProvider providerDependencyProvider + */ + public function test_dependency_provider(string $filename): void + { + self::assertSame( + FilenameSanitizer::DEPENDENCY_PROVIDER, + $this->filenameSanitizer->sanitize($filename) + ); + } + + public function providerDependencyProvider(): iterable + { + yield ['depe']; + yield ['dependency']; + yield ['pro']; + yield ['provider']; + yield ['de-pr']; + yield ['dependencyprovider']; + yield ['dependency-provider']; + } +}