- Overview
- GRMustacheRendering protocol
- Trivial Example
- Example: Wrapping the content of a section tag
- Example: Have a section render an alternate template string
- Example: Dynamic partials
- Example: Objects that render themselves
- Example: A Handlebars.js Helper
- More Sample Code
- Compatibility with other Mustache implementations
The Runtime Guide describes what happens whenever a tag such as {{ name }}
or {{# items }}...{{/ items }}
gets rendered. Strings are HTML-escaped, arrays are iterated, numbers control boolean sections, etc.
But sometimes you need something more dynamic, you need to inject your own code into the template rendering, and extend the language. Orthodox Mustache provides with "lambda sections". Handlebars.js, an extended Mustache engine, has introduced "helpers".
Let us introduce GRMustache "rendering objects".
Rendering objects are quite versatile. Let's look at a few examples:
{{# localize }}...{{/ }}
localize
is part of the standard library. It performs a custom rendering by localizing the inner content of the section it renders.
{{> partial }} vs. {{ template }}
The {{> partial }}
tag renders a hard-coded template, identified by its name. By providing instead a GRMustacheTemplate object to a tag, which performs its own custom rendering, you can render a "dynamic partial".
{{# dateFormat }}...{{ birthDate }}...{{ joinDate }}...{{/ }}
NSDateFormatter is a rendering object able to format all dates in a section.
I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.
pluralize
is a filter that returns an object able to pluralize the content of the section (see sample code in issue #50).
{{# each(items) }}{{ @index }}: {{ name }}{{/ }}
each
is part of the standard library. It returns rendering objects that define extra keys such as @index
.
All examples above are built using public GRMustache APIs. Even the built-in ones such as localize
, each
, or the date formatter. Your own rendering objects are not artificially limited.
The last two examples involve filters. Filters themselves do not provide custom rendering: they just transform values. However, when they return objects that provide custom rendering, the fun can begin. This two-fold pattern is how GRMustache let you implement Handlebars-like helpers.
Let's begin the detailed tour.
This protocol declares the method that all rendering objects must implement:
@protocol GRMustacheRendering <NSObject>
- (NSString *)renderForMustacheTag:(GRMustacheTag *)tag
context:(GRMustacheContext *)context
HTMLSafe:(BOOL *)HTMLSafe
error:(NSError **)error;
@end
-
The tag represents the tag you must render for. It may be a variable tag
{{ name }}
, a section tag{{# name }}...{{/}}
, etc. -
The context represents the context stack, and all information that tags need to render.
-
HTMLSafe is a pointer to a BOOL: upon return, it must be set to YES or NO, depending on the safety of the string you render. If you forget to set it, it is of course assumed to be NO. Returning NO would have GRMustache HTML-escape the returned string before inserting it in the final rendering of HTML templates (see the HTML vs. Text Templates Guide for more information).
-
error is... the eventual error. You can return nil without setting any error: in this case, everything happens as if you returned the empty string.
See the GRMustacheTag Class Reference and GRMustacheContext Class Reference for a full documentation of GRMustacheTag and GRMustacheContext.
The +[GRMustacheRendering renderingObjectWithBlock:]
method comes in handy for creating a rendering object without declaring any class:
id renderingObject = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error)
{
switch(tag.type) {
case GRMustacheTagTypeVariable:
return @"I'm rendering a {{ variable }} tag.";
case GRMustacheTagTypeSection:
return @"I'm rendering a {{# regular }}...{{/ }} section tag.";
}
}];
Document.mustache
:
{{ name }}
{{{ name }}}
Render.m
:
id nameRenderingObject = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error)
{
return @"Arthur & Cie";
}];
id data = @{ @"name": nameRenderingObject };
NSString *rendering = [GRMustacheTemplate renderObject:data
fromResource:@"Document"
bundle:nil
error:NULL];
Final rendering:
Arthur & Cie
Arthur & Cie
Rendering objects are not difficult to trigger: when you know how to have a tag {{ name }}
render a regular value, you know how to have it handled by a rendering object.
The HTML escaping is negociated between the tag and the rendering object: {{ name }}
escapes HTML, when {{{ name }}}
does not. Since the rendering object does not explicitly set the HTMLSafe parameter to YES, the first tag escapes the result.
Let's write a rendering object which wraps a section in a <strong>
HTML tag:
Document.mustache
:
{{# strong }}
{{ name }} is awesome.
{{/ strong }}
Render.m
:
id strong = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error)
{
// First perform a raw rendering of the tag, using its
// `renderContentWithContext:HTMLSafe:error` method.
//
// We'll get `Arthur is awesome.`
NSString *rawRendering = [tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error];
// Return the raw rendering, wrapped in a <strong> HTML tag:
return [NSString stringWithFormat:@"<strong>%@</strong>", rawRendering];
}];
id data = @{
@"name": @"Arthur",
@"strong": strong,
};
NSString *rendering = [GRMustacheTemplate renderObject:data
fromResource:@"Document"
bundle:nil
error:NULL];
Final rendering:
<strong>Arthur is awesome.</strong>
Variable tags such as {{ name }}
don't have much inner content. But section tags do: {{# name }} inner content {{/ name }}
.
The renderContentWithContext:HTMLSafe:error:
method returns the rendering of the inner content, processing all the inner tags with the provided context.
It also sets the HTMLSafe
boolean for you, so that you do not have to worry about it. GRMustache templates render HTML by default, so HTMLSafe
will generally be YES. There are also text templates (see the HTML vs. Text Templates Guide): in this case, HTMLSafe
would be NO. Depending on how reusable you want your rendering object to be, you may have to deal with it.
Your rendering objects can thus delegate their rendering to the tag they are given. They can render the tag once or many times:
Document.mustache
:
{{# twice }}
Mustache is awesome!
{{/ twice }}
Render.m
:
id data = @{
@"twice": [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) {
NSMutableString *buffer = [NSMutableString string];
[buffer appendString:[tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error]];
[buffer appendString:[tag renderContentWithContext:context HTMLSafe:HTMLSafe error:error]];
return buffer;
}]
};
NSString *rendering = [GRMustacheTemplate renderObject:data
fromResource:@"Document"
bundle:nil
error:NULL];
Final rendering:
Mustache is awesome!
Mustache is awesome!
Let's write a rendering object that wraps a section in a HTML link. The URL of the link will be fetched with the url
key:
Document.mustache
:
{{# link }}{{ firstName }} {{ lastName }}{{/ link }}
Render.m
:
id link = [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error)
{
// Build an alternate template string by wrapping the inner content of
// the section in a `<a>` HTML tag:
//
// We'll get `<a href="{{ url }}">{{ firstName }} {{ lastName }}</a>`
NSString *innerTemplateString = tag.innerTemplateString;
NSString *format = @"<a href=\"{{ url }}\">%@</a>";
NSString *templateString = [NSString stringWithFormat:format, innerTemplateString];
// Build a new template, and return its rendering:
GRMustacheTemplate *template = [GRMustacheTemplate templateFromString:templateString error:NULL];
return [template renderContentWithContext:context HTMLSafe:HTMLSafe error:error];
}];
id data = @{
@"firstName": @"Orson",
@"lastName": @"Welles",
@"url": @"/people/123",
@"link": link,
};
NSString *rendering = [GRMustacheTemplate renderObject:data
fromResource:@"Document"
bundle:nil
error:NULL];
Final rendering:
<a href="/people/123">Orson Welles</a>
Again, variable tags such as {{ name }}
don't have much inner content, but section tags do: {{# name }} inner content {{/ name }}
.
The innerTemplateString
property returns the raw content of the section, with Mustache tags left untouched.
You can derive new template strings from this raw content, even by appending new tags to it (the {{ url }}
tag, above).
From those template strings, you create template objects, just as you usually do. Their renderContentWithContext:HTMLSafe:error:
method render in the given context.
The template sets the HTMLSafe
boolean for you, so that you do not have to worry about it.
When a {{> name }}
Mustache tag occurs in a template, GRMustache renders in place the content of another template, the partial, identified by its name.
Such partials are hard-coded.
You can still choose the rendered partial at runtime, with simple variable tags:
Document.mustache
:
{{# items }}
- {{ link }}
{{/ items }}
MovieLink.mustache
:
<a href="{{ url }}">{{ title }}</a>
PersonLink.mustache
:
<a href="{{ url }}">{{ firstName }} {{ lastName }}</a>
Render.m
:
id data = @{
@"items": @[
@{
@"title": @"Citizen Kane",
@"url":@"/movies/321",
@"link": [GRMustacheTemplate templateFromResource:@"MovieLink" bundle:nil error:nil],
},
@{
@"firstName": @"Orson",
@"lastName": @"Welles",
@"url":@"/people/123",
@"link": [GRMustacheTemplate templateFromResource:@"PersonLink" bundle:nil error:nil],
},
],
};
NSString *rendering = [GRMustacheTemplate renderObject:data
fromResource:@"Document"
bundle:nil
error:NULL];
Final rendering:
- <a href="/movies/123">Citizen Kane</a>
- <a href="">Orson Welles</a>
Let's say a handy technique: we haven't use the GRMustacheRendering
protocol here, because GRMustacheTemplate
does it for us.
Let's implement something similar to Ruby on Rails's <%= render @movie %>
:
Document.mustache
:
{{ movie }}
Movie.mustache
:
{{ title }} by {{ director }}
Person.mustache
:
{{ firstName }} {{ lastName }}
Render.m
:
Person *director = [Person personWithFirstName:@"Orson" lastName:@"Welles"];
Movie *movie = [Movie movieWithTitle:@"Citizen Kane" director:director];
id data = @{ @"movie": movie };
NSString *rendering = [GRMustacheTemplate renderObject:data
fromResource:@"Document"
bundle:nil
error:NULL];
Final rendering:
<Movie: 0x1011052a0>
Oops. GRMustache is written in Objective-C, not Ruby: there is no built-in automagic rendering of an object with a partial, through some conversion from a class name to a partial name.
We have to explicitely have our Movie and Person classes render with their dedicated Movie.mustache and Person.mustache partials:
// Declare categories on our classes so that they conform to the
// GRMustacheRendering protocol:
@interface Movie(GRMustache)<GRMustacheRendering>
@end
@interface Person(GRMustache)<GRMustacheRendering>
@end
// Now implement the protocol:
@implementation Movie(GRMustache)
- (NSString *)renderForMustacheTag:(GRMustacheTag *)tag context:(GRMustacheContext *)context HTMLSafe:(BOOL *)HTMLSafe error:(NSError **)error
{
// Extract the "Movie.mustache" partial:
GRMustacheTemplate *partial = [GRMustacheTemplate templateFromResource:@"Movie" bundle:nil error:NULL];
// Add self to the top of the context stack, so that the partial
// can access our keys:
context = [context contextByAddingObject:self];
// Return the rendering of the partial:
return [partial renderContentWithContext:context HTMLSafe:HTMLSafe error:error];
}
@end
@implementation Person(GRMustache)
- (NSString *)renderForMustacheTag:(GRMustacheTag *)tag context:(GRMustacheContext *)context HTMLSafe:(BOOL *)HTMLSafe error:(NSError **)error
{
// Extract the "Person.mustache" partial:
GRMustacheTemplate *partial = [GRMustacheTemplate templateFromResource:@"Person" bundle:nil error:NULL];
// Add self to the top of the context stack, so that the partial
// can access our keys:
context = [context contextByAddingObject:self];
// Return the rendering of the partial:
return [template renderContentWithContext:context HTMLSafe:HTMLSafe error:error];
}
@end
Final rendering:
Citizen Kane by Orson Welles
Two useful things:
-
GRMustacheRendering
is a protocol.Surely
+[GRMustacheRendering renderingObjectWithBlock:]
is convenient since it lets us create rendering objects from scratch. Yet the GRMustacheRendering protocol is available for you to use on your custom classes.You can even mix it with the GRMustacheFilter protocol. The conformance to both protocols gives you objects with multiple facets. For example, the NSFormatter class takes this opportunity to format values, as in
{{ format(value) }}
, and to format all variable tags in a section, when used as in{{# format }}...{{ value1 }}...{{ value2 }}...{{/ }}
. -
Rendering objects manage the context stack.
When GRMustache renders
{{ name }}
, it looks for thename
key in the context stack: for the title and names of our movies and people to render, movies and people must enter the context stack. This is the reason for the derivation of new contexts, using thecontextByAddingObject:
method, before partials are rendered.
See the GRMustacheContext Class Reference for a full documentation of the GRMustacheContext class.
From http://handlebarsjs.com/block_helpers.html:
For instance, let's create an iterator that creates a
<ul>
wrapper, and wraps each resulting element in an<li>
.{{#list nav}} <a href="{{url}}">{{title}}</a> {{/list}}
Let's build this "helper" with GRMustache:
Document.mustache
:
{{# list(nav) }}
<a href="{{url}}">{{title}}</a>
{{/ }}
Render.m
:
// Load the template
GRMustacheTemplate *template = [GRMustacheTemplate templateFromResource:@"Document" bundle:nil error:NULL];
// Extend the base context of the template, so that the "list" helper gets
// registered for all renderings.
id customHelperLibrary = @{
// `list` is a filter that takes an array, and returns a rendering object:
@"list": [GRMustacheFilter filterWithBlock:^id(NSArray *items) {
return [GRMustacheRendering renderingObjectWithBlock:^NSString *(GRMustacheTag *tag, GRMustacheContext *context, BOOL *HTMLSafe, NSError **error) {
NSMutableString *buffer = [NSMutableString string];
// Open the <ul> element.
[buffer appendString:@"<ul>"];
// Append a <li> element for each item.
for (id item in items) {
// Have each item enter the context stack on its turn...
GRMustacheContext *itemContext = [context contextByAddingObject:item];
// ... and render the inner content of the section.
NSString *itemRendering = [tag renderContentWithContext:itemContext HTMLSafe:HTMLSafe error:error];
// Wrap in a <li> element.
[buffer appendString:[NSString stringWithFormat:@"<li>%@</li>", itemRendering]];
}
// Close the <ul> tag, and return.
[buffer appendString:@"</ul>"];
return buffer;
}];
}]
};
[template extendBaseContextWithObject:customHelperLibrary];
// Set up rendered data, and render
id obj = @{
@"nav": @[
@{ @"url": @"http://mustache.github.io", @"title": @"Mustache" },
@{ @"url": @"http://github.com/groue/GRMustache", @"title": @"GRMustache" },
]
};
NSString *rendering = [template renderObject:obj error:NULL];
Final rendering:
<ul>
<li><a href="http://mustache.github.io">Mustache</a></li>
<li><a href="http://github.com/groue/GRMustache">GRMustache</a></li>
</ul>
The implementation of the Handlebars helper is fundamentally identical:
Handlebars.registerHelper('list', function(context, options) {
// Open the <ul> element.
var ret = "<ul>";
// Append a <li> element for each item.
for(var i=0, j=context.length; i<j; i++) {
// Render item
var itemRendering = options.fn(context[i]);
// Wrap in a <li> element.
ret = ret + "<li>" + itemRendering + "</li>";
}
// Close the <ul> tag, and return.
return ret + "</ul>";
});
A fundamental technique for advanced rendering: filters that return rendering objects.
You have more sample code in issue #50, which shows a helper able to pluralize the inner content of its section:
I have {{ cats.count }} {{# pluralize(cats.count) }}cat{{/ }}.
The each
filter of the standard library uses the protocol to make special keys such as @index
and @first
available to templates.
The localize
helper of the standard library uses the protocol to localize full template sections, as in {{# localize }}Hello {{ name }}{{/ localize }}
.
NSFormatter instances are rendering objets as well, so that {{# decimal }}{{ x }} + {{ y }} = {{ sum }}{{/ decimal }}
would render nice decimal numbers. Check the NSFormatter Guide.
Issue #50 contains sample code for pluralizing the inner content of a section.
The Mustache specification does not have the concept of "rendering objects".
However, many of the techniques seen above can be compared to "Mustache lambdas".
You can write specification-compliant "Mustache lambdas" with rendering objects. However those are more versatile.
As a consequence, if your goal is to design templates that are compatible with other Mustache implementations, use GRMustacheRendering
with great care.