Merge branch 'dev' into feature/higher-order-sections

This commit is contained in:
Justin Hileman
2010-08-18 09:50:01 -04:00
12 changed files with 201 additions and 61 deletions
+16 -18
View File
@@ -98,7 +98,7 @@ class Mustache {
self::PRAGMA_UNESCAPED self::PRAGMA_UNESCAPED
); );
protected $_localPragmas; protected $_localPragmas = array();
/** /**
* Mustache class constructor. * Mustache class constructor.
@@ -130,7 +130,7 @@ class Mustache {
public function __clone() { public function __clone() {
$this->_otag = '{{'; $this->_otag = '{{';
$this->_ctag = '}}'; $this->_ctag = '}}';
$this->_localPragmas = null; $this->_localPragmas = array();
if ($keys = array_keys($this->_context)) { if ($keys = array_keys($this->_context)) {
$last = array_pop($keys); $last = array_pop($keys);
@@ -205,7 +205,7 @@ class Mustache {
protected function _renderSection($template) { protected function _renderSection($template) {
$otag = preg_quote($this->_otag, '/'); $otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/'); $ctag = preg_quote($this->_ctag, '/');
$regex = '/' . $otag . '(\\^|\\#)\\s*(.+?)\\s*' . $ctag . '\\s*([\\s\\S]+?)' . $otag . '\\/\\s*\\2\\s*' . $ctag . '\\s*/m'; $regex = '/' . $otag . '(\\^|\\#)\\s*(.+?)\\s*' . $ctag . '\\s*([\\s\\S]+?)' . $otag . '\\/\\s*\\2\\s*' . $ctag . '\\s*/ms';
$matches = array(); $matches = array();
while (preg_match($regex, $template, $matches, PREG_OFFSET_CAPTURE)) { while (preg_match($regex, $template, $matches, PREG_OFFSET_CAPTURE)) {
@@ -289,7 +289,7 @@ class Mustache {
$otag = preg_quote($this->_otag, '/'); $otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/'); $ctag = preg_quote($this->_ctag, '/');
$regex = '/' . $otag . '%\\s*([\\w_-]+)((?: [\\w]+=[\\w]+)*)\\s*' . $ctag . '\\n?/'; $regex = '/' . $otag . '%\\s*([\\w_-]+)((?: [\\w]+=[\\w]+)*)\\s*' . $ctag . '\\n?/s';
return preg_replace_callback($regex, array($this, '_renderPragma'), $template); return preg_replace_callback($regex, array($this, '_renderPragma'), $template);
} }
@@ -389,7 +389,7 @@ class Mustache {
$otag = preg_quote($this->_otag, '/'); $otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/'); $ctag = preg_quote($this->_ctag, '/');
$this->_tagRegEx = '/' . $otag . "([#\^\/=!>\\{&])?(.+?)\\1?" . $ctag . "+/"; $this->_tagRegEx = '/' . $otag . "([#\^\/=!>\\{&])?(.+?)\\1?" . $ctag . "+/s";
$html = ''; $html = '';
$matches = array(); $matches = array();
@@ -456,14 +456,12 @@ class Mustache {
return $this->_renderUnescaped($tag_name); return $this->_renderUnescaped($tag_name);
} }
break; break;
case '': }
default:
if ($this->_hasPragma(self::PRAGMA_UNESCAPED)) { if ($this->_hasPragma(self::PRAGMA_UNESCAPED)) {
return $this->_renderUnescaped($tag_name); return $this->_renderUnescaped($modifier . $tag_name);
} else { } else {
return $this->_renderEscaped($tag_name); return $this->_renderEscaped($modifier . $tag_name);
}
break;
} }
} }
@@ -475,7 +473,7 @@ class Mustache {
* @return string * @return string
*/ */
protected function _renderEscaped($tag_name) { protected function _renderEscaped($tag_name) {
return htmlentities($this->_getVariable($tag_name), null, $this->_charset); return htmlentities($this->_getVariable($tag_name), ENT_COMPAT, $this->_charset);
} }
/** /**
@@ -527,7 +525,7 @@ class Mustache {
$otag = preg_quote($this->_otag, '/'); $otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/'); $ctag = preg_quote($this->_ctag, '/');
$this->_tagRegEx = '/' . $otag . "([#\^\/=!>\\{&])?(.+?)\\1?" . $ctag . "+/"; $this->_tagRegEx = '/' . $otag . "([#\^\/=!>\\{&])?(.+?)\\1?" . $ctag . "+/s";
return ''; return '';
} }
@@ -608,10 +606,10 @@ class Mustache {
protected function _findVariableInContext($tag_name, $context) { protected function _findVariableInContext($tag_name, $context) {
foreach ($context as $view) { foreach ($context as $view) {
if (is_object($view)) { if (is_object($view)) {
if (isset($view->$tag_name)) { if (method_exists($view, $tag_name)) {
return $view->$tag_name;
} else if (method_exists($view, $tag_name)) {
return $view->$tag_name(); return $view->$tag_name();
} else if (isset($view->$tag_name)) {
return $view->$tag_name;
} }
} else if (isset($view[$tag_name])) { } else if (isset($view[$tag_name])) {
return $view[$tag_name]; return $view[$tag_name];
+1 -1
View File
@@ -83,7 +83,7 @@ Known Issues
* Sections don't respect delimiter changes -- `delimiters` example currently fails with an * Sections don't respect delimiter changes -- `delimiters` example currently fails with an
"unclosed section" exception. "unclosed section" exception.
* Test coverage is incomplete. * Mustache isn't always very good at whitespace.
See Also See Also
+1 -1
View File
@@ -1,5 +1,5 @@
<?php <?php
class Escaped extends Mustache { class Escaped extends Mustache {
public $title = "Bear > Shark"; public $title = '"Bear" > "Shark"';
} }
+1 -1
View File
@@ -1 +1 @@
<h1>Bear &gt; Shark</h1> <h1>&quot;Bear&quot; &gt; &quot;Shark&quot;</h1>
@@ -3,38 +3,14 @@
class SectionIteratorObjects extends Mustache { class SectionIteratorObjects extends Mustache {
public $start = "It worked the first time."; public $start = "It worked the first time.";
public function middle() {
return new IteratorObject();
}
public $final = "Then, surprisingly, it worked the final time.";
}
class IteratorObject implements Iterator {
protected $_position = 0;
protected $_data = array( protected $_data = array(
array('item' => 'And it worked the second time.'), array('item' => 'And it worked the second time.'),
array('item' => 'As well as the third.'), array('item' => 'As well as the third.'),
); );
public function rewind() { public function middle() {
$this->_position = 0; return new ArrayIterator($this->_data);
} }
public function current() { public $final = "Then, surprisingly, it worked the final time.";
return $this->_data[$this->_position];
}
public function key() {
return $this->_position;
}
public function next() {
++$this->_position;
}
public function valid() {
return isset($this->_data[$this->_position]);
}
} }
@@ -0,0 +1,14 @@
<?php
class SectionsSpaces extends Mustache {
public $start = "It worked the first time.";
public function middle() {
return array(
array('item' => "And it worked the second time."),
array('item' => "As well as the third."),
);
}
public $final = "Then, surprisingly, it worked the final time.";
}
@@ -0,0 +1,9 @@
* {{ start }}
{{# middle }}
* {{ item }}
{{/ middle }}
* {{ final }}
* {{ start }}
{{# middle }} * {{ item }}{{/ middle }}
* {{ final }}
@@ -0,0 +1,9 @@
* It worked the first time.
* And it worked the second time.
* As well as the third.
* Then, surprisingly, it worked the final time.
* It worked the first time.
* And it worked the second time.
* As well as the third.
* Then, surprisingly, it worked the final time.
+97
View File
@@ -0,0 +1,97 @@
<?php
require_once '../Mustache.php';
class MustacheExceptionTest extends PHPUnit_Framework_TestCase {
const TEST_CLASS = 'Mustache';
protected $pickyMustache;
protected $slackerMustache;
public function setUp() {
$this->pickyMustache = new PickyMustache();
$this->slackerMustache = new SlackerMustache();
}
/**
* @expectedException MustacheException
*/
public function testThrowsUnknownVariableException() {
$this->pickyMustache->render('{{not_a_variable}}');
}
/**
* @expectedException MustacheException
*/
public function testThrowsUnclosedSectionException() {
$this->pickyMustache->render('{{#unclosed}}');
}
/**
* @expectedException MustacheException
*/
public function testThrowsUnexpectedCloseSectionException() {
$this->pickyMustache->render('{{/unopened}}');
}
/**
* @expectedException MustacheException
*/
public function testThrowsUnknownPartialException() {
$this->pickyMustache->render('{{>impartial}}');
}
/**
* @expectedException MustacheException
*/
public function testThrowsUnknownPragmaException() {
$this->pickyMustache->render('{{%SWEET-MUSTACHE-BRO}}');
}
public function testDoesntThrowUnclosedSectionException() {
$this->assertEquals('', $this->slackerMustache->render('{{#unclosed}}'));
}
public function testDoesntThrowUnexpectedCloseSectionException() {
$this->assertEquals('', $this->slackerMustache->render('{{/unopened}}'));
}
public function testDoesntThrowUnknownPartialException() {
$this->assertEquals('', $this->slackerMustache->render('{{>impartial}}'));
}
/**
* @expectedException MustacheException
*/
public function testGetPragmaOptionsThrowsExceptionsIfItThinksYouHaveAPragmaButItTurnsOutYouDont() {
$mustache = new TestableMustache();
$mustache->testableGetPragmaOptions('PRAGMATIC');
}
}
class PickyMustache extends Mustache {
protected $_throwsExceptions = array(
MustacheException::UNKNOWN_VARIABLE => true,
MustacheException::UNCLOSED_SECTION => true,
MustacheException::UNEXPECTED_CLOSE_SECTION => true,
MustacheException::UNKNOWN_PARTIAL => true,
MustacheException::UNKNOWN_PRAGMA => true,
);
}
class SlackerMustache extends Mustache {
protected $_throwsExceptions = array(
MustacheException::UNKNOWN_VARIABLE => false,
MustacheException::UNCLOSED_SECTION => false,
MustacheException::UNEXPECTED_CLOSE_SECTION => false,
MustacheException::UNKNOWN_PARTIAL => false,
MustacheException::UNKNOWN_PRAGMA => false,
);
}
class TestableMustache extends Mustache {
public function testableGetPragmaOptions($pragma_name) {
return $this->_getPragmaOptions($pragma_name);
}
}
+14
View File
@@ -17,6 +17,12 @@ class MustacheObjectSectionTest extends PHPUnit_Framework_TestCase {
$gamma = new Gamma(); $gamma = new Gamma();
$this->assertEquals('Foo', $gamma->render('{{#bar}}{{#foo}}{{name}}{{/foo}}{{/bar}}')); $this->assertEquals('Foo', $gamma->render('{{#bar}}{{#foo}}{{name}}{{/foo}}{{/bar}}'));
} }
public function testSectionObjectWithFunction() {
$alpha = new Alpha();
$alpha->foo = new Delta();
$this->assertEquals('Foo', $alpha->render('{{#foo}}{{name}}{{/foo}}'));
}
} }
class Alpha extends Mustache { class Alpha extends Mustache {
@@ -53,4 +59,12 @@ class Gamma extends Mustache {
public function __construct() { public function __construct() {
$this->bar = new Beta(); $this->bar = new Beta();
} }
}
class Delta extends Mustache {
protected $_name = 'Foo';
public function name() {
return $this->_name;
}
} }
@@ -1,7 +1,6 @@
<?php <?php
require_once '../Mustache.php'; require_once '../Mustache.php';
require_once 'PHPUnit/Framework.php';
class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase { class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase {
+36 -12
View File
@@ -35,6 +35,11 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
const TEST_CLASS = 'Mustache'; const TEST_CLASS = 'Mustache';
protected $knownIssues = array(
'Delimiters' => "Known issue: sections don't respect delimiter changes",
'SectionsSpaces' => "Known issue: Mustache fails miserably at whitespace",
);
/** /**
* Test Mustache constructor. * Test Mustache constructor.
* *
@@ -136,6 +141,23 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('Zappa, Frank', $m->render('{{last_name}}, {{first_name}}', array('first_name' => 'Frank', 'last_name' => 'Zappa'))); $this->assertEquals('Zappa, Frank', $m->render('{{last_name}}, {{first_name}}', array('first_name' => 'Frank', 'last_name' => 'Zappa')));
} }
public function testRenderWithPartials() {
$m = new Mustache('{{>stache}}', null, array('stache' => '{{first_name}} {{last_name}}'));
$this->assertEquals('Charlie Chaplin', $m->render(null, array('first_name' => 'Charlie', 'last_name' => 'Chaplin')));
$this->assertEquals('Zappa, Frank', $m->render('{{last_name}}, {{first_name}}', array('first_name' => 'Frank', 'last_name' => 'Zappa')));
}
/**
* Mustache should allow newlines (and other whitespace) in comments and all other tags.
*
* @access public
* @return void
*/
public function testNewlinesInComments() {
$m = new Mustache("{{! comment \n \t still a comment... }}");
$this->assertEquals('', $m->render());
}
/** /**
* Mustache should return the same thing when invoked multiple times. * Mustache should return the same thing when invoked multiple times.
* *
@@ -166,10 +188,9 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
$this->assertEquals($first, $second); $this->assertEquals($first, $second);
} }
/** /**
* Mustache should not use templates passed to the render() method for subsequent invocations. * Mustache should not use templates passed to the render() method for subsequent invocations.
* *
* @access public * @access public
* @return void * @return void
*/ */
@@ -177,7 +198,7 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
$m = new Mustache('Sirve.'); $m = new Mustache('Sirve.');
$this->assertEquals('No sirve.', $m->render('No sirve.')); $this->assertEquals('No sirve.', $m->render('No sirve.'));
$this->assertEquals('Sirve.', $m->render()); $this->assertEquals('Sirve.', $m->render());
$m2 = new Mustache(); $m2 = new Mustache();
$this->assertEquals('No sirve.', $m2->render('No sirve.')); $this->assertEquals('No sirve.', $m2->render('No sirve.'));
$this->assertEquals('', $m2->render()); $this->assertEquals('', $m2->render());
@@ -186,14 +207,17 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
/** /**
* testClone function. * testClone function.
* *
* @group examples
* @dataProvider getExamples * @dataProvider getExamples
* @access public * @access public
* @param string $class
* @param string $template
* @param string $output
* @return void * @return void
*/ */
public function test__clone($class, $template, $output) { public function test__clone($class, $template, $output) {
if ($class == 'Delimiters') { if (isset($this->knownIssues[$class])) {
$this->markTestSkipped("Known issue: sections don't respect delimiter changes"); return $this->markTestSkipped($this->knownIssues[$class]);
return;
} }
$m = new $class; $m = new $class;
@@ -214,17 +238,17 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
/** /**
* Test everything in the `examples` directory. * Test everything in the `examples` directory.
* *
* @group examples
* @dataProvider getExamples * @dataProvider getExamples
* @access public * @access public
* @param mixed $class * @param string $class
* @param mixed $template * @param string $template
* @param mixed $output * @param string $output
* @return void * @return void
*/ */
public function testExamples($class, $template, $output) { public function testExamples($class, $template, $output) {
if ($class == 'Delimiters') { if (isset($this->knownIssues[$class])) {
$this->markTestSkipped("Known issue: sections don't respect delimiter changes"); return $this->markTestSkipped($this->knownIssues[$class]);
return;
} }
$m = new $class; $m = new $class;