Skip to content

Commit

Permalink
Provide valid formats from the format manager given a data type to fo…
Browse files Browse the repository at this point in the history
…rmat.
  • Loading branch information
greg-1-anderson committed Apr 28, 2016
1 parent 3c952e5 commit f43aeb9
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 58 deletions.
23 changes: 15 additions & 8 deletions src/Exception/IncompatibleDataException.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,28 @@ public function __construct(FormatterInterface $formatter, $data, $allowedTypes)
parent::__construct($message, 1);
}

/**
* Return a description of the data type represented by the provided parameter.
*
* @param \ReflectionClass $data The data type to describe. Note that
* \ArrayObject is used as a proxy to mean an array primitive (or an ArrayObject).
* @return string
*/
protected static function describeDataType($data)
{
if ($data instanceof \ReflectionClass) {
if (is_array($data) || ($data instanceof \ReflectionClass)) {
if (is_array($data) || ($data->getName() == 'ArrayObject')) {
return 'an array';
}
return 'an instance of ' . $data->getName();
}
if (is_object($data)) {
return 'an instance of ' . get_class($data);
}
if (is_array($data)) {
return 'an array';
}
if (is_string($data)) {
return 'a string';
}
return '<' . var_export($data) . '>';
if (is_object($data)) {
return 'an instance of ' . get_class($data);
}
throw new \Exception("Undescribable data error: " . var_export($data, true));
}

protected static function describeAllowedTypes($allowedTypes)
Expand Down
68 changes: 66 additions & 2 deletions src/FormatterManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
namespace Consolidation\OutputFormatters;

use Symfony\Component\Console\Output\OutputInterface;
use Consolidation\OutputFormatters\FormatterInterface;
use Consolidation\OutputFormatters\Exception\UnknownFormatException;
use Consolidation\OutputFormatters\Formatters\RenderDataInterface;

Expand Down Expand Up @@ -32,6 +31,58 @@ public function __construct()
$this->formatters[''] = $this->formatters['string'];
}

/**
* Add a formatter
*
* @param FormatterInterface $formatter the formatter to add
* @return FormatterManager
*/
public function add(FormatterInterface $formatter)
{
$this->formatters[] = $formatter;
return $this;
}

/**
* Return the identifiers for all valid data types that have been registered.
*
* @param mixed $dataType \ReflectionObject or other description of the produced data type
* @return array
*/
public function validFormats($dataType)
{
$validFormats = [];
foreach ($this->formatters as $formatId => $formatterName) {
$formatter = $this->getFormatter($formatId);
if ($this->isValidFormat($formatter, $dataType)) {
$validFormats[] = $formatId;
}
}
return $validFormats;
}

public function isValidFormat(FormatterInterface $formatter, $dataType)
{
if (is_array($dataType)) {
$dataType = new \ReflectionClass('\ArrayObject');
}
if (!$dataType instanceOf \ReflectionClass) {
$dataType = new \ReflectionClass($dataType);
}
// If the formatter does not implement ValidationInterface, then
// it is presumed that the formatter only accepts arrays.
if (!$formatter instanceof ValidationInterface) {
return $dataType->isSubclassOf('ArrayObject') || ($dataType->getName() == 'ArrayObject');
}
$supportedTypes = $formatter->validDataTypes();
foreach ($supportedTypes as $supportedType) {
if (($dataType->getName() == $supportedType->getName()) || $dataType->isSubclassOf($supportedType->getName())) {
return true;
}
}
return false;
}

/**
* Format and write output
*
Expand All @@ -47,6 +98,8 @@ public function write(OutputInterface $output, $format, $structuredOutput, Forma
$formatter->write($output, $structuredOutput, $options);
}



protected function validateAndRestructure(FormatterInterface $formatter, $structuredOutput, FormatterOptions $options)
{
// Give the formatter a chance to do something with the
Expand Down Expand Up @@ -84,6 +137,9 @@ public function getFormatter($format)
return $formatter;
}

/**
* Test to see if the stipulated format exists
*/
public function hasFormatter($format)
{
return array_key_exists($format, $this->formatters);
Expand Down Expand Up @@ -126,7 +182,15 @@ public function validateData(FormatterInterface $formatter, $structuredOutput)
if ($structuredOutput instanceof \ArrayObject) {
return $structuredOutput->getArrayCopy();
}

// If the formatter does not implmeent ValidationInterface, then
// we will further presume that it will *only* accept arrays.
if (!is_array($structuredOutput)) {
throw new IncompatibleDataException(
$formatter,
$structuredOutput,
[]
);
}
return $structuredOutput;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Formatters/CsvFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function validDataTypes()
[
new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\RowsOfFields'),
new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\AssociativeList'),
[],
new \ReflectionClass('\ArrayObject'),
];
}

Expand Down
22 changes: 21 additions & 1 deletion src/Formatters/StringFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Consolidation\OutputFormatters\Formatters;

use Consolidation\OutputFormatters\FormatterInterface;
use Consolidation\OutputFormatters\ValidationInterface;
use Consolidation\OutputFormatters\FormatterOptions;
use Symfony\Component\Console\Output\OutputInterface;

Expand All @@ -13,7 +14,7 @@
* provided data only if it is a string; if any other
* type is given, then nothing is printed.
*/
class StringFormatter implements FormatterInterface
class StringFormatter implements FormatterInterface, ValidationInterface
{
/**
* @inheritdoc
Expand All @@ -24,4 +25,23 @@ public function write(OutputInterface $output, $data, FormatterOptions $options)
$output->writeln($data);
}
}

/**
* Do not return any valid data types -- this formatter will never show up
* in a list of valid formats.
*/
public function validDataTypes()
{
return [];
}

/**
* Always validate any data, though. This format will never
* cause an error if it is selected for an incompatible data type; at
* worse, it simply does not print any data.
*/
public function validate($structuredData)
{
return $structuredData;
}
}
55 changes: 55 additions & 0 deletions src/StructuredData/AbstractStructuredList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
namespace Consolidation\OutputFormatters\StructuredData;

use Consolidation\OutputFormatters\RestructureInterface;
use Consolidation\OutputFormatters\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\ListDataInterface;
use Consolidation\OutputFormatters\Transformations\ReorderFields;
use Consolidation\OutputFormatters\Transformations\TableTransformation;

/**
* Holds an array where each element of the array is one row,
* and each row contains an associative array where the keys
* are the field names, and the values are the field data.
*
* It is presumed that every row contains the same keys.
*/
abstract class AbstractStructuredList extends \ArrayObject implements RestructureInterface, ListDataInterface
{
protected $data;

public function __construct($data)
{
parent::__construct($data);
}

public abstract function restructure(FormatterOptions $options);

public abstract function getListData();

protected function createTableTransformation($data, $options)
{
$defaults = $this->defaultOptions();

$reorderer = new ReorderFields();
$fieldLabels = $reorderer->reorder($options->get('fields', $defaults), $options->get('field-labels', $defaults), $data);

$tableTransformer = new TableTransformation($data, $fieldLabels, $options->get('row-labels', $defaults));
if ($options->get('list-orientation', $defaults)) {
$tableTransformer->setLayout(TableTransformation::LIST_LAYOUT);
}

return $tableTransformer;
}

protected function defaultOptions()
{
return [
'list-orientation' => false,
'fields' => [],
'field-labels' => [],
'row-labels' => [],
'default-fields' => [],
];
}
}
9 changes: 1 addition & 8 deletions src/StructuredData/AssociativeList.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,8 @@
* key : value pair. The keys must be unique, as is typically
* the case for associative arrays.
*/
class AssociativeList extends RowsOfFields implements ListDataInterface
class AssociativeList extends AbstractStructuredList
{
protected $data;

public function __construct($data)
{
parent::__construct($data);
}

public function restructure(FormatterOptions $options)
{
$data = [$this->getArrayCopy()];
Expand Down
39 changes: 1 addition & 38 deletions src/StructuredData/RowsOfFields.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<?php
namespace Consolidation\OutputFormatters\StructuredData;

use Consolidation\OutputFormatters\RestructureInterface;
use Consolidation\OutputFormatters\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\ListDataInterface;
use Consolidation\OutputFormatters\Transformations\ReorderFields;
use Consolidation\OutputFormatters\Transformations\TableTransformation;

/**
* Holds an array where each element of the array is one row,
Expand All @@ -14,15 +10,8 @@
*
* It is presumed that every row contains the same keys.
*/
class RowsOfFields extends \ArrayObject implements RestructureInterface, ListDataInterface
class RowsOfFields extends AbstractStructuredList
{
protected $data;

public function __construct($data)
{
parent::__construct($data);
}

public function restructure(FormatterOptions $options)
{
$data = $this->getArrayCopy();
Expand All @@ -33,30 +22,4 @@ public function getListData()
{
return array_keys($this->getArrayCopy());
}

protected function createTableTransformation($data, $options)
{
$defaults = $this->defaultOptions();

$reorderer = new ReorderFields();
$fieldLabels = $reorderer->reorder($options->get('fields', $defaults), $options->get('field-labels', $defaults), $data);

$tableTransformer = new TableTransformation($data, $fieldLabels, $options->get('row-labels', $defaults));
if ($options->get('list-orientation', $defaults)) {
$tableTransformer->setLayout(TableTransformation::LIST_LAYOUT);
}

return $tableTransformer;
}

protected function defaultOptions()
{
return [
'list-orientation' => false,
'fields' => [],
'field-labels' => [],
'row-labels' => [],
'default-fields' => [],
];
}
}
54 changes: 54 additions & 0 deletions tests/testFormatters.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,60 @@ function setup() {
//$this->logger = new Logger($this->output);
}

function testValidFormats()
{
$arrayObjectRef = new \ReflectionClass('\ArrayObject');
$associativeListRef = new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\AssociativeList');
$rowsOfFieldsRef = new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\RowsOfFields');
$notADataType = new \ReflectionClass('\Consolidation\OutputFormatters\FormatterManager');

$jsonFormatter = $this->formatterManager->getFormatter('json');
$isValid = $this->formatterManager->isValidFormat($jsonFormatter, $notADataType);
$this->assertFalse($isValid);
$isValid = $this->formatterManager->isValidFormat($jsonFormatter, new \ArrayObject());
$this->assertTrue($isValid);
$isValid = $this->formatterManager->isValidFormat($jsonFormatter, $arrayObjectRef);
$this->assertTrue($isValid);
$isValid = $this->formatterManager->isValidFormat($jsonFormatter, []);
$this->assertTrue($isValid);
$isValid = $this->formatterManager->isValidFormat($jsonFormatter, $associativeListRef);
$this->assertTrue($isValid);
$isValid = $this->formatterManager->isValidFormat($jsonFormatter, $rowsOfFieldsRef);
$this->assertTrue($isValid);

$sectionsFormatter = $this->formatterManager->getFormatter('sections');
$isValid = $this->formatterManager->isValidFormat($sectionsFormatter, $notADataType);
$this->assertFalse($isValid);
$isValid = $this->formatterManager->isValidFormat($sectionsFormatter, []);
$this->assertFalse($isValid);
$isValid = $this->formatterManager->isValidFormat($sectionsFormatter, $arrayObjectRef);
$this->assertFalse($isValid);
$isValid = $this->formatterManager->isValidFormat($sectionsFormatter, $rowsOfFieldsRef);
$this->assertTrue($isValid);
$isValid = $this->formatterManager->isValidFormat($sectionsFormatter, $associativeListRef);
$this->assertFalse($isValid);

// Check to see which formats can handle a simple array
$validFormats = $this->formatterManager->validFormats([]);
sort($validFormats);
$this->assertEquals('csv,json,list,php,print-r,var_export,yaml', implode(',', $validFormats));

// Check to see which formats can handle an AssociativeList
$validFormats = $this->formatterManager->validFormats($associativeListRef);
sort($validFormats);
$this->assertEquals('csv,json,list,php,print-r,table,var_export,yaml', implode(',', $validFormats));

// Check to see which formats can handle an RowsOfFields
$validFormats = $this->formatterManager->validFormats(new \ReflectionClass('\Consolidation\OutputFormatters\StructuredData\RowsOfFields'));
sort($validFormats);
$this->assertEquals('csv,json,list,php,print-r,sections,table,var_export,yaml', implode(',', $validFormats));

// Test error case: no formatter should handle something that is not a data type.
$validFormats = $this->formatterManager->validFormats($notADataType);
sort($validFormats);
$this->assertEquals('', implode(',', $validFormats));
}

function assertFormattedOutputMatches($expected, $format, $data, FormatterOptions $options = null, $userOptions = []) {
if (!$options) {
$options = new FormatterOptions();
Expand Down

0 comments on commit f43aeb9

Please sign in to comment.