Permalink
Browse files

Rewrite indexes sample code with filters (WIP)

  • Loading branch information...
1 parent ea8f1a8 commit e741dbcd72ff3c68875826ad5387a22bd7a6c3b6 @groue committed Aug 3, 2012
Showing with 217 additions and 51 deletions.
  1. +145 −2 Guides/sample_code/indexes.md
  2. +72 −49 Guides/sample_code/indexes/GRMustacheIndexes/PositionFilter.m
@@ -3,11 +3,14 @@
Indexes
=======
+In a genuine Mustache way
+-------------------------
+
Mustache is a simple template language. Its [specification](https://github.com/mustache/spec) does not provide any built-in access to loop indexes. It does not provide any way to render a section at the beginning of the loop, and another section at the end. It does not help you render different sections for odd and even indexes.
If your goal is to design your templates so that they are compatible with [other Mustache implementations](https://github.com/defunkt/mustache/wiki/Other-Mustache-implementations), the best way to render indices and provide custom looping logic is to have each of your data objects provide with its index, regardless of how tedious it may be for you to prepare the rendered data.
-For instance, instead of `[ { name:'Alice' }, { name:'Bob' } ]`, you would provide: `[ { name:'Alice', index:0, first:true, last:false, even:false }, { name:'Bob', index:1, first:false, last:true, even:true } ]`.
+For instance, instead of `[ { name:'Alice' }, { name:'Bob' } ]`, you would provide: `[ { name:'Alice', position:1, isFirst:true, isOdd:true }, { name:'Bob', position:2, isFirst:false, isOdd:false } ]`.
GRMustache solution: filters
@@ -139,10 +142,150 @@ We have everything we need to render our template:
}
```
-Now it's time to implement this nifty filter.
+Now it's time to implement this nifty PositionFilter filter.
+
+We have already seen above its declaration: it's simply a class that conforms to the GRMustacheFilter protocol:
+
+```objc
+@interface PositionFilter : NSObject<GRMustacheFilter>
+@end
+```
+
+As such, it must implement the `transformedValue:` method:
+
+```objc
+@implementation PositionFilter
+- (id)transformedValue:(id)object
+{
+ return ...;
+}
+```
+
+Provided with an array, it returns another array filled with objects "that forward all keys to the original array items, but the following: position, isOdd, isFirst". We have to implement those objects as well. In order to do their job, they have to now both the original item in the original array, and its index. Here is the declaration of those objects:
+
+```objc
+/**
+ * PositionFilterItem's responsability is, given an array and an index, to
+ * forward to the original item in the array all keys but:
+ *
+ * - position: returns the 1-based index of the item
+ * - isOdd: returns YES if the position of the item is odd
+ * - isFirst: returns YES if the item is at position 1
+ *
+ * All other keys are forwared to the original item.
+ */
+@interface PositionFilterItem : NSObject
+@property (nonatomic, readonly) NSUInteger position;
+@property (nonatomic, readonly) BOOL isFirst;
+@property (nonatomic, readonly) BOOL isOdd;
+- (id)initWithObjectAtIndex:(NSUInteger)index inArray:(NSArray *)array;
+@end
+```
+
+Now we can implement the PositionFilter class itself:
+
+```objc
+@implementation PositionFilter
+
+/**
+ * GRMustacheFilter protocol required method
+ */
+- (id)transformedValue:(id)object
+{
+ /**
+ * Let's first validate the input: we can only filter arrays.
+ */
+
+ NSAssert([object isKindOfClass:[NSArray class]], @"Not an NSArray");
+ NSArray *array = (NSArray *)object;
+
+
+ /**
+ * Let's return a new array made of PositionFilterItem instances.
+ * They will provide the `position`, `isOdd` and `isFirst` keys while
+ * letting original array items provide the other keys.
+ */
+
+ NSMutableArray *replacementArray = [NSMutableArray arrayWithCapacity:array.count];
+ [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ PositionFilterItem *item = [[PositionFilterItem alloc] initWithObjectAtIndex:idx inArray:array];
+ [replacementArray addObject:item];
+ }];
+ return replacementArray;
+}
+
+@end
+```
+
+And finally, write the PositionFilterItem implementation:
+
+```objc
+@implementation PositionFilterItem {
+ /**
+ * The original 0-based index and the array of original items are stored in
+ * ivars without any exposed property: we do not want GRMustache to render
+ * {{ index }} or {{ array }}
+ */
+ NSUInteger _index;
+ NSArray *_array;
+}
+- (id)initWithObjectAtIndex:(NSUInteger)index inArray:(NSArray *)array
+{
+ self = [super init];
+ if (self) {
+ _index = index;
+ _array = array;
+ }
+ return self;
+}
+/**
+ * The implementation of `description` is required so that whenever GRMustache
+ * wants to render the original item itself (with a `{{ . }}` tag, for
+ * instance).
+ */
+- (NSString *)description
+{
+ id originalObject = [_array objectAtIndex:_index];
+ return [originalObject description];
+}
+/**
+ * Support for `{{position}}`: return a 1-based index.
+ */
+- (NSUInteger)position
+{
+ return _index + 1;
+}
+
+/**
+ * Support for `{{isFirst}}`: return YES if element is the first
+ */
+- (BOOL)isFirst
+{
+ return _index == 0;
+}
+
+/**
+ * Support for `{{isOdd}}`: return YES if element's position is odd.
+ */
+- (BOOL)isOdd
+{
+ return (_index % 2) == 0;
+}
+
+/**
+ * Support for other keys: forward to original array element
+ */
+- (id)valueForUndefinedKey:(NSString *)key
+{
+ id originalObject = [_array objectAtIndex:_index];
+ return [originalObject valueForKey:key];
+}
+
+@end
+```
**[Download the code](../../../../tree/master/Guides/sample_code/indexes)**
@@ -22,10 +22,10 @@
#import "PositionFilter.h"
-#pragma mark - PositionFilterProxy
+#pragma mark - PositionFilterItem declaration
/**
- * PositionFilterProxy's responsability is, given an array and an index, to
+ * PositionFilterItem's responsability is, given an array and an index, to
* forward to the original item in the array all keys but:
*
* - position: returns the 1-based index of the item
@@ -34,92 +34,115 @@
*
* All other keys are forwared to the original item.
*/
-@interface PositionFilterProxy : NSObject {
- NSUInteger _index;
-}
+@interface PositionFilterItem : NSObject
@property (nonatomic, readonly) NSUInteger position;
-@property (nonatomic, strong) NSArray *array;
+@property (nonatomic, readonly) BOOL isFirst;
+@property (nonatomic, readonly) BOOL isOdd;
- (id)initWithObjectAtIndex:(NSUInteger)index inArray:(NSArray *)array;
@end
-@implementation PositionFilterProxy
-@synthesize array=_array;
+
+#pragma mark - PositionFilter
+
+// Documented in PositionFilter.h
+@implementation PositionFilter
+
+/**
+ * GRMustacheFilter protocol required method
+ */
+- (id)transformedValue:(id)object
+{
+ /**
+ * Let's first validate the input: we can only filter arrays.
+ */
+
+ NSAssert([object isKindOfClass:[NSArray class]], @"Not an NSArray");
+ NSArray *array = (NSArray *)object;
+
+
+ /**
+ * Let's return a new array made of PositionFilterItem instances.
+ * They will provide the `position`, `isOdd` and `isFirst` keys while
+ * letting original array items provide the other keys.
+ */
+
+ NSMutableArray *replacementArray = [NSMutableArray arrayWithCapacity:array.count];
+ [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ PositionFilterItem *item = [[PositionFilterItem alloc] initWithObjectAtIndex:idx inArray:array];
+ [replacementArray addObject:item];
+ }];
+ return replacementArray;
+}
+
+@end
+
+
+#pragma mark - PositionFilterItem implementation
+
+@implementation PositionFilterItem {
+ /**
+ * The original 0-based index and the array of original items are stored in
+ * ivars without any exposed property: we do not want GRMustache to render
+ * the unintended {{ index }} or {{ array }}.
+ */
+ NSUInteger _index;
+ NSArray *_array;
+}
- (id)initWithObjectAtIndex:(NSUInteger)index inArray:(NSArray *)array
{
self = [super init];
if (self) {
_index = index;
- self.array = array;
+ _array = array;
}
return self;
}
-// Support for `{{.}}`: forward to original array element
+/**
+ * The implementation of `description` is required so that whenever GRMustache
+ * wants to render the original item itself (with a `{{ . }}` tag, for
+ * instance).
+ */
- (NSString *)description
{
- id originalObject = [self.array objectAtIndex:_index];
+ id originalObject = [_array objectAtIndex:_index];
return [originalObject description];
}
-// Support for `{{position}}`: return a 1-based index.
+/**
+ * Support for `{{position}}`: return a 1-based index.
+ */
- (NSUInteger)position
{
return _index + 1;
}
-// Support for `{{isFirst}}`: return YES if element is the first
+/**
+ * Support for `{{isFirst}}`: return YES if element is the first
+ */
- (BOOL)isFirst
{
return _index == 0;
}
-// Support for `{{isOdd}}`: return YES if element's position is odd.
+/**
+ * Support for `{{isOdd}}`: return YES if element's position is odd.
+ */
- (BOOL)isOdd
{
return (_index % 2) == 0;
}
-// Support for other keys: forward to original array element
+/**
+ * Support for other keys: forward to original array element
+ */
- (id)valueForUndefinedKey:(NSString *)key
{
- id originalObject = [self.array objectAtIndex:_index];
+ id originalObject = [_array objectAtIndex:_index];
return [originalObject valueForKey:key];
}
@end
-#pragma mark - PositionFilter
-
-// Documented in PositionFilter.h
-@implementation PositionFilter
-
-/**
- * GRMustacheFilter protocol required method
- */
-- (id)transformedValue:(id)object
-{
- /**
- * Let's first validate the input: we can only filter arrays.
- */
-
- NSAssert([object isKindOfClass:[NSArray class]], @"Not an NSArray");
- NSArray *array = (NSArray *)object;
-
-
- /**
- * Let's return a new array made of PositionFilterProxy instances.
- * They will provide the `position`, `isOdd` and `isFirst` keys while
- * letting original array items provide the other keys.
- */
-
- NSMutableArray *proxiesArray = [NSMutableArray arrayWithCapacity:array.count];
- [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- PositionFilterProxy *proxy = [[PositionFilterProxy alloc] initWithObjectAtIndex:idx inArray:array];
- [proxiesArray addObject:proxy];
- }];
- return proxiesArray;
-}
-
-@end

0 comments on commit e741dbc

Please sign in to comment.