Skip to content

Commit

Permalink
Initial commit of the extenstion driver locator component
Browse files Browse the repository at this point in the history
  • Loading branch information
Tibor Kotosz committed Nov 8, 2015
1 parent 21c6ebe commit 378dc91
Show file tree
Hide file tree
Showing 7 changed files with 381 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Composer packages directory
/vendor

# Composer executable files directory
/bin
31 changes: 31 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "bex/behat-extension-driver-locator",
"type": "library",
"description": "Driver locator tool for behat extensions",
"keywords": ["tdd","bdd","behat"],
"homepage": "https://github.com/tkotosz/behat-extension-driver-locator",
"license": "MIT",
"authors": [
{
"name": "Tibor Kotosz",
"email": "[email protected]",
"homepage": "https://github.com/tkotosz",
"role": "Developer"
}
],
"require": {
"php" : "^5.4",
"behat/behat" : "^3.0.0"
},
"require-dev": {
"phpspec/phpspec" : "^2.3.0"
},
"config": {
"bin-dir": "bin"
},
"autoload": {
"psr-0": {
"": "src"
}
}
}
45 changes: 45 additions & 0 deletions src/Bex/Behat/ExtensionDriverLocator/DriverClassNameResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Bex\Behat\ExtensionDriverLocator;

use Bex\Behat\ExtensionDriverLocator\ClassValidator;
use Symfony\Component\DependencyInjection\Container as DIContainer;

class DriverClassNameResolver
{
/**
* @var string
*/
private $namespace;

/**
* @var ClassValidator
*/
private $classValidator;

/**
* @param string $namespace
* @param ClassValidator $classValidator
*/
public function __construct($namespace, ClassValidator $classValidator)
{
$this->namespace = $namespace;
$this->classValidator = $classValidator;
}

/**
* @param string $driverKey
*
* @return string
*/
public function getClassNameByDriverKey($driverKey)
{
$driverClass = $this->namespace . '\\' . ucfirst(DIContainer::camelize($driverKey));

if (!$this->classValidator->isValidDriverClass($driverClass)) {
throw new \Exception(sprintf('Driver %s was not found in %s', $driverKey, $driverClass));
}

return $driverClass;
}
}
44 changes: 44 additions & 0 deletions src/Bex/Behat/ExtensionDriverLocator/DriverClassValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Bex\Behat\ExtensionDriverLocator;

use Bex\Behat\ExtensionDriverLocator\DriverInterface;


class DriverClassValidator
{
/**
* @var string
*/
private $parent;

/**
* @param string $parent
*/
public function __construct($parent = '')
{
$this->parent = $parent;
}

/**
* @param string $className
*
* @return boolean
*/
public function isValidDriverClass($className)
{
if (!class_exists($className)) {
return false;
}

if (!is_subclass_of($className, DriverInterface::class)) {
return false;
}

if (!empty($this->parent) && !is_subclass_of($className, $this->parent)) {
return false;
}

return true;
}
}
24 changes: 24 additions & 0 deletions src/Bex/Behat/ExtensionDriverLocator/DriverInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Bex\Behat\ExtensionDriverLocator;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;

interface DriverInterface
{
/**
* @param ArrayNodeDefinition $builder
*
* @return void
*/
public function configure(ArrayNodeDefinition $builder);

/**
* @param ContainerBuilder $container
* @param array $config
*
* @return void
*/
public function load(ContainerBuilder $container, array $config);
}
133 changes: 133 additions & 0 deletions src/Bex/Behat/ExtensionDriverLocator/DriverLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

namespace Bex\Behat\ExtensionDriverLocator;

use Bex\Behat\ExtensionDriverLocator\DriverInterface;
use Bex\Behat\ExtensionDriverLocator\ClassNameResolver;
use Bex\Behat\ExtensionDriverLocator\ClassValidator;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\NodeInterface;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class DriverLocator
{
/**
* @var ClassNameResolver
*/
private $classNameResolver;

/**
* @var DriverInterface[]
*/
private $drivers = [];

/**
* @param ClassNameResolver $classNameResolver
*/
public function __construct(ClassNameResolver $classNameResolver)
{
$this->classNameResolver = $classNameResolver;
}

/**
* @param string $namespace
* @param string $parent
*
* @return Locator
*/
public static function getInstance($namespace, $parent = '')
{
return new self(new ClassNameResolver($namespace, new ClassValidator($parent)));
}

/**
* @param ContainerBuilder $container
* @param array $configs
*
* @return DriverInterface[]
*/
public function findDrivers(ContainerBuilder $container, array $activeDrivers, array $driverConfigs)
{
$this->createDrivers($activeDrivers);
$configTree = $this->configureDrivers($driverConfigs);
$driverConfigs = $this->processDriverConfiguration($configTree, $driverConfigs);
$this->loadDrivers($container, $driverConfigs);

return $this->drivers;
}

/**
* @return DriverInterface[]
*/
public function getDrivers()
{
return $this->drivers;
}

/**
* @param array $driverKeys
*
* @return DriverInterface[]
*/
private function createDrivers($driverKeys)
{
$this->drivers = [];

foreach ($driverKeys as $driverKey) {
$driverClass = $this->classNameResolver->getClassNameByDriverKey($driverKey);
$this->drivers[$driverKey] = new $driverClass();
}

return $this->drivers;
}

/**
* @param array $driverConfigs
*
* @return NodeInterface
*/
private function configureDrivers($driverConfigs)
{
$tree = new TreeBuilder();
$root = $tree->root('drivers');

foreach ($this->drivers as $driverKey => $driver) {
$driver->configure($root->children()->arrayNode($driverKey));
}

return $tree->buildTree();
}

/**
* @param NodeInterface $configTree
* @param array $configs
*
* @return array The processed configuration
*/
private function processDriverConfiguration(NodeInterface $configTree, array $configs)
{
$configProcessor = new Processor();

foreach ($this->drivers as $driverKey => $driver) {
$configs[$driverKey] = isset($configs[$driverKey]) ? $configs[$driverKey] : [];
}

return $configProcessor->process($configTree, ['drivers' => $configs]);
}

/**
* @param ContainerBuilder $container
* @param array $driverConfigs
*
* @return DriverInterface[]
*/
private function loadDrivers(ContainerBuilder $container, array $driverConfigs)
{
foreach ($this->drivers as $driverKey => $driver) {
$driver->load($container, $driverConfigs[$driverKey]);
}

return $this->drivers;
}
}
99 changes: 99 additions & 0 deletions src/Bex/Behat/ExtensionDriverLocator/DriverNodeBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace Bex\Behat\ExtensionDriverLocator;

use Bex\Behat\ExtensionDriverLocator\ClassNameResolver;
use Bex\Behat\ExtensionDriverLocator\ClassValidator;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;

class DriverNodeBuilder
{
/**
* @var ClassNameResolver
*/
private $driverClassNameResolver;

/**
* @param ClassNameResolver $driverClassNameResolver
*/
public function __construct(ClassNameResolver $driverClassNameResolver)
{
$this->driverClassNameResolver = $driverClassNameResolver;
}

/**
* @param string $namespace
* @param string $parent
*
* @return NodeBuilder
*/
public static function getInstance($namespace, $parent = '')
{
return new self(new ClassNameResolver($namespace, new ClassValidator($parent)));
}

/**
* @param ArrayNodeDefinition $builder
* @param string $activeDriversNodeName
* @param string $driversNodeName
* @param array $defaultActiveDrivers
*
* @return void
*/
public function buildDriverNodes(
ArrayNodeDefinition $builder,
$activeDriversNodeName,
$driversNodeName,
$defaultActiveDrivers
) {
$builder
->children()
->arrayNode($activeDriversNodeName)
->defaultValue($defaultActiveDrivers)
->beforeNormalization()
->ifString()
->then($this->getDefaultValueInitializer())
->end()
->validate()
->ifTrue($this->getDriverKeyValidator())
->thenInvalid('%s')
->end()
->prototype('scalar')->end()
->end()
->end()
->fixXmlConfig($driversNodeName . '_child', $driversNodeName)
->children()
->arrayNode($driversNodeName)
->prototype('array')
->prototype('scalar')->end()
->end()
->end()
->end();
}

/**
* @return \Closure
*/
private function getDefaultValueInitializer()
{
return function ($value) {
return [$value];
};
}

/**
* @return \Closure
*/
private function getDriverKeyValidator()
{
$classNameResolver = $this->driverClassNameResolver;

return function ($driverKeys) use ($classNameResolver) {
foreach ($driverKeys as $driverKey) {
$classNameResolver->getClassNameByDriverKey($driverKey);
}

return false;
};
}
}

0 comments on commit 378dc91

Please sign in to comment.