Merge branch 'release/0.8.0'
This commit is contained in:
+32
-5
@@ -14,7 +14,8 @@
|
|||||||
*/
|
*/
|
||||||
class Mustache {
|
class Mustache {
|
||||||
|
|
||||||
const VERSION = '0.7.1';
|
const VERSION = '0.8.0';
|
||||||
|
const SPEC_VERSION = '1.1.2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should this Mustache throw exceptions when it finds unexpected tags?
|
* Should this Mustache throw exceptions when it finds unexpected tags?
|
||||||
@@ -238,7 +239,10 @@ class Mustache {
|
|||||||
|
|
||||||
// regular section
|
// regular section
|
||||||
case '#':
|
case '#':
|
||||||
if ($this->_varIsIterable($val)) {
|
// higher order sections
|
||||||
|
if ($this->_varIsCallable($val)) {
|
||||||
|
$rendered_content = $this->_renderTemplate(call_user_func($val, $content));
|
||||||
|
} else if ($this->_varIsIterable($val)) {
|
||||||
foreach ($val as $local_context) {
|
foreach ($val as $local_context) {
|
||||||
$this->_pushContext($local_context);
|
$this->_pushContext($local_context);
|
||||||
$rendered_content .= $this->_renderTemplate($content);
|
$rendered_content .= $this->_renderTemplate($content);
|
||||||
@@ -615,7 +619,8 @@ class Mustache {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function _renderEscaped($tag_name, $leading, $trailing) {
|
protected function _renderEscaped($tag_name, $leading, $trailing) {
|
||||||
return $leading . htmlentities($this->_getVariable($tag_name), ENT_COMPAT, $this->_charset) . $trailing;
|
$rendered = htmlentities($this->_renderUnescaped($tag_name, '', ''), ENT_COMPAT, $this->_charset);
|
||||||
|
return $leading . $rendered . $trailing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -647,7 +652,13 @@ class Mustache {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function _renderUnescaped($tag_name, $leading, $trailing) {
|
protected function _renderUnescaped($tag_name, $leading, $trailing) {
|
||||||
return $leading . $this->_getVariable($tag_name) . $trailing;
|
$val = $this->_getVariable($tag_name);
|
||||||
|
|
||||||
|
if ($this->_varIsCallable($val)) {
|
||||||
|
$val = $this->_renderTemplate(call_user_func($val));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $leading . $val . $trailing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -829,6 +840,23 @@ class Mustache {
|
|||||||
protected function _varIsIterable($var) {
|
protected function _varIsIterable($var) {
|
||||||
return $var instanceof Traversable || (is_array($var) && !array_diff_key($var, array_keys(array_keys($var))));
|
return $var instanceof Traversable || (is_array($var) && !array_diff_key($var, array_keys(array_keys($var))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Higher order sections helper: tests whether the section $var is a valid callback.
|
||||||
|
*
|
||||||
|
* In Mustache.php, a variable is considered 'callable' if the variable is:
|
||||||
|
*
|
||||||
|
* 1. an anonymous function.
|
||||||
|
* 2. an object and the name of a public function, i.e. `array($SomeObject, 'methodName')`
|
||||||
|
* 3. a class name and the name of a public static function, i.e. `array('SomeClass', 'methodName')`
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @param mixed $var
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function _varIsCallable($var) {
|
||||||
|
return !is_string($var) && is_callable($var);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -859,4 +887,3 @@ class MustacheException extends Exception {
|
|||||||
const UNKNOWN_PRAGMA = 4;
|
const UNKNOWN_PRAGMA = 4;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once '../Mustache.php';
|
||||||
|
|
||||||
|
class MustacheHigherOrderSectionsTest extends PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->foo = new Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAnonymousFunctionSectionCallback() {
|
||||||
|
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||||
|
$this->markTestSkipped('Unable to test anonymous function section callbacks in PHP < 5.3');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->foo->wrapper = function($text) {
|
||||||
|
return sprintf('<div class="anonymous">%s</div>', $text);
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
sprintf('<div class="anonymous">%s</div>', $this->foo->name),
|
||||||
|
$this->foo->render('{{#wrapper}}{{name}}{{/wrapper}}')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSectionCallback() {
|
||||||
|
$this->assertEquals(sprintf('%s', $this->foo->name), $this->foo->render('{{name}}'));
|
||||||
|
$this->assertEquals(sprintf('<em>%s</em>', $this->foo->name), $this->foo->render('{{#wrap}}{{name}}{{/wrap}}'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRuntimeSectionCallback() {
|
||||||
|
$this->foo->double_wrap = array($this->foo, 'wrapWithBoth');
|
||||||
|
$this->assertEquals(
|
||||||
|
sprintf('<strong><em>%s</em></strong>', $this->foo->name),
|
||||||
|
$this->foo->render('{{#double_wrap}}{{name}}{{/double_wrap}}')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStaticSectionCallback() {
|
||||||
|
$this->foo->trimmer = array(get_class($this->foo), 'staticTrim');
|
||||||
|
$this->assertEquals($this->foo->name, $this->foo->render('{{#trimmer}} {{name}} {{/trimmer}}'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testViewArraySectionCallback() {
|
||||||
|
$data = array(
|
||||||
|
'name' => 'Bob',
|
||||||
|
'trim' => array(get_class($this->foo), 'staticTrim'),
|
||||||
|
);
|
||||||
|
$this->assertEquals($data['name'], $this->foo->render('{{#trim}} {{name}} {{/trim}}', $data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testViewArrayAnonymousSectionCallback() {
|
||||||
|
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||||
|
$this->markTestSkipped('Unable to test anonymous function section callbacks in PHP < 5.3');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$data = array(
|
||||||
|
'name' => 'Bob',
|
||||||
|
'wrap' => function($text) {
|
||||||
|
return sprintf('[[%s]]', $text);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
sprintf('[[%s]]', $data['name']),
|
||||||
|
$this->foo->render('{{#wrap}}{{name}}{{/wrap}}', $data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMonsters() {
|
||||||
|
$frank = new Monster();
|
||||||
|
$frank->title = 'Dr.';
|
||||||
|
$frank->name = 'Frankenstein';
|
||||||
|
$this->assertEquals('Dr. Frankenstein', $frank->render());
|
||||||
|
|
||||||
|
$dracula = new Monster();
|
||||||
|
$dracula->title = 'Count';
|
||||||
|
$dracula->name = 'Dracula';
|
||||||
|
$this->assertEquals('Count Dracula', $dracula->render());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Foo extends Mustache {
|
||||||
|
public $name = 'Justin';
|
||||||
|
public $lorem = 'Lorem ipsum dolor sit amet,';
|
||||||
|
public $wrap;
|
||||||
|
|
||||||
|
public function __construct($template = null, $view = null, $partials = null) {
|
||||||
|
$this->wrap = array($this, 'wrapWithEm');
|
||||||
|
parent::__construct($template, $view, $partials);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function wrapWithEm($text) {
|
||||||
|
return sprintf('<em>%s</em>', $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function wrapWithStrong($text) {
|
||||||
|
return sprintf('<strong>%s</strong>', $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function wrapWithBoth($text) {
|
||||||
|
return self::wrapWithStrong(self::wrapWithEm($text));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function staticTrim($text) {
|
||||||
|
return trim($text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Monster extends Mustache {
|
||||||
|
public $_template = '{{#title}}{{title}} {{/title}}{{name}}';
|
||||||
|
public $title;
|
||||||
|
public $name;
|
||||||
|
}
|
||||||
+49
-20
@@ -57,20 +57,38 @@ class MustacheSpecTest extends PHPUnit_Framework_TestCase {
|
|||||||
$this->assertEquals($expected, $m->render(), $desc);
|
$this->assertEquals($expected, $m->render(), $desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * @group lambdas
|
* @group lambdas
|
||||||
// * @dataProvider loadLambdasSpec
|
* @dataProvider loadLambdasSpec
|
||||||
// */
|
*/
|
||||||
// public function testLambdasSpec($desc, $template, $data, $partials, $expected) {
|
public function testLambdasSpec($desc, $template, $data, $partials, $expected) {
|
||||||
// $this->markTestSkipped("Lambdas for PHP haven't made it into the spec yet, so we'll skip them to avoid a bajillion failed tests.");
|
if (!version_compare(PHP_VERSION, '5.3.0', '>=')) {
|
||||||
//
|
$this->markTestSkipped('Unable to test Lambdas spec with PHP < 5.3.');
|
||||||
// if (!version_compare(PHP_VERSION, '5.3.0', '>=')) {
|
}
|
||||||
// $this->markTestSkipped('Unable to test Lambdas spec with PHP < 5.3.');
|
|
||||||
// }
|
$data = $this->prepareLambdasSpec($data);
|
||||||
//
|
$m = new Mustache($template, $data, $partials);
|
||||||
// $m = new Mustache($template, $data, $partials);
|
$this->assertEquals($expected, $m->render(), $desc);
|
||||||
// $this->assertEquals($expected, $m->render(), $desc);
|
}
|
||||||
// }
|
|
||||||
|
/**
|
||||||
|
* Extract and lambdafy any 'lambda' values found in the $data array.
|
||||||
|
*/
|
||||||
|
protected function prepareLambdasSpec($data) {
|
||||||
|
foreach ($data as $key => $val) {
|
||||||
|
if ($key === 'lambda') {
|
||||||
|
if (!isset($val['php'])) {
|
||||||
|
$this->markTestSkipped(sprintf('PHP lambda test not implemented for this test.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$func = $val['php'];
|
||||||
|
$data[$key] = function($text = null) use ($func) { return eval($func); };
|
||||||
|
} else if (is_array($val)) {
|
||||||
|
$data[$key] = $this->prepareLambdasSpec($val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group partials
|
* @group partials
|
||||||
@@ -106,9 +124,9 @@ class MustacheSpecTest extends PHPUnit_Framework_TestCase {
|
|||||||
return $this->loadSpec('inverted');
|
return $this->loadSpec('inverted');
|
||||||
}
|
}
|
||||||
|
|
||||||
// public function loadLambdasSpec() {
|
public function loadLambdasSpec() {
|
||||||
// return $this->loadSpec('lambdas');
|
return $this->loadSpec('~lambdas');
|
||||||
// }
|
}
|
||||||
|
|
||||||
public function loadPartialsSpec() {
|
public function loadPartialsSpec() {
|
||||||
return $this->loadSpec('partials');
|
return $this->loadSpec('partials');
|
||||||
@@ -133,12 +151,23 @@ class MustacheSpecTest extends PHPUnit_Framework_TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
$yaml = new sfYamlParser();
|
$yaml = new sfYamlParser();
|
||||||
|
$file = file_get_contents($filename);
|
||||||
|
|
||||||
$spec = $yaml->parse(file_get_contents($filename));
|
// @hack: pre-process the 'lambdas' spec so the Symfony YAML parser doesn't complain.
|
||||||
|
if ($name === '~lambdas') {
|
||||||
|
$file = str_replace(" !code\n", "\n", $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
$spec = $yaml->parse($file);
|
||||||
foreach ($spec['tests'] as $test) {
|
foreach ($spec['tests'] as $test) {
|
||||||
$data[] = array($test['name'] . ': ' . $test['desc'], $test['template'], $test['data'], isset($test['partials']) ? $test['partials'] : array(), $test['expected']);
|
$data[] = array(
|
||||||
|
$test['name'] . ': ' . $test['desc'],
|
||||||
|
$test['template'],
|
||||||
|
$test['data'],
|
||||||
|
isset($test['partials']) ? $test['partials'] : array(),
|
||||||
|
$test['expected'],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user