[REBASE ME] Refactored Router for simplicity, nearly done with design. Initial support for relationship loading from object manager.

This commit is contained in:
Blake Watters
2012-06-20 16:43:32 -04:00
parent c4a329cc58
commit 6c3b75039b
9 changed files with 319 additions and 267 deletions

View File

@@ -8,15 +8,24 @@
#import "RKRequest.h"
// Class cluster. Not to be directly instantiated.
@interface RKRoute : NSObject
@property (nonatomic, retain) NSString *name; // can be nil
@property (nonatomic, retain) Class objectClass; // can be nil
@property (nonatomic, assign) RKRequestMethod method;
@property (nonatomic, retain) NSString *resourcePathPattern;
@property (nonatomic, retain, readonly) NSString *name;
@property (nonatomic, retain, readonly) Class objectClass;
@property (nonatomic, assign, readonly) RKRequestMethod method;
@property (nonatomic, retain, readonly) NSString *resourcePathPattern;
@property (nonatomic, assign) BOOL shouldEscapeResourcePath; // when YES, the path will be escaped when interpolated
+ (id)routeWithName:(NSString *)name resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method;
+ (id)routeWithClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method;
+ (id)routeWithRelationshipName:(NSString *)name objectClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method;
- (BOOL)isNamedRoute;
- (BOOL)isClassRoute;
- (BOOL)isRelationshipRoute;
- (NSString *)resourcePathForObject:(id)object;
@end

View File

@@ -7,6 +7,23 @@
//
#import "RKRoute.h"
#import "RKPathMatcher.h"
@interface RKRoute ()
@property (nonatomic, retain, readwrite) NSString *name;
@property (nonatomic, retain, readwrite) Class objectClass;
@property (nonatomic, assign, readwrite) RKRequestMethod method;
@property (nonatomic, retain, readwrite) NSString *resourcePathPattern;
@end
@interface RKNamedRoute : RKRoute
@end
@interface RKClassRoute : RKRoute
@end
@interface RKRelationshipRoute : RKRoute
@end
@implementation RKRoute
@@ -16,28 +33,122 @@
@synthesize resourcePathPattern = _resourcePathPattern;
@synthesize shouldEscapeResourcePath = _shouldEscapeResourcePath;
+ (id)routeWithName:(NSString *)name resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method
{
NSParameterAssert(name);
NSParameterAssert(resourcePathPattern);
RKNamedRoute *route = [[RKNamedRoute new] autorelease];
route.name = name;
route.resourcePathPattern = resourcePathPattern;
route.method = method;
return route;
}
+ (id)routeWithClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method
{
NSParameterAssert(objectClass);
NSParameterAssert(resourcePathPattern);
RKClassRoute *route = [[RKClassRoute new] autorelease];
route.objectClass = objectClass;
route.resourcePathPattern = resourcePathPattern;
route.method = method;
return route;
}
+ (id)routeWithRelationshipName:(NSString *)relationshipName objectClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method
{
NSParameterAssert(relationshipName);
NSParameterAssert(objectClass);
RKRelationshipRoute *route = [[RKRelationshipRoute new] autorelease];
route.name = relationshipName;
route.objectClass = objectClass;
route.resourcePathPattern = resourcePathPattern;
route.method = method;
return route;
}
- (id)init
{
self = [super init];
if (self) {
if ([self isMemberOfClass:[RKRoute class]]) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"%@ is not meant to be directly instantiated. Use one of the initializer methods instead..",
NSStringFromClass([self class])]
userInfo:nil];
}
}
return self;
}
- (BOOL)isNamedRoute
{
return [self.name length] > 0;
return NO;
}
- (BOOL)isClassRoute
{
return self.objectClass != nil;
return NO;
}
- (BOOL)isRelationshipRoute
{
return NO;
}
- (NSString *)resourcePathForObject:(id)object
{
if (!object) return self.resourcePathPattern;
RKPathMatcher *pathMatcher = [RKPathMatcher matcherWithPattern:self.resourcePathPattern];
return [pathMatcher pathFromObject:object addingEscapes:self.shouldEscapeResourcePath];
}
@end
@implementation RKNamedRoute
- (BOOL)isNamedRoute
{
return YES;
}
- (NSString *)description
{
if ([self isNamedRoute]) {
return [NSString stringWithFormat:@"<%@: %p name=%@ resourcePathPattern=%@>",
NSStringFromClass([self class]), self, self.name, self.resourcePathPattern];
} else if ([self isClassRoute]) {
return [NSString stringWithFormat:@"<%@: %p objectClass=%@ method=%@ resourcePathPattern=%@>",
NSStringFromClass([self class]), self, NSStringFromClass(self.objectClass),
RKRequestMethodNameFromType(self.method), self.resourcePathPattern];
}
return [super description];
return [NSString stringWithFormat:@"<%@: %p name=%@ resourcePathPattern=%@>",
NSStringFromClass([self class]), self, self.name, self.resourcePathPattern];
}
@end
@implementation RKClassRoute
- (BOOL)isClassRoute
{
return YES;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p objectClass=%@ method=%@ resourcePathPattern=%@>",
NSStringFromClass([self class]), self, NSStringFromClass(self.objectClass),
RKRequestMethodNameFromType(self.method), self.resourcePathPattern];
}
@end
@implementation RKRelationshipRoute
- (BOOL)isRelationshipRoute
{
return YES;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p relationshipName=%@ objectClass=%@ method=%@ resourcePathPattern=%@>",
NSStringFromClass([self class]), self, self.name, NSStringFromClass(self.objectClass),
RKRequestMethodNameFromType(self.method), self.resourcePathPattern];
}
@end

View File

@@ -16,33 +16,23 @@ extern RKRequestMethod const RKRequestMethodAny;
- (NSArray *)allRoutes;
- (NSArray *)namedRoutes;
- (NSArray *)classRoutes;
- (NSArray *)relationshipRoutes;
- (void)addRoute:(RKRoute *)route;
- (void)removeRoute:(RKRoute *)route;
- (BOOL)containsRoute:(RKRoute *)route;
- (BOOL)containsRouteForName:(NSString *)name;
- (BOOL)containsRouteForResourcePathPattern:(NSString *)resourcePathPattern;
- (BOOL)containsRouteForClass:(Class)objectClass method:(RKRequestMethod)method;
- (RKRoute *)routeForName:(NSString *)name;
- (RKRoute *)routeForClass:(Class)objectClass method:(RKRequestMethod)method;
- (RKRoute *)routeForRelationship:(NSString *)relationship ofClass:(Class)objectClass method:(RKRequestMethod)method;
- (NSArray *)routesForClass:(Class)objectClass; // routes specifically for the class
- (NSArray *)routesForObject:(id)object; // routes for class and superclasses
- (NSArray *)routesForRelationship:(NSString *)relationshipName ofClass:(Class)objectClass;
// NOTE: Will return an exact match for the object class and then
// search for a superclass match
- (RKRoute *)routeForObject:(id)object method:(RKRequestMethod)method;
- (NSArray *)routesForResourcePathPattern:(NSString *)resourcePathPattern;
// Convenience methods
- (void)addRouteWithName:(NSString *)name resourcePathPattern:(NSString *)resourcePathPattern;
- (void)addRouteWithClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method;
- (void)addRouteWithClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern;
- (NSString *)resourcePathForObject:(id)object method:(RKRequestMethod)method;
- (NSString *)resourcePathForRouteNamed:(NSString *)routeName;
- (NSString *)resourcePathForRouteNamed:(NSString *)routeName interpolatedWithObject:(id)object;
- (NSArray *)routesWithResourcePathPattern:(NSString *)resourcePathPattern;
@end

View File

@@ -56,15 +56,26 @@ RKRequestMethod const RKRequestMethodAny = RKRequestMethodInvalid;
return [NSArray arrayWithArray:routes];
}
- (NSArray *)relationshipRoutes
{
NSIndexSet *indexes = [self.routes indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [(RKRoute *)obj isRelationshipRoute];
}];
return [self.routes objectsAtIndexes:indexes];
}
- (void)addRoute:(RKRoute *)route
{
NSAssert([route isNamedRoute] || [route isClassRoute], @"A route must have either a name or a target class.");
NSAssert([route.resourcePathPattern length] > 0, @"A route must have a resource path pattern.");
NSAssert(![self containsRoute:route], @"Cannot add a route that is already added to the router.");
NSAssert(![route isNamedRoute] || ![self containsRouteForName:route.name], @"Cannot add a route with the same name as an existing route.");
NSAssert(![route isNamedRoute] || [self routeForName:route.name] == nil, @"Cannot add a route with the same name as an existing route.");
if ([route isClassRoute]) {
RKRoute *existingRoute = [self routeForClass:route.objectClass method:route.method];
NSAssert(existingRoute == nil || (existingRoute.method == RKRequestMethodAny && route.method != RKRequestMethodAny), @"Cannot add a route with the same class and method as an existing route.");
} else if ([route isRelationshipRoute]) {
NSArray *routes = [self routesForRelationship:route.name ofClass:route.objectClass];
for (RKRoute *existingRoute in routes) {
NSAssert(existingRoute.method != route.method, @"Cannot add a relationship route with the same name and class as an existing route.");
}
}
[self.routes addObject:route];
}
@@ -80,21 +91,6 @@ RKRequestMethod const RKRequestMethodAny = RKRequestMethodInvalid;
return [self.routes containsObject:route];
}
- (BOOL)containsRouteForName:(NSString *)name
{
return [[self.routes valueForKey:@"name"] containsObject:name];
}
- (BOOL)containsRouteForResourcePathPattern:(NSString *)resourcePathPattern
{
return [[self.routes valueForKey:@"resourcePathPattern"] containsObject:resourcePathPattern];
}
- (BOOL)containsRouteForClass:(Class)objectClass method:(RKRequestMethod)method
{
return [self routeForClass:objectClass method:method] != nil;
}
- (RKRoute *)routeForName:(NSString *)name
{
for (RKRoute *route in [self namedRoutes]) {
@@ -125,6 +121,18 @@ RKRequestMethod const RKRequestMethodAny = RKRequestMethodInvalid;
return nil;
}
- (RKRoute *)routeForRelationship:(NSString *)relationshipName ofClass:(Class)objectClass method:(RKRequestMethod)method
{
for (RKRoute *route in [self relationshipRoutes]) {
if ([route.name isEqualToString:relationshipName] && [route.objectClass isEqual:objectClass] && route.method == method) {
return route;
}
}
return nil;
}
- (NSArray *)routesForClass:(Class)objectClass
{
NSMutableArray *routes = [NSMutableArray new];
@@ -149,6 +157,15 @@ RKRequestMethod const RKRequestMethodAny = RKRequestMethodInvalid;
return [NSArray arrayWithArray:routes];
}
- (NSArray *)routesForRelationship:(NSString *)relationshipName ofClass:(Class)objectClass
{
NSIndexSet *indexes = [self.relationshipRoutes indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [[(RKRoute *)obj objectClass] isEqual:objectClass] && [[(RKRoute *)obj name] isEqualToString:relationshipName];
}];
return [self.relationshipRoutes objectsAtIndexes:indexes];
}
- (RKRoute *)routeForObject:(id)object method:(RKRequestMethod)method
{
NSArray *routesForObject = [self routesForObject:object];
@@ -176,7 +193,7 @@ RKRequestMethod const RKRequestMethodAny = RKRequestMethodInvalid;
return bestMatch;
}
- (NSArray *)routesForResourcePathPattern:(NSString *)resourcePathPattern
- (NSArray *)routesWithResourcePathPattern:(NSString *)resourcePathPattern
{
NSMutableArray *routes = [NSMutableArray array];
for (RKRoute *route in self.routes) {
@@ -188,49 +205,4 @@ RKRequestMethod const RKRequestMethodAny = RKRequestMethodInvalid;
return [NSArray arrayWithArray:routes];
}
- (void)addRouteWithName:(NSString *)name resourcePathPattern:(NSString *)resourcePathPattern
{
RKRoute *route = [[RKRoute new] autorelease];
route.name = name;
route.resourcePathPattern = resourcePathPattern;
[self addRoute:route];
}
- (void)addRouteWithClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern method:(RKRequestMethod)method
{
RKRoute *route = [[RKRoute new] autorelease];
route.objectClass = objectClass;
route.resourcePathPattern = resourcePathPattern;
route.method = method;
[self addRoute:route];
}
- (void)addRouteWithClass:(Class)objectClass resourcePathPattern:(NSString *)resourcePathPattern
{
[self addRouteWithClass:objectClass resourcePathPattern:resourcePathPattern method:RKRequestMethodAny];
}
- (NSString *)resourcePathForObject:(id)object method:(RKRequestMethod)method
{
RKRoute *route = [self routeForObject:object method:method];
RKPathMatcher *matcher = [RKPathMatcher matcherWithPattern:route.resourcePathPattern];
return [matcher pathFromObject:object addingEscapes:route.shouldEscapeResourcePath];
}
- (NSString *)resourcePathForRouteNamed:(NSString *)routeName
{
return [self resourcePathForRouteNamed:routeName interpolatedWithObject:nil];
}
- (NSString *)resourcePathForRouteNamed:(NSString *)routeName interpolatedWithObject:(id)object
{
RKRoute *route = [self routeForName:routeName];
if (object) {
RKPathMatcher *matcher = [RKPathMatcher matcherWithPattern:route.resourcePathPattern];
return [matcher pathFromObject:object addingEscapes:route.shouldEscapeResourcePath];
}
return route.resourcePathPattern;
}
@end

View File

@@ -304,6 +304,7 @@ typedef enum {
- (id)loaderForObject:(id<NSObject>)object method:(RKRequestMethod)method;
// TODO: loaderForRoute || loaderWithRoute: ???
//- (id)objectLoaderForRouteWithName: interpolatedWithObject:;
/**
Creates and returns an RKObjectPaginator instance targeting the specified resource path pattern.
@@ -376,6 +377,10 @@ typedef enum {
*/
- (void)loadObjectsAtResourcePath:(NSString *)resourcePath usingBlock:(RKObjectLoaderBlock)block;
// TODO: Docs....
// Always uses a GET request...
- (void)loadRelationship:(NSString *)relationshipName ofObject:(id)object usingBlock:(void(^)(RKObjectLoader *))block;
/*
Configure and send an object loader after yielding it to a block for configuration. This allows for very succinct on-the-fly
configuration of the request without obtaining an object reference via objectLoaderForObject: and then sending it yourself.

View File

@@ -261,7 +261,8 @@ static NSOperationQueue *defaultMappingQueue = nil;
- (id)loaderForObject:(id<NSObject>)object method:(RKRequestMethod)method
{
NSString *resourcePath = (method == RKRequestMethodInvalid) ? nil : [self.router resourcePathForObject:object method:method];
RKRoute *route = [self.router routeForObject:object method:method];
NSString* resourcePath = (method == RKRequestMethodInvalid) ? nil : [route resourcePathForObject:object];
RKObjectLoader *loader = [self loaderWithResourcePath:resourcePath];
loader.method = method;
loader.sourceObject = object;
@@ -345,7 +346,8 @@ static NSOperationQueue *defaultMappingQueue = nil;
- (void)sendObject:(id<NSObject>)object method:(RKRequestMethod)method usingBlock:(void(^)(RKObjectLoader *))block
{
NSString *resourcePath = [self.router resourcePathForObject:object method:method];
RKRoute *route = [self.router routeForObject:object method:method];
NSString *resourcePath = [route resourcePathForObject:object];
[self sendObject:object toResourcePath:resourcePath usingBlock:^(RKObjectLoader *loader) {
loader.method = method;
block(loader);
@@ -372,6 +374,19 @@ static NSOperationQueue *defaultMappingQueue = nil;
[self sendObject:object method:RKRequestMethodDELETE usingBlock:block];
}
- (void)loadRelationship:(NSString *)relationshipName ofObject:(id)object usingBlock:(void(^)(RKObjectLoader *))block
{
// TODO: Try to pull the path/url off of the object (relationshipResourcePath | relationshipURL)
RKRoute *route = [self.router routeForRelationship:relationshipName ofClass:[object class] method:RKRequestMethodGET];
NSString *resourcePath = [route resourcePathForObject:object];
[self loadObjectsAtResourcePath:resourcePath usingBlock:block];
// id targetObject = [object valueForKey:relationshipName];
// [self sendObject:targetObject toResourcePath:resourcePath usingBlock:^(RKObjectLoader *loader) {
// loader.method = RKRequestMethodGET;
// block(loader);
// }];
}
#endif // NS_BLOCKS_AVAILABLE
#pragma mark - Object Instance Loaders for Non-nested JSON

View File

@@ -44,9 +44,7 @@
- (void)testAddingRoute
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
RKRoute *route = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route];
assertThat([router allRoutes], hasCountOf(1));
}
@@ -54,46 +52,17 @@
- (void)testRemovingRoute
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
RKRoute *route = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route];
assertThat([router allRoutes], hasCountOf(1));
[router removeRoute:route];
assertThat([router allRoutes], hasCountOf(0));
}
- (void)testThatAddingRouteRequiresNameOrObjectClass
{
RKRouter *router = [RKRouter new];
RKRoute *blankRoute = [RKRoute new];
RKRoute *routeWithName = [RKRoute new];
routeWithName.name = @"whatever";
RKRoute *routeWithObjectClass = [RKRoute new];
routeWithObjectClass.objectClass = [RKTestUser class];
STAssertThrowsSpecificNamed([router addRoute:blankRoute], NSException, NSInternalInconsistencyException, @"A route must have either a name or a target class.");
STAssertThrowsSpecificNamed([router addRoute:routeWithName], NSException, NSInternalInconsistencyException, @"A route must have a resource path pattern.");
STAssertThrowsSpecificNamed([router addRoute:routeWithObjectClass], NSException, NSInternalInconsistencyException, @"A route must have a resource path pattern.");
}
- (void)testThatAddingRouteRequiresResourcePathPattern
{
RKRouter *router = [RKRouter new];
RKRoute *routeWithoutPattern = [RKRoute new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
STAssertThrowsSpecificNamed([router addRoute:routeWithoutPattern], NSException, NSInternalInconsistencyException, @"A route must have a resource path pattern.");
STAssertNoThrowSpecificNamed([router addRoute:route], NSException, NSInternalInconsistencyException, @"A route must have either a name or a target class.");
assertThatBool([router containsRoute:route], is(equalToBool(YES)));
}
- (void)testCannotAddARouteThatIsAlreadyAdded
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
RKRoute *route = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route];
STAssertThrowsSpecificNamed([router addRoute:route], NSException, NSInternalInconsistencyException, @"Cannot add a route that is already added to the router.");
}
@@ -101,46 +70,28 @@
- (void)testCannotAddARouteWithAnExistingName
{
RKRouter *router = [RKRouter new];
RKRoute *route1 = [RKRoute new];
route1.name = @"test_router";
route1.resourcePathPattern = @"/routes";
RKRoute *route1 = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route1];
RKRoute *route2 = [RKRoute new];
route2.name = @"test_router";
route2.resourcePathPattern = @"/routes2";
RKRoute *route2 = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes2" method:RKRequestMethodAny];
STAssertThrowsSpecificNamed([router addRoute:route2], NSException, NSInternalInconsistencyException, @"Cannot add a route with the same name as an existing route.");
}
- (void)testCanAddARouteWithAnExistingResourcePathPattern
{
RKRouter *router = [RKRouter new];
RKRoute *route1 = [RKRoute new];
route1.name = @"test_router";
route1.resourcePathPattern = @"/routes";
RKRoute *route1 = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route1];
RKRoute *route2 = [RKRoute new];
route2.name = @"test_router2";
route2.resourcePathPattern = @"/routes";
RKRoute *route2 = [RKRoute routeWithName:@"test_router2" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
STAssertNoThrowSpecificNamed([router addRoute:route2], NSException, NSInternalInconsistencyException, @"Cannot add a route with the same resource path pattern as an existing route.");
}
- (void)testCannotAddARouteWithAnExistingObjectClassAndMethod
{
RKRouter *router = [RKRouter new];
RKRoute *routeWithObjectClassAndMethod = [RKRoute new];
routeWithObjectClassAndMethod.objectClass = [RKTestUser class];
routeWithObjectClassAndMethod.resourcePathPattern = @"/routes";
routeWithObjectClassAndMethod.method = RKRequestMethodGET;
RKRoute *routeWithObjectClassAndMethod = [RKRoute routeWithClass:[RKTestUser class] resourcePathPattern:@"/routes" method:RKRequestMethodGET];
RKRoute *routeWithObjectClassAndDifferentMethod = [RKRoute routeWithClass:[RKTestUser class] resourcePathPattern:@"/routes" method:RKRequestMethodPOST];
RKRoute *routeWithObjectClassAndDifferentPath = [RKRoute routeWithClass:[RKTestUser class] resourcePathPattern:@"/routes2" method:RKRequestMethodPOST];
RKRoute *routeWithObjectClassAndDifferentMethod = [RKRoute new];
routeWithObjectClassAndDifferentMethod.objectClass = [RKTestUser class];
routeWithObjectClassAndDifferentMethod.resourcePathPattern = @"/routes";
routeWithObjectClassAndDifferentMethod.method = RKRequestMethodPOST;
RKRoute *routeWithObjectClassAndDifferentPath = [RKRoute new];
routeWithObjectClassAndDifferentPath.objectClass = [RKTestUser class];
routeWithObjectClassAndDifferentPath.resourcePathPattern = @"/routes2";
routeWithObjectClassAndDifferentPath.method = RKRequestMethodPOST;
[router addRoute:routeWithObjectClassAndMethod];
STAssertNoThrowSpecificNamed([router addRoute:routeWithObjectClassAndDifferentMethod], NSException, NSInternalInconsistencyException, @"Cannot add a route with the same class and method as an existing route.");
@@ -148,39 +99,42 @@
STAssertThrowsSpecificNamed([router addRoute:routeWithObjectClassAndDifferentPath], NSException, NSInternalInconsistencyException, @"Cannot add a route with the same class and method as an existing route.");
}
- (void)testCannotAddARouteForAnExistingRelationshipNameAndMethod
{
RKRouter *router = [RKRouter new];
RKRoute *routeWithObjectClassAndMethod = [RKRoute routeWithRelationshipName:@"friends" objectClass:[RKTestUser class] resourcePathPattern:@"/friends" method:RKRequestMethodGET];
RKRoute *routeWithObjectClassAndDifferentMethod = [RKRoute routeWithRelationshipName:@"friends" objectClass:[RKTestUser class] resourcePathPattern:@"/friends" method:RKRequestMethodPOST];
RKRoute *routeWithIdenticalClassAndMethod = [RKRoute routeWithRelationshipName:@"friends" objectClass:[RKTestUser class] resourcePathPattern:@"/friends" method:RKRequestMethodGET];
[router addRoute:routeWithObjectClassAndMethod];
STAssertNoThrowSpecificNamed([router addRoute:routeWithObjectClassAndDifferentMethod], NSException, NSInternalInconsistencyException, @"Cannot add a relationship route with the same name and class as an existing route.");
STAssertThrowsSpecificNamed([router addRoute:routeWithIdenticalClassAndMethod], NSException, NSInternalInconsistencyException, @"Cannot add a relationship route with the same name and class as an existing route.");
}
- (void)testCanAddARouteWithAnExistingObjectClassIfMethodIsAny
{
RKRouter *router = [RKRouter new];
RKRoute *route1 = [RKRoute new];
route1.objectClass = [RKTestUser class];
route1.resourcePathPattern = @"/routes";
route1.method = RKRequestMethodAny;
RKRoute *route1 = [RKRoute routeWithClass:[RKTestUser class] resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route1];
RKRoute *route2 = [RKRoute new];
route2.objectClass = [RKTestUser class];
route2.resourcePathPattern = @"/routes";
route2.method = RKRequestMethodPOST;
RKRoute *route2 = [RKRoute routeWithClass:[RKTestUser class] resourcePathPattern:@"/routes" method:RKRequestMethodPOST];
STAssertNoThrowSpecificNamed([router addRoute:route2], NSException, NSInternalInconsistencyException, @"Cannot add a route with the same class and method as an existing route.");
}
- (void)testCannotRemoveARouteThatDoesNotExistInRouter
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
RKRoute *route = [RKRoute routeWithName:@"fake" resourcePathPattern:@"whatever" method:RKRequestMethodGET];
STAssertThrowsSpecificNamed([router removeRoute:route], NSException, NSInternalInconsistencyException, @"Cannot remove a route that is not added to the router.");
}
- (void)testAllRoutes
{
RKRouter *router = [RKRouter new];
RKRoute *route1 = [RKRoute new];
route1.name = @"test_router";
route1.resourcePathPattern = @"/routes";
RKRoute *route1 = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route1];
RKRoute *route2 = [RKRoute new];
route2.name = @"test_router2";
route2.resourcePathPattern = @"/routes2";
RKRoute *route2 = [RKRoute routeWithName:@"test_router2" resourcePathPattern:@"/routes2" method:RKRequestMethodAny];
[router addRoute:route2];
assertThat([router allRoutes], contains(route1, route2, nil));
}
@@ -188,18 +142,11 @@
- (void)testNamedRoutes
{
RKRouter *router = [RKRouter new];
RKRoute *route1 = [RKRoute new];
route1.name = @"test_router";
route1.resourcePathPattern = @"/routes";
RKRoute *route1 = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route1];
RKRoute *route2 = [RKRoute new];
route2.name = @"test_router2";
route2.resourcePathPattern = @"/routes2";
RKRoute *route2 = [RKRoute routeWithName:@"test_router2" resourcePathPattern:@"/routes2" method:RKRequestMethodAny];
[router addRoute:route2];
RKRoute *route3 = [RKRoute new];
route3.objectClass = [RKTestUser class];
route3.method = RKRequestMethodPUT;
route3.resourcePathPattern = @"/routes2";
RKRoute *route3 = [RKRoute routeWithClass:[RKTestUser class] resourcePathPattern:@"/routes2" method:RKRequestMethodPUT];
[router addRoute:route3];
assertThat([router namedRoutes], contains(route1, route2, nil));
}
@@ -207,18 +154,11 @@
- (void)testClassRoutes
{
RKRouter *router = [RKRouter new];
RKRoute *route1 = [RKRoute new];
route1.name = @"test_router";
route1.resourcePathPattern = @"/routes";
RKRoute *route1 = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route1];
RKRoute *route2 = [RKRoute new];
route2.name = @"test_router2";
route2.resourcePathPattern = @"/routes2";
RKRoute *route2 = [RKRoute routeWithName:@"test_router2" resourcePathPattern:@"/routes2" method:RKRequestMethodAny];
[router addRoute:route2];
RKRoute *route3 = [RKRoute new];
route3.objectClass = [RKTestUser class];
route3.method = RKRequestMethodPUT;
route3.resourcePathPattern = @"/routes2";
RKRoute *route3 = [RKRoute routeWithClass:[RKTestUser class] resourcePathPattern:@"/routes2" method:RKRequestMethodPUT];
[router addRoute:route3];
assertThat([router classRoutes], contains(route3, nil));
}
@@ -226,31 +166,16 @@
- (void)testHasRouteForName
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
RKRoute *route = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route];
assertThatBool([router containsRouteForName:@"test_router"], is(equalToBool(YES)));
assertThatBool([router containsRouteForName:@"test_router2"], is(equalToBool(NO)));
}
- (void)testHasRouteForResourcePathPattern
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
[router addRoute:route];
assertThatBool([router containsRouteForResourcePathPattern:@"/routes"], is(equalToBool(YES)));
assertThatBool([router containsRouteForResourcePathPattern:@"test_router2"], is(equalToBool(NO)));
assertThat([router routeForName:@"test_router"], is(notNilValue()));
assertThat([router routeForName:@"test_router"], is(nilValue()));
}
- (void)testRouteForName
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
RKRoute *route = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route];
assertThat([router routeForName:@"test_router"], is(equalTo(route)));
}
@@ -258,26 +183,25 @@
- (void)testRouteForResourcePathPattern
{
RKRouter *router = [RKRouter new];
RKRoute *route = [RKRoute new];
route.name = @"test_router";
route.resourcePathPattern = @"/routes";
RKRoute *route = [RKRoute routeWithName:@"test_router" resourcePathPattern:@"/routes" method:RKRequestMethodAny];
[router addRoute:route];
assertThat([router routesForResourcePathPattern:@"/routes"], contains(route, nil));
assertThat([router routesWithResourcePathPattern:@"/routes"], contains(route, nil));
}
- (void)testAddRouteWithName
{
RKRouter *router = [RKRouter new];
[router addRouteWithName:@"testing" resourcePathPattern:@"/route"];
[router addRouteWithName:@"testing" resourcePathPattern:@"/route" method:RKRequestMethodGET];
RKRoute *route = [router routeForName:@"testing"];
assertThat(route.name, is(equalTo(@"testing")));
assertThat(route.resourcePathPattern, is(equalTo(@"/route")));
assertThatInteger(route.method, is(equalToInteger(RKRequestMethodGET)));
}
- (void)testAddRouteWithClassAndMethod
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestUser class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestUser class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
RKRoute *route = [router routeForClass:[RKTestUser class] method:RKRequestMethodGET];
assertThat(route.objectClass, is(equalTo([RKTestUser class])));
assertThatInteger(route.method, is(equalToInteger(RKRequestMethodGET)));
@@ -286,7 +210,7 @@
- (void)testAddRouteWithClass
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestUser class] resourcePathPattern:@"/users/:userID"];
[router addRouteForClass:[RKTestUser class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodAny];
RKRoute *route = [router routeForClass:[RKTestUser class] method:RKRequestMethodGET];
assertThat(route.objectClass, is(equalTo([RKTestUser class])));
assertThatInteger(route.method, is(equalToInteger(RKRequestMethodAny)));
@@ -295,7 +219,7 @@
- (void)testRouteForObjectAndMethodWithExactMatch
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestUser class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestUser class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
RKTestUser *user = [RKTestUser new];
RKRoute *route = [router routeForObject:user method:RKRequestMethodGET];
assertThat(route, is(notNilValue()));
@@ -306,7 +230,7 @@
- (void)testRouteForObjectAndMethodWithSuperclassMatch
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
RKTestSubclassedObject *subclassedObject = [RKTestSubclassedObject new];
RKRoute *route = [router routeForObject:subclassedObject method:RKRequestMethodGET];
assertThat(route, is(notNilValue()));
@@ -318,9 +242,9 @@
- (void)testRoutesForClassReturnsAllRoutesForClass
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodPOST];
[router addRouteWithClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodPOST];
[router addRouteForClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
NSArray *routes = [router routesForClass:[RKTestObject class]];
assertThat(routes, hasCountOf(2));
}
@@ -328,9 +252,9 @@
- (void)testRouteForObjectReturnsAllRoutesForClassAndSuperclasses
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodPOST];
[router addRouteWithClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodPOST];
[router addRouteForClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodGET];
RKTestSubclassedObject *subclassed = [[RKTestSubclassedObject new] autorelease];
NSArray *routes = [router routesForObject:subclassed];
@@ -340,9 +264,9 @@
- (void)testRouteForObjectAndMethodFavorsExactMatchOverSuperclass
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodPOST];
[router addRouteWithClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID/3" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodPOST];
[router addRouteForClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID/3" method:RKRequestMethodGET];
RKTestSubclassedObject *subclassed = [[RKTestSubclassedObject new] autorelease];
RKRoute *route = [router routeForObject:subclassed method:RKRequestMethodGET];
@@ -354,9 +278,9 @@
- (void)testRouteForObjectAndMethodFavorsWildcardMatchOnExactClassOverSuperclass
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodPOST];
[router addRouteWithClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID/3" method:RKRequestMethodAny];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodPOST];
[router addRouteForClass:[RKTestSubclassedObject class] resourcePathPattern:@"/users/:userID/3" method:RKRequestMethodAny];
RKTestSubclassedObject *subclassed = [[RKTestSubclassedObject new] autorelease];
RKRoute *route = [router routeForObject:subclassed method:RKRequestMethodGET];
@@ -368,8 +292,8 @@
- (void)testRouteForObjectAndMethodFavorsExactSuperclassMethodMatchOverWildcard
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodAny];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodAny];
RKTestSubclassedObject *subclassed = [[RKTestSubclassedObject new] autorelease];
RKRoute *route = [router routeForObject:subclassed method:RKRequestMethodGET];
@@ -381,8 +305,8 @@
- (void)testRouteForObjectAndMethodFallsBackToSuperclassWildcardMatch
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteWithClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodAny];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/1" method:RKRequestMethodGET];
[router addRouteForClass:[RKTestObject class] resourcePathPattern:@"/users/:userID/2" method:RKRequestMethodAny];
RKTestSubclassedObject *subclassed = [[RKTestSubclassedObject new] autorelease];
RKRoute *route = [router routeForObject:subclassed method:RKRequestMethodPOST];
@@ -391,46 +315,72 @@
assertThat(route.resourcePathPattern, is(equalTo(@"/users/:userID/2")));
}
- (void)testResourcePathForObject
{
RKRouter *router = [RKRouter new];
[router addRouteWithClass:[RKTestUser class] resourcePathPattern:@"/users/:userID"];
RKTestUser *user = [RKTestUser new];
user.userID = [NSNumber numberWithInteger:12345];
NSString *resourcePath = [router resourcePathForObject:user method:RKRequestMethodGET];
assertThat(resourcePath, is(equalTo(@"/users/12345")));
}
- (void)testResourcePathForRouteNamed
{
RKRouter *router = [RKRouter new];
[router addRouteWithName:@"airlines_list" resourcePathPattern:@"/airlines.json"];
NSString *resourcePath = [router resourcePathForRouteNamed:@"airlines_list"];
assertThat(resourcePath, is(equalTo(@"/airlines.json")));
}
- (void)testResourcePathForRouteNamedInterpolatedWithObject
{
RKRouter *router = [RKRouter new];
[router addRouteWithName:@"user_bookmarks_path" resourcePathPattern:@"/users/:userID/bookmarks"];
RKTestUser *user = [RKTestUser new];
user.userID = [NSNumber numberWithInteger:12345];
NSString *resourcePath = [router resourcePathForRouteNamed:@"user_bookmarks_path" interpolatedWithObject:user];
assertThat(resourcePath, is(equalTo(@"/users/12345/bookmarks")));
}
//- (void)testResourcePathForObject
//{
// RKRouter *router = [RKRouter new];
// [router addRouteForClass:[RKTestUser class] resourcePathPattern:@"/users/:userID" method:RKRequestMethodAny];
// RKTestUser *user = [RKTestUser new];
// user.userID = [NSNumber numberWithInteger:12345];
// NSString *resourcePath = [router resourcePathForObject:user method:RKRequestMethodGET];
// assertThat(resourcePath, is(equalTo(@"/users/12345")));
//}
//
//- (void)testResourcePathForRouteNamed
//{
// RKRouter *router = [RKRouter new];
// [router addRouteWithName:@"airlines_list" resourcePathPattern:@"/airlines.json"];
// NSString *resourcePath = [router resourcePathForRouteNamed:@"airlines_list"];
// assertThat(resourcePath, is(equalTo(@"/airlines.json")));
//}
//
//- (void)testResourcePathForRouteNamedInterpolatedWithObject
//{
// RKRouter *router = [RKRouter new];
// [router addRouteWithName:@"user_bookmarks_path" resourcePathPattern:@"/users/:userID/bookmarks"];
// RKTestUser *user = [RKTestUser new];
// user.userID = [NSNumber numberWithInteger:12345];
// NSString *resourcePath = [router resourcePathForRouteNamed:@"user_bookmarks_path" interpolatedWithObject:user];
// assertThat(resourcePath, is(equalTo(@"/users/12345/bookmarks")));
//}
// TODO: This is broken. Not sure why...
- (void)testOptionallyEscapesPathWhenInterpolating
{
RKRouter *router = [RKRouter new];
[router addRouteWithName:@"user_bookmarks_path" resourcePathPattern:@"/users/:userID/bookmarks/:name"];
[router addRouteWithName:@"user_bookmarks_path" resourcePathPattern:@"/users/:userID/bookmarks/:name" method:RKRequestMethodGET];
RKRoute *route = [router routeForName:@"user_bookmarks_path"];
route.shouldEscapeResourcePath = YES;
RKTestUser *user = [RKTestUser new];
user.userID = [NSNumber numberWithInteger:12345];
user.name = @"This/That";
NSString *resourcePath = [router resourcePathForRouteNamed:@"user_bookmarks_path" interpolatedWithObject:user];
NSString *resourcePath = [route resourcePathForObject:user];
// NSString *resourcePath = [router resourcePathForRouteNamed:@"user_bookmarks_path" interpolatedWithObject:user];
assertThat(resourcePath, is(equalTo(@"/users/12345/bookmarks")));
}
// TODO: Add tests for superclass match in routeForObject:
- (void)testRouteForRelationshipOfObject
{
RKRouter *router = [RKRouter new];
[router addRouteForRelationship:@"friends" ofClass:[RKTestUser class] withResourcePathPattern:@"/friends" method:RKRequestMethodGET];
RKTestUser *user = [RKTestUser new];
RKRoute *route = [router routeForRelationship:@"friends" ofObject:user method:RKRequestMethodGET];
assertThat(route, is(notNilValue()));
assertThat(route.name, is(equalTo(@"friends")));
assertThat(route.resourcePathPattern, is(equalTo(@"/friends")));
assertThatInteger(route.method, is(equalToInteger(RKRequestMethodGET)));
}
- (void)testRoutesForRelationship
{
RKRouter *router = [RKRouter new];
[router addRouteForRelationship:@"friends" ofClass:[RKTestUser class] withResourcePathPattern:@"/friends" method:RKRequestMethodGET];
[router addRouteForRelationship:@"friends" ofClass:[RKTestUser class] withResourcePathPattern:@"/friends" method:RKRequestMethodPOST];
[router addRouteForRelationship:@"enemies" ofClass:[RKTestUser class] withResourcePathPattern:@"/enemies" method:RKRequestMethodGET];
NSArray *routes = [router routesForRelationship:@"friends" ofClass:[RKTestUser class]];
assertThat(routes, hasCountOf(2));
}
@end

View File

@@ -295,7 +295,7 @@
RKObjectMapping *serializationMapping = [mapping inverseMapping];
RKObjectManager *objectManager = [RKTestFactory objectManager];
[objectManager.router addRouteWithClass:[RKTestComplexUser class] resourcePathPattern:@"/204"];
[objectManager.router addRoute:[RKRoute routeWithClass:[RKTestComplexUser class] resourcePathPattern:@"/204"method:RKRequestMethodAny]];
[objectManager.mappingProvider setSerializationMapping:serializationMapping forClass:[RKTestComplexUser class]];
RKTestComplexUser *user = [[RKTestComplexUser new] autorelease];
@@ -320,7 +320,7 @@
RKObjectMapping *serializationMapping = [mapping inverseMapping];
RKObjectManager *objectManager = [RKTestFactory objectManager];
[objectManager.router addRouteWithClass:[RKTestComplexUser class] resourcePathPattern:@"/notNestedUser"];
[objectManager.router addRoute:[RKRoute routeWithClass:[RKTestComplexUser class] resourcePathPattern:@"/notNestedUsed" method:RKRequestMethodAny]];
[objectManager.mappingProvider setSerializationMapping:serializationMapping forClass:[RKTestComplexUser class]];
RKTestComplexUser *user = [[RKTestComplexUser new] autorelease];

View File

@@ -384,7 +384,7 @@
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKObjectMapperTestModel class]];
mapping.rootKeyPath = @"human";
[objectManager.mappingProvider registerObjectMapping:mapping withRootKeyPath:@"human"];
[objectManager.router addRouteWithClass:[RKObjectMapperTestModel class] resourcePathPattern:@"/human/1"];
[objectManager.router addRouteForClass:[RKObjectMapperTestModel class] resourcePathPattern:@"/human/1" method:RKRequestMethodAny];
objectManager.serializationMIMEType = RKMIMETypeJSON;
RKTestResponseLoader *responseLoader = [RKTestResponseLoader responseLoader];