Skip to content

Commit

Permalink
Merge pull request #56 from dg/extension
Browse files Browse the repository at this point in the history
added PresentersExtension WIP
  • Loading branch information
dg committed Jan 31, 2015
2 parents f8f283b + 1bec5ba commit f06a689
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 15 deletions.
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -29,6 +29,7 @@
"require-dev": {
"nette/tester": "~1.3",
"nette/forms": "~2.2",
"nette/robot-loader": "~2.2",
"latte/latte": "~2.3"
},
"conflict": {
Expand Down
30 changes: 19 additions & 11 deletions src/Application/PresenterFactory.php
Expand Up @@ -29,10 +29,14 @@ class PresenterFactory extends Nette\Object implements IPresenterFactory
/** @var Nette\DI\Container */
private $container;

/** @var bool */
private $autoRebuild;

public function __construct(Nette\DI\Container $container)

public function __construct(Nette\DI\Container $container, $autoRebuild = FALSE)
{
$this->container = $container;
$this->autoRebuild = $autoRebuild;
}


Expand All @@ -44,21 +48,25 @@ public function __construct(Nette\DI\Container $container)
public function createPresenter($name)
{
$class = $this->getPresenterClass($name);
if (count($services = $this->container->findByType($class)) === 1) {
$presenter = $this->container->createService($services[0]);
$tags = $this->container->findByTag(Nette\DI\Extensions\InjectExtension::TAG_INJECT);
if (empty($tags[$services[0]])) {
$this->container->callInjects($presenter);
$services = array_keys($this->container->findByTag('nette.presenter'), $class);
if (count($services) > 1) {
throw new InvalidPresenterException("Multiple services of type $class found: " . implode(', ', $services) . '.');

} elseif (!$services) {
if ($this->autoRebuild) {
$rc = new \ReflectionClass($this->container);
@unlink($rc->getFileName()); // @ file may not exists
}
} else {

$presenter = $this->container->createInstance($class);
$this->container->callInjects($presenter);
if ($presenter instanceof UI\Presenter && $presenter->invalidLinkMode === NULL) {
$presenter->invalidLinkMode = $this->container->parameters['debugMode'] ? UI\Presenter::INVALID_LINK_WARNING : UI\Presenter::INVALID_LINK_SILENT;
}
return $presenter;
}

if ($presenter instanceof UI\Presenter && $presenter->invalidLinkMode === NULL) {
$presenter->invalidLinkMode = $this->container->parameters['debugMode'] ? UI\Presenter::INVALID_LINK_WARNING : UI\Presenter::INVALID_LINK_SILENT;
}
return $presenter;
return $this->container->createService($services[0]);
}


Expand Down
86 changes: 82 additions & 4 deletions src/Bridges/ApplicationDI/ApplicationExtension.php
Expand Up @@ -7,7 +7,8 @@

namespace Nette\Bridges\ApplicationDI;

use Nette;
use Nette,
Nette\Application\UI;


/**
Expand All @@ -21,13 +22,22 @@ class ApplicationExtension extends Nette\DI\CompilerExtension
'debugger' => TRUE,
'errorPresenter' => 'Nette:Error',
'catchExceptions' => NULL,
'mapping' => NULL
'mapping' => NULL,
'scanDirs' => array(),
'scanComposer' => NULL,
'scanFilter' => 'Presenter',
);

/** @var bool */
private $debugMode;

public function __construct($debugMode = FALSE)

public function __construct($debugMode = FALSE, array $scanDirs = NULL)
{
$this->defaults['scanDirs'] = (array) $scanDirs;
$this->defaults['scanComposer'] = class_exists('Composer\Autoload\ClassLoader');
$this->defaults['catchExceptions'] = !$debugMode;
$this->debugMode = $debugMode;
}


Expand All @@ -48,7 +58,7 @@ public function loadConfiguration()

$presenterFactory = $container->addDefinition($this->prefix('presenterFactory'))
->setClass('Nette\Application\IPresenterFactory')
->setFactory('Nette\Application\PresenterFactory');
->setFactory('Nette\Application\PresenterFactory', array(1 => $this->debugMode));

if ($config['mapping']) {
$presenterFactory->addSetup('setMapping', array($config['mapping']));
Expand All @@ -65,4 +75,72 @@ public function loadConfiguration()
}
}


public function beforeCompile()
{
$this->validateConfig($this->defaults);
$container = $this->getContainerBuilder();
$all = array();

foreach ($container->findByType('Nette\Application\IPresenter', FALSE) as $name) {
$def = $container->getDefinition($name);
$all[strtolower($def->class)] = $def;
}

$counter = 0;
foreach ($this->findPresenters() as $class) {
if (empty($all[$tmp = strtolower($class)])) {
$all[$tmp] = $container->addDefinition($this->prefix(++$counter))->setClass($class);
}
}

foreach ($all as $def) {
$def->setInject(TRUE)->setAutowired(FALSE)->addTag('nette.presenter', $def->class);
if (is_subclass_of($def->class, 'Nette\Application\UI\Presenter')) {
$def->addSetup('$invalidLinkMode', array(
$this->debugMode ? UI\Presenter::INVALID_LINK_WARNING : UI\Presenter::INVALID_LINK_SILENT
));
}
}
}


/** @return string[] */
private function findPresenters()
{
$config = $this->getConfig();
$classes = array();

if ($config['scanDirs']) {
$robot = new Nette\Loaders\RobotLoader;
$robot->setCacheStorage(new Nette\Caching\Storages\DevNullStorage);
$robot->addDirectory($config['scanDirs']);
$robot->acceptFiles = '*' . $config['scanFilter'] . '*.php';
$robot->rebuild();
$classes = array_keys($robot->getIndexedClasses());
}

if ($config['scanComposer']) {
$rc = new \ReflectionClass('Composer\Autoload\ClassLoader');
$classFile = dirname($rc->getFileName()) . '/autoload_classmap.php';
if (is_file($classFile)) {
$this->getContainerBuilder()->addDependency($classFile);
$classes = array_merge($classes, array_keys(call_user_func(function($path) {
return require $path;
}, $classFile)));
}
}

$presenters = array();
foreach (array_unique($classes) as $class) {
if (strpos($class, $config['scanFilter']) !== FALSE && class_exists($class)
&& ($rc = new \ReflectionClass($class)) && $rc->implementsInterface('Nette\Application\IPresenter')
&& !$rc->isAbstract()
) {
$presenters[] = $rc->getName();
}
}
return $presenters;
}

}
91 changes: 91 additions & 0 deletions tests/Application.DI/ApplicationExtension.scan.phpt
@@ -0,0 +1,91 @@
<?php

/**
* Test: ApplicationExtension
*/

use Nette\DI,
Nette\Bridges\ApplicationDI\ApplicationExtension,
Tester\Assert;


require __DIR__ . '/../bootstrap.php';
require __DIR__ . '/files/MyPresenter.php';


test(function() {
$compiler = new DI\Compiler;
$compiler->addExtension('application', new ApplicationExtension);

$builder = $compiler->getContainerBuilder();
$builder->addDefinition('myRouter')->setClass('Nette\Application\Routers\SimpleRouter');
$builder->addDefinition('myHttpRequest')->setFactory('Nette\Http\Request', array(new DI\Statement('Nette\Http\UrlScript')));
$builder->addDefinition('myHttpResponse')->setClass('Nette\Http\Response');
$code = $compiler->compile(array(
'application' => array('debugger' => FALSE),
), 'Container1');
eval($code);

$container = new Container1;
$tags = $container->findByTag('nette.presenter');
Assert::count( 1, array_keys($tags, 'NetteModule\ErrorPresenter') );
Assert::count( 1, array_keys($tags, 'NetteModule\MicroPresenter') );
Assert::count( 0, array_keys($tags, 'Nette\Application\UI\Presenter') );
});


test(function() {
$compiler = new DI\Compiler;
$compiler->addExtension('application', new ApplicationExtension);

$builder = $compiler->getContainerBuilder();
$builder->addDefinition('myRouter')->setClass('Nette\Application\Routers\SimpleRouter');
$builder->addDefinition('myHttpRequest')->setFactory('Nette\Http\Request', array(new DI\Statement('Nette\Http\UrlScript')));
$builder->addDefinition('myHttpResponse')->setClass('Nette\Http\Response');
$code = $compiler->compile(array(
'application' => array(
'scanDirs' => array(__DIR__ . '/files'),
'debugger' => FALSE,
),
), 'Container2');
eval($code);

$container = new Container2;
$tags = $container->findByTag('nette.presenter');
Assert::count( 1, array_keys($tags, 'BasePresenter') );
Assert::count( 1, array_keys($tags, 'Presenter1') );
Assert::count( 1, array_keys($tags, 'Presenter2') );
});


test(function() {
$compiler = new DI\Compiler;
$compiler->addExtension('application', new ApplicationExtension(FALSE, array(__DIR__ . '/files')));

$builder = $compiler->getContainerBuilder();
$builder->addDefinition('myRouter')->setClass('Nette\Application\Routers\SimpleRouter');
$builder->addDefinition('myHttpRequest')->setFactory('Nette\Http\Request', array(new DI\Statement('Nette\Http\UrlScript')));
$builder->addDefinition('myHttpResponse')->setClass('Nette\Http\Response');
$loader = new DI\Config\Loader;
$config = $loader->load(Tester\FileMock::create('
application:
debugger: no
services:
-
factory: Presenter1
setup:
- setView(test)
', 'neon'));
$code = $compiler->compile($config, 'Container3');
eval($code);

$container = new Container3;
$tags = $container->findByTag('nette.presenter');
Assert::count( 1, array_keys($tags, 'BasePresenter') );
Assert::count( 1, array_keys($tags, 'Presenter1') );
Assert::count( 1, array_keys($tags, 'Presenter2') );

$tmp = array_keys($tags, 'Presenter1');
Assert::same( 'test', $container->getService($tmp[0])->getView() );
});
12 changes: 12 additions & 0 deletions tests/Application.DI/files/MyPresenter.php
@@ -0,0 +1,12 @@
<?php


class BasePresenter extends Nette\Application\UI\Presenter
{}


class Presenter1 extends BasePresenter
{}

class Presenter2 extends BasePresenter
{}

0 comments on commit f06a689

Please sign in to comment.