Reworked parsing stuff somewhat and created a new ManagedObjectStore for encapsulating the managed object context and friends

This commit is contained in:
Blake Watters
2009-09-22 18:22:13 -04:00
parent a6d6c891ce
commit ab6cf9c648
10 changed files with 201 additions and 48 deletions

View File

@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
2525EBEB106961DD0069EBED /* OTRestManagedObjectStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 2525EBE9106961DD0069EBED /* OTRestManagedObjectStore.h */; };
2525EBEC106961DD0069EBED /* OTRestManagedObjectStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 2525EBEA106961DD0069EBED /* OTRestManagedObjectStore.m */; };
2580B068102E0F1000832D07 /* OTRestModelLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 2580B066102E0F1000832D07 /* OTRestModelLoader.h */; };
2580B069102E0F1000832D07 /* OTRestModelLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 2580B067102E0F1000832D07 /* OTRestModelLoader.m */; };
2580B0C7102E1EBC00832D07 /* libElementParser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F4E18DB102DD31E00320118 /* libElementParser.a */; };
@@ -98,6 +100,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
2525EBE9106961DD0069EBED /* OTRestManagedObjectStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTRestManagedObjectStore.h; sourceTree = "<group>"; };
2525EBEA106961DD0069EBED /* OTRestManagedObjectStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTRestManagedObjectStore.m; sourceTree = "<group>"; };
2580B066102E0F1000832D07 /* OTRestModelLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTRestModelLoader.h; sourceTree = "<group>"; };
2580B067102E0F1000832D07 /* OTRestModelLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTRestModelLoader.m; sourceTree = "<group>"; };
25FCDDD91035BC85005418A7 /* OTRestManagedModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTRestManagedModel.h; sourceTree = "<group>"; };
@@ -269,6 +273,8 @@
25FCDE9B1035E901005418A7 /* OTRestModelMappableProtocol.h */,
3FBF1D37103614E500E307AC /* OTRestModelManager.h */,
3FBF1D38103614E500E307AC /* OTRestModelManager.m */,
2525EBE9106961DD0069EBED /* OTRestManagedObjectStore.h */,
2525EBEA106961DD0069EBED /* OTRestManagedObjectStore.m */,
);
name = Modeling;
sourceTree = "<group>";
@@ -401,6 +407,7 @@
3FCDFB69103A51B600D7CD66 /* SBJsonParser.h in Headers */,
3FCDFB6B103A51B600D7CD66 /* SBJsonWriter.h in Headers */,
3F0150FF103B148300BE3E24 /* OTRestModelMapper_Private.h in Headers */,
2525EBEB106961DD0069EBED /* OTRestManagedObjectStore.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -544,6 +551,7 @@
3FCDFB68103A51B600D7CD66 /* SBJsonBase.m in Sources */,
3FCDFB6A103A51B600D7CD66 /* SBJsonParser.m in Sources */,
3FCDFB6C103A51B600D7CD66 /* SBJsonWriter.m in Sources */,
2525EBEC106961DD0069EBED /* OTRestManagedObjectStore.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -583,6 +591,7 @@
1DEB922008733DC00010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ADDITIONAL_SDKS = "";
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
DSTROOT = /tmp/OTRestFramework.dst;
@@ -590,6 +599,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = OTRestFramework_Prefix.pch;
INSTALL_PATH = /usr/local/lib;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = OTRestFramework;
};
name = Release;

View File

@@ -9,19 +9,18 @@
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "OTRestModelMappableProtocol.h"
#ifdef TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <UIKit/UIKit.h>
#define myContext [[[UIApplication sharedApplication] delegate] managedObjectContext]
#else
#import <AppKit/AppKit.h>
#define myContext [[[NSApplication sharedApplication] delegate] managedObjectContext]
#endif
#import "OTRestModelManager.h"
@interface OTRestManagedModel : NSManagedObject <OTRestModelMappable> {
}
/**
* The Core Data managed object context from the OTRestModelManager's objectStore
* that is managing this model
*/
+ (NSManagedObjectContext*)managedObjectContext;
/**
* The NSEntityDescription for the Subclass
* defaults to the subclass className, may be overridden

View File

@@ -14,9 +14,13 @@
#pragma mark -
#pragma mark NSManagedObject helper methods
+ (NSManagedObjectContext*)managedObjectContext {
return [[[OTRestModelManager manager] objectStore] managedObjectContext];
}
+ (NSEntityDescription*)entity {
NSString* className = [NSString stringWithCString:class_getName([self class])];
return [NSEntityDescription entityForName:className inManagedObjectContext:myContext];
NSString* className = [NSString stringWithCString:class_getName([self class]) encoding:NSUnicodeStringEncoding];
return [NSEntityDescription entityForName:className inManagedObjectContext:[self managedObjectContext]];
}
+ (NSFetchRequest*)request {
@@ -29,10 +33,10 @@
+ (NSArray*)collectionWithRequest:(NSFetchRequest*)request {
NSError* error = nil;
//NSLog(@"Context: %@", context);
NSArray* collection = [myContext executeFetchRequest:request error:&error];
NSArray* collection = [[self managedObjectContext] executeFetchRequest:request error:&error];
if (error != nil) {
NSLog(@"Error: %@", [error localizedDescription]);
// TODO: Error handling
}
return collection;
}
@@ -40,9 +44,11 @@
+ (id)objectWithRequest:(NSFetchRequest*)request {
[request setFetchLimit:1];
NSArray* collection = [self collectionWithRequest:request];
if ([collection count] == 0)
if ([collection count] == 0) {
return nil;
return [collection objectAtIndex:0];
} else {
return [collection objectAtIndex:0];
}
}
+ (NSArray*)collectionWithPredicate:(NSPredicate*)predicate {
@@ -70,7 +76,7 @@
}
+ (id)newObject {
id model = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:myContext];
id model = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:[self managedObjectContext]];
return [model autorelease];
}

View File

@@ -0,0 +1,33 @@
//
// OTRestManagedObjectStore.h
// OTRestFramework
//
// Created by Blake Watters on 9/22/09.
// Copyright 2009 Objective 3. All rights reserved.
//
#import <CoreData/CoreData.h>
@interface OTRestManagedObjectStore : NSObject {
NSString* _storeFilename;
NSManagedObjectModel* _managedObjectModel;
NSPersistentStoreCoordinator* _persistentStoreCoordinator;
NSManagedObjectContext* _managedObjectContext;
}
@property (nonatomic, readonly) NSString* storeFilename;
@property (nonatomic, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
/**
* Initialize a new managed object store with a SQLite database with the filename specified
*/
- (id)initWithStoreFilename:(NSString*)storeFilename;
/**
* Save the current contents of the managed object store
*/
- (NSError*)save;
@end

View File

@@ -0,0 +1,86 @@
//
// OTRestManagedObjectStore.m
// OTRestFramework
//
// Created by Blake Watters on 9/22/09.
// Copyright 2009 Objective 3. All rights reserved.
//
#import "OTRestManagedObjectStore.h"
@interface OTRestManagedObjectStore (Private)
- (void)createPersistentStoreCoordinator;
- (NSString *)applicationDocumentsDirectory;
@end
@implementation OTRestManagedObjectStore
@synthesize storeFilename = _storeFilename;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize managedObjectContext = _managedObjectContext;
- (id)initWithStoreFilename:(NSString*)storeFilename {
if (self = [self init]) {
_storeFilename = [storeFilename retain];
_managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
[self createPersistentStoreCoordinator];
_managedObjectContext = [[NSManagedObjectContext alloc] init];
_managedObjectContext.persistentStoreCoordinator = _persistentStoreCoordinator;
}
return self;
}
- (void)dealloc {
[_storeFilename release];
[_managedObjectContext release];
[_managedObjectModel release];
[_persistentStoreCoordinator release];
[super dealloc];
}
/**
Performs the save action for the application, which is to send the save:
message to the application's managed object context.
*/
- (NSError*)save {
NSError *error;
if (NO == [[self managedObjectContext] save:&error]) {
return error;
} else {
return nil;
}
}
- (void)createPersistentStoreCoordinator {
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent:_storeFilename]];
NSError *error;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModel];
// Allow inferred migration from the original version of the application.
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Handle the error.
}
}
#pragma mark -
#pragma mark Helpers
/**
Returns the path to the application's documents directory.
*/
- (NSString *)applicationDocumentsDirectory {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
@end

View File

@@ -8,10 +8,12 @@
#import "OTRestModelMapper.h"
#import "OTRestClient.h"
#import "OTRestManagedObjectStore.h"
@interface OTRestModelManager : NSObject {
OTRestClient* _client;
OTRestModelMapper* _mapper;
OTRestManagedObjectStore* _objectStore;
}
+ (OTRestModelManager*)manager;
@@ -20,7 +22,7 @@
- (id)initWithBaseURL:(NSString*)baseURL;
/**
* The rest client for this manager
* The REST client for this manager
*/
@property (nonatomic, readonly) OTRestClient* client;
@@ -34,6 +36,11 @@
*/
@property(nonatomic, readonly) OTRestModelMapper* mapper;
/**
* A Core Data backed object store for persisting objects that have been fetched from the Web
*/
@property(nonatomic, retain) OTRestManagedObjectStore* objectStore;
/**
* Fetch a resource via an HTTP GET and invoke a callback with the model for the resulting payload
*/
@@ -49,5 +56,4 @@
- (OTRestRequest*)putModel:(id<OTRestModelMappable>)model delegate:(id)delegate callback:(SEL)callback;
- (OTRestRequest*)deleteModel:(id<OTRestModelMappable>)model delegate:(id)delegate callback:(SEL)callback;
@end

View File

@@ -20,6 +20,7 @@ static OTRestModelManager* sharedManager = nil;
@synthesize mapper = _mapper;
@synthesize client = _client;
@synthesize objectStore = _objectStore;
- (id)initWithBaseURL:(NSString*)baseURL {
if (self = [super init]) {

View File

@@ -13,16 +13,16 @@
#define kRailsToXMLDateFormatterString @"yyyy-MM-dd'T'HH:mm:ss'Z'" // 2009-08-08T17:23:59Z
typedef enum {
OTRestParsingStyleXML = 0,
OTRestParsingStyleJSON
} OTRestParsingStyle;
OTRestMappingFormatXML = 0,
OTRestMappingFormatJSON
} OTRestMappingFormat;
@interface OTRestModelMapper : NSObject {
NSMutableDictionary* _elementToClassMappings;
OTRestParsingStyle _style;
OTRestMappingFormat _format;
}
- (id)initWithParsingStyle:(OTRestParsingStyle)style;
@property(nonatomic, assign) OTRestMappingFormat format;
/**
* Register a mapping for a given class for an XML element with the given tag name
@@ -31,16 +31,23 @@ typedef enum {
- (void)registerModel:(Class)aClass forElementNamed:(NSString*)elementName;
/**
* Digest an XML payload and return the an instance of the model class registered for the element
* Digest an XML/JSON payload and return the an instance of the model class registered for the element
*/
- (id)buildModelFromString:(NSString*)payload;
/**
* Digest an XML/JSON payload and return the resulting collection of model instances
*/
- (NSArray*)buildModelsFromString:(NSString*)payload;
/**
* Digests an XML payload and updates the object with its properties and relationships
* Digests an XML payload and updates the object with its properties and relationships
*/
- (void)setAttributes:(id)object fromXML:(Element*)XML;
/**
* Digests a JSON payload and updates the object with its properties and relationships
*/
- (void)setAttributes:(id)object fromJSONDict:(NSDictionary*)dict;
@end
@end

View File

@@ -14,19 +14,14 @@
// Used for detecting property types at runtime
#import <objc/runtime.h>
@implementation OTRestModelMapper
@synthesize format = _format;
- (id)init {
if (self = [super init]) {
_elementToClassMappings = [[NSMutableDictionary alloc] init];
}
return self;
}
- (id)initWithParsingStyle:(OTRestParsingStyle)style {
if (self = [self init]) {
_style = style;
_format = OTRestMappingFormatXML;
}
return self;
}
@@ -36,41 +31,47 @@
[super dealloc];
}
- (BOOL)mappingFromJSON {
return _format == OTRestMappingFormatJSON;
}
- (BOOL)mappingFromXML {
return _format == OTRestMappingFormatXML;
}
- (void)registerModel:(Class)aClass forElementNamed:(NSString*)elementName {
[_elementToClassMappings setObject:aClass forKey:elementName];
}
- (id)buildModelFromString:(NSString*)string {
id object = nil;
if (_style == OTRestParsingStyleJSON) {
if ([self mappingFromJSON]) {
object = [self buildModelFromJSON:string];
} else if (_style == OTRestParsingStyleXML) {
} else if ([self mappingFromXML]) {
Element* e = [[[[[ElementParser alloc] init] autorelease] parseXML:string] firstChild];
object = [self buildModelFromXML:e];
} else {
[NSException raise:@"No Parsing Style Set" format:@"you must init your Mapper with a valid mapping style %d == %d", _style, OTRestParsingStyleJSON];
[NSException raise:@"No Parsing Style Set" format:@"you must specify a valid mapping format"];
}
return object;
}
- (NSArray*)buildModelsFromString:(NSString*)string {
NSMutableArray* objects = [NSMutableArray array];
if (_style == OTRestParsingStyleJSON) {
//object = [self buildModelFromJSON:string];
if ([self mappingFromJSON]) {
NSArray* collectionDicts = [[[[SBJSON alloc] init] autorelease] objectWithString:string];
for (NSDictionary* dict in collectionDicts) {
id object = [self buildModelFromJSONDict:dict];
[objects addObject:object];
}
} else if (_style == OTRestParsingStyleXML) {
}
} else if ([self mappingFromXML]) {
Element* collectionElement = [[[[[ElementParser alloc] init] autorelease] parseXML:string] firstChild];
for (Element* e in [collectionElement childElements]) {
id object = [self buildModelFromXML:e];
[objects addObject:object];
}
} else {
[NSException raise:@"No Parsing Style Set" format:@"you must init your Mapper with a valid mapping style %d == %d", _style, OTRestParsingStyleJSON];
[NSException raise:@"No Parsing Style Set" format:@"you must specify a valid mapping format"];
}
return (NSArray*)objects;
}
@@ -142,7 +143,7 @@
object = [[[class alloc] init] autorelease];
}
}
// check to see if we should hand the object the xml to set it's own properties
// check to see if we should hand the object the JSON to set it's own properties
// (custom implementation)
if ([object respondsToSelector:@selector(digestJSONDict:)]) {
[object digestJSONDict:dict];

View File

@@ -46,7 +46,8 @@
}
- (void)testJSONMapping {
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] initWithParsingStyle:OTRestParsingStyleJSON];
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] init];
mapper.format = OTRestMappingFormatJSON;
[mapper registerModel:[TestSerialization class] forElementNamed:@"test_serialization_class"];
[mapper registerModel:[TestSerialization class] forElementNamed:@"test_serialization_class"];
[mapper registerModel:[TestSerializationAssociation class] forElementNamed:@"has_many"];
@@ -66,7 +67,8 @@
}
- (void)testJSONCollectionMapping {
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] initWithParsingStyle:OTRestParsingStyleJSON];
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] init];
mapper.format = OTRestMappingFormatJSON;
[mapper registerModel:[TestSerialization class] forElementNamed:@"test_serialization_class"];
[mapper registerModel:[TestSerialization class] forElementNamed:@"test_serialization_class"];
[mapper registerModel:[TestSerializationAssociation class] forElementNamed:@"has_many"];
@@ -87,7 +89,8 @@
}
- (void)testXMLMapping {
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] initWithParsingStyle:OTRestParsingStyleXML];
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] init];
mapper.format = OTRestMappingFormatXML;
[mapper registerModel:[TestSerialization class] forElementNamed:@"test_serialization_class"];
[mapper registerModel:[TestSerializationAssociation class] forElementNamed:@"has_many"];
[mapper registerModel:[TestSerializationAssociation class] forElementNamed:@"has_one"];
@@ -106,7 +109,8 @@
}
- (void)testXMLCollectionMapping {
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] initWithParsingStyle:OTRestParsingStyleXML];
OTRestModelMapper* mapper = [[OTRestModelMapper alloc] init];
mapper.format = OTRestMappingFormatXML;
[mapper registerModel:[TestSerialization class] forElementNamed:@"test_serialization_class"];
[mapper registerModel:[TestSerializationAssociation class] forElementNamed:@"has_many"];
[mapper registerModel:[TestSerializationAssociation class] forElementNamed:@"has_one"];