From 20ace3e74c79f0af1239cb33f6553166f80dfea4 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Thu, 29 Apr 2010 22:49:50 -0400 Subject: [PATCH 1/4] Implemented implicit iterator pragma. Fixed getPragmaOptions to return empty array if no options are set. Fixed getVariable to return exact variable name matches before dot notation matches (i.e. the default iterator -- '.' -- will match the iterator and not try to do some crazy dot notation parsing. --- Mustache.php | 33 ++++++++++++---- .../implicit_iterator/ImplicitIterator.php | 5 +++ .../implicit_iterator.mustache | 4 ++ .../implicit_iterator/implicit_iterator.txt | 5 +++ test/MustachePragmaImplicitIteratorTest.php | 38 +++++++++++++++++++ 5 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 examples/implicit_iterator/ImplicitIterator.php create mode 100644 examples/implicit_iterator/implicit_iterator.mustache create mode 100644 examples/implicit_iterator/implicit_iterator.txt create mode 100644 test/MustachePragmaImplicitIteratorTest.php diff --git a/Mustache.php b/Mustache.php index e47e532..3f36d5e 100644 --- a/Mustache.php +++ b/Mustache.php @@ -25,7 +25,8 @@ class Mustache { // Override charset passed to htmlentities() and htmlspecialchars(). Defaults to UTF-8. protected $charset = 'UTF-8'; - const PRAGMA_DOT_NOTATION = 'DOT-NOTATION'; + const PRAGMA_DOT_NOTATION = 'DOT-NOTATION'; + const PRAGMA_IMPLICIT_ITERATOR = 'IMPLICIT-ITERATOR'; protected $tagRegEx; @@ -35,7 +36,8 @@ class Mustache { protected $pragmas = array(); protected $pragmasImplemented = array( - self::PRAGMA_DOT_NOTATION + self::PRAGMA_DOT_NOTATION, + self::PRAGMA_IMPLICIT_ITERATOR ); /** @@ -151,8 +153,23 @@ class Mustache { // regular section case '#': if ($this->varIsIterable($val)) { + if ($this->hasPragma(self::PRAGMA_IMPLICIT_ITERATOR)) { + if ($opt = $this->getPragmaOptions(self::PRAGMA_IMPLICIT_ITERATOR)) { + $iterator = $opt['iterator']; + } else { + $iterator = '.'; + } + } else { + $iterator = false; + } + foreach ($val as $local_context) { - $replace .= $this->_render($content, $this->getContext($context, $local_context)); + if ($iterator) { + $c = array($iterator => $local_context); + $replace .= $this->_render($content, $this->getContext($context, $c)); + } else { + $replace .= $this->_render($content, $this->getContext($context, $local_context)); + } } } else if ($val) { if (is_array($val) || is_object($val)) { @@ -231,11 +248,11 @@ class Mustache { } protected function getPragmaOptions($pragma_name) { - if (!$this->hasPragma()) { + if (!$this->hasPragma($pragma_name)) { throw new MustacheException('Unknown pragma: ' . $pragma_name, MustacheException::UNKNOWN_PRAGMA); } - return $this->pragmas[$pragma_name]; + return (is_array($this->pragmas[$pragma_name])) ? $this->pragmas[$pragma_name] : array(); } /** @@ -426,7 +443,9 @@ class Mustache { * @return string */ protected function getVariable($tag_name, &$context) { - if ($this->hasPragma(self::PRAGMA_DOT_NOTATION)) { + if ($ret = $this->_getVariable($tag_name, $context)) { + return $ret; + } else if ($this->hasPragma(self::PRAGMA_DOT_NOTATION)) { $chunks = explode('.', $tag_name); $first = array_shift($chunks); @@ -438,7 +457,7 @@ class Mustache { } return $ret; } else { - return $this->_getVariable($tag_name, $context); + return $ret; } } diff --git a/examples/implicit_iterator/ImplicitIterator.php b/examples/implicit_iterator/ImplicitIterator.php new file mode 100644 index 0000000..c01fef0 --- /dev/null +++ b/examples/implicit_iterator/ImplicitIterator.php @@ -0,0 +1,5 @@ +getMock('Mustache', array('renderPragma'), array('{{%IMPLICIT-ITERATOR}}')); + $m->expects($this->exactly(1)) + ->method('renderPragma') + ->with(array('{{%IMPLICIT-ITERATOR}}', 'IMPLICIT-ITERATOR', null)); + $m->render(); + } + + public function testImplicitIterator() { + $m1 = new Mustache('{{%IMPLICIT-ITERATOR}}{{#items}}{{.}}{{/items}}', array('items' => array('a', 'b', 'c'))); + $this->assertEquals('abc', $m1->render()); + + $m2 = new Mustache('{{%IMPLICIT-ITERATOR}}{{#items}}{{.}}{{/items}}', array('items' => array(1, 2, 3))); + $this->assertEquals('123', $m2->render()); + } + + public function testDotNotationCollision() { + $m = new Mustache(null, array('items' => array('foo', 'bar', 'baz'))); + + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR}}{{%DOT-NOTATION}}{{#items}}{{.}}{{/items}}')); + $this->assertEquals('foobarbaz', $m->render('{{%DOT-NOTATION}}{{%IMPLICIT-ITERATOR}}{{#items}}{{.}}{{/items}}')); + } + + public function testCustomIterator() { + $m = new Mustache(null, array('items' => array('foo', 'bar', 'baz'))); + + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=item}}{{#items}}{{item}}{{/items}}')); + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=items}}{{#items}}{{items}}{{/items}}')); + } + +} \ No newline at end of file From 1330cf1d4822b20b6378a1e2edf64f3ddb209c9a Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Sun, 23 May 2010 00:48:43 -0400 Subject: [PATCH 2/4] Updated implicit iterator test to reflect changes in mustache function names. --- test/MustachePragmaImplicitIteratorTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/MustachePragmaImplicitIteratorTest.php b/test/MustachePragmaImplicitIteratorTest.php index 20e3c4b..cd4b711 100644 --- a/test/MustachePragmaImplicitIteratorTest.php +++ b/test/MustachePragmaImplicitIteratorTest.php @@ -6,9 +6,9 @@ require_once 'PHPUnit/Framework.php'; class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase { public function testEnablePragma() { - $m = $this->getMock('Mustache', array('renderPragma'), array('{{%IMPLICIT-ITERATOR}}')); + $m = $this->getMock('Mustache', array('_renderPragma'), array('{{%IMPLICIT-ITERATOR}}')); $m->expects($this->exactly(1)) - ->method('renderPragma') + ->method('_renderPragma') ->with(array('{{%IMPLICIT-ITERATOR}}', 'IMPLICIT-ITERATOR', null)); $m->render(); } @@ -31,7 +31,8 @@ class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase { public function testCustomIterator() { $m = new Mustache(null, array('items' => array('foo', 'bar', 'baz'))); - $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=item}}{{#items}}{{item}}{{/items}}')); + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR}}{{#items}}{{.}}{{/items}}')); + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{#items}}{{i}}{{/items}}')); $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=items}}{{#items}}{{items}}{{/items}}')); } From 20184491842224aab9979ab3caba9055eb45c2cf Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Wed, 2 Jun 2010 02:08:54 -0400 Subject: [PATCH 3/4] added test and known issue for dot notation/implicit iterator woes. --- README.markdown | 2 ++ test/MustachePragmaImplicitIteratorTest.php | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/README.markdown b/README.markdown index 1a029cc..4e843fd 100644 --- a/README.markdown +++ b/README.markdown @@ -85,6 +85,8 @@ Known Issues to all subsequent templates and partials rendered by this Mustache instance. * Sections don't respect delimiter changes -- `delimiters` example currently fails with an "unclosed section" exception. + * Dot notation and implicit iterators don't really play nice. A non-traversible local context is + passed to the section. * Test coverage is incomplete. diff --git a/test/MustachePragmaImplicitIteratorTest.php b/test/MustachePragmaImplicitIteratorTest.php index cd4b711..f8217ee 100644 --- a/test/MustachePragmaImplicitIteratorTest.php +++ b/test/MustachePragmaImplicitIteratorTest.php @@ -36,4 +36,20 @@ class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase { $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=items}}{{#items}}{{items}}{{/items}}')); } + public function testDotNotationContext() { + $m = new Mustache(null, array('items' => array( + array('index' => 1, 'name' => 'foo'), + array('index' => 2, 'name' => 'bar'), + array('index' => 3, 'name' => 'baz'), + ))); + + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR}}{{#items}}{{#.}}{{name}}{{/.}}{{/items}}')); + + // skip the last two tests here, they just break... + $this->markTestSkipped('Implicit iterator not completely compatible with dot notation'); + + $this->assertEquals('123', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{#items}}{{i.index}}{{/items}}')); + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{#items}}{{i.name}}{{/items}}')); + } + } \ No newline at end of file From 06ee1f224a720ca16bc78e927c00d8ff7a67746f Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Sun, 11 Jul 2010 22:54:57 -0400 Subject: [PATCH 4/4] Fixed implicit iterator + dot notation tests, this feature is now ready to merge :) --- README.markdown | 2 -- test/MustachePragmaImplicitIteratorTest.php | 8 ++------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 3123c6d..ab27846 100644 --- a/README.markdown +++ b/README.markdown @@ -83,8 +83,6 @@ Known Issues * Sections don't respect delimiter changes -- `delimiters` example currently fails with an "unclosed section" exception. - * Dot notation and implicit iterators don't really play nice. A non-traversible local context is - passed to the section. * Test coverage is incomplete. diff --git a/test/MustachePragmaImplicitIteratorTest.php b/test/MustachePragmaImplicitIteratorTest.php index f8217ee..bf39091 100644 --- a/test/MustachePragmaImplicitIteratorTest.php +++ b/test/MustachePragmaImplicitIteratorTest.php @@ -44,12 +44,8 @@ class MustachePragmaImplicitIteratorTest extends PHPUnit_Framework_TestCase { ))); $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR}}{{#items}}{{#.}}{{name}}{{/.}}{{/items}}')); - - // skip the last two tests here, they just break... - $this->markTestSkipped('Implicit iterator not completely compatible with dot notation'); - - $this->assertEquals('123', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{#items}}{{i.index}}{{/items}}')); - $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{#items}}{{i.name}}{{/items}}')); + $this->assertEquals('123', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{%DOT-NOTATION}}{{#items}}{{i.index}}{{/items}}')); + $this->assertEquals('foobarbaz', $m->render('{{%IMPLICIT-ITERATOR iterator=i}}{{%DOT-NOTATION}}{{#items}}{{i.name}}{{/items}}')); } } \ No newline at end of file