Merge remote branch 'kriswallsmith/assetic/runtime-is-funtime'

* kriswallsmith/assetic/runtime-is-funtime:
  [AsseticBundle] fixed router and controller
  [AsseticBundle] removed fake front controller from URL before creating route
  [AsseticBundle] updated twig integration to check debug mode at runtime rather than compile time since twig cannot vary its cache by debug mode
This commit is contained in:
Fabien Potencier 2011-04-21 18:38:03 +02:00
commit c67dd2bb68
17 changed files with 221 additions and 228 deletions

View File

@ -12,6 +12,7 @@
namespace Symfony\Bundle\AsseticBundle\Controller;
use Assetic\Asset\AssetCache;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\LazyAssetManager;
use Assetic\Cache\CacheInterface;
use Symfony\Component\HttpFoundation\Request;
@ -36,13 +37,17 @@ class AsseticController
$this->cache = $cache;
}
public function render($name)
public function render($name, $pos = null)
{
if (!$this->am->has($name)) {
throw new NotFoundHttpException('Asset Not Found');
throw new NotFoundHttpException(sprintf('The "%s" asset could not be found.', $name));
}
$asset = $this->am->get($name);
if (null !== $pos && !$asset = $this->findAssetLeaf($asset, $pos)) {
throw new NotFoundHttpException(sprintf('The "%s" asset does not include a leaf at position %d.', $name, $pos));
}
$asset = $this->getAsset($name);
$response = $this->createResponse();
// last-modified
@ -63,7 +68,7 @@ class AsseticController
return $response;
}
$response->setContent($asset->dump());
$response->setContent($this->cachifyAsset($asset)->dump());
return $response;
}
@ -73,8 +78,17 @@ class AsseticController
return new Response();
}
protected function getAsset($name)
protected function cachifyAsset(AssetInterface $asset)
{
return new AssetCache($this->am->get($name), $this->cache);
return new AssetCache($asset, $this->cache);
}
private function findAssetLeaf(AssetInterface $asset, $pos)
{
$leaves = array_values(iterator_to_array($asset));
if (isset($leaves[$pos])) {
return $leaves[$pos];
}
}
}

View File

@ -74,12 +74,10 @@ class AsseticExtension extends Extension
// choose dynamic or static
if ($parameterBag->resolveValue($parameterBag->get('assetic.use_controller'))) {
$loader->load('controller.xml');
$container->setParameter('assetic.twig_extension.class', '%assetic.twig_extension.dynamic.class%');
$container->getDefinition('assetic.helper.dynamic')->addTag('templating.helper', array('alias' => 'assetic'));
$container->removeDefinition('assetic.helper.static');
} else {
$loader->load('asset_writer.xml');
$container->setParameter('assetic.twig_extension.class', '%assetic.twig_extension.static.class%');
$container->getDefinition('assetic.helper.static')->addTag('templating.helper', array('alias' => 'assetic'));
$container->removeDefinition('assetic.helper.dynamic');
}

View File

@ -15,23 +15,19 @@ use Assetic\Asset\AssetInterface;
use Assetic\Factory\Worker\WorkerInterface;
/**
* Prepends a fake front controller to every asset's target URL.
*
* This worker should only be added when the use_controller configuration
* option is true. It changes the target URL to include the front controller.
* Prepends a fake front controller so the asset knows where it is-ish.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class PrependControllerWorker implements WorkerInterface
class UseControllerWorker implements WorkerInterface
{
const CONTROLLER = 'front_controller/';
public function process(AssetInterface $asset)
{
$targetUrl = $asset->getTargetUrl();
if ($targetUrl && '/' != $targetUrl[0] && 0 !== strpos($targetUrl, self::CONTROLLER)) {
$asset->setTargetUrl(self::CONTROLLER.$targetUrl);
if ($targetUrl && '/' != $targetUrl[0] && 0 !== strpos($targetUrl, '_controller/')) {
$asset->setTargetUrl('_controller/'.$targetUrl);
}
return $asset;
}
}

View File

@ -8,7 +8,7 @@
<parameter key="assetic.controller.class">Symfony\Bundle\AsseticBundle\Controller\AsseticController</parameter>
<parameter key="assetic.routing_loader.class">Symfony\Bundle\AsseticBundle\Routing\AsseticLoader</parameter>
<parameter key="assetic.cache.class">Assetic\Cache\FilesystemCache</parameter>
<parameter key="assetic.prepend_controller_worker.class">Symfony\Bundle\AsseticBundle\Factory\Worker\PrependControllerWorker</parameter>
<parameter key="assetic.use_controller_worker.class">Symfony\Bundle\AsseticBundle\Factory\Worker\UseControllerWorker</parameter>
</parameters>
<services>
@ -24,7 +24,7 @@
<service id="assetic.cache" class="%assetic.cache.class%" public="false">
<argument>%assetic.cache_dir%/assets</argument>
</service>
<service id="assetic.prepend_controller_worker" class="%assetic.prepend_controller_worker.class%" public="false">
<service id="assetic.use_controller_worker" class="%assetic.use_controller_worker.class%" public="false">
<tag name="assetic.factory_worker" />
</service>
</services>

View File

@ -5,8 +5,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="assetic.twig_extension.dynamic.class">Symfony\Bundle\AsseticBundle\Twig\DynamicExtension</parameter>
<parameter key="assetic.twig_extension.static.class">Symfony\Bundle\AsseticBundle\Twig\StaticExtension</parameter>
<parameter key="assetic.twig_extension.class">Symfony\Bundle\AsseticBundle\Twig\AsseticExtension</parameter>
<parameter key="assetic.twig_formula_loader.class">Assetic\Extension\Twig\TwigFormulaLoader</parameter>
</parameters>
@ -16,6 +15,7 @@
<tag name="assetic.templating.twig" />
<argument type="service" id="assetic.asset_factory" />
<argument>%assetic.debug%</argument>
<argument>%assetic.use_controller%</argument>
</service>
<service id="assetic.twig_formula_loader" class="%assetic.cached_formula_loader.class%" public="false">
<tag name="assetic.formula_loader" alias="twig" />

View File

@ -11,6 +11,7 @@
namespace Symfony\Bundle\AsseticBundle\Routing;
use Assetic\Asset\AssetInterface;
use Assetic\Factory\LazyAssetManager;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Config\Resource\FileResource;
@ -62,22 +63,56 @@ class AsseticLoader extends Loader
// routes
foreach ($this->am->getNames() as $name) {
$asset = $this->am->get($name);
$formula = $this->am->getFormula($name);
$defaults = array(
'_controller' => 'assetic.controller:render',
'name' => $name,
);
$this->loadRouteForAsset($routes, $asset, $name);
if ($extension = pathinfo($asset->getTargetUrl(), PATHINFO_EXTENSION)) {
$defaults['_format'] = $extension;
// add a route for each "leaf" in debug mode
if (isset($formula[2]['debug']) ? $formula[2]['debug'] : $this->am->isDebug()) {
$i = 0;
foreach ($asset as $leaf) {
$this->loadRouteForAsset($routes, $leaf, $name, $i++);
}
}
$routes->add('assetic_'.$name, new Route($asset->getTargetUrl(), $defaults));
}
return $routes;
}
/**
* Loads a route to serve an supplied asset.
*
* The fake front controller that {@link UseControllerWorker} adds to the
* target URL will be removed before set as a route pattern.
*
* @param RouteCollection $routes The route collection
* @param AssetInterface $asset The asset
* @param string $name The name to use
* @param integer $pos The leaf index
*/
private function loadRouteForAsset(RouteCollection $routes, AssetInterface $asset, $name, $pos = null)
{
$defaults = array(
'_controller' => 'assetic.controller:render',
'name' => $name,
'pos' => $pos,
);
// remove the fake front controller
$pattern = str_replace('_controller/', '', $asset->getTargetUrl());
if ($format = pathinfo($pattern, PATHINFO_EXTENSION)) {
$defaults['_format'] = $format;
}
$route = '_assetic_'.$name;
if (null !== $pos) {
$route .= '_'.$pos;
}
$routes->add($route, new Route($pattern, $defaults));
}
public function supports($resource, $type = null)
{
return 'assetic' == $type;

View File

@ -40,6 +40,6 @@ class DynamicAsseticHelper extends AsseticHelper
protected function getAssetUrl(AssetInterface $asset, $options = array())
{
return $this->routerHelper->generate('assetic_'.$options['name']);
return $this->routerHelper->generate('_assetic_'.$options['name']);
}
}

View File

@ -44,7 +44,7 @@ class FunctionalTest extends \PHPUnit_Framework_TestCase
}
/**
* @dataProvider provideDebugAndAssetCount
* @dataProvider provideAmDebugAndAssetCount
*/
public function testKernel($debug, $count)
{
@ -55,7 +55,7 @@ class FunctionalTest extends \PHPUnit_Framework_TestCase
}
/**
* @dataProvider provideDebugAndAssetCount
* @dataProvider provideRouterDebugAndAssetCount
*/
public function testRoutes($debug, $count)
{
@ -64,7 +64,7 @@ class FunctionalTest extends \PHPUnit_Framework_TestCase
$matches = 0;
foreach (array_keys($kernel->getContainer()->get('router')->getRouteCollection()->all()) as $name) {
if (0 === strpos($name, 'assetic_')) {
if (0 === strpos($name, '_assetic_')) {
++$matches;
}
}
@ -102,11 +102,18 @@ class FunctionalTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(2, count($crawler->filter('script[src$=".js"]')));
}
public function provideDebugAndAssetCount()
public function provideAmDebugAndAssetCount()
{
// totals include assets defined in both php and twig templates
return array(
array(true, 6),
array(true, 3),
array(false, 3),
);
}
public function provideRouterDebugAndAssetCount()
{
return array(
array(true, 9),
array(false, 3),
);
}

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticExtension as BaseAsseticExtension;
use Assetic\Factory\AssetFactory;
/**
* Assetic extension.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticExtension extends BaseAsseticExtension
{
private $useController;
public function __construct(AssetFactory $factory, $debug = false, $useController = false)
{
parent::__construct($factory, $debug);
$this->useController = $useController;
}
public function getTokenParsers()
{
return array(
new AsseticTokenParser($this->factory, 'javascripts', 'js/*.js', false, array('package')),
new AsseticTokenParser($this->factory, 'stylesheets', 'css/*.css', false, array('package')),
new AsseticTokenParser($this->factory, 'image', 'images/*', true, array('package')),
);
}
public function getGlobals()
{
$globals = parent::getGlobals();
$globals['assetic']['use_controller'] = $this->useController;
return $globals;
}
}

View File

@ -0,0 +1,57 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Asset\AssetInterface;
use Assetic\Extension\Twig\AsseticNode as BaseAsseticNode;
/**
* Assetic node.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticNode extends BaseAsseticNode
{
protected function compileAssetUrl(\Twig_Compiler $compiler, AssetInterface $asset, $name)
{
$compiler
->raw('isset($context[\'assetic\'][\'use_controller\']) && $context[\'assetic\'][\'use_controller\'] ? ')
->subcompile($this->getPathFunction($name))
->raw(' : ')
->subcompile($this->getAssetFunction($asset->getTargetUrl()))
;
}
private function getPathFunction($name)
{
return new \Twig_Node_Expression_Function(
new \Twig_Node_Expression_Name('path', $this->getLine()),
new \Twig_Node(array(new \Twig_Node_Expression_Constant('_assetic_'.$name, $this->getLine()))),
$this->getLine()
);
}
private function getAssetFunction($path)
{
$arguments = array(new \Twig_Node_Expression_Constant($path, $this->getLine()));
if ($this->hasAttribute('package')) {
$arguments[] = new \Twig_Node_Expression_Constant($this->getAttribute('package'), $this->getLine());
}
return new \Twig_Node_Expression_Function(
new \Twig_Node_Expression_Name('asset', $this->getLine()),
new \Twig_Node($arguments),
$this->getLine()
);
}
}

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Asset\AssetInterface;
use Assetic\Extension\Twig\AsseticTokenParser as BaseAsseticTokenParser;
/**
* Assetic token parser.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class AsseticTokenParser extends BaseAsseticTokenParser
{
protected function createNode(AssetInterface $asset, \Twig_NodeInterface $body, array $inputs, array $filters, $name, array $attributes = array(), $lineno = 0, $tag = null)
{
return new AsseticNode($asset, $body, $inputs, $filters, $name, $attributes, $lineno, $tag);
}
}

View File

@ -1,32 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticExtension;
use Assetic\Factory\AssetFactory;
/**
* The dynamic extension is used when use_controllers is enabled.
*
* @author Kris Wallsmith <kris.wallsmith@symfony.com>
*/
class DynamicExtension extends AsseticExtension
{
public function getTokenParsers()
{
return array(
new DynamicTokenParser($this->factory, 'javascripts', 'js/*.js', $this->debug, false, array('package')),
new DynamicTokenParser($this->factory, 'stylesheets', 'css/*.css', $this->debug, false, array('package')),
new DynamicTokenParser($this->factory, 'image', 'images/*', $this->debug, true, array('package')),
);
}
}

View File

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticNode;
/**
* The "dynamic" node uses a controller to render assets.
*
* @author Kris Wallsmith <kris.wallsmith@symfony.com>
*/
class DynamicNode extends AsseticNode
{
/**
* Renders the asset URL using Symfony's path() function.
*/
protected function getAssetUrlNode(\Twig_NodeInterface $body)
{
return new \Twig_Node_Expression_Function(
new \Twig_Node_Expression_Name('path', $body->getLine()),
new \Twig_Node(array(
new \Twig_Node_Expression_Constant('assetic_'.$this->getAttribute('name'), $body->getLine()),
)),
$body->getLine()
);
}
}

View File

@ -1,27 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticTokenParser;
/**
* Parses an Assetic tag.
*
* @author Kris Wallsmith <kris.wallsmith@symfony.com>
*/
class DynamicTokenParser extends AsseticTokenParser
{
static protected function createNode(\Twig_NodeInterface $body, array $inputs, array $filters, array $attributes, $lineno = 0, $tag = null)
{
return new DynamicNode($body, $inputs, $filters, $attributes, $lineno, $tag);
}
}

View File

@ -1,32 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticExtension;
use Assetic\Factory\AssetFactory;
/**
* The static extension is used when use_controllers is disabled.
*
* @author Kris Wallsmith <kris.wallsmith@symfony.com>
*/
class StaticExtension extends AsseticExtension
{
public function getTokenParsers()
{
return array(
new StaticTokenParser($this->factory, 'javascripts', 'js/*.js', $this->debug, false, array('package')),
new StaticTokenParser($this->factory, 'stylesheets', 'css/*.css', $this->debug, false, array('package')),
new StaticTokenParser($this->factory, 'image', 'images/*', $this->debug, true, array('package')),
);
}
}

View File

@ -1,37 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticNode;
/**
* The "static" node references a file in the web directory.
*
* @author Kris Wallsmith <kris.wallsmith@symfony.com>
*/
class StaticNode extends AsseticNode
{
/**
* Renders the asset URL using Symfony's asset() function.
*/
protected function getAssetUrlNode(\Twig_NodeInterface $body)
{
return new \Twig_Node_Expression_Function(
new \Twig_Node_Expression_Name('asset', $body->getLine()),
new \Twig_Node(array(
new \Twig_Node_Expression_Constant($this->getAttribute('output'), $body->getLine()),
new \Twig_Node_Expression_Constant($this->hasAttribute('package') ? $this->getAttribute('package') : null, $body->getLine()),
)),
$body->getLine()
);
}
}

View File

@ -1,27 +0,0 @@
<?php
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Symfony\Bundle\AsseticBundle\Twig;
use Assetic\Extension\Twig\AsseticTokenParser;
/**
* Parses an Assetic tag.
*
* @author Kris Wallsmith <kris.wallsmith@symfony.com>
*/
class StaticTokenParser extends AsseticTokenParser
{
static protected function createNode(\Twig_NodeInterface $body, array $inputs, array $filters, array $attributes, $lineno = 0, $tag = null)
{
return new StaticNode($body, $inputs, $filters, $attributes, $lineno, $tag);
}
}