Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Documentation
  • Loading branch information
groue committed Oct 20, 2012
1 parent 7d34000 commit 45d4393
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 58 deletions.
10 changes: 6 additions & 4 deletions src/classes/GRMustacheFilter.m
Expand Up @@ -27,7 +27,8 @@
#pragma mark - Private concrete class GRMustacheBlockFilter

/**
* Private subclass of GRMustacheFilter that filter values by calling a block.
* Private subclass of GRMustacheFilter that filters a single argument by
* calling a block.
*/
@interface GRMustacheBlockFilter: GRMustacheFilter {
@private
Expand All @@ -41,7 +42,8 @@ - (id)initWithBlock:(id(^)(id value))block;
#pragma mark - Private concrete class GRMustacheBlockVariadicFilter

/**
* TODO
* Private subclass of GRMustacheFilter that filters an array of arguments by
* calling a block.
*/
@interface GRMustacheBlockVariadicFilter: GRMustacheProxy<GRMustacheFilter> {
@private
Expand Down Expand Up @@ -132,9 +134,9 @@ - (void)dealloc
[super dealloc];
}

- (id)resolveSurrogate
- (void)loadDelegate
{
return _block(_arguments);
self.delegate = _block(_arguments);
}

#pragma mark <GRMustacheFilter>
Expand Down
15 changes: 14 additions & 1 deletion src/classes/GRMustacheNSUndefinedKeyExceptionGuard_private.h
Expand Up @@ -53,7 +53,20 @@
+ (id)valueForKey:(NSString *)key inObject:(id)object GRMUSTACHE_API_INTERNAL;

/**
* TODO
* Wrapper around the `valueForKey:` method, that avoids NSUndefinedException to
* be raised, as long as the object's implementation of `valueForUndefinedKey:`
* is the one of NSObject or NSManagedObject.
*
* For objects that have a custom implementation of `valueForUndefinedKey:`,
* this method does not guarantee that NSUndefinedException will be avoided.
*
* This method is used by the GRMustacheProxy class.
*
* @param key The key
* @param super_data A pointer to an objc_super struct
*
* @return The result of `objc_msgSendSuper(super_data, @selector(valueForKey:), key);`,
* or nil if this function call raises an NSUndefinedException.
*/
+ (id)valueForKey:(NSString *)key inSuper:(struct objc_super *)super_data GRMUSTACHE_API_INTERNAL;
@end
95 changes: 91 additions & 4 deletions src/classes/GRMustacheProxy.h
Expand Up @@ -23,14 +23,101 @@
#import <Foundation/Foundation.h>
#import "GRMustacheAvailabilityMacros.h"

/**
* When thrown in the Mustache rendering engine, GRMustacheProxy instances have
* the same behavior as another object, named their "delegate":
*
* Mustache variable tags and section tags, `{{name}}`, `{{#name}}`, and
* `{{^name}}` render *exactly* the same whenever the `name` key resolves to an
* object or to a proxy whose delegate is that object.
*
* You will generally subclass the GRMustacheProxy class in order to extend the
* abilities of the delegate.
*
* For instance, you may define some extra keys: the `valueForKey:`
* implementation of GRMustacheProxy looks for custom keys in the proxy before
* forwarding the lookup in the delegate object. This is the technique used
* by the PositionFilter filter in the "indexes" sample code (see
* https://github.com/groue/GRMustache/blob/master/Guides/sample_code/indexes.md).
*
* GRMustacheProxies provides two initialization methods: `initWithDelegate:`,
* and `init`. The `initWithDelegate:` sets the delegate of the proxy, which is
* from now on ready to use. The `init` method does not set the delegate: you
* will generally provide your own implementation of the `loadDelegate` method,
* whose responsability is to lazily set the delegate of the proxy.
*
* **Companion guide:** https://github.com/groue/GRMustache/blob/master/Guides/proxies.md
*
* @see https://github.com/groue/GRMustache/blob/master/Guides/sample_code/indexes.md
*
* @since v5.5
*/
@interface GRMustacheProxy : NSObject {
@private
BOOL _surrogateResolved;
id _surrogate;
BOOL _delegateLoaded;
id _delegate;
}

/**
* The delegate object of the proxy.
*
* Proxies initialized with the `initWithDelegate:` method have a delegate
* property whose value is the provided delegate object.
*
* Proxies initialized with the `init` method have no default value for their
* delegate property. If this property is accessed, the proxy automatically
* calls the `loadDelegate` method, and returns the resulting delegate.
*
* @see init
* @see initWithDelegate
* @see loadDelegate
*
* @since v5.5
*/
@property (nonatomic, retain) id delegate;

/**
* Returns a newly initialized proxy without any delegate.
*
* Unless you subclass GRMustacheProxy, and provide your own implementation of
* the `loadDelegate` method, the delegate property will resolve to `nil`.
*
* @return A newly initialized GRMustacheProxy object.
*
* @see initWithDelegate:
* @see loadDelegate:
* @see delegate
*
* @since v5.5
*/
- (id)init;
- (id)initWithSurrogate:(id)surrogate;
- (id)resolveSurrogate;

/**
* Returns a newly initialized proxy with the provided delegate.
*
* @param delegate The value for the delegate property.
*
* @return A newly initialized GRMustacheProxy object.
*
* @see delegate
*
* @since v5.5
*/
- (id)initWithDelegate:(id)delegate;

/**
* You should never call this method directly. The proxy calls this method when
* its delegate property is requested but has not been set yet. This method
* assigns nil to the delegate property.
*
* You can override this method in order to load a custom delegate object.
* If you choose to do so, assign any object to the delegate property.
*
* @see delegate
*
* @since v5.5
*/
- (void)loadDelegate;

@end

65 changes: 32 additions & 33 deletions src/classes/GRMustacheProxy.m
Expand Up @@ -22,17 +22,12 @@

#import "GRMustacheProxy_private.h"
#import "GRMustacheRuntime_private.h"
#import "GRMustacheError.h"

@interface GRMustacheProxy()
@property (nonatomic, readonly) id surrogate;
@end

@implementation GRMustacheProxy

- (void)dealloc
{
[_surrogate release];
[_delegate release];
[super dealloc];
}

Expand All @@ -41,19 +36,35 @@ - (id)init
return [super init];
}

- (id)initWithSurrogate:(id)surrogate
- (id)initWithDelegate:(id)delegate
{
self = [self init];
if (self) {
_surrogate = [surrogate retain];
_surrogateResolved = YES;
self.delegate = delegate;
}
return self;
}

- (id)resolveSurrogate
- (void)loadDelegate
{
self.delegate = nil;
}

- (id)delegate
{
if (!_delegateLoaded) {
[self loadDelegate];
}
return _delegate;
}

- (void)setDelegate:(id)delegate
{
return nil;
if (delegate != _delegate) {
[_delegate release];
_delegate = [delegate retain];
}
_delegateLoaded = YES;
}


Expand All @@ -66,7 +77,7 @@ - (BOOL)respondsToSelector:(SEL)aSelector
if ([super respondsToSelector:aSelector]) {
return YES;
}
return [self.surrogate respondsToSelector:aSelector];
return [self.delegate respondsToSelector:aSelector];
}

// Support for NSNull
Expand All @@ -75,7 +86,7 @@ - (BOOL)isKindOfClass:(Class)aClass
if ([super isKindOfClass:aClass]) {
return YES;
}
return [self.surrogate isKindOfClass:aClass];
return [self.delegate isKindOfClass:aClass];
}

// Support for optional methods of protocols used by GRMustache:
Expand All @@ -85,7 +96,7 @@ - (BOOL)conformsToProtocol:(Protocol *)aProtocol
if ([super conformsToProtocol:aProtocol]) {
return YES;
}
return [self.surrogate conformsToProtocol:aProtocol];
return [self.delegate conformsToProtocol:aProtocol];
}

// https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsobject_Class/Reference/Reference.html
Expand All @@ -99,7 +110,7 @@ - (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
signature = [self.surrogate methodSignatureForSelector:aSelector];
signature = [self.delegate methodSignatureForSelector:aSelector];
}
return signature;
}
Expand All @@ -108,9 +119,9 @@ - (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
// NSFastEnumeration, GRMustacheSectionTagHelper, GRMustacheVariableTagHelper, GRMustacheTemplateDelegate
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
id surrogate = self.surrogate;
if ([surrogate respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:surrogate];
id delegate = self.delegate;
if ([delegate respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:delegate];
} else {
[super forwardInvocation:anInvocation];
}
Expand All @@ -119,7 +130,7 @@ - (void)forwardInvocation:(NSInvocation *)anInvocation
// Support for {{ proxy }}
- (NSString *)description
{
return [self.surrogate description];
return [self.delegate description];
}

// Support for {{ proxy.key }}
Expand All @@ -131,20 +142,8 @@ - (id)valueForKey:(NSString *)key
return value;
}

// ... and on failure, ask surrogate
return [self.surrogate valueForKey:key];
}


#pragma mark - Private

- (id)surrogate
{
if (!_surrogateResolved) {
_surrogate = [[self resolveSurrogate] retain];
_surrogateResolved = YES;
}
return _surrogate;
// ... and on failure, ask delegate
return [self.delegate valueForKey:key];
}

@end
Expand Down
11 changes: 7 additions & 4 deletions src/classes/GRMustacheProxy_private.h
Expand Up @@ -26,17 +26,20 @@
// Documented in GRMustacheProxy.h
@interface GRMustacheProxy : NSObject {
@private
BOOL _surrogateResolved;
id _surrogate;
BOOL _delegateLoaded;
id _delegate;
}

// Documented in GRMustacheProxy.h
@property (nonatomic, retain) id delegate;

// Documented in GRMustacheProxy.h
- (id)init;

// Documented in GRMustacheProxy.h
- (id)initWithSurrogate:(id)surrogate;
- (id)initWithDelegate:(id)delegate;

// Documented in GRMustacheProxy.h
- (id)resolveSurrogate;
- (void)loadDelegate;
@end

14 changes: 13 additions & 1 deletion src/classes/GRMustacheRuntime_private.h
Expand Up @@ -87,7 +87,19 @@ extern BOOL GRMustacheRuntimeDidCatchNSUndefinedKeyException;
+ (id)valueForKey:(NSString *)key inObject:(id)object GRMUSTACHE_API_INTERNAL;

/**
* TODO
* Invokes `objc_msgSendSuper(super_data, @selector(valueForKey:), key);`, and
* returns the result. Should this function call raise an
* NSUndefinedKeyException, returns nil.
*
* This method is used by the GRMustacheProxy class.
*
* @param key The searched key
* @param super_data A pointer to an objc_super struct
*
* @return The result of `objc_msgSendSuper(super_data, @selector(valueForKey:), key);`,
* or nil if this function call raises an NSUndefinedException.
*
* @see GRMustacheProxy
*/
+ (id)valueForKey:(NSString *)key inSuper:(struct objc_super *)super_data GRMUSTACHE_API_INTERNAL;

Expand Down
12 changes: 1 addition & 11 deletions src/tests/Public/future/GRPositionFilterTest.m
Expand Up @@ -23,7 +23,6 @@
#import "GRMustachePublicAPITest.h"

@interface GRPositionFilterItem : GRMustacheProxy {
NSArray *array_;
NSUInteger index_;
}
@property (nonatomic, readonly) NSUInteger position;
Expand Down Expand Up @@ -51,26 +50,22 @@ - (id)transformedValue:(id)object
@end

@interface GRPositionFilterItem()
@property (nonatomic, retain) NSArray *array_;
@property (nonatomic) NSUInteger index_;
@end

@implementation GRPositionFilterItem
@synthesize array_;
@synthesize index_;

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

- (id)initWithObjectAtIndex:(NSUInteger)index fromArray:(NSArray *)array
{
self = [super init];
self = [super initWithDelegate:[array objectAtIndex:index]];
if (self) {
self.index_ = index;
self.array_ = array;
}
return self;
}
Expand All @@ -80,11 +75,6 @@ - (NSUInteger)position
return self.index_ + 1;
}

- (id)resolveSurrogate
{
return [self.array_ objectAtIndex:self.index_];
}

@end


Expand Down

0 comments on commit 45d4393

Please sign in to comment.