Skip to content

Instantly share code, notes, and snippets.

@beccadax
Last active December 20, 2015 17:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beccadax/6170253 to your computer and use it in GitHub Desktop.
Save beccadax/6170253 to your computer and use it in GitHub Desktop.
What I imagine a Cocoa-like Objective-C web framework woud look like. Pseudocode, so don't take it too seriously.
@interface MyRootController: OWWebController
// GET /
- (BOOL)get:(OWRequest*)request error:(NSError**)error {
return [request.responder renderTemplate:@"Root" withObject:nil error:error];
}
// Everything under /users is handled by MyUsersController
- (OWWebController*)controllerForUsersRequest:(OWRequest*)request error:(NSError**)error {
return [[MyUsersController alloc] initWithManagedObjectContext:self.managedObjectContext];
}
// Stuff inherited:
- (BOOL)request:(OWRequest*)request error:(NSError**)error;
- (OWController*)controllerForRequest:(OWRequest*)request error:(NSError**)error;
- (NSURL*)URLForComponents:(NSArray*)components andParameters:(NSDictionary*)params; // And, for instance, -URLForUsersAndParameters:, etc.
@end
@interface MyUsersController: OWRestfulWebController
- (NSString*)entityName {
return @"User";
}
// Maybe there would be predicateForObjectsWith: and sortDescriptors in the parent class, and you could override those?
- (NSFetchRequest*)fetchRequestForObjectsWith:(NSString*)username forRequest:(OWRequest*)request error:(NSError**)error {
NSFetchRequest * fetchReq = [super fetchRequestForObjectsWith:username forRequest:request error:error];
if(username) {
fetchReq.predicate = [NSPredicate predicateWithFormat:@"username = %@", username];
}
else {
fetchReq.sortDescriptors = @[ [NSSortDescriptor sortDescriptorForKey:@"username" ascending:YES] ];
}
return YES;
}
// Ensures only you can edit your own account.
// There are also methods like -canPut:with:error: if there's only one action to worry about.
- (BOOL)canPerformAction:(SEL)action forRequest:(OWRequest*)request withComponents:(NSArray*)components error:(NSError**)error {
if(![super canPerformAction:action forRequest:request withComponents:components error:error]) {
return NO;
}
if(action == @selector(put:with:error:) || action == @selector(delete:with:error:) || action == @selector(get:with:editWithError:) || action == @selector(post:with:followWith:error:)) {
User * accessingUser = [self objectWith:components.firstObject forRequest:request error:error];
User * authenticatingUser = [self objectWith:request.session[@"authenticatedUser"] forRequest:request error:error];
// Should sessions be structured somehow? Or should we expect people to declare appropriate categories on OWSession/OWWebController?
if(authenticatingUser != accessingUser && !authenticatingUser.administrator) {
*error = [NSError errorWithDomain:OWErrorDomain code:OWErrorUnauthorized userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"You are not authorized to access this user's data.", @"") }];
return NO;
}
}
return YES;
}
// These actions are all implemented by OWRestfulWebController, so we get them for free:
// GET .
- (BOOL)get:(OWRequest*)request error:(NSError**)error;
// POST .
- (BOOL)post:(OWRequest*)request error:(NSError**)error;
// GET ./new
- (BOOL)get:(OWRequest*)request newWithError:(NSError**)error;
// GET ./$username
- (BOOL)get:(OWRequest*)request with:(NSString*)component error:(NSError**)error;
// PUT ./$username
- (BOOL)put:(OWRequest*)request with:(NSString*)component error:(NSError**)error;
// DELETE ./$username
- (BOOL)delete:(OWRequest*)request with:(NSString*)component error:(NSError**)error;
// GET ./$username/edit
- (BOOL)get:(OWRequest*)request with:(NSString*)component editWithError:(NSError**)error;
// Everything under ./$username/posts is handled by MyPostsController
- (OWWebController*)controllerWith:(NSString*)username postsWithRequest:(OWRequest*)request error:(NSError**)error {
OWUser * user = [self objectWith:username forRequest:request error:error];
// This would be a custom-implemented initializer
return [[MyPostsController alloc] initWithUser:user];
}
// Here are some additional non-restful actions...
// GET ./suspended
- (BOOL)get:(OWRequest*)request suspendedWithError:(NSError**)error {
NSFetchRequest * fetchReq = [self fetchRequestForObjectsWith:nil forRequest:request error:error];
if(!fetchReq) {
return nil;
}
fetchReq.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:[NSPredicate predicateWithFormat:@"suspended == YES"], fetchReq.predicate, nil]];
NSArray * users = [self.managedObjectContext executeFetchRequest:fetchReq error:error];
if(!users) {
return NO;
}
return [request.responder renderTemplate:@"MyUsersController/suspended" withObject:users error:error];
}
// POST ./$foo/follow/$bar
- (BOOL)post:(OWRequest*)request with:(NSString*)username followWith:(NSString*)otherUsername error:(NSError**)error {
OWAccount * me = [self objectWith:username forRequest:request error:error];
OWAccount * other = [self objectWith:otherUsername forRequest:request error:error];
if(!me || !other) {
return NO;
}
[me addFollowing:other];
if(![self.managedObjectContext save:error]) {
return NO;
}
return [request.responder redirectTo:[OWApplication.sharedApplication.rootWebController URLForUsersWith:otherUsername andParameters:nil]];
}
// DELETE ./$foo/follow/$bar
- (BOOL)delete:(OWRequest*)request with:(NSString*)username followWith:(NSString*)otherUsername error:(NSError**)error {
OWUser * me = [self objectWith:username forRequest:request error:error];
OSUser * other = [self objectWith:otherUsername forRequest:request error:error];
if(!me || !other) {
return NO;
}
[me removeFollowing:other];
if(![self.managedObjectContext save:error]) {
return NO;
}
return [request.responder redirectTo:[OWApplication.sharedApplication.rootWebController URLForUsersWith:otherUsername andParameters:nil]];
}
// Stuff inherited:
- (NSArray*)objectsWith:(NSString*)username forRequest:(OWRequest*)request error:(NSError**)error;
- (NSManagedObject*)objectWith:(NSString*)username forRequest:(OWRequest*)request error:(NSError**)error; // Requires exactly one, errors otherwise
- (NSManagedObject*)newObjectForRequest:(OWRequest*)request error:(NSError**)error;
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment