Change format of module configuration to make them implement their events on a single calss.

This commit is contained in:
Austen McDonald
2016-07-21 06:37:19 +00:00
parent 53b25a85ed
commit 31c0f4341c
2 changed files with 82 additions and 156 deletions
+48 -24
View File
@@ -3,12 +3,13 @@ declare (strict_types=1);
namespace LotGD\Core;
use Composer\Package\PackageInterface;
use Doctrine\ORM\EntityManagerInterface;
use LotGD\Core\Models\Module;
use LotGD\Core\Exceptions\KeyNotFoundException;
use LotGD\Core\Exceptions\ModuleAlreadyExistsException;
use LotGD\Core\Exceptions\ModuleDoesNotExistException;
use Composer\Package\PackageInterface;
use LotGD\Core\Models\Module as ModuleModel;
/**
* Handles the adding and removing of modules to the game.
@@ -27,18 +28,16 @@ class ModuleManager
private static function getPackageSubscriptions(PackageInterface $package): array
{
$name = $package->getName();
$extra = $package->getExtra();
if (!empty($extra['subscriptions'])) {
$subscriptions = array();
// Minimal scrub to the subscriptions list.
foreach ($extra['subscriptions'] as $s) {
if (!isset($s['pattern']) ||
!isset($s['class']) ||
!is_string($s['pattern']) ||
!is_string($s['class']))
if (!is_string($s))
{
// TODO: log this but continue on.
$this->g->getLogger()->error("Module {$name} has invalid event subscription: {$s}.");
continue;
}
array_push($subscriptions, $s);
@@ -57,27 +56,49 @@ class ModuleManager
* @param PackageInterface $package Composer package containing this module.
* @throws ModuleAlreadyExistsException if the module is already installed.
* @throws ClassNotFoundException if an event subscription class cannot be resolved.
* @throws WrongTypeException if an event subscription class does not implement the EventHandler
* @throws WrongTypeException if an event subscription class does not implement the Module
* interface or the pattern is not a valid regular expression.
*/
public function register(string $library, PackageInterface $package)
{
$m = $this->g->getEntityManager()->getRepository(Module::class)->find($library);
$m = $this->g->getEntityManager()->getRepository(ModuleModel::class)->find($library);
if ($m) {
throw new ModuleAlreadyExistsException($library);
} else {
// TODO: handle error cases here.
$m = new Module($library);
$m = new ModuleModel($library);
$m->save($this->g->getEntityManager());
$name = $package->getName();
$class = $package->getExtra()['class'];
if (!isset($package->getExtra()['class']) ||
!is_string($class))
{
throw new KeyNotFoundException("Module {$name} is missing a valid 'class' entry in its extra field.");
}
try {
$klass = new \ReflectionClass($class);
} catch (\LogicException $e) {
throw new ClassNotFoundException("Could not load class ${class} while registering module {$name}.");
} catch (\ReflectionException $e) {
throw new ClassNotFoundException("Could not find class ${class} while registering module {$name}.");
}
// Verify that the class is a module class.
$interface = Module::class;
if (!$klass->implementsInterface($interface)) {
throw new WrongTypeException("Class {$class} does not implement {$interface} while registering module {$name}.");
}
// Subscribe to the module's events.
$subscriptions = ModuleManager::getPackageSubscriptions($package);
foreach ($subscriptions as $s) {
$pattern = $s['pattern'];
$class = $s['class'];
$this->g->getEventManager()->subscribe($pattern, $class, $library);
$this->g->getEventManager()->subscribe($s, $class, $library);
}
// Run the module's onRegister handler.
$class::onRegister($this->g);
}
}
@@ -91,25 +112,27 @@ class ModuleManager
*/
public function unregister(string $library, PackageInterface $package)
{
$m = $this->g->getEntityManager()->getRepository(Module::class)->find($library);
$m = $this->g->getEntityManager()->getRepository(ModuleModel::class)->find($library);
if (!$m) {
throw new ModuleDoesNotExistException($library);
} else {
// TODO: handle error cases here.
$m->delete($this->g->getEntityManager());
$class = $package->getExtra()['class'];
// Unsubscribe the module's events.
$subscriptions = ModuleManager::getPackageSubscriptions($package);
foreach ($subscriptions as $s) {
$pattern = $s['pattern'];
$class = $s['class'];
try {
$this->g->getEventManager()->unsubscribe($pattern, $class, $library);
$this->g->getEventManager()->unsubscribe($s, $class, $library);
} catch (SubscriptionNotFoundException $e) {
// TODO: log this but continue on.
}
}
// Run the module's onUnregister handler.
$class::onUnregister($this->g);
}
}
@@ -118,7 +141,7 @@ class ModuleManager
* @return array<Module> Array of modules.
*/
public function getModules(): array {
return $this->g->getEntityManager()->getRepository(Module::class)->findAll();
return $this->g->getEntityManager()->getRepository(ModuleModel::class)->findAll();
}
/**
@@ -149,6 +172,7 @@ class ModuleManager
$currentSubscriptions = $this->g->getEventManager()->getSubscriptions();
foreach ($packages as $p) {
$name = $p->getName();
$class = $p->getExtra()['class'];
$expectedSubscriptions = ModuleManager::getPackageSubscriptions($p);
$currentSubscriptionsForThisPackage = array_filter($currentSubscriptions, function($s) use ($name) {
@@ -162,8 +186,8 @@ class ModuleManager
$found = false;
foreach ($currentSubscriptionsForThisPackage as $c) {
// Count the subscriptions for this
if ($c->getPattern() === $e['pattern'] &&
$c->getClass() === $e['class'] &&
if ($c->getPattern() === $e &&
$c->getClass() === $class &&
$c->getLibrary() === $p->getName())
{
$found = true;
@@ -171,8 +195,8 @@ class ModuleManager
}
}
if (!$found) {
$pattern = $e['pattern'];
$class = $e['class'];
$pattern = $e;
$class = $class;
array_push($result, "Error: Couldn't find subscription ({$pattern} => ${class}) for module {$name}.");
}
}
+34 -132
View File
@@ -9,21 +9,18 @@ use LotGD\Core\EventHandler;
use LotGD\Core\EventManager;
use LotGD\Core\EventSubscription;
use LotGD\Core\ModuleManager;
use LotGD\Core\Models\Module;
use LotGD\Core\Module;
use LotGD\Core\Exceptions\ModuleAlreadyExistsException;
use LotGD\Core\Exceptions\ModuleDoesNotExistException;
use LotGD\Core\Tests\ModelTestCase;
use Composer\Package\PackageInterface;
use Composer\Composer;
class ModuleManagerTestSubscriber implements EventHandler
{
public static function handleEvent(string $event, array $context) {}
}
class ModuleManagerTestAnotherSubscriber implements EventHandler
class ModuleManagerTestModule implements Module
{
public static function handleEvent(string $event, array $context) {}
public static function onRegister(Game $g) {}
public static function onUnregister(Game $g) {}
}
class ModuleManagerTest extends ModelTestCase
@@ -57,7 +54,7 @@ class ModuleManagerTest extends ModelTestCase
public function testGetModules()
{
$modules = $this->mm->getModules();
$this->assertContainsOnlyInstancesOf(Module::class, $modules);
$this->assertContainsOnlyInstancesOf(\LotGD\Core\Models\Module::class, $modules);
// This is a little fragile, but assertContains() doesn't seem to work.
$this->assertEquals(new \DateTime('2016-05-01'), $modules[0]->getCreatedAt());
@@ -75,7 +72,9 @@ class ModuleManagerTest extends ModelTestCase
public function testUnregisterWithNoEvents()
{
$package = $this->getMockForAbstractClass(PackageInterface::class);
$package->method('getExtra')->willReturn(array());
$package->method('getExtra')->willReturn(array(
'class' => ModuleManagerTestModule::class
));
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
@@ -92,20 +91,16 @@ class ModuleManagerTest extends ModelTestCase
public function testUnregisterWithEvents()
{
$subscriptions = array(
array(
'pattern' => '/pattern1/',
'class' => 'SomeClass1'
),
array(
'pattern' => '/pattern2/',
'class' => 'SomeClass2'
),
'/pattern1/',
'/pattern2/'
);
$class = ModuleManagerTestModule::class;
$library = 'lotgd/tests';
$package = $this->getMockForAbstractClass(PackageInterface::class);
$package->method('getExtra')->willReturn(array(
'class' => $class,
'subscriptions' => $subscriptions
));
@@ -116,46 +111,8 @@ class ModuleManagerTest extends ModelTestCase
$eventManager->expects($this->exactly(2))
->method('unsubscribe')
->withConsecutive(
array($this->equalTo($subscriptions[0]['pattern']), $this->equalTo($subscriptions[0]['class']), $library),
array($this->equalTo($subscriptions[1]['pattern']), $this->equalTo($subscriptions[1]['class']), $library)
);
$this->game->method('getEventManager')->willReturn($eventManager);
$this->mm->unregister($library, $package);
$modules = $this->mm->getModules();
$this->assertEmpty($modules);
}
public function testUnregisterWithInvalidEvents()
{
$subscriptions = array(
array(
'pattern' => '/pattern1/',
'class' => 'SomeClass1'
),
array(
// Invalid subscription.
'crazy' => 'making'
),
);
$library = 'lotgd/tests';
$package = $this->getMockForAbstractClass(PackageInterface::class);
$package->method('getExtra')->willReturn(array(
'subscriptions' => $subscriptions
));
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('unsubscribe'))
->getMock();
$eventManager->expects($this->exactly(1))
->method('unsubscribe')
->withConsecutive(
array($this->equalTo($subscriptions[0]['pattern']), $this->equalTo($subscriptions[0]['class']), $library)
array($this->equalTo($subscriptions[0]), $this->equalTo($class), $library),
array($this->equalTo($subscriptions[1]), $this->equalTo($class), $library)
);
$this->game->method('getEventManager')->willReturn($eventManager);
@@ -169,7 +126,9 @@ class ModuleManagerTest extends ModelTestCase
public function testRegisterWithNoEvents()
{
$package = $this->getMockForAbstractClass(PackageInterface::class);
$package->method('getExtra')->willReturn(array());
$package->method('getExtra')->willReturn(array(
'class' => ModuleManagerTestModule::class
));
$library = 'lotgd/tests2';
@@ -193,20 +152,16 @@ class ModuleManagerTest extends ModelTestCase
public function testRegisterWithEvents()
{
$subscriptions = array(
array(
'pattern' => '/pattern1/',
'class' => ModuleManagerTestSubscriber::class
),
array(
'pattern' => '/pattern2/',
'class' => ModuleManagerTestAnotherSubscriber::class
),
'/pattern1/',
'/pattern2/',
);
$class = ModuleManagerTestModule::class;
$library = 'lotgd/tests2';
$package = $this->getMockForAbstractClass(PackageInterface::class);
$package->method('getExtra')->willReturn(array(
'class' => $class,
'subscriptions' => $subscriptions
));
@@ -217,51 +172,8 @@ class ModuleManagerTest extends ModelTestCase
$eventManager->expects($this->exactly(2))
->method('subscribe')
->withConsecutive(
array($this->equalTo($subscriptions[0]['pattern']), $this->equalTo($subscriptions[0]['class']), $library),
array($this->equalTo($subscriptions[1]['pattern']), $this->equalTo($subscriptions[1]['class']), $library)
);
$this->game->method('getEventManager')->willReturn($eventManager);
$this->mm->register($library, $package);
$modules = $this->mm->getModules();
// Timestamps should be within 5 seconds :)
$timeDiff = (new \DateTime())->getTimestamp() - $modules[1]->getCreatedAt()->getTimestamp();
$this->assertLessThanOrEqual(5, $timeDiff);
$this->assertGreaterThanOrEqual(-5, $timeDiff);
$this->assertEquals($library, $modules[1]->getLibrary());
}
public function testRegisterWithInvalidEvents()
{
$subscriptions = array(
array(
'pattern' => '/pattern1/',
'class' => ModuleManagerTestSubscriber::class
),
array(
// Invalid subscription.
'crazy' => 'making'
),
);
$library = 'lotgd/tests2';
$package = $this->getMockForAbstractClass(PackageInterface::class);
$package->method('getExtra')->willReturn(array(
'subscriptions' => $subscriptions
));
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('subscribe'))
->getMock();
$eventManager->expects($this->exactly(1))
->method('subscribe')
->withConsecutive(
array($this->equalTo($subscriptions[0]['pattern']), $this->equalTo($subscriptions[0]['class']), $library)
array($this->equalTo($subscriptions[0]), $this->equalTo($class), $library),
array($this->equalTo($subscriptions[1]), $this->equalTo($class), $library)
);
$this->game->method('getEventManager')->willReturn($eventManager);
@@ -283,16 +195,14 @@ class ModuleManagerTest extends ModelTestCase
$class = "LotGD\\Core\\Tests\\EventManagerTestInstalledSubscriber";
$library = 'lotgd/tests';
$subscriptions = array(
array(
'pattern' => $pattern,
'class' => $class
),
$pattern,
);
$p1 = $this->getMockBuilder(PackageInterface::class)
->getMock();
$p1->method('getName')->willReturn($library);
$p1->method('getExtra')->willReturn(array(
'class' => $class,
'subscriptions' => $subscriptions
));
@@ -331,20 +241,15 @@ class ModuleManagerTest extends ModelTestCase
$class = "LotGD\\Core\\Tests\\EventManagerTestInstalledSubscriber";
$library = 'lotgd/tests';
$subscriptions = array(
array(
'pattern' => $pattern,
'class' => $class
),
array(
'pattern' => '/another pattern/',
'class' => $class
)
$pattern,
'/another pattern/',
);
$p1 = $this->getMockBuilder(PackageInterface::class)
->getMock();
$p1->method('getName')->willReturn($library);
$p1->method('getExtra')->willReturn(array(
'class' => $class,
'subscriptions' => $subscriptions
));
@@ -419,16 +324,14 @@ class ModuleManagerTest extends ModelTestCase
$library = 'lotgd/tests';
$subscriptions = array(
array(
'pattern' => $pattern,
'class' => $class
),
$pattern
);
$p1 = $this->getMockBuilder(PackageInterface::class)
->getMock();
$p1->method('getName')->willReturn($library);
$p1->method('getExtra')->willReturn(array(
'class' => $class,
'subscriptions' => $subscriptions
));
@@ -436,6 +339,7 @@ class ModuleManagerTest extends ModelTestCase
->getMock();
$p2->method('getName')->willReturn('lotgd/tests-another');
$p2->method('getExtra')->willReturn(array(
'class' => $class,
'subscriptions' => $subscriptions
));
@@ -475,16 +379,14 @@ class ModuleManagerTest extends ModelTestCase
$library = 'lotgd/tests';
$subscriptions = array(
array(
'pattern' => $pattern,
'class' => $class
),
$pattern
);
$p1 = $this->getMockBuilder(PackageInterface::class)
->getMock();
$p1->method('getName')->willReturn($library);
$p1->method('getExtra')->willReturn(array(
'class' => $class,
'subscriptions' => $subscriptions
));
@@ -509,7 +411,7 @@ class ModuleManagerTest extends ModelTestCase
->setMethods(array('getPattern', 'getClass', 'getLibrary'))
->getMock();
$s2->method('getPattern')->willReturn('/some pattern/');
$s2->method('getClass')->willReturn('SomeClass');
$s2->method('getClass')->willReturn(ModuleManagerTestModule::class);
$s2->method('getLibrary')->willReturn($library);
$installedSubscriptions = array($s1, $s2);