Skip to content

Commit

Permalink
Rails validation error parsing and unit tests
Browse files Browse the repository at this point in the history
This initial commit has full support for JSON with unit tests that demonstrate the functionality.  When switching into XML mode some unit tests fail so the XML support for this new functionality still needs attention.

Signed-off-by: Joshua Vickery <josh@yfactorial.com>
  • Loading branch information
adamalex authored and Joshua Vickery committed Mar 12, 2009
1 parent eadcef5 commit e1f8531
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 13 deletions.
30 changes: 30 additions & 0 deletions Classes/DogTest.m
Expand Up @@ -35,6 +35,36 @@ -(void) testWithBadAuth {
[aError release];
}

-(void) testWithValidationError{
NSError *aError = nil;
Dog * aDog = [[Dog alloc] init];
aDog.personId = owner.personId;

aDog.name = @"ReservedName";
BOOL success = [aDog saveRemoteWithResponse:&aError];

STAssertTrue(success == NO,@"Should not have been successful");
STAssertTrue([[aError errors] count] == 1,@"Should have one error");
STAssertTrue([[[aError errors] objectAtIndex:0] isEqualToString:@"name is reserved"],@"Should equal: name is reserved");
STAssertTrue(aError.code == 422,@"Should be 422");

[aDog release];
}

-(void) testWithNoErrors{
NSError *aError = nil;
Dog * aDog = [[Dog alloc] init];
aDog.personId = owner.personId;

aDog.name = @"AllowedName";
BOOL success = [aDog saveRemoteWithResponse:&aError];

STAssertTrue(success == YES,@"Should have been successful");
STAssertTrue([[aError errors] count] == 0,@"Should have no errors");

[aDog release];
}

-(void) testDogProperties {

Dog * aDog = [[Dog alloc] init];
Expand Down
13 changes: 13 additions & 0 deletions Classes/NSError+Error.h
@@ -0,0 +1,13 @@
//
// NSError+Error.h
// objective_resource
//
// Created by Adam Alexander on 3/10/09.
// Copyright 2009 yFactorial, LLC. All rights reserved.
//

@interface NSError(Error)

-(NSArray *)errors;

@end
17 changes: 17 additions & 0 deletions Classes/NSError+Error.m
@@ -0,0 +1,17 @@
//
// NSError+Error.m
// objective_resource
//
// Created by Adam Alexander on 3/10/09.
// Copyright 2009 yFactorial, LLC. All rights reserved.
//

#import "NSError+Error.h"

@implementation NSError(Error)

-(NSArray *)errors {
return [self.userInfo valueForKey:NSLocalizedRecoveryOptionsErrorKey];
}

@end
6 changes: 3 additions & 3 deletions Classes/lib/NSHTTPURLResponse+Error.h
Expand Up @@ -8,8 +8,8 @@

@interface NSHTTPURLResponse(Error)

-(NSError *) error;
-(NSError *) errorWithBody:(NSData *)data;
-(BOOL) isSuccess;
+ (NSError *)buildResponseError:(int)statusCode;

+ (NSError *)buildResponseError:(int)statusCode withBody:(NSData *)data;
+ (NSArray *)errorArrayForBody:(NSData *)data;
@end
17 changes: 13 additions & 4 deletions Classes/lib/NSHTTPURLResponse+Error.m
Expand Up @@ -7,22 +7,31 @@
//

#import "NSHTTPURLResponse+Error.h"
#import "ObjectiveResourceConfig.h"

@implementation NSHTTPURLResponse(Error)

+ (NSError *)buildResponseError:(int)statusCode{
+ (NSError *)buildResponseError:(int)statusCode withBody:(NSData *)data{
NSMutableDictionary *description = [NSMutableDictionary dictionary];
[description setObject:[NSString stringWithFormat:@"%i Error",statusCode] forKey:NSLocalizedFailureReasonErrorKey];
[description setObject:[[self class] localizedStringForStatusCode:statusCode] forKey:NSLocalizedDescriptionKey];
[description setObject:[[self class] localizedStringForStatusCode:statusCode] forKey:NSLocalizedDescriptionKey];
[description setObject:[[self class] errorArrayForBody:data] forKey:NSLocalizedRecoveryOptionsErrorKey];
return [NSError errorWithDomain:@"com.yfactorial.objectiveresource" code:statusCode userInfo:description];
}

-(NSError *) error {
+ (NSArray *)errorArrayForBody:(NSData *)data {
NSMutableArray *returnStrings = [NSMutableArray array];
NSArray *errorArrays = [[self class] performSelector:[ObjectiveResourceConfig getParseDataMethod] withObject:data];
for (NSArray *error in errorArrays) [returnStrings addObject:[error componentsJoinedByString:@" "]];
return returnStrings;
}

-(NSError *) errorWithBody:(NSData *)data {
if([self isSuccess]) {
return nil;
}
else {
return [[self class] buildResponseError:[self statusCode]];
return [[self class] buildResponseError:[self statusCode] withBody:data];
}
}

Expand Down
3 changes: 2 additions & 1 deletion Classes/lib/ObjectiveResource.h
Expand Up @@ -7,4 +7,5 @@
//

#import "NSObject+ObjectiveResource.h"
#import "ObjectiveResourceConfig.h"
#import "ObjectiveResourceConfig.h"
#import "NSError+Error.h"
8 changes: 4 additions & 4 deletions Classes/lib/Response.m
Expand Up @@ -17,11 +17,11 @@ + (id)responseFrom:(NSHTTPURLResponse *)response withBody:(NSData *)data andErro
return [[[self alloc] initFrom:response withBody:data andError:aError] autorelease];
}

- (void)normalizeError:(NSError *)aError {
- (void)normalizeError:(NSError *)aError withBody:(NSData *)data{
switch ([aError code]) {
case NSURLErrorUserCancelledAuthentication:
self.statusCode = 401;
self.error = [NSHTTPURLResponse buildResponseError:401];
self.error = [NSHTTPURLResponse buildResponseError:401 withBody:data];
break;
default:
self.error = aError;
Expand All @@ -35,10 +35,10 @@ - (id)initFrom:(NSHTTPURLResponse *)response withBody:(NSData *)data andError:(N
if(response) {
self.statusCode = [response statusCode];
self.headers = [response allHeaderFields];
self.error = [response error];
self.error = [response errorWithBody:data];
}
else {
[self normalizeError:aError];
[self normalizeError:aError withBody:data];
}
return self;
}
Expand Down
8 changes: 8 additions & 0 deletions objective_resource.xcodeproj/project.pbxproj
Expand Up @@ -164,6 +164,8 @@
35CA32650F1E67A1001513AA /* ConnectionDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 35CA32640F1E67A1001513AA /* ConnectionDelegate.m */; };
35CA32660F1E67A1001513AA /* ConnectionDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 35CA32640F1E67A1001513AA /* ConnectionDelegate.m */; };
6679FFB00F5AF22E00412ABF /* ConnectionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6679FFAF0F5AF22E00412ABF /* ConnectionManager.m */; };
6624BF650F676B0C006FD4EE /* NSError+Error.m in Sources */ = {isa = PBXBuildFile; fileRef = 6624BF640F676B0C006FD4EE /* NSError+Error.m */; };
6624BF660F676B0C006FD4EE /* NSError+Error.m in Sources */ = {isa = PBXBuildFile; fileRef = 6624BF640F676B0C006FD4EE /* NSError+Error.m */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -290,6 +292,8 @@
35CA32640F1E67A1001513AA /* ConnectionDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ConnectionDelegate.m; path = lib/ConnectionDelegate.m; sourceTree = "<group>"; };
6679FFAE0F5AF22E00412ABF /* ConnectionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConnectionManager.h; path = lib/ConnectionManager.h; sourceTree = "<group>"; };
6679FFAF0F5AF22E00412ABF /* ConnectionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ConnectionManager.m; path = lib/ConnectionManager.m; sourceTree = "<group>"; };
6624BF630F676B0C006FD4EE /* NSError+Error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+Error.h"; sourceTree = "<group>"; };
6624BF640F676B0C006FD4EE /* NSError+Error.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+Error.m"; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -481,6 +485,8 @@
357A91B00E9A55EF0025D9AF /* Response.m */,
23C922F10F25066900EDE8AF /* NSMutableURLRequest+ResponseType.h */,
23C922F20F25066900EDE8AF /* NSMutableURLRequest+ResponseType.m */,
6624BF630F676B0C006FD4EE /* NSError+Error.h */,
6624BF640F676B0C006FD4EE /* NSError+Error.m */,
);
name = Connection;
sourceTree = "<group>";
Expand Down Expand Up @@ -837,6 +843,7 @@
23510DE80F4CB31B00B0796F /* NSArray+JSONSerializableSupport.m in Sources */,
23510DEC0F4CB32900B0796F /* NSNumber+XMLSerializableSupport.m in Sources */,
6679FFB00F5AF22E00412ABF /* ConnectionManager.m in Sources */,
6624BF650F676B0C006FD4EE /* NSError+Error.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -890,6 +897,7 @@
23B8A4940F55EF7C00F74584 /* ObjectiveResourceConfig.m in Sources */,
23B8A4950F55EF7C00F74584 /* NSArray+JSONSerializableSupport.m in Sources */,
23B8A4960F55EF7C00F74584 /* NSNumber+XMLSerializableSupport.m in Sources */,
6624BF660F676B0C006FD4EE /* NSError+Error.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
2 changes: 1 addition & 1 deletion sample_rails_app/app/controllers/dogs_controller.rb
Expand Up @@ -59,7 +59,7 @@ def create
else
format.html { render :action => "new" }
format.xml { render :xml => @dog.errors, :status => :unprocessable_entity }
format.json { render :json => @dog.errors }
format.json { render :json => @dog.errors, :status => :unprocessable_entity }
end
end
end
Expand Down
1 change: 1 addition & 0 deletions sample_rails_app/app/models/dog.rb
@@ -1,3 +1,4 @@
class Dog < ActiveRecord::Base
belongs_to :person
validates_exclusion_of :name, :in => ["ReservedName"]
end

0 comments on commit e1f8531

Please sign in to comment.