Skip to content

Commit

Permalink
template loaders guide
Browse files Browse the repository at this point in the history
  • Loading branch information
groue committed Feb 9, 2012
1 parent 40378b2 commit c978751
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -19,7 +19,7 @@ What you get

**Compatibility with other Mustache implementations**: [Mustache specification v1.1.2](https://github.com/mustache/spec) conformance, and a touch of [Handlebars.js](https://github.com/wycats/handlebars.js) (details in [guides/flavors.md](GRMustache/blob/master/guides/flavors.md)).

**Compatibility with previous GRMustache versions**: update GRMustache, enjoy performance improvements and bugfixes, and don't change a line of your code.
**Compatibility with previous GRMustache versions**: update GRMustache, enjoy performance improvements and bugfixes, and don't change a line of your code. Check the [release notes](RELEASE_NOTES.md).

**Number and date formatting.** Handy, and built-in.

Expand Down
2 changes: 1 addition & 1 deletion guides/flavors.md
Expand Up @@ -20,7 +20,7 @@ How to choose a flavor

The only difference so far between the two flavors implementation lies in the syntax for key paths: genuine Mustache reads `{{foo.bar.baz}}`, Handlebars reads `{{foo/bar/baz}}`, and even `{{../foo/bar/baz}}`.

If your templates do not use compound key paths, you can ignore this guide entirely.
If your templates do not use compound key paths, you can [skip](forking.md) this guide entirely.

If you are designing new templates from scratch, we encourage you writing your templates in the genuine Mustache flavor. Beware that GRMustache defaults to Handlebars: keep on reading.

Expand Down
4 changes: 2 additions & 2 deletions guides/runtime/booleans.md
Expand Up @@ -26,7 +26,7 @@ We'll first talk about some simple cases. We'll then discuss caveats.
The simplest way to provide booleans to GRMustache is to provide objects returned by the `[NSNumber numberWithBool:]` method:

NSString *templateString = @"{{#pretty}}whistle{{/pretty}}";
GRMustacheTemplate *template = [GRMustacheTemplate parseString:templateString error:nil];
GRMustacheTemplate *template = [GRMustacheTemplate parseString:templateString error:NULL];

// @"whistle"
[template renderObject:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
Expand Down Expand Up @@ -106,7 +106,7 @@ For the very same reason, you should not use your property getters in templates:

// Note the use of the property getter, this time
NSString *templateString = @"{{#isPretty}}whistle{{/isPretty}}";
GRMustacheTemplate *template = [GRMustacheTemplate parseString:templateString error:nil];
GRMustacheTemplate *template = [GRMustacheTemplate parseString:templateString error:NULL];

// @"whistle"
[template renderObject:dave];
Expand Down
2 changes: 1 addition & 1 deletion guides/runtime/context_stack.md
Expand Up @@ -41,7 +41,7 @@ For instance, the following code will ask `@"foo"` for the key `XXX`. The string

[GRMustacheTemplate renderObject:@"foo"
fromString:@"{{XXX}}"
error:nil];
error:NULL];

When debugging your project, those exceptions may become a real annoyance, because you tell your debugger to stop on every Objective-C exceptions.

Expand Down
141 changes: 140 additions & 1 deletion guides/template_loaders.md
@@ -1,4 +1,143 @@
[up](../../../../GRMustache), [next](runtime.md)

Template loaders
================

TODO
GRMustache provides [convenient methods](templates.md) for loading UTF8-encoded templates and partials from the file system.

However, we may have more needs: your templates and partials hierarchy may not be stored in the file system. If they are, they may not be UTF8-encoded.

This is where the GRMustacheTemplateLoader class comes in. GRMustacheTemplateLoader objects are generally initialized with a template source, a location where to load templates from. Their role is then to provide template strings whenever a template or a partial is needed.

Loading templates from the file system
--------------------------------------

GRMustacheTemplateLoader ships with the following class methods:

// Loads templates and partials from a directory, with "mustache" extension, encoded in UTF8 (from MacOS 10.6 and iOS 4.0)
+ (id)templateLoaderWithBaseURL:(NSURL *)url;

// Loads templates and partials from a directory, with provided extension, encoded in UTF8 (from MacOS 10.6 and iOS 4.0)
+ (id)templateLoaderWithBaseURL:(NSURL *)url
extension:(NSString *)ext;

// Loads templates and partials from a directory, with provided extension, encoded in provided encoding (from MacOS 10.6 and iOS 4.0)
+ (id)templateLoaderWithBaseURL:(NSURL *)url
extension:(NSString *)ext
encoding:(NSStringEncoding)encoding;

// Loads templates and partials from a directory, with "mustache" extension, encoded in UTF8
+ (id)templateLoaderWithDirectory:(NSString *)path;

// Loads templates and partials from a directory, with provided extension, encoded in UTF8
+ (id)templateLoaderWithDirectory:(NSString *)path
extension:(NSString *)ext;

// Loads templates and partials from a directory, with provided extension, encoded in provided encoding
+ (id)templateLoaderWithDirectory:(NSString *)path
extension:(NSString *)ext
encoding:(NSStringEncoding)encoding;

// Loads templates and partials from a bundle, with "mustache" extension, encoded in UTF8
+ (id)templateLoaderWithBundle:(NSBundle *)bundle;

// Loads templates and partials from a bundle, with provided extension, encoded in UTF8
+ (id)templateLoaderWithBundle:(NSBundle *)bundle
extension:(NSString *)ext;

// Loads templates and partials from a bundle, with provided extension, encoded in provided encoding
+ (id)templateLoaderWithBundle:(NSBundle *)bundle
extension:(NSString *)ext
encoding:(NSStringEncoding)encoding;

For instance:

GRMustacheTemplateLoader *loader = [GRMustacheTemplate templateLoaderWithBaseURL:...];

You may now load a template from its location:

GRMustacheTemplate *template = [loader parseTemplateNamed:@"document" error:NULL];

You may also have the loader parse a template string. Only partials would then be loaded from the loader's location:

GRMustacheTemplate *template = [loader parseString:@"..." error:NULL];

The rendering is done as usual:

NSString *rendering = [template renderObject:...];

Other sources for templates
---------------------------

GRMustache allows you to subclass the GRMustacheTemplateLoader in order to load templates from any location.

We provide below the implementation of a template loader which loads partials from a dictionary containing template strings.

The header file:

#import "GRMustache.h"

@interface DictionaryTemplateLoader : GRMustacheTemplateLoader
+ (id)loaderWithDictionary:(NSDictionary *)templatesByName;
@end

In our implementation file, import the `GRMustacheTemplateLoader_protected.h` header, dedicated to GRMustacheTemplateLoader subclasses:

#import "GRMustacheTemplateLoader_protected.h"

@interface DictionaryTemplateLoader()
@property (nonatomic, retain) NSDictionary *templatesByName;
@end

@implementation DictionaryTemplateLoader
@synthetise templatesByName;

+ (id)loaderWithDictionary:(NSDictionary *)templatesByName {
// initWithExtension:encoding: is the designated initializer.
// provide it with some values, even if we won't use them.
DictionaryTemplateLoader *loader = [[[self alloc] initWithExtension:nil encoding:NSUTF8StringEncoding] autorelease];
loader.templatesByName = templatesByName;
return loader;
}

- (void)dealloc {
self.templatesByName = nil;
[super dealloc];
}

Now let's implement the `templateIdForTemplateNamed:relativeToTemplateId:` method.

Provided with a partial name that comes from a `{{>name}}` mustache tag, it should return an object which uniquely identifies a template. In our case, we ignore the second argument that would come in handy when implementing a partial hierarchy. However, the template name looks like a perfect way to identify the partials:

- (id)templateIdForTemplateNamed:(NSString *)name relativeToTemplateId:(id)baseTemplateId {
return name;
}

And finally, we have to provide template strings:

- (NSString *)templateStringForTemplateId:(id)templateId error:(NSError **)outError {
return [self.templatesByName objectForKey:templateId];
}

@end

Now we may instanciate one:

NSDictionary *templates = [NSDictionary dictionaryWithObject:@"It works!" forKey:@"partial"];
DictionaryTemplateLoader *loader = [DictionaryTemplateLoader loaderWithDictionary:templates];

Then load templates from it:

GRMustacheTemplate *template1 = [loader parseString:@"{{>partial}}" error:NULL];
GRMustacheTemplate *template2 = [loader parseTemplateNamed:@"partial" error:NULL];

And finally render:

[template1 render]; // "It works!"
[template2 render]; // "It works!"


Flavors
-------

Remember GRMustache supports two flavors of the Mustache language: check [guides/flavors.md](flavors.md)
4 changes: 2 additions & 2 deletions guides/templates.md
@@ -1,4 +1,4 @@
[up](../../../../GRMustache), [next](runtime.md)
[up](../../../../GRMustache), [next](template_loaders.md)

Templates
=========
Expand Down Expand Up @@ -123,4 +123,4 @@ More loading options

All methods above load UTF8-encoded templates and partials from disk. If this does not fulfill your needs, check [guides/template_loaders.md](template_loaders.md)

[up](../../../../GRMustache), [next](runtime.md)
[up](../../../../GRMustache), [next](template_loaders.md)

0 comments on commit c978751

Please sign in to comment.