Skip to content
Browse files

dot variable extension

  • Loading branch information...
1 parent 3d550c9 commit 070c126cf4b5516a90d16a3a43c9f90f7360b829 @groue committed Nov 2, 2010
View
49 Classes/GRMustacheContext.m
@@ -24,6 +24,45 @@
#import "GRMustacheContext_private.h"
+@interface GRMustacheValueWrapper: NSObject<GRMustacheContext> {
+ id value;
+}
+@property (nonatomic, retain) id value;
++ (id)wrapperWithValue:(id)value;
+- (id)initWithValue:(id)value;
+@end
+
+@implementation GRMustacheValueWrapper
+@synthesize value;
+
++ (id)wrapperWithValue:(id)value {
+ return [[[self alloc] initWithValue:value] autorelease];
+}
+
+- (id)initWithValue:(id)theValue {
+ if (self = [self init]) {
+ value = [theValue retain];
+ }
+ return self;
+}
+
+- (NSString *)description {
+ return [value description];
+}
+
+- (id)valueForKey:(NSString *)key {
+ return nil;
+}
+
+- (void)dealloc {
+ [value release];
+ [super dealloc];
+}
+
+@end
+
+
+
@interface GRMustacheContext()
- (id)initWithObject:(id)object;
@end
@@ -50,11 +89,13 @@ - (void)pushObject:(id)object {
[objects addObject:[NSNull null]];
break;
case GRMustacheObjectKindContext:
- case GRMustacheObjectKindLambda:
[objects addObject:object];
break;
+ case GRMustacheObjectKindTrueValue:
+ [objects addObject:[GRMustacheValueWrapper wrapperWithValue:object]];
+ break;
default:
- NSAssert(NO, @"object is not a NSDictionary, or does not conform to GRMustacheContext protocol, or is not a GRMustacheLambda.");
+ NSAssert(NO, ([NSString stringWithFormat:@"Invalid context object: %@", object]));
break;
}
}
@@ -65,10 +106,14 @@ - (void)pop {
- (id)valueForKey:(NSString *)key {
id value;
+ BOOL dotKey = [key isEqualToString:@"."];
for (id object in [objects reverseObjectEnumerator]) {
if (object == [NSNull null]) {
continue;
}
+ if (dotKey) {
+ return object;
+ }
value = [object valueForKey:key];
if (value != nil) {
return value;
View
7 Classes/GRMustacheSectionElement.m
@@ -73,13 +73,6 @@ - (NSString *)renderContext:(GRMustacheContext *)context {
break;
case GRMustacheObjectKindTrueValue:
- if (!inverted) {
- for (GRMustacheElement *elem in elems) {
- [buffer appendString:[elem renderContext:context]];
- }
- }
- break;
-
case GRMustacheObjectKindContext:
if (!inverted) {
[context pushObject:value];
View
6 GRMustache.xcodeproj/project.pbxproj
@@ -38,6 +38,7 @@
561159A4127F2C7900F01AD0 /* inner_partial.mustache in Resources */ = {isa = PBXBuildFile; fileRef = 561159A3127F2C7900F01AD0 /* inner_partial.mustache */; };
561159B7127F2D5F00F01AD0 /* inner_partial.txt in Resources */ = {isa = PBXBuildFile; fileRef = 561159B5127F2D5F00F01AD0 /* inner_partial.txt */; };
561159B8127F2D5F00F01AD0 /* template_partial.txt in Resources */ = {isa = PBXBuildFile; fileRef = 561159B6127F2D5F00F01AD0 /* template_partial.txt */; };
+ 56115B6E127FE08C00F01AD0 /* GRExtensionsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 56115B6D127FE08C00F01AD0 /* GRExtensionsTest.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -94,6 +95,8 @@
561159B5127F2D5F00F01AD0 /* inner_partial.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = inner_partial.txt; sourceTree = "<group>"; };
561159B6127F2D5F00F01AD0 /* template_partial.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = template_partial.txt; sourceTree = "<group>"; };
56115A3B127F32EA00F01AD0 /* GRMustacheTest-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GRMustacheTest-Info.plist"; sourceTree = "<group>"; };
+ 56115B6C127FE08C00F01AD0 /* GRExtensionsTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GRExtensionsTest.h; sourceTree = "<group>"; };
+ 56115B6D127FE08C00F01AD0 /* GRExtensionsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRExtensionsTest.m; sourceTree = "<group>"; };
56CF962A127EC5540043796D /* GRMustacheTest.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GRMustacheTest.octest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -196,6 +199,8 @@
5611586A127F17A000F01AD0 /* GRMustacheTest.m */,
56115993127F2B9300F01AD0 /* GRPartialTest.h */,
56115994127F2B9300F01AD0 /* GRPartialTest.m */,
+ 56115B6C127FE08C00F01AD0 /* GRExtensionsTest.h */,
+ 56115B6D127FE08C00F01AD0 /* GRExtensionsTest.m */,
);
path = Tests;
sourceTree = "<group>";
@@ -332,6 +337,7 @@
56115506127ED04F00F01AD0 /* GRMustacheTestBase.m in Sources */,
5611586B127F17A000F01AD0 /* GRMustacheTest.m in Sources */,
56115995127F2B9300F01AD0 /* GRPartialTest.m in Sources */,
+ 56115B6E127FE08C00F01AD0 /* GRExtensionsTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
41 README.md
@@ -3,7 +3,7 @@ GRMustache
GRMustache is an Objective-C implementation of the [Mustache](http://mustache.github.com/) logic-less template engine.
-It's been highly inspired by the Mustache [go implementation](http://github.com/hoisie/mustache.go/). Its tests are based on the [Ruby](http://github.com/defunkt/mustache) one, that we have considered as a reference.
+Its implementation has been highly inspired by the Mustache [go implementation](http://github.com/hoisie/mustache.go/). Its tests are based on the [Ruby](http://github.com/defunkt/mustache) one, that we have considered as a reference.
It supports the following Mustache features:
@@ -16,6 +16,10 @@ It supports the following Mustache features:
- lambda sections
- partials and recursive partials
+It supports some extensions to the regular [Mustache syntax](http://mustache.github.com/mustache.5.html):
+
+- `{{.}}` dot variable tag
+
Embedding in your XCode project
-------------------------------
@@ -213,9 +217,7 @@ Such a section is rendered according to the value for key `name` in the context.
When `nil`, `[NSNull null]`, or empty enumerable object, the section is rendered with an empty string.
-If the value is a `NSDictionary`, or conforms to the `GRMustacheContext` protocol, the section is rendered within a context extended by the value.
-
-Otherwise, the section is rendered within the same context.
+Otherwise, the section is rendered within a context extended by the value.
### Inverted sections `{{^name}}...{{/name}}`
@@ -243,6 +245,37 @@ Depending on the method which has been used to create the original template, the
Recursive partials are possible. Just avoid infinite loops in your context objects.
+Extensions
+----------
+
+The Mustache syntax is described at [http://mustache.github.com/mustache.5.html](http://mustache.github.com/mustache.5.html).
+
+GRMustache adds the following extensions:
+
+### Dot Variable tag `{{.}}`
+
+This extension has been inspired by the dot variable tag introduced in [mustache.js](http://github.com/janl/mustache.js).
+
+This variable tags output the `description` of the current context.
+
+For instance:
+
+ NSString *templateString = @"{{#name}}: <ul>{{#item}}<li>{{.}}</li>{{/item}}</ul>";
+ NSDictionary *context = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"Groue's shopping cart", @"name",
+ [NSArray arrayWithObjects: @"beer", @"ham", nil], @"item",
+ nil];
+ // Returns @"Groue's shopping cart: <ul><li>bear</li><li>ham</li></ul>"
+ [GRMustacheTemplate renderObject:context fromString:templateString error:nil];
+
+Beware that only dictionaries and objects conforming to `GRMustacheContext` protocol are the only objects whose KVC capabilities are used.
+
+This allows both those templates to render the same thing, when the key `name` refers to a `NSString`:
+
+ @"{{#name}}{{.}}{{/name}}"
+ @"{{#name}}{{name}}{{/name}}" // would raise if key `name` was loaded from a string
+
+
Errors
------
View
30 Tests/GRExtensionsTest.h
@@ -0,0 +1,30 @@
+// The MIT License
+//
+// Copyright (c) 2010 Gwendal Roué
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "GRMustacheTestBase.h"
+
+
+@interface GRExtensionsTest : GRMustacheTestBase {
+
+}
+
+@end
View
72 Tests/GRExtensionsTest.m
@@ -0,0 +1,72 @@
+// The MIT License
+//
+// Copyright (c) 2010 Gwendal Roué
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#import "GRExtensionsTest.h"
+
+
+@implementation GRExtensionsTest
+
+- (void)testDotVariable {
+ NSString *templateString = @"{{.}}";
+ NSString *result = [GRMustacheTemplate renderObject:@"foobar" fromString:templateString error:nil];
+ STAssertEqualObjects(result, @"foobar", nil);
+}
+
+- (void)testDotVariableInEnumeration {
+ NSString *templateString = @"{{#names}}{{.}}{{/names}}";
+ NSDictionary *context = [NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:
+ @"foo",
+ @"bar",
+ nil
+ ]
+ forKey:@"names"];
+ NSString *result = [GRMustacheTemplate renderObject:context fromString:templateString error:nil];
+ STAssertEqualObjects(result, @"foobar", nil);
+}
+
+- (void)testNonGRMustacheContextCanDefineBooleanSection {
+ NSString *templateString = @"{{#item}}{{#name}}{{name}},{{/name}}{{/item}}";
+ NSDictionary *context = [NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:
+ [NSDictionary dictionaryWithObject:@"foo" forKey:@"name"],
+ [NSDictionary dictionary],
+ [NSDictionary dictionaryWithObject:@"bar" forKey:@"name"],
+ nil
+ ]
+ forKey:@"item"];
+ NSString *result = [GRMustacheTemplate renderObject:context fromString:templateString error:nil];
+ STAssertEqualObjects(result, @"foo,bar,", nil);
+}
+
+- (void)testDotVariableInNonGRMustacheContextBooleanSection {
+ NSString *templateString = @"{{#item}}{{#name}}{{.}},{{/name}}{{/item}}";
+ NSDictionary *context = [NSDictionary dictionaryWithObject:[NSArray arrayWithObjects:
+ [NSDictionary dictionaryWithObject:@"foo" forKey:@"name"],
+ [NSDictionary dictionary],
+ [NSDictionary dictionaryWithObject:@"bar" forKey:@"name"],
+ nil
+ ]
+ forKey:@"item"];
+ NSString *result = [GRMustacheTemplate renderObject:context fromString:templateString error:nil];
+ STAssertEqualObjects(result, @"foo,bar,", nil);
+}
+
+@end
View
5 Tests/GRPartialTest.h
@@ -23,8 +23,5 @@
#import "GRMustacheTestBase.h"
-@interface PPartialTest : GRMustacheTestBase {
-
-}
-
+@interface PPartialTest : GRMustacheTestBase
@end

0 comments on commit 070c126

Please sign in to comment.
Something went wrong with that request. Please try again.