diff --git a/Mustache.php b/Mustache.php
index 806e76a..88adef3 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/README.markdown b/README.markdown
index 4282594..a9685e1 100644
--- a/README.markdown
+++ b/README.markdown
@@ -83,7 +83,6 @@ Known Issues
* There's no way to toggle a pragma other than checking out the feature branch for each pragma... Need a clean way to do this.
* 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());
}
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
diff --git a/test/MustacheTest.php b/test/MustacheTest.php
new file mode 100644
index 0000000..b6a6384
--- /dev/null
+++ b/test/MustacheTest.php
@@ -0,0 +1,110 @@
+assertEquals($output, $m->render($template));
+ }
+
+
+ /**
+ * 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