Skip to content

Commit

Permalink
Merge 5ad0848 into ba1614c
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-1-anderson committed Sep 18, 2016
2 parents ba1614c + 5ad0848 commit 48993f7
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 10 deletions.
10 changes: 9 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,19 @@
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2"
},
"require-dev": {
"consolidation/output-formatters": "^2.0.0-beta2",
"consolidation/output-formatters": "dev-automatic-options",
"phpunit/phpunit": "4.*",
"satooshi/php-coveralls": "^1.0",
"squizlabs/php_codesniffer": "2.*"
},
"suggest": {
"consolidation/output-formatters": "Format text by applying transformations provided by plug-in formatters."
},
"scripts": {
"cs": "phpcs --standard=PSR2 -n src",
"cbf": "phpcbf --standard=PSR2 -n src",
"test": "phpunit"
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
Expand Down
95 changes: 87 additions & 8 deletions src/AnnotatedCommand.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<?php
namespace Consolidation\AnnotatedCommand;

use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
use Consolidation\OutputFormatters\FormatterManager;
use Consolidation\OutputFormatters\FormatterOptions;
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 Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\AnnotatedCommand\Parser\CommandInfo;
use Symfony\Component\Console\Output\OutputInterface;

/**
* AnnotatedCommands are created automatically by the
Expand Down Expand Up @@ -173,8 +175,63 @@ protected function getCommandArgumentMode($hasDefault, $defaultValue)

protected function setCommandOptions($commandInfo)
{
$automaticOptions = $this->automaticOptions($commandInfo);
$explicitOptions = $this->explicitOptions($commandInfo);

$this->addOptions($explicitOptions + $automaticOptions, $automaticOptions);
}

protected function addOptions($inputOptions, $automaticOptions)
{
foreach ($inputOptions as $name => $inputOption) {
$default = $inputOption->getDefault();
$description = $inputOption->getDescription();

if (empty($description) && isset($automaticOptions[$name])) {
$description = $automaticOptions[$name]->getDescription();
}

// Recover the 'mode' value, because Symfony is stubborn
$mode = 0;
if ($inputOption->isValueRequired()) {
$mode |= InputOption::VALUE_REQUIRED;
}
if ($inputOption->isValueOptional()) {
$mode |= InputOption::VALUE_OPTIONAL;
}
if ($inputOption->isArray()) {
$mode |= InputOption::VALUE_IS_ARRAY;
}
if (!$mode) {
$mode = InputOption::VALUE_NONE;
$default = null;
}

// Add the option; note that Symfony doesn't have a convenient
// method to do this that takes an InputOption
$this->addOption(
$inputOption->getName(),
$inputOption->getShortcut(),
$mode,
$description,
$default
);
}
}

/**
* Get the options that are explicitly defined, e.g. via
* @option annotations, or via $options = ['someoption' => 'defaultvalue']
* in the command method parameter list.
*
* @return InputOption[]
*/
protected function explicitOptions($commandInfo)
{
$explicitOptions = [];

$opts = $commandInfo->options()->getValues();
foreach ($opts as $name => $val) {
foreach ($opts as $name => $defaultValue) {
$description = $commandInfo->options()->getDescription($name);

$fullName = $name;
Expand All @@ -183,12 +240,34 @@ protected function setCommandOptions($commandInfo)
list($fullName, $shortcut) = explode('|', $name, 2);
}

if (is_bool($val)) {
$this->addOption($fullName, $shortcut, InputOption::VALUE_NONE, $description);
if (is_bool($defaultValue)) {
$explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_NONE, $description);
} elseif ($defaultValue === InputOption::VALUE_REQUIRED) {
$explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_REQUIRED, $description);
} else {
$this->addOption($fullName, $shortcut, InputOption::VALUE_OPTIONAL, $description, $val);
$explicitOptions[$fullName] = new InputOption($fullName, $shortcut, InputOption::VALUE_OPTIONAL, $description, $defaultValue);
}
}

return $explicitOptions;
}

/**
* Get the options that are implied by annotations, e.g. @fields implies
* that there should be a --fields and a --format option.
*
* @return InputOption[]
*/
protected function automaticOptions($commandInfo)
{
$formatManager = $this->getCommandProcessor()->formatterManager();
if ($formatManager) {
$formatterOptions = new FormatterOptions($this->annotationData->getArrayCopy());
$dataType = $commandInfo->getReturnType();
$automaticOptions = $formatManager->automaticOptions($formatterOptions, $dataType);
return $automaticOptions;
}
return [];
}

protected function getArgsWithPassThrough($input)
Expand Down
37 changes: 36 additions & 1 deletion tests/testFullStack.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ function testValidFormats()
$this->assertEquals('\Consolidation\OutputFormatters\StructuredData\RowsOfFields', $commandInfo->getReturnType());
}

function testAutomaticOptions()
{
$commandFileInstance = new \Consolidation\TestUtils\alpha\AlphaCommandFile;
$commandFactory = new AnnotatedCommandFactory();
$formatter = new FormatterManager();
$commandFactory->commandProcessor()->setFormatterManager($formatter);
$commandInfo = $commandFactory->createCommandInfo($commandFileInstance, 'exampleTable');

$command = $commandFactory->createCommand($commandInfo, $commandFileInstance);
$this->application->add($command);

$containsList =
[
'--format[=FORMAT] Format the result data. Available formats: csv,json,list,php,print-r,sections,string,table,tsv,var_export,xml,yaml [default: "table"]',
'--fields[=FIELDS] Available fields: I (first), II (second), III (third) [default: ""]',
];
$this->assertRunCommandViaApplicationContains('help example:table', $containsList);
}

function testCommandsAndHooks()
{
// First, search for commandfiles in the 'alpha'
Expand Down Expand Up @@ -78,7 +97,7 @@ function testCommandsAndHooks()
$returnType = $exampleTableCommand->getReturnType();
$this->assertEquals('\Consolidation\OutputFormatters\StructuredData\RowsOfFields', $returnType);
$validFormats = $formatter->validFormats($returnType);
$this->assertEquals('csv,json,list,php,print-r,sections,table,tsv,var_export,xml,yaml', implode(',', $validFormats));
$this->assertEquals('csv,json,list,php,print-r,sections,string,table,tsv,var_export,xml,yaml', implode(',', $validFormats));

// Control: run commands without hooks.
$this->assertRunCommandViaApplicationEquals('always:fail', 'This command always fails.', 13);
Expand Down Expand Up @@ -165,6 +184,22 @@ function assertRunCommandViaApplicationEquals($cmd, $expectedOutput, $expectedSt
$this->assertEquals($expectedStatusCode, $statusCode);
}

function assertRunCommandViaApplicationContains($cmd, $containsList, $expectedStatusCode = 0)
{
$input = new StringInput($cmd);
$output = new BufferedOutput();

$statusCode = $this->application->run($input, $output);
$commandOutput = trim($output->fetch());

$commandOutput = $this->simplifyWhitespace($commandOutput);

foreach ($containsList as $expectedToContain) {
$this->assertContains($this->simplifyWhitespace($expectedToContain), $commandOutput);
}
$this->assertEquals($expectedStatusCode, $statusCode);
}

function simplifyWhitespace($data)
{
return trim(preg_replace('#[ \t]+$#m', '', $data));
Expand Down

0 comments on commit 48993f7

Please sign in to comment.