From 6e508d5b482819ec33eb2f67a31c991a60f85d68 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Sun, 25 Apr 2010 17:53:51 -0400 Subject: [PATCH 1/6] Rudimentary unit test script. --- test/MustacheTest.php | 110 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 test/MustacheTest.php diff --git a/test/MustacheTest.php b/test/MustacheTest.php new file mode 100644 index 0000000..ac02ada --- /dev/null +++ b/test/MustacheTest.php @@ -0,0 +1,110 @@ +assertEquals($m->render($template), $output); + } + + + /** + * Data provider for testExamples method. + * + * Assumes that an `examples` directory exists inside parent directory. + * This examples directory should contain any number of subdirectories, each of which contains + * three files: one Mustache class (.php), one Mustache template (.mustache), and one output file + * (.txt). + * + * 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 :) + * + * @access public + * @return array + */ + public function getExamples() { + $basedir = dirname(__FILE__) . '/../examples/'; + + $ret = array(); + + $files = new RecursiveDirectoryIterator($basedir); + while ($files->valid()) { + + if ($files->hasChildren() && $children = $files->getChildren()) { + $example = $files->getSubPathname(); + $class = null; + $template = null; + $output = null; + + foreach ($children as $file) { + if (!$file->isFile()) continue; + + $filename = $file->getPathInfo(); + $info = pathinfo($filename); + + switch($info['extension']) { + case 'php': + $class = $info['filename']; + include_once($filename); + break; + + case 'mustache': + $template = file_get_contents($filename); + break; + + case 'txt': + $output = file_get_contents($filename); + break; + } + } + + $ret[$example] = array($class, $template, $output); + } + + $files->next(); + } + return $ret; + } +} \ No newline at end of file From 3108aff56d3fae25c098cb64efa5f440bd75c3d8 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Sun, 25 Apr 2010 17:55:43 -0400 Subject: [PATCH 2/6] fixing documentation for unit tests --- test/MustacheTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/MustacheTest.php b/test/MustacheTest.php index ac02ada..3cff074 100644 --- a/test/MustacheTest.php +++ b/test/MustacheTest.php @@ -27,7 +27,7 @@ require_once 'PHPUnit/Framework.php'; * * 1. {@link http://www.phpunit.de/manual/current/en/installation.html Install PHPUnit} * 2. run phpunit from the `test` directory: - * `phpunit MustacheTest` or just `phpunit` + * `phpunit MustacheTest` * 3. Fix bugs. Lather, rinse, repeat. * * @extends PHPUnit_Framework_TestCase From 5400cd91a03809537b6e64e1bc226c2415e8d897 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Fri, 23 Apr 2010 11:48:29 -0400 Subject: [PATCH 3/6] Removing now-deprecated HandlebarMustache class. --- HandlebarMustache.php | 160 ------------------------------------------ 1 file changed, 160 deletions(-) delete mode 100644 HandlebarMustache.php diff --git a/HandlebarMustache.php b/HandlebarMustache.php deleted file mode 100644 index 7dbdb8b..0000000 --- a/HandlebarMustache.php +++ /dev/null @@ -1,160 +0,0 @@ -templateBase)) { - $this->setTemplateBase(dirname(__FILE__)); - } - - // default template name is the underscorified class name. - if (!isset($this->templateName)) { - $this->templateName = strtolower(preg_replace('#(?templateBase = $dir; - } - - /** - * Override the default templateName. - * - * @access public - * @param string $name - * @return void - */ - public function setTemplateName($name) { - $this->templateName = $name; - } - - /** - * Load a template file. This file will be relative to $this->templateBase. - * A '.mustache' file extension is assumed if none is provided in $file. - * - * @access public - * @param string $name - * @return void - */ - public function loadTemplate($name) { - if (strpos($name, '.') === false) { - $name .= '.mustache'; - } - - $filename = $this->templateBase . $name; - if (file_exists($filename)) { - $this->template = file_get_contents($filename); - } else { - $this->template = null; - } - } - - /** - * Load a partial, either from $this->partials or from a file in the templateBase - * directory. - * - * @access protected - * @param string $tag_name - * @return string Partial template. - */ - protected function getPartial($tag_name) { - try { - if ($result = parent::getPartial($tag_name)) { - return $result; - } - } catch (MustacheException $e) { - // Ignore the UNKNOWN_PARTIAL exceptions, we'll just look for a template file. - if ($e->getCode() !== MustacheException::UNKNOWN_PARTIAL) { - throw $e; - } - } - - $filename = $this->templateBase . $tag_name . '.mustache'; - if (file_exists($filename)) { - $this->partials[$tag_name] = file_get_contents($filename); - return $this->partials[$tag_name]; - } else { - if ($this->throwPartialExceptions) { - throw new MustacheException( - 'Unknown partial: ' . $tag_name, - MustacheException::UNKNOWN_PARTIAL - ); - } else { - return ''; - } - } - } - - /** - * Render the given template and view object. - * - * Defaults to the template and view passed to the class constructor unless a new one is provided. - * Optionally, pass an associative array of partials as well. - * - * @access public - * @param string $template (default: null) - * @param mixed $view (default: null) - * @param array $partials (default: null) - * @return string Rendered Mustache template. - */ - public function render($template = null, $view = null, $partials = null) { - if ($template === null && !isset($this->template)) { - $this->loadTemplate($this->templateName); - } - - return parent::render($template, $view, $partials); - } -} \ No newline at end of file From fc170fca1103d68a4b1ae31cbfb4a9c4daf64105 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 26 Apr 2010 18:58:58 -0400 Subject: [PATCH 4/6] Add inverted section. --- Mustache.php | 35 +++++++++++++------ examples/inverted_section/InvertedSection.php | 5 +++ .../inverted_section.mustache | 2 ++ .../inverted_section/inverted_section.txt | 1 + 4 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 examples/inverted_section/InvertedSection.php create mode 100644 examples/inverted_section/inverted_section.mustache create mode 100644 examples/inverted_section/inverted_section.txt diff --git a/Mustache.php b/Mustache.php index d8d42f6..1b53c39 100644 --- a/Mustache.php +++ b/Mustache.php @@ -106,7 +106,7 @@ class Mustache { } /** - * Render boolean and enumerable sections. + * Render boolean, enumerable and inverted sections. * * @access protected * @param string $template @@ -120,23 +120,36 @@ class Mustache { $otag = $this->prepareRegEx($this->otag); $ctag = $this->prepareRegEx($this->ctag); - $regex = '/' . $otag . '\\#(.+?)' . $ctag . '\\s*([\\s\\S]+?)' . $otag . '\\/\\1' . $ctag . '\\s*/m'; + $regex = '/' . $otag . '(\\^|\\#)(.+?)' . $ctag . '\\s*([\\s\\S]+?)' . $otag . '\\/\\2' . $ctag . '\\s*/m'; $matches = array(); while (preg_match($regex, $template, $matches, PREG_OFFSET_CAPTURE)) { $section = $matches[0][0]; $offset = $matches[0][1]; - $tag_name = trim($matches[1][0]); - $content = $matches[2][0]; + $type = $matches[1][0]; + $tag_name = trim($matches[2][0]); + $content = $matches[3][0]; $replace = ''; $val = $this->getVariable($tag_name, $context); - if (is_array($val)) { - foreach ($val as $local_context) { - $replace .= $this->_render($content, $this->getContext($context, $local_context)); - } - } else if ($val) { - $replace .= $content; + switch($type) { + // inverted section + case '^': + if (empty($val)) { + $replace .= $content; + } + break; + + // regular section + case '#': + if (is_array($val)) { + foreach ($val as $local_context) { + $replace .= $this->_render($content, $this->getContext($context, $local_context)); + } + } else if ($val) { + $replace .= $content; + } + break; } $template = substr_replace($template, $replace, $offset, strlen($section)); @@ -293,7 +306,7 @@ class Mustache { $otag = $this->prepareRegEx($this->otag); $ctag = $this->prepareRegEx($this->ctag); - $this->tagRegEx = '/' . $otag . "(#|\/|=|!|>|\\{|&)?([^\/#]+?)\\1?" . $ctag . "+/"; + $this->tagRegEx = '/' . $otag . "(#|\/|=|!|>|\\{|&)?([^\/#\^]+?)\\1?" . $ctag . "+/"; return ''; } diff --git a/examples/inverted_section/InvertedSection.php b/examples/inverted_section/InvertedSection.php new file mode 100644 index 0000000..18eeb86 --- /dev/null +++ b/examples/inverted_section/InvertedSection.php @@ -0,0 +1,5 @@ +{{name}}{{/repo}} +{{^repo}}No repos :({{/repo}} \ No newline at end of file diff --git a/examples/inverted_section/inverted_section.txt b/examples/inverted_section/inverted_section.txt new file mode 100644 index 0000000..2b9ed3f --- /dev/null +++ b/examples/inverted_section/inverted_section.txt @@ -0,0 +1 @@ +No repos :( \ No newline at end of file From 57a6aafff1ebe15490a1c257fe84c0e6d9499cc0 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 26 Apr 2010 19:01:57 -0400 Subject: [PATCH 5/6] Fixed complex example to use inverted sections rather than fancy ruby-ish context swizzling (which never worked in PHP anyway). --- README.markdown | 1 - examples/complex/complex.mustache | 4 ++-- examples/complex/complex.php | 5 ----- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index bc50bf5..0634a0f 100644 --- a/README.markdown +++ b/README.markdown @@ -82,7 +82,6 @@ Known Issues ------------ * Sections don't respect delimiter changes -- `delimiters` example currently fails with an "unclosed section" exception. - * Since `complex` example emulates some fancy swizzling available in Ruby, it fails. Need to convert example to PHPisms (In PHP, Mustache class doesn't maintain current context stack -- available context is passed to methods via params). * Test coverage is incomplete. diff --git a/examples/complex/complex.mustache b/examples/complex/complex.mustache index f3d8fea..4bf1687 100644 --- a/examples/complex/complex.mustache +++ b/examples/complex/complex.mustache @@ -5,9 +5,9 @@ {{#current}}
  • {{name}}
  • {{/current}} - {{#isLink}} + {{^current}}
  • {{name}}
  • - {{/isLink}} + {{/current}} {{/item}} {{/notEmpty}} diff --git a/examples/complex/complex.php b/examples/complex/complex.php index ca32ed7..ea55418 100644 --- a/examples/complex/complex.php +++ b/examples/complex/complex.php @@ -9,11 +9,6 @@ class Complex extends Mustache { array('name' => 'blue', 'current' => false, 'url' => '#Blue'), ); - public function isLink() { - // Exploit the fact that the current iteration item is at the top of the context stack. - return $this->getVariable('current', $this->context) != true; - } - public function notEmpty() { return !($this->isEmpty()); } From 6350e6180b6010820c6c39bcdbd0e9a6d1d898a3 Mon Sep 17 00:00:00 2001 From: Justin Hileman Date: Mon, 26 Apr 2010 19:24:29 -0400 Subject: [PATCH 6/6] Inverting expected/actual param order in MustacheTest so the unit test output makes sense. --- test/MustacheTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/MustacheTest.php b/test/MustacheTest.php index 3cff074..b6a6384 100644 --- a/test/MustacheTest.php +++ b/test/MustacheTest.php @@ -46,7 +46,7 @@ class MustacheTest extends PHPUnit_Framework_TestCase { */ public function testExamples($class, $template, $output) { $m = new $class; - $this->assertEquals($m->render($template), $output); + $this->assertEquals($output, $m->render($template)); }