Merge branch 'dev' into feature/mustache-spec

This commit is contained in:
Justin Hileman
2010-11-24 04:15:42 -05:00
11 changed files with 225 additions and 33 deletions
+70 -15
View File
@@ -191,7 +191,7 @@ class Mustache {
* @return string Rendered Mustache template. * @return string Rendered Mustache template.
*/ */
protected function _renderTemplate($template) { protected function _renderTemplate($template) {
$template = $this->_renderSection($template); $template = $this->_renderSections($template);
return $this->_renderTags($template); return $this->_renderTags($template);
} }
@@ -202,18 +202,9 @@ class Mustache {
* @param string $template * @param string $template
* @return string * @return string
*/ */
protected function _renderSection($template) { protected function _renderSections($template) {
$otag = preg_quote($this->_otag, '/'); while ($section_data = $this->_findSection($template)) {
$ctag = preg_quote($this->_ctag, '/'); list($section, $offset, $type, $tag_name, $content) = $section_data;
$regex = '/' . $otag . '(\\^|\\#)\\s*(.+?)\\s*' . $ctag . '\\s*([\\s\\S]+?)' . $otag . '\\/\\s*\\2\\s*' . $ctag . '\\s*/ms';
$matches = array();
while (preg_match($regex, $template, $matches, PREG_OFFSET_CAPTURE)) {
$section = $matches[0][0];
$offset = $matches[0][1];
$type = $matches[1][0];
$tag_name = trim($matches[2][0]);
$content = $matches[3][0];
$replace = ''; $replace = '';
$val = $this->_getVariable($tag_name); $val = $this->_getVariable($tag_name);
@@ -267,6 +258,71 @@ class Mustache {
return $template; return $template;
} }
/**
* Extract a section from $template.
*
* This is a helper function to find sections needed by _renderSections.
*
* @access protected
* @param string $template
* @return array $section, $offset, $type, $tag_name and $content
*/
protected function _findSection($template) {
$otag = preg_quote($this->_otag, '/');
$ctag = preg_quote($this->_ctag, '/');
$regex = '/' . $otag . '([\\^\\#\\/])\\s*(.+?)\\s*' . $ctag . '\\s*/ms';
$section_start = null;
$section_type = null;
$content_start = null;
$search_offset = 0;
$section_stack = array();
$matches = array();
while (preg_match($regex, $template, $matches, PREG_OFFSET_CAPTURE, $search_offset)) {
$match = $matches[0][0];
$offset = $matches[0][1];
$type = $matches[1][0];
$tag_name = trim($matches[2][0]);
$search_offset = $offset + strlen($match);
switch ($type) {
case '^':
case '#':
if (empty($section_stack)) {
$section_start = $offset;
$section_type = $type;
$content_start = $search_offset;
}
array_push($section_stack, $tag_name);
break;
case '/':
if (empty($section_stack) || ($tag_name !== array_pop($section_stack))) {
if ($this->_throwsException(MustacheException::UNEXPECTED_CLOSE_SECTION)) {
throw new MustacheException('Unexpected close section: ' . $tag_name, MustacheException::UNEXPECTED_CLOSE_SECTION);
}
}
if (empty($section_stack)) {
$section = substr($template, $section_start, $search_offset - $section_start);
$content = substr($template, $content_start, $offset - $content_start);
return array($section, $section_start, $section_type, $tag_name, $content);
}
break;
}
}
if (!empty($section_stack)) {
if ($this->_throwsException(MustacheException::UNCLOSED_SECTION)) {
throw new MustacheException('Unclosed section: ' . $section_stack[0], MustacheException::UNCLOSED_SECTION);
}
}
}
/** /**
* Initialize pragmas and remove all pragma tags. * Initialize pragmas and remove all pragma tags.
* *
@@ -353,7 +409,6 @@ class Mustache {
return (is_array($this->_localPragmas[$pragma_name])) ? $this->_localPragmas[$pragma_name] : array(); return (is_array($this->_localPragmas[$pragma_name])) ? $this->_localPragmas[$pragma_name] : array();
} }
/** /**
* Check whether this Mustache instance throws a given exception. * Check whether this Mustache instance throws a given exception.
* *
@@ -609,7 +664,7 @@ class Mustache {
} else if (isset($view->$tag_name)) { } else if (isset($view->$tag_name)) {
return $view->$tag_name; return $view->$tag_name;
} }
} else if (isset($view[$tag_name])) { } else if (array_key_exists($tag_name, $view)) {
return $view[$tag_name]; return $view[$tag_name];
} }
} }
@@ -0,0 +1,33 @@
<?php
class SectionsNested extends Mustache {
public $name = 'Little Mac';
public function enemies() {
return array(
array(
'name' => 'Von Kaiser',
'enemies' => array(
array('name' => 'Super Macho Man'),
array('name' => 'Piston Honda'),
array('name' => 'Mr. Sandman'),
)
),
array(
'name' => 'Mike Tyson',
'enemies' => array(
array('name' => 'Soda Popinski'),
array('name' => 'King Hippo'),
array('name' => 'Great Tiger'),
array('name' => 'Glass Joe'),
)
),
array(
'name' => 'Don Flamenco',
'enemies' => array(
array('name' => 'Bald Bull'),
)
),
);
}
}
@@ -0,0 +1,7 @@
Enemies of {{ name }}:
{{# enemies }}
{{ name }} ... who also has enemies:
{{# enemies }}
--> {{ name }}
{{/ enemies }}
{{/ enemies }}
@@ -0,0 +1,12 @@
Enemies of Little Mac:
Von Kaiser ... who also has enemies:
--> Super Macho Man
--> Piston Honda
--> Mr. Sandman
Mike Tyson ... who also has enemies:
--> Soda Popinski
--> King Hippo
--> Great Tiger
--> Glass Joe
Don Flamenco ... who also has enemies:
--> Bald Bull
+23
View File
@@ -15,6 +15,7 @@ class MustacheExceptionTest extends PHPUnit_Framework_TestCase {
} }
/** /**
* @group interpolation
* @expectedException MustacheException * @expectedException MustacheException
*/ */
public function testThrowsUnknownVariableException() { public function testThrowsUnknownVariableException() {
@@ -22,6 +23,7 @@ class MustacheExceptionTest extends PHPUnit_Framework_TestCase {
} }
/** /**
* @group sections
* @expectedException MustacheException * @expectedException MustacheException
*/ */
public function testThrowsUnclosedSectionException() { public function testThrowsUnclosedSectionException() {
@@ -29,6 +31,15 @@ class MustacheExceptionTest extends PHPUnit_Framework_TestCase {
} }
/** /**
* @group sections
* @expectedException MustacheException
*/
public function testThrowsUnclosedInvertedSectionException() {
$this->pickyMustache->render('{{^unclosed}}');
}
/**
* @group sections
* @expectedException MustacheException * @expectedException MustacheException
*/ */
public function testThrowsUnexpectedCloseSectionException() { public function testThrowsUnexpectedCloseSectionException() {
@@ -36,6 +47,7 @@ class MustacheExceptionTest extends PHPUnit_Framework_TestCase {
} }
/** /**
* @group partials
* @expectedException MustacheException * @expectedException MustacheException
*/ */
public function testThrowsUnknownPartialException() { public function testThrowsUnknownPartialException() {
@@ -43,25 +55,36 @@ class MustacheExceptionTest extends PHPUnit_Framework_TestCase {
} }
/** /**
* @group pragmas
* @expectedException MustacheException * @expectedException MustacheException
*/ */
public function testThrowsUnknownPragmaException() { public function testThrowsUnknownPragmaException() {
$this->pickyMustache->render('{{%SWEET-MUSTACHE-BRO}}'); $this->pickyMustache->render('{{%SWEET-MUSTACHE-BRO}}');
} }
/**
* @group sections
*/
public function testDoesntThrowUnclosedSectionException() { public function testDoesntThrowUnclosedSectionException() {
$this->assertEquals('', $this->slackerMustache->render('{{#unclosed}}')); $this->assertEquals('', $this->slackerMustache->render('{{#unclosed}}'));
} }
/**
* @group sections
*/
public function testDoesntThrowUnexpectedCloseSectionException() { public function testDoesntThrowUnexpectedCloseSectionException() {
$this->assertEquals('', $this->slackerMustache->render('{{/unopened}}')); $this->assertEquals('', $this->slackerMustache->render('{{/unopened}}'));
} }
/**
* @group partials
*/
public function testDoesntThrowUnknownPartialException() { public function testDoesntThrowUnknownPartialException() {
$this->assertEquals('', $this->slackerMustache->render('{{>impartial}}')); $this->assertEquals('', $this->slackerMustache->render('{{>impartial}}'));
} }
/** /**
* @group pragmas
* @expectedException MustacheException * @expectedException MustacheException
*/ */
public function testGetPragmaOptionsThrowsExceptionsIfItThinksYouHaveAPragmaButItTurnsOutYouDont() { public function testGetPragmaOptionsThrowsExceptionsIfItThinksYouHaveAPragmaButItTurnsOutYouDont() {
+3
View File
@@ -2,6 +2,9 @@
require_once '../Mustache.php'; require_once '../Mustache.php';
/**
* @group sections
*/
class MustacheObjectSectionTest extends PHPUnit_Framework_TestCase { class MustacheObjectSectionTest extends PHPUnit_Framework_TestCase {
public function testBasicObject() { public function testBasicObject() {
$alpha = new Alpha(); $alpha = new Alpha();
+3
View File
@@ -2,6 +2,9 @@
require_once '../Mustache.php'; require_once '../Mustache.php';
/**
* @group pragmas
*/
class MustachePragmaDotNotationTest extends PHPUnit_Framework_TestCase { class MustachePragmaDotNotationTest extends PHPUnit_Framework_TestCase {
public function testDotTraversal() { public function testDotTraversal() {
@@ -2,6 +2,9 @@
require_once '../Mustache.php'; require_once '../Mustache.php';
/**
* @group pragmas
*/
class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase { class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase {
public function testEnablePragma() { public function testEnablePragma() {
@@ -47,4 +50,34 @@ class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{%DOT-NOTATION}}{{#items}}{{i.name}}{{/items}}')); $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{%DOT-NOTATION}}{{#items}}{{i.name}}{{/items}}'));
} }
/**
* @dataProvider recursiveSectionData
*/
public function testRecursiveSections($template, $view, $result) {
$m = new Mustache();
$this->assertEquals($result, $m->render($template, $view));
}
public function recursiveSectionData() {
return array(
array(
'{{%IMPLICIT-ITERATOR}}{{#items}}{{#.}}{{.}}{{/.}}{{/items}}',
array('items' => array(array('a', 'b', 'c'), array('d', 'e', 'f'))),
'abcdef'
),
array(
'{{%IMPLICIT-ITERATOR}}{{#items}}{{#.}}{{#.}}{{.}}{{/.}}{{/.}}{{/items}}',
array('items' => array(array(array('a', 'b'), array('c')), array(array('d'), array('e', 'f')))),
'abcdef'
),
array(
'{{%IMPLICIT-ITERATOR}}{{#items}}{{#.}}{{#items}}{{.}}{{/items}}{{/.}}{{/items}}',
array('items' => array(
array('items' => array('a', 'b', 'c')),
array('items' => array('d', 'e', 'f')),
)),
'abcdef'
),
);
}
} }
+3
View File
@@ -2,6 +2,9 @@
require_once '../Mustache.php'; require_once '../Mustache.php';
/**
* @group pragmas
*/
class MustachePragmaTest extends PHPUnit_Framework_TestCase { class MustachePragmaTest extends PHPUnit_Framework_TestCase {
public function testUnknownPragmaException() { public function testUnknownPragmaException() {
+3
View File
@@ -2,6 +2,9 @@
require_once '../Mustache.php'; require_once '../Mustache.php';
/**
* @group pragmas
*/
class MustachePragmaUnescapedTest extends PHPUnit_Framework_TestCase { class MustachePragmaUnescapedTest extends PHPUnit_Framework_TestCase {
public function testPragmaUnescaped() { public function testPragmaUnescaped() {
+35 -18
View File
@@ -132,8 +132,7 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
/** /**
* Test render() with data. * Test render() with data.
* *
* @access public * @group interpolation
* @return void
*/ */
public function testRenderWithData() { public function testRenderWithData() {
$m = new Mustache('{{first_name}} {{last_name}}'); $m = new Mustache('{{first_name}} {{last_name}}');
@@ -141,6 +140,9 @@ 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')));
} }
/**
* @group partials
*/
public function testRenderWithPartials() { public function testRenderWithPartials() {
$m = new Mustache('{{>stache}}', null, array('stache' => '{{first_name}} {{last_name}}')); $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('Charlie Chaplin', $m->render(null, array('first_name' => 'Charlie', 'last_name' => 'Chaplin')));
@@ -150,8 +152,7 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
/** /**
* Mustache should allow newlines (and other whitespace) in comments and all other tags. * Mustache should allow newlines (and other whitespace) in comments and all other tags.
* *
* @access public * @group comments
* @return void
*/ */
public function testNewlinesInComments() { public function testNewlinesInComments() {
$m = new Mustache("{{! comment \n \t still a comment... }}"); $m = new Mustache("{{! comment \n \t still a comment... }}");
@@ -160,9 +161,6 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
/** /**
* Mustache should return the same thing when invoked multiple times. * Mustache should return the same thing when invoked multiple times.
*
* @access public
* @return void
*/ */
public function testMultipleInvocations() { public function testMultipleInvocations() {
$m = new Mustache('x'); $m = new Mustache('x');
@@ -176,8 +174,7 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
/** /**
* Mustache should return the same thing when invoked multiple times. * Mustache should return the same thing when invoked multiple times.
* *
* @access public * @group interpolation
* @return void
*/ */
public function testMultipleInvocationsWithTags() { public function testMultipleInvocationsWithTags() {
$m = new Mustache('{{one}} {{two}}', array('one' => 'foo', 'two' => 'bar')); $m = new Mustache('{{one}} {{two}}', array('one' => 'foo', 'two' => 'bar'));
@@ -190,9 +187,6 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
/** /**
* 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
* @return void
*/ */
public function testResetTemplateForMultipleInvocations() { public function testResetTemplateForMultipleInvocations() {
$m = new Mustache('Sirve.'); $m = new Mustache('Sirve.');
@@ -205,15 +199,14 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
} }
/** /**
* testClone function. * Test the __clone() magic function.
* *
* @group examples * @group examples
* @dataProvider getExamples * @dataProvider getExamples
* @access public *
* @param string $class * @param string $class
* @param string $template * @param string $template
* @param string $output * @param string $output
* @return void
*/ */
public function test__clone($class, $template, $output) { public function test__clone($class, $template, $output) {
if (isset($this->knownIssues[$class])) { if (isset($this->knownIssues[$class])) {
@@ -240,11 +233,10 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
* *
* @group examples * @group examples
* @dataProvider getExamples * @dataProvider getExamples
* @access public *
* @param string $class * @param string $class
* @param string $template * @param string $template
* @param string $output * @param string $output
* @return void
*/ */
public function testExamples($class, $template, $output) { public function testExamples($class, $template, $output) {
if (isset($this->knownIssues[$class])) { if (isset($this->knownIssues[$class])) {
@@ -266,7 +258,6 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
* This whole mess will be refined later to be more intuitive and less prescriptive, but it'll * This whole mess will be refined later to be more intuitive and less prescriptive, but it'll
* do for now. Especially since it means we can have unit tests :) * do for now. Especially since it means we can have unit tests :)
* *
* @access public
* @return array * @return array
*/ */
public function getExamples() { public function getExamples() {
@@ -317,6 +308,9 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
return $ret; return $ret;
} }
/**
* @group delimiters
*/
public function testCrazyDelimiters() { public function testCrazyDelimiters() {
$m = new Mustache(null, array('result' => 'success')); $m = new Mustache(null, array('result' => 'success'));
$this->assertEquals('success', $m->render('{{=[[ ]]=}}[[ result ]]')); $this->assertEquals('success', $m->render('{{=[[ ]]=}}[[ result ]]'));
@@ -327,10 +321,33 @@ class MustacheTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('success', $m->render('{{=// \\\\}}// result \\\\')); $this->assertEquals('success', $m->render('{{=// \\\\}}// result \\\\'));
} }
/**
* @group delimiters
*/
public function testResetDelimiters() { public function testResetDelimiters() {
$m = new Mustache(null, array('result' => 'success')); $m = new Mustache(null, array('result' => 'success'));
$this->assertEquals('success', $m->render('{{=[[ ]]=}}[[ result ]]')); $this->assertEquals('success', $m->render('{{=[[ ]]=}}[[ result ]]'));
$this->assertEquals('success', $m->render('{{=<< >>=}}<< result >>')); $this->assertEquals('success', $m->render('{{=<< >>=}}<< result >>'));
$this->assertEquals('success', $m->render('{{=<% %>=}}<% result %>')); $this->assertEquals('success', $m->render('{{=<% %>=}}<% result %>'));
} }
/**
* @group sections
* @dataProvider poorlyNestedSections
* @expectedException MustacheException
*/
public function testPoorlyNestedSections($template) {
$m = new Mustache($template);
$m->render();
}
public function poorlyNestedSections() {
return array(
array('{{#foo}}'),
array('{{#foo}}{{/bar}}'),
array('{{#foo}}{{#bar}}{{/foo}}'),
array('{{#foo}}{{#bar}}{{/foo}}{{/bar}}'),
array('{{#foo}}{{/bar}}{{/foo}}'),
);
}
} }