Skip to content
This repository has been archived by the owner on Mar 23, 2021. It is now read-only.

Commit

Permalink
Refactor route construction and allow prefixed remoteCreate (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
dingbat committed Jun 6, 2012
1 parent 15d6be5 commit e33fcc2
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 37 deletions.
5 changes: 3 additions & 2 deletions nsrails/Source/NSRMacros.h
Expand Up @@ -61,7 +61,8 @@
//definitely check out how this works - it's cool
#define _CAT(a, b) _PRIMITIVE_CAT(a, b)
#define _PRIMITIVE_CAT(a, b) a##b
#define _N_ARGS(...) _N_ARGS_1(__VA_ARGS__, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0)
//typically descends from 16... here, any more args than 2 will cap at 2 so we don't have to define a million different _NSR_Prefix flavors
#define _N_ARGS(...) _N_ARGS_1(__VA_ARGS__, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0)
#define _N_ARGS_1(...) _N_ARGS_2(__VA_ARGS__)
#define _N_ARGS_2(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, n, ...) n

Expand Down Expand Up @@ -98,6 +99,6 @@
// and return an array of HTTP verbs where this will be used
#define _NSR_Prefix(member, array) \
- (NSRRemoteObject *) NSRUseResourcePrefix { return member; }; \
- (NSArray *) NSRUseResourcePrefixMethods { return array; }
+ (NSArray *) NSRUseResourcePrefixMethods { return array; }


84 changes: 52 additions & 32 deletions nsrails/Source/NSRRemoteObject.m
Expand Up @@ -60,8 +60,7 @@ - (NSRPropertyCollection *) propertyCollection;
- (NSRRemoteObject *) makeRelevantModelFromClass:(NSString *)classN basedOn:(NSDictionary *)dict;
- (id) remoteRepresentationOfObjectForProperty:(NSRProperty *)prop;

+ (NSString *) routeForControllerMethod:(NSString *)customRESTMethod;
- (NSString *) routeForInstanceMethod:(NSString *)customRESTMethod httpVerb:(NSString *)verb;
- (NSString *) routeForInstanceMethod:(NSString *)method withID:(NSNumber *)rID httpMethod:(NSString *)verb;

+ (NSString *) NSRMap;
+ (NSString *) NSRUseModelName;
Expand Down Expand Up @@ -810,30 +809,20 @@ - (NSDictionary *) dictionaryOfRemotePropertiesFromNesting:(BOOL)nesting

#pragma mark - HTTP Request stuff

+ (NSString *) routeForControllerMethod:(NSString *)method
+ (NSString *) routeForMethod:(NSString *)method withObject:(NSRRemoteObject *)obj httpMethod:(NSString *)verb
{
NSString *controller = [self masterPluralName];
NSString *route = (method ? method : @"");
//if !controller, means it was called on NSRRemoteObject (to access a "root method"), so don't modify the route
//otherwise, add the controller to the beginning
if (controller)

NSString *controller = [self masterPluralName];
if (!controller)
{
route = [controller stringByAppendingPathComponent:method];
//means it was called on NSRRemoteObject (to access a "root method"), so don't modify the route
return route;
}

return route;
}

- (NSString *) routeForInstanceMethod:(NSString *)method httpMethod:(NSString *)verb
{
if (!self.remoteID)
{
[NSException raise:NSRNullRemoteIDException format:@"Attempt to %@ to %@/:id/%@ with %@ instance that has a nil remoteID.",verb,[[self class] masterPluralName],method ? method : @"",[self class]];
}

NSRRemoteObject *prefix = nil;
BOOL shouldUsePrefixConstruction = NO;
if ([self respondsToSelectorWithoutClimbingHierarchy:@selector(NSRUseResourcePrefix)])
if ([obj respondsToSelectorWithoutClimbingHierarchy:@selector(NSRUseResourcePrefix)])
{
NSArray *allowedVerbs = [self performSelectorWithoutClimbingHierarchy:@selector(NSRUseResourcePrefixMethods)];

Expand All @@ -843,27 +832,45 @@ - (NSString *) routeForInstanceMethod:(NSString *)method httpMethod:(NSString *)

if (shouldUsePrefixConstruction)
{
prefix = [self performSelectorWithoutClimbingHierarchy:@selector(NSRUseResourcePrefix)];
prefix = [obj performSelectorWithoutClimbingHierarchy:@selector(NSRUseResourcePrefix)];
if (!prefix.remoteID)
{
[NSException raise:NSRNullRemoteIDException format:@"Attempt to %@ %@ instance with a prefix association (%@ instance) that has a nil remoteID.",verb,[self class],[prefix class]];
}
}
}

// 1/something
// 1/something (if there's an ID)
// posts/1/something
// other/5/posts/1/something (only if prefix - use everything as instance method of prefix object)
// other/5/posts/1/something (if there's a prefix association)

NSString *route = [[self.remoteID stringValue] stringByAppendingPathComponent:method];
route = [[self class] routeForControllerMethod:route];
if (obj.remoteID)
route = [[obj.remoteID stringValue] stringByAppendingPathComponent:route];

route = [controller stringByAppendingPathComponent:route];

if (shouldUsePrefixConstruction)
route = [prefix routeForInstanceMethod:route httpMethod:verb];

return route;
}

- (NSString *) routeForInstanceMethod:(NSString *)customRESTMethod httpMethod:(NSString *)verb
{
if (!self.remoteID)
{
[NSException raise:NSRNullRemoteIDException format:@"Attempt to make a remote instance request (instance of %@) but remoteID is nil.",[self class]];
}

return [[self class] routeForMethod:customRESTMethod withObject:self httpMethod:verb];
}

+ (NSString *) routeForControllerMethod:(NSString *)customRESTMethod
{
//http method is only relevant for instances bc it's only relevant for prefixes
return [self routeForMethod:customRESTMethod withObject:nil httpMethod:nil];
}


#pragma mark Performing actions on instances

Expand Down Expand Up @@ -951,7 +958,13 @@ + (void) remoteGET:(NSString *)customRESTMethod async:(NSRHTTPCompletionBlock)co

- (BOOL) remoteCreate:(NSError **)error
{
NSDictionary *jsonResponse = [[self class] remoteRequest:@"POST" method:nil bodyAsObject:self error:error];
NSString *route = [[self class] routeForMethod:nil withObject:self httpMethod:@"POST"];

NSDictionary *jsonResponse = [self.getRelevantConfig makeRequest:@"POST"
requestBody:[self remoteDictionaryRepresentationWrapped:YES]
route:route
sync:error
orAsync:nil];
if (!jsonResponse)
return NO;

Expand All @@ -962,15 +975,22 @@ - (BOOL) remoteCreate:(NSError **)error

- (void) remoteCreateAsync:(NSRBasicCompletionBlock)completionBlock
{
[[self class] remoteRequest:@"POST" method:nil bodyAsObject:self async:
^(id result, NSError *error)
{
if (result)
[self setPropertiesUsingRemoteDictionary:result applyToRemoteAttributes:YES];
completionBlock(error);
}];
NSString *route = [[self class] routeForMethod:nil withObject:self httpMethod:@"POST"];

[self.getRelevantConfig makeRequest:@"POST"
requestBody:[self remoteDictionaryRepresentationWrapped:YES]
route:route
sync:nil
orAsync:^(id result, NSError *error)
{
if (result)
[self setPropertiesUsingRemoteDictionary:result
applyToRemoteAttributes:YES];
completionBlock(error);
}];
}


#pragma mark Update

- (BOOL) remoteUpdate:(NSError **)error
Expand Down
1 change: 1 addition & 0 deletions nsrails/Tests/NSRAsserts.h
Expand Up @@ -31,6 +31,7 @@

- (NSString *) routeForInstanceMethod:(NSString *)route httpMethod:(NSString *)str;
+ (NSString *) routeForControllerMethod:(NSString *)route;
+ (NSString *) routeForMethod:(NSString *)method withObject:(NSRRemoteObject *)obj httpMethod:(NSString *)verb;

+ (NSString *) typeForProperty:(NSString *)prop;

Expand Down
31 changes: 28 additions & 3 deletions nsrails/Tests/NSRRemoteObject.m
Expand Up @@ -389,7 +389,7 @@ - (void) test_routes_with_prefixes

// Instance
Prefixer *smth = [[Prefixer alloc] init];
STAssertNil([smth NSRUseResourcePrefixMethods], @"Should have nil allowed array");
STAssertNil([[smth class] NSRUseResourcePrefixMethods], @"Should have nil allowed array");

STAssertThrowsSpecificNamed([smth routeForInstanceMethod:nil httpMethod:verb], NSException, NSRNullRemoteIDException, @"Should have been an exception getting instance route if nil remoteID");

Expand All @@ -404,6 +404,12 @@ - (void) test_routes_with_prefixes
STAssertEqualObjects([smth routeForInstanceMethod:nil httpMethod:verb], @"empties/15/prefixers/1", @"Nil instance route failed");
STAssertEqualObjects([smth routeForInstanceMethod:@"action" httpMethod:verb], @"empties/15/prefixers/1/action", @"Instance route failed");

STAssertEqualObjects([Prefixer routeForMethod:nil withObject:smth httpMethod:@"POST"], @"empties/15/prefixers/1", @"Controller route w/object failed");

smth.remoteID = nil;

STAssertEqualObjects([Prefixer routeForMethod:nil withObject:smth httpMethod:@"POST"], @"empties/15/prefixers", @"Should still work even without a remoteID");


// Now double nested + custom names
// Controller (class)
Expand All @@ -413,15 +419,24 @@ - (void) test_routes_with_prefixes
STAssertEqualObjects([Prefixer2 routeForControllerMethod:nil], @"premans", @"Nil controller route failed");
STAssertEqualObjects([Prefixer2 routeForControllerMethod:@"action"], @"premans/action", @"Controller route failed");

//should be normal for DELETE
NSRAssertEqualArraysNoOrder([Prefixer2 NSRUseResourcePrefixMethods], NSRArray(@"GET", @"patch"));

// Instance
Prefixer2 *smth2 = [[Prefixer2 alloc] init];

NSRAssertEqualArraysNoOrder([smth2 NSRUseResourcePrefixMethods], NSRArray(@"GET", @"patch"));
STAssertThrowsSpecificNamed([Prefixer2 routeForMethod:nil withObject:smth2 httpMethod:verb], NSException, NSRNullRemoteIDException, @"Should have been an exception getting controller route if nil association");


STAssertEqualObjects([Prefixer2 routeForControllerMethod:@"action"], @"premans/action", @"Controller route failed");

STAssertThrowsSpecificNamed([smth2 routeForInstanceMethod:nil httpMethod:verb], NSException, NSRNullRemoteIDException, @"Should have been an exception getting instance route if nil remoteID");

smth2.remoteID = [NSNumber numberWithInt:15];

STAssertThrowsSpecificNamed([Prefixer2 routeForMethod:nil withObject:smth2 httpMethod:verb], NSException, NSRNullRemoteIDException, @"Should have been an exception getting controller route if nil association, even with ID");


//should be normal for DELETE
STAssertEqualObjects([smth2 routeForInstanceMethod:nil httpMethod:@"DELETE"], @"premans/15", @"Should be normal for non-included");
STAssertEqualObjects([smth2 routeForInstanceMethod:@"something" httpMethod:@"DELETE"], @"premans/15/something", @"Should be normal for non-included");

Expand Down Expand Up @@ -454,6 +469,16 @@ - (void) test_routes_with_prefixes

STAssertEqualObjects([smth2 routeForInstanceMethod:nil httpMethod:verb], @"empties/23/prefixers/15/premans/1", @"Nil instance route failed");
STAssertEqualObjects([smth2 routeForInstanceMethod:@"action" httpMethod:verb], @"empties/23/prefixers/15/premans/1/action", @"Instance route failed");

STAssertEqualObjects([Prefixer2 routeForMethod:nil withObject:smth2 httpMethod:verb], @"empties/23/prefixers/15/premans/1", @"Should build controller route w/object");

STAssertEqualObjects([Prefixer2 routeForMethod:@"something" withObject:smth2 httpMethod:verb], @"empties/23/prefixers/15/premans/1/something", @"Should build controller route w/object");

smth2.remoteID = nil;

STAssertEqualObjects([Prefixer2 routeForMethod:nil withObject:smth2 httpMethod:verb], @"empties/23/prefixers/15/premans", @"Should build controller route w/o remoteID");

STAssertEqualObjects([Prefixer2 routeForMethod:@"something" withObject:smth2 httpMethod:verb], @"empties/23/prefixers/15/premans/something", @"Should build controller route w/o remoteID");
}
}

Expand Down

0 comments on commit e33fcc2

Please sign in to comment.