Permalink
Browse files

Add the hat (^) - support for inverted sections.

  • Loading branch information...
defunkt committed Mar 30, 2010
1 parent 305aceb commit 2bd85727de12f6995601101b5e281fbb2c6957c3
View
@@ -1,5 +1,10 @@
Hello {{name}}
You have just won ${{value}}!
+
{{#in_ca}}
Well, ${{taxed_value}}, after taxes.
{{/in_ca}}
+
+{{^in_ca}}
+You're lucky - in CA you'd be taxed like crazy!
+{{/in_ca}}
View
@@ -109,6 +109,23 @@ def on_section(name, content)
compiled
end
+ # Fired when we find an inverted section. Just like `on_section`,
+ # we're passed the inverted section name and the array of tokens.
+ def on_inverted_section(name, content)
+ # Convert the tokenized content of this section into a Ruby
+ # string we can use.
+ code = compile(content)
+
+ # Compile the Ruby for this inverted section now that we know
+ # what's inside.
+ ev(<<-compiled)
+ v = ctx[#{name.to_sym.inspect}]
+ if v.nil? || v == false || v.respond_to?(:empty?) && v.empty?
+ #{code}
+ end
+ compiled
+ end
+
# Fired when the compiler finds a partial. We want to return code
# which calls a partial at runtime instead of expanding and
# including the partial's body to allow for recursive partials.
View
@@ -45,7 +45,7 @@ def to_s
end
# After these types of tags, all whitespace will be skipped.
- SKIP_WHITESPACE = [ '#', '/' ]
+ SKIP_WHITESPACE = [ '#', '^', '/' ]
# The content allowed in a tag name.
ALLOWED_CONTENT = /(\w|[?!\/-])*/
@@ -102,7 +102,7 @@ def scan_tags
# Since {{= rewrites ctag, we store the ctag which should be used
# when parsing this specific tag.
current_ctag = self.ctag
- type = @scanner.scan(/#|\/|=|!|<|>|&|\{/)
+ type = @scanner.scan(/#|\^|\/|=|!|<|>|&|\{/)
@scanner.skip(/\s*/)
# ANY_CONTENT tags allow any character inside of them, while
@@ -124,6 +124,11 @@ def scan_tags
@result << [:mustache, :section, content, block]
@sections << [content, position, @result]
@result = block
+ when '^'
+ block = [:multi]
+ @result << [:mustache, :inverted_section, content, block]
+ @sections << [content, position, @result]
+ @result = block
when '/'
section, pos, result = @sections.pop
@result = result
View
@@ -41,15 +41,15 @@ values. This document explains the different types of Mustache tags.
## TAG TYPES
-Tags are indicated by the double mustaches. `{{name}}` is a tag. Let's
-talk about the different types of tags.
+Tags are indicated by the double mustaches. `{{name}}` is a tag, as is
+`{{#name}}`. Let's talk about the different types of tags.
### Variables
The most basic tag is the variable. A `{{name}}` tag in a basic
-template will try to call the `name` method on your view. If there is
-no `name` method, an exception will be raised.
+template will try to find the `name` key or method on your view. If
+there is no `name` method, nothing will be rendered.
All variables are HTML escaped by default. If you want to return
unescaped HTML, use the triple mustache: `{{{name}}}`.
@@ -58,7 +58,8 @@ You can also use `&` to unescape a variable: `{{& name}}`. This may be
useful when changing delimiters (see "Set Delimter" below).
By default a variable "miss" returns an empty string. This can usually
-be configured in your Mustache library.
+be configured in your Mustache library. The Ruby version of Mustache
+supports raising an exception in this situation, for instance.
Template:
@@ -81,6 +82,7 @@ Output:
* &lt;b&gt;GitHub&lt;/b&gt;
* <b>GitHub</b>
+
### Boolean Sections
A section begins with a pound and ends with a slash. That is,
@@ -111,6 +113,7 @@ Output:
Shown!
+
### Enumerable Sections
Enumerable sections are syntactically identical to boolean sections in
@@ -145,6 +148,37 @@ Output:
<b>rip</b>
+### Inverted Sections
+
+An inverted section begins with a caret (hat) and ends with a
+slash. That is `{{^person}}` begins a "person" inverted section while
+`{{/person}}` ends it.
+
+While sections can be used to render text one or more times based on the
+value of the key given, inverted sections may render text once based
+on the inverse value of the key given. That is, they will be rendered
+if the key doesn't exist, is false, or is an empty list.
+
+Template:
+
+ {{#repo}}
+ <b>{{name}}</b>
+ {{/repo}}
+ {{^repo}}
+ No repos :(
+ {{/repo}}
+
+Hash:
+
+ {
+ "repo": []
+ }
+
+Output:
+
+ No repos :(
+
+
### Comments
Comments begin with a bang and are ignored. The following template:
@@ -160,9 +194,23 @@ Will render as follows:
Partials begin with a greater than sign, like `{{> box}}`.
-It is useful to think of partials as a "template expansion" - that is,
-the actual partial tag will be replaced with the content of the
-partial. Therefor partials share the current context.
+Partials are rendered at runtime (as opposed to compile time), so
+recursive partials are possible. Just avoid infinite loops.
+
+They also inherit the calling context. Whereas in ERB you may have
+this:
+
+ <%= partial :next_more, :start => start, :size => size %>
+
+Mustache requires only this:
+
+ {{> next_more}}
+
+Why? Because the `next_more.mustache` file will inherit the `size` and
+`start` methods from the calling context.
+
+In this way you may want to think of partials as includes, or template
+expansion, even though it's not literally true.
For example, this template and partial:
@@ -11,6 +11,7 @@
{{/item}}
</ul>
{{/list}}
-{{#empty}}
+
+{{^list}}
<p>The list is empty.</p>
-{{/empty}}
+{{/list}}
@@ -0,0 +1,7 @@
+{{^t}}
+ * first
+{{/t}}
+* {{two}}
+{{^t}}
+ * third
+{{/t}}
@@ -0,0 +1,14 @@
+$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
+require 'mustache'
+
+class InvertedSection < Mustache
+ self.path = File.dirname(__FILE__)
+
+ def t
+ false
+ end
+
+ def two
+ "second"
+ end
+end
@@ -11,6 +11,7 @@
{{/item}}
</ul>
{{/list}}
-{{#empty}}
+
+{{^list}}
<p>The list is empty.</p>
-{{/empty}}
+{{/list}}
View
@@ -123,6 +123,14 @@ def test_double_section
end_section
end
+ def test_inverted_section
+ assert_equal <<-end_section.strip, InvertedSection.render.strip
+* first
+* second
+* third
+end_section
+ end
+
def test_comments
assert_equal "<h1>A Comedy of Errors</h1>\n", Comments.render
end

4 comments on commit 2bd8572

janl replied Apr 1, 2010

sweet!

Nice...does this mean that there would be support for AND, OR etc in the future?

Owner

defunkt replied Apr 2, 2010

@shakhan We already support AND, OR, etc in your view class.

@defunkt thanks, i'll check it out.

Please sign in to comment.