From e739e216a69f46e8d1721f4ed0334394fa9aa2e3 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Sun, 27 Jan 2013 02:35:37 -0800 Subject: [PATCH] Implement section (and inverted section) filters. This is a super powerful construct. It's also prolly a violation of the "logic-less-ness" of Mustache templates :( --- src/Mustache/Compiler.php | 21 +++- .../Functional/SectionFiltersTest.php | 101 ++++++++++++++++++ 2 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 test/Mustache/Test/FiveThree/Functional/SectionFiltersTest.php diff --git a/src/Mustache/Compiler.php b/src/Mustache/Compiler.php index 8bac251..c38ca43 100644 --- a/src/Mustache/Compiler.php +++ b/src/Mustache/Compiler.php @@ -178,7 +178,8 @@ class Mustache_Compiler const SECTION_CALL = ' // %s section - $buffer .= $this->section%s($context, $indent, $context->%s(%s)); + $value = $context->%s(%s);%s + $buffer .= $this->section%s($context, $indent, $value); '; const SECTION = ' @@ -216,6 +217,12 @@ class Mustache_Compiler */ private function section($nodes, $id, $start, $end, $otag, $ctag, $level) { + $filters = ''; + + if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { + list($id, $filters) = $this->getFilters($id, $level); + } + $method = $this->getFindMethod($id); $id = var_export($id, true); $source = var_export(substr($this->source, $start, $end - $start), true); @@ -233,12 +240,12 @@ class Mustache_Compiler $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2)); } - return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $key, $method, $id); + return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); } const INVERTED_SECTION = ' // %s inverted section - $value = $context->%s(%s); + $value = $context->%s(%s);%s if (empty($value)) { %s }'; @@ -254,10 +261,16 @@ class Mustache_Compiler */ private function invertedSection($nodes, $id, $level) { + $filters = ''; + + if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { + list($id, $filters) = $this->getFilters($id, $level); + } + $method = $this->getFindMethod($id); $id = var_export($id, true); - return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $this->walk($nodes, $level)); + return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $filters, $this->walk($nodes, $level)); } const PARTIAL = ' diff --git a/test/Mustache/Test/FiveThree/Functional/SectionFiltersTest.php b/test/Mustache/Test/FiveThree/Functional/SectionFiltersTest.php new file mode 100644 index 0000000..cbd6823 --- /dev/null +++ b/test/Mustache/Test/FiveThree/Functional/SectionFiltersTest.php @@ -0,0 +1,101 @@ +mustache = new Mustache_Engine; + } + + public function testSingleFilter() + { + $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{# word | echo }}{{ . }}!{{/ word | echo }}'); + + $this->mustache->addHelper('echo', function($value) { + return array($value, $value, $value); + }); + + $this->assertEquals('bacon!bacon!bacon!', $tpl->render(array('word' => 'bacon'))); + } + + const CHAINED_FILTERS_TPL = <<mustache->loadTemplate(self::CHAINED_FILTERS_TPL); + + $this->mustache->addHelper('echo', function($value) { + return array($value, $value, $value); + }); + + $this->mustache->addHelper('with_index', function($value) { + return array_map(function($k, $v) { + return array( + 'key' => $k, + 'value' => $v, + ); + }, array_keys($value), $value); + }); + + $this->assertEquals("0: bacon\n1: bacon\n2: bacon\n", $tpl->render(array('word' => 'bacon'))); + } + + public function testInterpolateFirst() + { + $tpl = $this->mustache->loadTemplate('{{% FILTERS }}{{# foo | bar }}{{ . }}{{/ foo | bar }}'); + $this->assertEquals('win!', $tpl->render(array( + 'foo' => 'FOO', + 'bar' => function($value) { + return ($value === 'FOO') ? 'win!' : 'fail :('; + }, + ))); + } + + /** + * @expectedException Mustache_Exception_UnknownFilterException + * @dataProvider getBrokenPipes + */ + public function testThrowsExceptionForBrokenPipes($tpl, $data) + { + $this->mustache + ->loadTemplate(sprintf('{{%% FILTERS }}{{# %s }}{{ . }}{{/ %s }}', $tpl, $tpl)) + ->render($data); + } + + public function getBrokenPipes() + { + return array( + array('foo | bar', array()), + array('foo | bar', array('foo' => 'FOO')), + array('foo | bar', array('foo' => 'FOO', 'bar' => 'BAR')), + array('foo | bar', array('foo' => 'FOO', 'bar' => array(1, 2))), + array('foo | bar | baz', array('foo' => 'FOO', 'bar' => function() { return 'BAR'; })), + array('foo | bar | baz', array('foo' => 'FOO', 'baz' => function() { return 'BAZ'; })), + array('foo | bar | baz', array('bar' => function() { return 'BAR'; })), + array('foo | bar | baz', array('baz' => function() { return 'BAZ'; })), + array('foo | bar.baz', array('foo' => 'FOO', 'bar' => function() { return 'BAR'; }, 'baz' => function() { return 'BAZ'; })), + ); + } + +}