From 939c58bbee6bd0f8056e7073d719893836fc47be Mon Sep 17 00:00:00 2001 From: fzerorubigd Date: Wed, 26 Feb 2014 20:26:51 +0330 Subject: [PATCH] Some test improvement and add an ArrayLoader --- src/Handlebars/Loader/ArrayLoader.php | 76 +++++++++++ src/Handlebars/Template.php | 176 ++++++++++++++++---------- tests/Xamin/HandlebarsTest.php | 54 +++++++- 3 files changed, 235 insertions(+), 71 deletions(-) create mode 100644 src/Handlebars/Loader/ArrayLoader.php diff --git a/src/Handlebars/Loader/ArrayLoader.php b/src/Handlebars/Loader/ArrayLoader.php new file mode 100644 index 0000000..09007b9 --- /dev/null +++ b/src/Handlebars/Loader/ArrayLoader.php @@ -0,0 +1,76 @@ + + * @copyright 2014 (c) f0ruD + * @license MIT + * @version GIT: $Id$ + * @link http://xamin.ir + */ + +namespace Handlebars\Loader; + +use Handlebars\Loader; + +/** + * Handlebars Template array Loader implementation. + * + * @category Xamin + * @package Handlebars + * @author fzerorubigd + * @copyright 2014 (c) f0ruD + * @license MIT + * @version Release: @package_version@ + * @link http://xamin.ir * + */ +class ArrayLoader implements Loader +{ + private $_templates; + + /** + * Create a new loader with associative array style + * + * @param array $templates the templates to load + */ + public function __construct(array $templates = array()) + { + $this->_templates = $templates; + } + + /** + * Add a template to list + * + * @param string $name template name + * @param string $template the template + * + * @return void + */ + public function addTemplate($name, $template) + { + $this->_templates[$name] = $template; + } + + /** + * Load a Template by name. + * + * @param string $name template name to load + * + * @throws \RuntimeException + * @return String + */ + public function load($name) + { + if (isset($this->_templates[$name])) { + return $this->_templates[$name]; + } + throw new \RuntimeException( + "Can not find the $name template" + ); + } +} \ No newline at end of file diff --git a/src/Handlebars/Template.php b/src/Handlebars/Template.php index cfbea32..0e8a777 100644 --- a/src/Handlebars/Template.php +++ b/src/Handlebars/Template.php @@ -243,6 +243,100 @@ class Template array_push($this->_stack, $topStack); } + /** + * Process handlebars section style + * + * @param Context $context current context + * @param array $current section node data + * + * @return mixed|string + */ + private function _handlebarsStyleSection(Context $context, $current) + { + $helpers = $this->handlebars->getHelpers(); + $sectionName = $current[Tokenizer::NAME]; + + if (isset($current[Tokenizer::END])) { + $source = substr( + $this->getSource(), + $current[Tokenizer::INDEX], + $current[Tokenizer::END] - $current[Tokenizer::INDEX] + ); + } else { + $source = ''; + } + $params = array( + $this, //First argument is this template + $context, //Second is current context + $current[Tokenizer::ARGS], //Arguments + $source + ); + + $return = call_user_func_array($helpers->$sectionName, $params); + if ($return instanceof String) { + return $this->handlebars->loadString($return)->render($context); + } else { + return $return; + } + } + + /** + * Process Mustache section style + * + * @param Context $context current context + * @param array $current section node data + * + * @throws \RuntimeException + * @return mixed|string + */ + private function _mustacheStyleSection(Context $context, $current) + { + $sectionName = $current[Tokenizer::NAME]; + + // fallback to mustache style each/with/for just if there is + // no argument at all. + try { + $sectionVar = $context->get($sectionName, true); + } catch (\InvalidArgumentException $e) { + throw new \RuntimeException( + $sectionName . ' is not registered as a helper' + ); + } + $buffer = ''; + if (is_array($sectionVar) || $sectionVar instanceof \Traversable) { + $isList = is_array($sectionVar) && + (array_keys($sectionVar) == range(0, count($sectionVar) - 1)); + $index = 0; + $lastIndex = $isList ? (count($sectionVar) - 1) : false; + + foreach ($sectionVar as $key => $d) { + $specialVariables = array( + '@index' => $index, + '@first' => ($index === 0), + '@last' => ($index === $lastIndex), + ); + if (!$isList) { + $specialVariables['@key'] = $key; + } + $context->pushSpecialVariables($specialVariables); + $context->push($d); + $buffer .= $this->render($context); + $context->pop(); + $context->popSpecialVariables(); + $index++; + } + } elseif (is_object($sectionVar)) { + //Act like with + $context->push($sectionVar); + $buffer = $this->render($context); + $context->pop(); + } elseif ($sectionVar) { + $buffer = $this->render($context); + } + + return $buffer; + } + /** * Process section nodes * @@ -257,70 +351,9 @@ class Template $helpers = $this->handlebars->getHelpers(); $sectionName = $current[Tokenizer::NAME]; if ($helpers->has($sectionName)) { - if (isset($current[Tokenizer::END])) { - $source = substr( - $this->getSource(), - $current[Tokenizer::INDEX], - $current[Tokenizer::END] - $current[Tokenizer::INDEX] - ); - } else { - $source = ''; - } - $params = array( - $this, //First argument is this template - $context, //Second is current context - $current[Tokenizer::ARGS], //Arguments - $source - ); - - $return = call_user_func_array($helpers->$sectionName, $params); - if ($return instanceof String) { - return $this->handlebars->loadString($return)->render($context); - } else { - return $return; - } + return $this->_handlebarsStyleSection($context, $current); } elseif (trim($current[Tokenizer::ARGS]) == '') { - // fallback to mustache style each/with/for just if there is - // no argument at all. - try { - $sectionVar = $context->get($sectionName, true); - } catch (\InvalidArgumentException $e) { - throw new \RuntimeException( - $sectionName . ' is not registered as a helper' - ); - } - $buffer = ''; - if (is_array($sectionVar) || $sectionVar instanceof \Traversable) { - $isList = is_array($sectionVar) && (array_keys($sectionVar) == range(0, count($sectionVar) - 1)); - $index = 0; - $lastIndex = $isList ? (count($sectionVar) - 1) : false; - - foreach ($sectionVar as $key => $d) { - $specialVariables = array( - '@index' => $index, - '@first' => ($index === 0), - '@last' => ($index === $lastIndex), - ); - if (!$isList) { - $specialVariables['@key'] = $key; - } - $context->pushSpecialVariables($specialVariables); - $context->push($d); - $buffer .= $this->render($context); - $context->pop(); - $context->popSpecialVariables(); - $index++; - } - } elseif (is_object($sectionVar)) { - //Act like with - $context->push($sectionVar); - $buffer = $this->render($context); - $context->pop(); - } elseif ($sectionVar) { - $buffer = $this->render($context); - } - - return $buffer; + return $this->_mustacheStyleSection($context, $current); } else { throw new \RuntimeException( $sectionName . ' is not registered as a helper' @@ -380,6 +413,7 @@ class Template $helpers = $this->getEngine()->getHelpers(); // Tokenizer doesn't process the args -if any- so be aware of that $name = explode(' ', $current[Tokenizer::NAME], 2); + return $helpers->has(reset($name)); } @@ -457,32 +491,34 @@ class Template return $value; } - + /** * Break an argument string into an array of strings * * @param string $string Argument String as passed to a helper * + * @throws \RuntimeException * @return array the argument list as an array */ public function parseArguments($string) { $args = array(); preg_match_all('#(?:[^\'"\[\]\s]|\[.+?\])+|(?assertEquals('Test', $engine->render('{{safeStringTest}}', array())); } + public function testInvalidHelperMustacheStyle() + { + $this->setExpectedException('RuntimeException'); + $loader = new \Handlebars\Loader\StringLoader(); + $engine = new \Handlebars\Handlebars(array('loader' => $loader)); + $engine->render('{{#NOTVALID}}XXX{{/NOTVALID}}', array()); + } + + public function testInvalidHelper() + { + $this->setExpectedException('RuntimeException'); + $loader = new \Handlebars\Loader\StringLoader(); + $engine = new \Handlebars\Handlebars(array('loader' => $loader)); + $engine->render('{{#NOTVALID argument}}XXX{{/NOTVALID}}', array()); + } + /** * Test mustache style loop and if */ @@ -412,6 +428,18 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase $this->assertEquals(1, count(glob($path . '/*'))); } + public function testArrayLoader() + { + $loader = new \Handlebars\Loader\ArrayLoader(array('test' => 'HELLO')); + $loader->addTemplate('another', 'GOODBYE'); + $engine = new \Handlebars\Handlebars(array('loader' => $loader)); + $this->assertEquals($engine->render('test', array()), 'HELLO'); + $this->assertEquals($engine->render('another', array()), 'GOODBYE'); + + $this->setExpectedException('RuntimeException'); + $engine->render('invalid-template', array()); + } + /** * Test file system loader */ @@ -477,6 +505,29 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase $this->assertEquals('test', $engine->render('{{>loader}}', array())); } + public function testPartial() + { + $loader = new \Handlebars\Loader\StringLoader(); + $partialLoader = new \Handlebars\Loader\ArrayLoader(array('test' => '{{key}}', 'bar' => 'its foo')); + $partialAliasses = array('foo' => 'bar'); + $engine = new \Handlebars\Handlebars( + array( + 'loader' => $loader, + 'partials_loader' => $partialLoader, + 'partials_alias' => $partialAliasses + ) + ); + + $this->assertEquals('HELLO', $engine->render('{{>test parameter}}', array('parameter' => array('key' => 'HELLO')))); + $this->assertEquals('its foo', $engine->render('{{>foo}}', array())); + $engine->registerPartial('foo-again', 'bar'); + $this->assertEquals('its foo', $engine->render('{{>foo-again}}', array())); + $engine->unRegisterPartial('foo-again'); + + $this->setExpectedException('RuntimeException'); + $engine->render('{{>foo-again}}', array()); + } + /** * test variable access */ @@ -588,11 +639,12 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase array('"arg1.[value 1]" arg2', array("arg1.[value 1]", 'arg2')), ); } + /** * Test Argument Parser * * @param string $arg_string argument text - * @param array $arg_ array data + * @param $expected_array * * @dataProvider argumentParserProvider *