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