You'll learn here how your data is rendered. The loading of templates is covered in the Templates Guide. Common patterns for feeding templates are described in the ViewModel Guide.
- How keys are accessed
- Variable tags
- Expressions
- Section tags
- The context stack
- Fine tuning of key lookup
- Detailed description of GRMustache handling of
valueForKey:
- Compatibility with other Mustache implementations
Most Mustache tags will look for keys in your rendered objects. In the example below, the {{name}}
tag fetches the key name
from a dictionary, leading to the "Hello Arthur!" rendering:
NSDictionary *dictionary = @{ @"name": @"Arthur" };
NSString *rendering = [GRMustacheTemplate renderObject:dictionary fromString:@"Hello {{name}}!" error:NULL];
Dictionaries are an easy way to provide keys. Your own custom objects can be rendered as well, as long as they declare properties for keys used in templates:
@interface Person : NSObject
@property (nonatomic) NSString *name;
@end
NSDictionary *person = [[Person alloc] init];
person.name = @"Arthur";
// "Hello Arthur!"
NSString *rendering = [GRMustacheTemplate renderObject:person fromString:@"Hello {{name}}!" error:NULL];
Precisely, here is how keys are fetched:
- If the object responds to the keyed subscripting
objectForKeyedSubscript:
method, this method is used. - Otherwise, if the key is safe, then the
valueForKey:
method is used. - Otherwise, the key is considered missed.
By default, a key is safe if it is backed by a declared Objective-C property, or a Core Data attribute (for managed objects).
You can mitigate this limitation, though. For example, -[NSArray count]
is a method, not a property. However, GRMustache can render {{ items.count }}
. This is because NSArray conforms to the GRMustacheSafeKeyAccess
protocol. Check the Security Guide for more information.
Variable tags {{ name }}
, {{{ name }}}
and {{& name }}
look for the name
key in the object you provide, and render the returned value.
{{ name }}
renders HTML-escaped values, when {{{ name }}}
and {{& name }}
render unescaped values (the two last forms are equivalent).
Most objects are rendered with the description
standard method.
Objects conforming to the NSFastEnumeration protocol (but NSDictionary), such as NSArray are rendered as the concatenation of their elements:
id data = @{ @"voyels": @[@"A", @"E", @"I", @"O", @"U"] };
// Renders "AEIOU"
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:@"{{voyels}}"
error:NULL];
Finally, objects that implement the GRMustacheRendering
protocol take full charge of their own rendering. See the Rendering Objects Guide for further details.
Variable tags render simple keys as seen above, and, more generally, expressions, such as the key path person.name
and the filtered expression uppercase(person.name)
.
Person *craig = [Person personWithName:@"Craig"];
id data = @{ @"person": craig };
// Renders "Hello CRAIG!"
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:@"Hello {{ uppercase(person.name) }}!"
error:NULL];
GRMustache first looks for the person
key, extracts its name
, and applies the uppercase
built-in filter of the standard library. The variable tag eventually renders the resulting string.
The rendering of section tags such as {{# name }}...{{/ name }}
and {{^ name }}...{{/ name }}
depend on the value attached to the name
expression.
Generally speaking, inverted sections {{^ name }}...{{/ name }}
render when regular sections {{# name }}...{{/ name }}
do not. You can think of the caret ^
as the Mustache "unless".
Precisely speaking:
If the value is false, regular sections are omitted, and inverted sections rendered:
id data = @{ @"red": @NO, @"blue": @YES };
// Renders "Not red"
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:@"{{#red}}Red{{/red}}{{^red}}Not red{{/red}}"
error:NULL];
// Renders "Blue"
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:@"{{#blue}}Blue{{/blue}}{{^blue}}Not blue{{/blue}}"
error:NULL];
When an inverted sections follows a regular section with the same expression, you can use the short {{# name }}...{{^ name }}...{{/ name }}
form, avoiding the closing tag for {{# name }}
. Think of "if ... else ... end". For brevity's sake, you can also omit the expression after the opening tag: {{#name}}...{{^}}...{{/}}
is valid.
The full list of false values are:
nil
and missing keys[NSNull null]
NSNumber
instances whoseboolValue
method returnsNO
- empty strings
@""
- empty enumerables.
They all prevent Mustache sections {{# name }}...{{/ name }}
rendering.
They all trigger inverted sections {{^ name }}...{{/ name }}
rendering.
If the value attached to a section conforms to the NSFastEnumeration protocol (except NSDictionary), regular sections are rendered as many times as there are items in the enumerable object:
NSArray *friends = @[
[Person personWithName:@"Dennis"],
[Person personWithName:@"Eugene"],
[Person personWithName:@"Fiona"]];
id data = @{ @"friends": friends };
NSString *templateString = @"My friends are:\n"
@"{{# friends }}"
@"- {{ name }}\n"
@"{{/ friends }}";
// My friends are:
// - Dennis
// - Eugene
// - Fiona
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
Each item in the collection gets, each on its turn, available for the key lookup: that is how the {{ name }}
tag renders each of my friend's name. Items that conform to the GRMustacheRendering
protocol can provide their own rendering of their iteration step (see the Rendering Objects Guide).
Inverted sections render if and only if the collection is empty:
id data = @{ @"friends": @[] }; // empty array
NSString *templateString = @"{{^ friends }}"
@"I have no friend, sob."
@"{{/ friends }}";
// I have no friend, sob.
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
You may render a collection of strings with the dot expression .
, aka "implicit iterator":
id data = @{ @"items": @[@"Ham", @"Jam"] };
NSString *templateString = @"{{# items }}"
@"- {{ . }}"
@"{{/ items }}";
// - Ham
// - Jam
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
Sections render as many times as they contain items.
However, you may want to render a section once if and only if a collection is not empty. For example, when rendering a single <ul>
HTML element that wraps several <li>
.
A template that is compatible with other Mustache implementations needs an extra boolean key that states whether the collection is empty or not:
NSArray *friends = ...;
id data = @{
@"hasFriends": @(friends.count > 0),
@"friends": friends };
NSString *templateString = @"{{# hasFriends }}"
@"<ul>"
@" {{# friends }}"
@" <li>{{ name }}</li>"
@" {{/ friends }}";
@"</ul>"
@"{{/ hasFriends }}";
// <ul>
// <li>Dennis</li>
// <li>Eugene</li>
// <li>Fiona</li>
// </ul>
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
If you do not care about compatibility, you can simply use the count
property of NSArray, and use the fact that GRMustache considers zero numbers as false:
NSArray *friends = ...;
id data = @{ @"friends": friends };
NSString *templateString = @"{{# friends.count }}"
@"<ul>"
@" {{# friends }}"
@" <li>{{ name }}</li>"
@" {{/ friends }}";
@"</ul>"
@"{{/ friends.count }}";
// <ul>
// <li>Dennis</li>
// <li>Eugene</li>
// <li>Fiona</li>
// </ul>
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
Mustache defines lambda sections, that is, sections that execute your own application code, and allow you to extend the core Mustache engine.
Such sections are fully documented in the Rendering Objects Guide, but here is a preview:
id data = @{
@"name1": @"Gustave",
@"name2": @"Henriett" };
NSString *templateString = @"{{#localize}}Hello {{name1}}, do you know {{name2}}?{{/localize}}";
// Assuming a Spanish locale:
// Hola Gustave, sabes Henriett?
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
The localize
key is attached to a rendering object that is built in the standard library shipped with GRMustache.
When a section renders a value that is not false, not enumerable, not a rendering object, it renders once, making the value available for the key lookup inside the section:
Person *ignacio = [Person personWithName:@"Ignacio"];
id data = @{ @"person": ignacio };
// Renders "Hello Ignacio!"
NSString *templateString = @"{{# person }}Hello {{ name }}!{{/ person }}";
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
Just as variable tags, section tags render any well-formed expressions:
id data = @{
@"person": @{
@"friends": @[
[Person personWithName:@"José"],
[Person personWithName:@"Karl"],
[Person personWithName:@"Lubitza"]]
}
};
NSString *templateString = @"{{# each(person.friends) }}"
@" {{ @indexPlusOne }}: {{ name }}"
@"{{/}}";
// 1: José
// 2: Karl
// 3: Lubitza
NSString *rendering = [GRMustacheTemplate renderObject:data
fromString:templateString
error:NULL];
The each
filter is part of the standard library.
We have seen that values rendered by sections are made available for the key lookup inside the section.
As a matter of fact, objects that were the context of enclosing sections are still available: the latest object has just entered the top of the context stack.
An example should make this clearer. Let's consider the template below:
{{# title }}
{{ length }}
{{/ title }}
{{# title }}
{{ title }}
{{/ title }}
In the first section, the length
key will be fetched from the title
string which has just entered the context stack: it will be rendered as "6" if the title is "Hamlet".
In the last section, the title string is still the context. However it has no title
key. Thus GRMustache looks for it in the enclosing context, finds again the title string, and renders it:
6
Hamlet
This technique allows, for example, the conditional rendering of a <h1>
HTML tag if and only if the title is not empty (empty strings are considered false, see "false sections" above):
{{# title }}
<h1>{{ title }}</h1> {{! rendered if there is a title }}
{{/ title }}
These three template snippets are quite similar, but not stricly equivalent:
...{{# foo }}{{ bar }}{{/ foo }}...
...{{# foo }}{{ .bar }}{{/ foo }}...
...{{ foo.bar }}...
The first will look for bar
anywhere in the context stack, starting with the foo
object.
The two others are identical: they ensure the bar
key comes from the very foo
object. If foo
is not found, the bar
lookup will fail as well, regardless of bar
keys defined by enclosing contexts.
A priority key is always evaluated to the same value, regardless of objects that enter the context stack. Check the Security Guide.
Values extracted from the context stack are directly rendered unless you had some tag delegates enter the game. They help you render default values for missing keys, for example. See the Tag Delegates Guide for a full discussion.
As seen above, GRMustache looks for a key in your data objects with the objectForKeyedSubscript:
and valueForKey:
methods. If an object responds to objectForKeyedSubscript:
, this method is used. For other objects, valueForKey:
is used, as long as the key is safe (see the Security Guide).
GRMustache does not use valueForKey:
for NSArray, NSSet, and NSOrderedSet. Instead, it directly invokes methods of those objects. As a consequence, keys like count
, firstObject
, etc. can be used in templates.
NSDictionary never complains when asked for an unknown key. However, the default NSObject implementation of valueForKey:
raises an NSUndefinedKeyException
.
GRMustache catches those exceptions, so that the key lookup can continue down the context stack.
Some of you may feel uncomfortable with those exceptions. See the paragraph below.
Objective-C exceptions have several drawbacks, particularly:
- they play badly with autorelease pools, and are reputed to leak memory.
- they usually stop your debugger when you are developping your application.
The first point is indeed a matter of worry: Apple does not guarantee that exceptions raised by valueForKey:
do not leak memory. However, I never had any evidence of such a leak from NSObject's implementation.
Should you still worry, we recommend that you avoid the valueForKey:
method altogether. Instead, implement the keyed subscripting objectForKeyedSubscript:
method on objects that you provide to GRMustache.
The second point is valid also: NSUndefinedKeyException raised by template rendering may become a real annoyance when you are debugging your project, because it's likely you've told your debugger to stop on every Objective-C exceptions.
You can avoid them as well: make sure you invoke once, early in your application, the following method:
[GRMustache preventNSUndefinedKeyExceptionAttack];
Depending on the number of NSUndefinedKeyException that get prevented, you will experience a slight performance hit, or a performance improvement.
Since the main use case for this method is to avoid Xcode breaks on rendering exceptions, the best practice is to conditionally invoke this method, using the NS_BLOCK_ASSERTIONS that helps identifying the Debug configuration of your targets:
#if !defined(NS_BLOCK_ASSERTIONS)
// Debug configuration: keep GRMustache quiet
[GRMustache preventNSUndefinedKeyExceptionAttack];
#endif
The Mustache specification does not enforce the list of false values, the values that trigger or prevent the rendering of sections and inverted sections:
There is no guarantee that {{# value }}...{{/ value }}
and {{^ value }}...{{/ value }}
will render the same, provided with the exact same input, in all Mustache implementations.
That's unfortunate. Anyway, for the record, here is a reminder of all false values in GRMustache:
nil
and missing keys[NSNull null]
NSNumber
instances whoseboolValue
method returnsNO
- empty strings
@""
- empty enumerables.