main
  1//
  2//  KWUserDefinedMatcher.m
  3//  Kiwi
  4//
  5//  Created by Luke Redpath on 16/06/2011.
  6//  Copyright 2011 Allen Ding. All rights reserved.
  7//
  8
  9#import "KWUserDefinedMatcher.h"
 10
 11@implementation KWUserDefinedMatcher
 12
 13@synthesize selector;
 14@synthesize failureMessageForShould;
 15@synthesize failureMessageForShouldNot;
 16@synthesize matcherBlock;
 17@synthesize description;
 18
 19+ (id)matcherWithSubject:(id)aSubject block:(KWUserDefinedMatcherBlock)aBlock
 20{
 21    return [[[self alloc] initWithSubject:aSubject block:aBlock] autorelease];
 22}
 23
 24- (id)initWithSubject:(id)aSubject block:(KWUserDefinedMatcherBlock)aBlock
 25{
 26    if ((self = [super initWithSubject:aSubject])) {
 27        matcherBlock = [aBlock copy];
 28        self.description = @"match user defined matcher";
 29    }
 30    return self;
 31}
 32
 33- (void)dealloc
 34{
 35    [invocation release];
 36    [matcherBlock release];
 37    [super dealloc];
 38}
 39
 40- (BOOL)evaluate
 41{
 42    BOOL result;
 43
 44    if (invocation.methodSignature.numberOfArguments == 3) {
 45        id argument;
 46        [invocation getArgument:&argument atIndex:2];
 47        result = matcherBlock(self.subject, argument);
 48    } else {
 49        result = matcherBlock(self.subject);
 50    }
 51    return result;
 52}
 53
 54- (void)setSubject:(id)aSubject {
 55    if (aSubject != subject) {
 56        [subject release];
 57        subject = [aSubject retain];
 58    }
 59}
 60
 61#pragma mark -
 62#pragma mark Message forwarding
 63
 64- (BOOL)respondsToSelector:(SEL)aSelector
 65{
 66    if (aSelector == self.selector) {
 67        return YES;
 68    }
 69    return [super respondsToSelector:aSelector];
 70}
 71
 72- (void)forwardInvocation:(NSInvocation *)anInvocation
 73{
 74    [invocation autorelease];
 75    invocation = [anInvocation retain];
 76}
 77
 78- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
 79{
 80    if (aSelector == self.selector) {
 81        NSString *selectorString = NSStringFromSelector(self.selector);
 82
 83        /**
 84         *   TODO: find a way of doing this that:
 85         *   - doesn't require dummy methods (create the method signatures manually)
 86         *   - supports an unlimited number of arguments
 87         */
 88        if ([selectorString hasSuffix:@":"]) {
 89            return [self methodSignatureForSelector:@selector(matcherMethodWithArgument:)];
 90        } else {
 91            return [self methodSignatureForSelector:@selector(matcherMethodWithoutArguments)];
 92        }
 93    }
 94    return [super methodSignatureForSelector:aSelector];
 95}
 96
 97- (void)matcherMethodWithoutArguments {}
 98- (void)matcherMethodWithArgument:(id)argument {}
 99
100@end
101
102#pragma mark -
103
104@implementation KWUserDefinedMatcherBuilder
105
106+ (id)builder
107{
108    return [self builderForSelector:nil];
109}
110
111+ (id)builderForSelector:(SEL)aSelector {
112    return [[[self alloc] initWithSelector:aSelector] autorelease];
113}
114
115- (id)initWithSelector:(SEL)aSelector {
116    if ((self = [super init])) {
117        matcher = [[KWUserDefinedMatcher alloc] init];
118        matcher.selector = aSelector;
119    }
120    return self;
121}
122
123- (void)dealloc
124{
125    [matcher release];
126    [failureMessageForShouldBlock release];
127    [super dealloc];
128}
129
130- (NSString *)key {
131    return NSStringFromSelector(matcher.selector);
132}
133
134#pragma mark -
135#pragma mark Configuring The Matcher
136
137- (void)match:(KWUserDefinedMatcherBlock)block {
138    matcher.matcherBlock = block;
139}
140
141- (void)failureMessageForShould:(KWUserDefinedMatcherMessageBlock)block {
142    [failureMessageForShouldBlock release];
143    failureMessageForShouldBlock = [block copy];
144}
145
146- (void)failureMessageForShouldNot:(KWUserDefinedMatcherMessageBlock)block {
147    [failureMessageForShouldNotBlock release];
148    failureMessageForShouldNotBlock = [block copy];
149}
150
151- (void)description:(NSString *)aDescription
152{
153    [description release];
154    description = [aDescription copy];
155}
156
157#pragma mark -
158#pragma mark Buiding The Matcher
159
160- (KWUserDefinedMatcher *)buildMatcherWithSubject:(id)subject {
161    [matcher setSubject:subject];
162
163    if (failureMessageForShouldBlock) {
164        [matcher setFailureMessageForShould:failureMessageForShouldBlock(subject)];
165    }
166
167    if (failureMessageForShouldNotBlock) {
168        [matcher setFailureMessageForShouldNot:failureMessageForShouldNotBlock(subject)];
169    }
170
171    if (description) {
172        [matcher setDescription:description];
173    }
174
175    return matcher;
176}
177
178@end