Refactoring

- changed "SPI" into something more familiar, like "implementation"
- throw exceptions on invalid implementation types or invalid class names
- use null instead of false when querying
- refactored the tests accordingly
main
nevvermind 9 years ago
parent ec8229ffa3
commit aa45a48283

@ -26,19 +26,17 @@ interface Capable
* with a special structure. * with a special structure.
* *
* The key must be a string, representing a fully qualified class/interface name * The key must be a string, representing a fully qualified class/interface name
* which Composer Plugin API exposes - named "API class". * which Composer Plugin API exposes.
* The value must be a string as well, representing the fully qualified class name * The value must be a string as well, representing the fully qualified class name
* of the API class - named "SPI class". * of the API class.
* *
* Every SPI must implement their API class. * Every implementation will be passed a single array parameter via their constructor.
* *
* Every SPI will be passed a single array parameter via their constructor. * @tutorial
* *
* Example:
* // API as key, SPI as value
* return array( * return array(
* 'Composer\Plugin\Capability\CommandProvider' => 'My\CommandProvider', * 'Composer\Plugin\Capability\CommandProvider' => 'My\CommandProvider',
* 'Composer\Plugin\Capability\Validator' => 'My\Validator', * 'Composer\Plugin\Capability\Validator' => 'My\Validator',
* ); * );
* *
* @return string[] * @return string[]

@ -186,6 +186,16 @@ class PluginManager
} }
} }
/**
* Returns the version of the internal composer-plugin-api package.
*
* @return string
*/
protected function getPluginApiVersion()
{
return PluginInterface::PLUGIN_API_VERSION;
}
/** /**
* Adds a plugin, activates it and registers it with the event dispatcher * Adds a plugin, activates it and registers it with the event dispatcher
* *
@ -291,56 +301,58 @@ class PluginManager
return $this->globalComposer->getInstallationManager()->getInstallPath($package); return $this->globalComposer->getInstallationManager()->getInstallPath($package);
} }
/**
* Returns the version of the internal composer-plugin-api package.
*
* @return string
*/
protected function getPluginApiVersion()
{
return PluginInterface::PLUGIN_API_VERSION;
}
/** /**
* @param PluginInterface $plugin * @param PluginInterface $plugin
* @param string $capability * @param string $capability
* @return bool|string The fully qualified class of the implementation or false if none was provided * @return null|string The fully qualified class of the implementation or null if Plugin is not of Capable type
* @throws \RuntimeException On empty or non-string implementation class name value
*/ */
protected function getCapabilityImplementationClassName(PluginInterface $plugin, $capability) protected function getCapabilityImplementationClassName(PluginInterface $plugin, $capability)
{ {
if (!($plugin instanceof Capable)) { if (!($plugin instanceof Capable)) {
return false; return null;
} }
$capabilities = (array) $plugin->getCapabilities(); $capabilities = (array) $plugin->getCapabilities();
if (!empty($capabilities[$capability]) && is_string($capabilities[$capability])) {
$capabilities[$capability] = trim($capabilities[$capability]);
}
if (empty($capabilities[$capability]) || !is_string($capabilities[$capability])) { if (empty($capabilities[$capability]) || !is_string($capabilities[$capability])) {
return false; throw new \RuntimeException('Plugin provided invalid capability class name(s)');
} }
return trim($capabilities[$capability]); return $capabilities[$capability];
} }
/** /**
* @param PluginInterface $plugin * @param PluginInterface $plugin
* @param string $capability The fully qualified name of the API interface which the plugin may provide * @param string $capabilityClassName The fully qualified name of the API interface which the plugin may provide
* an implementation. * an implementation.
* @param array $ctorArgs Arguments passed to Capability's constructor. * @param array $ctorArgs Arguments passed to Capability's constructor.
* Keeping it an array will allow future values to be passed w\o changing the signature. * Keeping it an array will allow future values to be passed w\o changing the signature.
* @return Capability|boolean Bool false if the Plugin has no implementation of the requested Capability. * @return null|Capability
*/ */
public function getPluginCapability(PluginInterface $plugin, $capability, array $ctorArgs = array()) public function getPluginCapability(PluginInterface $plugin, $capabilityClassName, array $ctorArgs = array())
{ {
if ($capabilityClass = $this->getCapabilityImplementationClassName($plugin, $capability)) { if ($capabilityClass = $this->getCapabilityImplementationClassName($plugin, $capabilityClassName)) {
if (class_exists($capabilityClass)) { if (class_exists($capabilityClass)) {
$capabilityObj = new $capabilityClass($ctorArgs); $capabilityObj = new $capabilityClass($ctorArgs);
if ($capabilityObj instanceof Capability && if ($capabilityObj instanceof Capability &&
$capabilityObj instanceof $capability $capabilityObj instanceof $capabilityClassName
) { ) {
return $capabilityObj; return $capabilityObj;
} else {
throw new \RuntimeException(
'Class ' . $capabilityClass . ' must be of both \Composer\Plugin\Capability\Capability and '.
$capabilityClassName . ' types.'
);
} }
} else {
throw new \RuntimeException("Cannot instantiate Capability, as class $capabilityClass does not exist.");
} }
} }
return false; return null;
} }
} }

@ -302,31 +302,31 @@ class PluginInstallerTest extends TestCase
$plugin = $this->getMockBuilder('Composer\Plugin\PluginInterface') $plugin = $this->getMockBuilder('Composer\Plugin\PluginInterface')
->getMock(); ->getMock();
$this->assertFalse($this->pm->getPluginCapability($plugin, 'Fake\Ability')); $this->assertNull($this->pm->getPluginCapability($plugin, 'Fake\Ability'));
} }
public function testCapabilityImplementsComposerPluginApiClassAndIsConstructedWithArgs() public function testCapabilityImplementsComposerPluginApiClassAndIsConstructedWithArgs()
{ {
$capabilityApi = 'Composer\Plugin\Capability\Capability'; $capabilityApi = 'Composer\Plugin\Capability\Capability';
$capabilitySpi = 'Composer\Test\Plugin\Mock\Capability'; $capabilityImplementation = 'Composer\Test\Plugin\Mock\Capability';
$plugin = $this->getMockBuilder('Composer\Test\Plugin\Mock\CapablePluginInterface') $plugin = $this->getMockBuilder('Composer\Test\Plugin\Mock\CapablePluginInterface')
->getMock(); ->getMock();
$plugin->expects($this->once()) $plugin->expects($this->once())
->method('getCapabilities') ->method('getCapabilities')
->will($this->returnCallback(function() use ($capabilitySpi, $capabilityApi) { ->will($this->returnCallback(function() use ($capabilityImplementation, $capabilityApi) {
return array($capabilityApi => $capabilitySpi); return array($capabilityApi => $capabilityImplementation);
})); }));
$capability = $this->pm->getPluginCapability($plugin, $capabilityApi, array('a' => 1, 'b' => 2)); $capability = $this->pm->getPluginCapability($plugin, $capabilityApi, array('a' => 1, 'b' => 2));
$this->assertInstanceOf($capabilityApi, $capability); $this->assertInstanceOf($capabilityApi, $capability);
$this->assertInstanceOf($capabilitySpi, $capability); $this->assertInstanceOf($capabilityImplementation, $capability);
$this->assertSame(array('a' => 1, 'b' => 2), $capability->args); $this->assertSame(array('a' => 1, 'b' => 2), $capability->args);
} }
public function invalidSpiValues() public function invalidImplementationClassNames()
{ {
return array( return array(
array(null), array(null),
@ -337,14 +337,22 @@ class PluginInstallerTest extends TestCase
array(array(1)), array(array(1)),
array(array()), array(array()),
array(new \stdClass()), array(new \stdClass()),
array("NonExistentClassLikeMiddleClass"), );
}
public function nonExistingOrInvalidImplementationClassTypes()
{
return array(
array('\stdClass'),
array('NonExistentClassLikeMiddleClass'),
); );
} }
/** /**
* @dataProvider invalidSpiValues * @dataProvider invalidImplementationClassNames
* @expectedException \RuntimeException
*/ */
public function testInvalidCapabilitySpiDeclarationsAreDisregarded($invalidSpi) public function testQueryingWithInvalidCapabilityClassNameThrows($invalidImplementationClassNames)
{ {
$capabilityApi = 'Composer\Plugin\Capability\Capability'; $capabilityApi = 'Composer\Plugin\Capability\Capability';
@ -353,10 +361,19 @@ class PluginInstallerTest extends TestCase
$plugin->expects($this->once()) $plugin->expects($this->once())
->method('getCapabilities') ->method('getCapabilities')
->will($this->returnCallback(function() use ($invalidSpi, $capabilityApi) { ->will($this->returnCallback(function() use ($invalidImplementationClassNames, $capabilityApi) {
return array($capabilityApi => $invalidSpi); return array($capabilityApi => $invalidImplementationClassNames);
})); }));
$this->assertFalse($this->pm->getPluginCapability($plugin, $capabilityApi)); $this->pm->getPluginCapability($plugin, $capabilityApi);
}
/**
* @dataProvider nonExistingOrInvalidImplementationClassTypes
* @expectedException \RuntimeException
*/
public function testQueryingWithNonExistingOrWrongCapabilityClassTypesThrows($wrongImplementationClassTypes)
{
$this->testQueryingWithInvalidCapabilityClassNameThrows($wrongImplementationClassTypes);
} }
} }

Loading…
Cancel
Save