diff --git a/.gitignore b/.gitignore index 29810c1..b91b548 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.log Podfile.lock +.DS_Store \ No newline at end of file diff --git a/Classes/NSManagedObject+ActiveRecord.h b/Classes/NSManagedObject+ActiveRecord.h index dc7b973..696be66 100644 --- a/Classes/NSManagedObject+ActiveRecord.h +++ b/Classes/NSManagedObject+ActiveRecord.h @@ -22,51 +22,10 @@ #import #import "NSManagedObject+Mappings.h" -#import "CoreDataManager.h" - -@interface NSManagedObjectContext (ActiveRecord) - -/** - The default context (as defined on the @c CoreDataManager singleton). - - @see -[CoreDataManager managedObjectContext] - - @return A managed object context. - */ -+ (NSManagedObjectContext *)defaultContext; - -@end @interface NSManagedObject (ActiveRecord) - -#pragma mark - Default Context - -- (BOOL)save; -- (void)delete; -+ (void)deleteAll; - -+ (id)create; -+ (id)create:(NSDictionary *)attributes; -- (void)update:(NSDictionary *)attributes; - -+ (NSArray *)all; -+ (NSArray *)allWithOrder:(id)order; -+ (NSArray *)where:(id)condition, ...; -+ (NSArray *)where:(id)condition order:(id)order; -+ (NSArray *)where:(id)condition limit:(NSNumber *)limit; -+ (NSArray *)where:(id)condition order:(id)order limit:(NSNumber *)limit; -+ (instancetype)findOrCreate:(NSDictionary *)attributes; -+ (instancetype)find:(id)condition, ...; -+ (NSUInteger)count; -+ (NSUInteger)countWhere:(id)condition, ...; - -#pragma mark - Custom Context - -+ (id)createInContext:(NSManagedObjectContext *)context; -+ (id)create:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context; - -+ (void)deleteAllInContext:(NSManagedObjectContext *)context; +#pragma mark - Finders + (NSArray *)allInContext:(NSManagedObjectContext *)context; + (NSArray *)allInContext:(NSManagedObjectContext *)context order:(id)order; @@ -76,9 +35,28 @@ + (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context order:(id)order limit:(NSNumber *)limit; + (instancetype)findOrCreate:(NSDictionary *)properties inContext:(NSManagedObjectContext *)context; + (instancetype)find:(id)condition inContext:(NSManagedObjectContext *)context; + +#pragma mark - Aggregation + + (NSUInteger)countInContext:(NSManagedObjectContext *)context; + (NSUInteger)countWhere:(id)condition inContext:(NSManagedObjectContext *)context; +#pragma mark - Creation / Deletion + ++ (id)createInContext:(NSManagedObjectContext *)context; ++ (id)create:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context; ++ (instancetype)updateOrCreate:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context; +- (void)update:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context; + +#pragma mark - Deletion + +- (void)deleteInContext:(NSManagedObjectContext *)contex; ++ (void)deleteAllInContext:(NSManagedObjectContext *)context; + +#pragma mark - Saving + +- (BOOL)saveInContext:(NSManagedObjectContext *)context; + #pragma mark - Naming + (NSString *)entityName; diff --git a/Classes/NSManagedObject+ActiveRecord.m b/Classes/NSManagedObject+ActiveRecord.m index f5b3b40..ba9c44e 100644 --- a/Classes/NSManagedObject+ActiveRecord.m +++ b/Classes/NSManagedObject+ActiveRecord.m @@ -23,17 +23,10 @@ #import "NSManagedObject+ActiveRecord.h" #import "ObjectiveSugar.h" -@implementation NSManagedObjectContext (ActiveRecord) - -+ (NSManagedObjectContext *)defaultContext { - return [[CoreDataManager sharedManager] managedObjectContext]; -} - -@end - @implementation NSObject(null) -- (BOOL)exists { +- (BOOL)exists +{ return self && self != [NSNull null]; } @@ -43,171 +36,158 @@ @implementation NSManagedObject (ActiveRecord) #pragma mark - Finders -+ (NSArray *)all { - return [self allInContext:[NSManagedObjectContext defaultContext]]; -} - -+ (NSArray *)allWithOrder:(id)order { - return [self allInContext:[NSManagedObjectContext defaultContext] order:order]; -} - -+ (NSArray *)allInContext:(NSManagedObjectContext *)context { ++ (NSArray *)allInContext:(NSManagedObjectContext *)context +{ return [self allInContext:context order:nil]; } -+ (NSArray *)allInContext:(NSManagedObjectContext *)context order:(id)order { ++ (NSArray *)allInContext:(NSManagedObjectContext *)context order:(id)order +{ return [self fetchWithCondition:nil inContext:context withOrder:order fetchLimit:nil]; } -+ (instancetype)findOrCreate:(NSDictionary *)properties { - return [self findOrCreate:properties inContext:[NSManagedObjectContext defaultContext]]; -} - -+ (instancetype)findOrCreate:(NSDictionary *)properties inContext:(NSManagedObjectContext *)context { ++ (instancetype)findOrCreate:(NSDictionary *)properties inContext:(NSManagedObjectContext *)context +{ NSDictionary *transformed = [[self class] transformProperties:properties withObject:nil context:context]; NSManagedObject *existing = [self where:transformed inContext:context].first; return existing ?: [self create:transformed inContext:context]; } -+ (instancetype)find:(id)condition, ... { - va_list va_arguments; - va_start(va_arguments, condition); - NSPredicate *predicate = [self predicateFromObject:condition arguments:va_arguments]; - va_end(va_arguments); - - return [self find:predicate inContext:[NSManagedObjectContext defaultContext]]; -} - -+ (instancetype)find:(id)condition inContext:(NSManagedObjectContext *)context { ++ (instancetype)find:(id)condition inContext:(NSManagedObjectContext *)context +{ return [self where:condition inContext:context limit:@1].first; } -+ (NSArray *)where:(id)condition, ... { - va_list va_arguments; - va_start(va_arguments, condition); - NSPredicate *predicate = [self predicateFromObject:condition arguments:va_arguments]; - va_end(va_arguments); - - return [self where:predicate inContext:[NSManagedObjectContext defaultContext]]; -} - -+ (NSArray *)where:(id)condition order:(id)order { - return [self where:condition inContext:[NSManagedObjectContext defaultContext] order:order]; -} - -+ (NSArray *)where:(id)condition limit:(NSNumber *)limit { - return [self where:condition inContext:[NSManagedObjectContext defaultContext] limit:limit]; -} - -+ (NSArray *)where:(id)condition order:(id)order limit:(NSNumber *)limit { - return [self where:condition inContext:[NSManagedObjectContext defaultContext] order:order limit:limit]; -} - -+ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context { ++ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context +{ return [self where:condition inContext:context order:nil limit:nil]; } -+ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context order:(id)order { ++ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context order:(id)order +{ return [self where:condition inContext:context order:order limit:nil]; } -+ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context limit:(NSNumber *)limit { ++ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context limit:(NSNumber *)limit +{ return [self where:condition inContext:context order:nil limit:limit]; } -+ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context order:(id)order limit:(NSNumber *)limit { ++ (NSArray *)where:(id)condition inContext:(NSManagedObjectContext *)context order:(id)order limit:(NSNumber *)limit +{ return [self fetchWithCondition:condition inContext:context withOrder:order fetchLimit:limit]; } #pragma mark - Aggregation -+ (NSUInteger)count { - return [self countInContext:[NSManagedObjectContext defaultContext]]; -} - -+ (NSUInteger)countWhere:(id)condition, ... { - va_list va_arguments; - va_start(va_arguments, condition); - NSPredicate *predicate = [self predicateFromObject:condition arguments:va_arguments]; - va_end(va_arguments); - - return [self countWhere:predicate inContext:[NSManagedObjectContext defaultContext]]; -} - -+ (NSUInteger)countInContext:(NSManagedObjectContext *)context { ++ (NSUInteger)countInContext:(NSManagedObjectContext *)context +{ return [self countForFetchWithPredicate:nil inContext:context]; } -+ (NSUInteger)countWhere:(id)condition inContext:(NSManagedObjectContext *)context { ++ (NSUInteger)countWhere:(id)condition inContext:(NSManagedObjectContext *)context +{ NSPredicate *predicate = [self predicateFromObject:condition]; return [self countForFetchWithPredicate:predicate inContext:context]; } -#pragma mark - Creation / Deletion - -+ (id)create { - return [self createInContext:[NSManagedObjectContext defaultContext]]; -} +#pragma mark - Creation -+ (id)create:(NSDictionary *)attributes { - return [self create:attributes inContext:[NSManagedObjectContext defaultContext]]; ++ (id)createInContext:(NSManagedObjectContext *)context +{ + return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context]; } -+ (id)create:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context { ++ (id)create:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context +{ unless([attributes exists]) return nil; - + NSManagedObject *newEntity = [self createInContext:context]; - [newEntity update:attributes]; - + [newEntity update:attributes inContext:context]; + return newEntity; } -+ (id)createInContext:(NSManagedObjectContext *)context { - return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] - inManagedObjectContext:context]; ++ (instancetype)updateOrCreate:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context +{ + NSString *localKey = [[self mappings] allKeysForObject:[self primaryKey]].first; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K = %@", [self primaryKey], attributes[localKey]]; + + NSManagedObject *existing = [self where:predicate inContext:context].first; + + if (!existing) { + return [self create:attributes inContext:context]; + } + + [existing update:attributes inContext:context]; + + return existing; } -- (void)update:(NSDictionary *)attributes { +- (void)update:(NSDictionary *)attributes inContext:(NSManagedObjectContext *)context +{ unless([attributes exists]) return; - NSDictionary *transformed = [[self class] transformProperties:attributes withObject:self context:self.managedObjectContext]; + NSDictionary *transformed = [[self class] transformProperties:attributes withObject:self context:context]; - for (NSString *key in transformed) [self willChangeValueForKey:key]; + for (NSString *key in transformed) { + [self willChangeValueForKey:key]; + } + [transformed each:^(NSString *key, id value) { [self setSafeValue:value forKey:key]; }]; - for (NSString *key in transformed) [self didChangeValueForKey:key]; + + for (NSString *key in transformed) { + [self didChangeValueForKey:key]; + } } -- (BOOL)save { - return [self saveTheContext]; -} +#pragma mark - Deletion -- (void)delete { - [self.managedObjectContext deleteObject:self]; +- (void)deleteInContext:(NSManagedObjectContext *)context +{ + [context deleteObject:self]; } -+ (void)deleteAll { - [self deleteAllInContext:[NSManagedObjectContext defaultContext]]; -} - -+ (void)deleteAllInContext:(NSManagedObjectContext *)context { ++ (void)deleteAllInContext:(NSManagedObjectContext *)context +{ [[self allInContext:context] each:^(id object) { - [object delete]; + [object deleteInContext:context]; }]; } +#pragma mark - Saving + +- (BOOL)saveInContext:(NSManagedObjectContext *)context +{ + if (context == nil || + ![context hasChanges]) return YES; + + NSError *error = nil; + BOOL save = [context save:&error]; + + if (!save || error) { + NSLog(@"Unresolved error in saving context for entity:\n%@!\nError: %@", self, error); + return NO; + } + + return YES; +} + #pragma mark - Naming -+ (NSString *)entityName { ++ (NSString *)entityName +{ return NSStringFromClass(self); } #pragma mark - Private -+ (NSDictionary *)transformProperties:(NSDictionary *)properties withObject:(NSManagedObject *)object context:(NSManagedObjectContext *)context { ++ (NSDictionary *)transformProperties:(NSDictionary *)properties withObject:(NSManagedObject *)object context:(NSManagedObjectContext *)context +{ NSEntityDescription *entity = [NSEntityDescription entityForName:[self entityName] inManagedObjectContext:context]; NSDictionary *attributes = [entity attributesByName]; @@ -232,23 +212,43 @@ + (NSDictionary *)transformProperties:(NSDictionary *)properties withObject:(NSM #endif } } + + for (NSString *attribute in [attributes allKeys]) { + NSString *keypath = [self keyPathForRemoteKey:attribute]; + + if (keypath) { + id value = [properties valueForKeyPath:keypath]; + NSString *localKey = [self keyForRemoteKey:attribute inContext:context]; + if (!value || !localKey) + continue; + transformed[localKey] = value; + } + } return transformed; } -+ (NSPredicate *)predicateFromDictionary:(NSDictionary *)dict { ++ (NSPredicate *)predicateFromDictionary:(NSDictionary *)dict +{ NSArray *subpredicates = [dict map:^(NSString *key, id value) { - return [NSPredicate predicateWithFormat:@"%K = %@", key, value]; + if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSSet class]]) { + return (id)[NSPredicate predicateWithFormat:@"%@ IN %K", value, key]; + } + else { + return (id)[NSPredicate predicateWithFormat:@"%K = %@", key, value]; + } }]; - + return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; } -+ (NSPredicate *)predicateFromObject:(id)condition { ++ (NSPredicate *)predicateFromObject:(id)condition +{ return [self predicateFromObject:condition arguments:NULL]; } -+ (NSPredicate *)predicateFromObject:(id)condition arguments:(va_list)arguments { ++ (NSPredicate *)predicateFromObject:(id)condition arguments:(va_list)arguments +{ if ([condition isKindOfClass:[NSPredicate class]]) return condition; @@ -261,23 +261,24 @@ + (NSPredicate *)predicateFromObject:(id)condition arguments:(va_list)arguments return nil; } -+ (NSSortDescriptor *)sortDescriptorFromDictionary:(NSDictionary *)dict { ++ (NSSortDescriptor *)sortDescriptorFromDictionary:(NSDictionary *)dict +{ BOOL isAscending = ![[dict.allValues.first uppercaseString] isEqualToString:@"DESC"]; - return [NSSortDescriptor sortDescriptorWithKey:dict.allKeys.first - ascending:isAscending]; + return [NSSortDescriptor sortDescriptorWithKey:dict.allKeys.first ascending:isAscending]; } -+ (NSSortDescriptor *)sortDescriptorFromString:(NSString *)order { ++ (NSSortDescriptor *)sortDescriptorFromString:(NSString *)order +{ NSArray *components = [order split]; NSString *key = [components firstObject]; NSString *value = [components count] > 1 ? components[1] : @"ASC"; return [self sortDescriptorFromDictionary:@{key: value}]; - } -+ (NSSortDescriptor *)sortDescriptorFromObject:(id)order { ++ (NSSortDescriptor *)sortDescriptorFromObject:(id)order +{ if ([order isKindOfClass:[NSSortDescriptor class]]) return order; @@ -290,7 +291,8 @@ + (NSSortDescriptor *)sortDescriptorFromObject:(id)order { return nil; } -+ (NSArray *)sortDescriptorsFromObject:(id)order { ++ (NSArray *)sortDescriptorsFromObject:(id)order +{ if ([order isKindOfClass:[NSString class]]) order = [order componentsSeparatedByString:@","]; @@ -302,19 +304,16 @@ + (NSArray *)sortDescriptorsFromObject:(id)order { return @[[self sortDescriptorFromObject:order]]; } -+ (NSFetchRequest *)createFetchRequestInContext:(NSManagedObjectContext *)context { ++ (NSFetchRequest *)createFetchRequestInContext:(NSManagedObjectContext *)context +{ NSFetchRequest *request = [NSFetchRequest new]; - NSEntityDescription *entity = [NSEntityDescription entityForName:[self entityName] - inManagedObjectContext:context]; + NSEntityDescription *entity = [NSEntityDescription entityForName:[self entityName] inManagedObjectContext:context]; [request setEntity:entity]; return request; } -+ (NSArray *)fetchWithCondition:(id)condition - inContext:(NSManagedObjectContext *)context - withOrder:(id)order - fetchLimit:(NSNumber *)fetchLimit { - ++ (NSArray *)fetchWithCondition:(id)condition inContext:(NSManagedObjectContext *)context withOrder:(id)order fetchLimit:(NSNumber *)fetchLimit +{ NSFetchRequest *request = [self createFetchRequestInContext:context]; if (condition) @@ -329,30 +328,16 @@ + (NSArray *)fetchWithCondition:(id)condition return [context executeFetchRequest:request error:nil]; } -+ (NSUInteger)countForFetchWithPredicate:(NSPredicate *)predicate - inContext:(NSManagedObjectContext *)context { ++ (NSUInteger)countForFetchWithPredicate:(NSPredicate *)predicate inContext:(NSManagedObjectContext *)context +{ NSFetchRequest *request = [self createFetchRequestInContext:context]; [request setPredicate:predicate]; return [context countForFetchRequest:request error:nil]; } -- (BOOL)saveTheContext { - if (self.managedObjectContext == nil || - ![self.managedObjectContext hasChanges]) return YES; - - NSError *error = nil; - BOOL save = [self.managedObjectContext save:&error]; - - if (!save || error) { - NSLog(@"Unresolved error in saving context for entity:\n%@!\nError: %@", self, error); - return NO; - } - - return YES; -} - -- (void)setSafeValue:(id)value forKey:(NSString *)key { +- (void)setSafeValue:(id)value forKey:(NSString *)key +{ if (value == nil || value == [NSNull null]) { [self setNilValueForKey:key]; return; @@ -382,15 +367,18 @@ - (void)setSafeValue:(id)value forKey:(NSString *)key { [self setPrimitiveValue:value forKey:key]; } -- (BOOL)isIntegerAttributeType:(NSAttributeType)attributeType { +- (BOOL)isIntegerAttributeType:(NSAttributeType)attributeType +{ return (attributeType == NSInteger16AttributeType) || (attributeType == NSInteger32AttributeType) || (attributeType == NSInteger64AttributeType); } + #pragma mark - Date Formatting -- (NSDateFormatter *)defaultFormatter { +- (NSDateFormatter *)defaultFormatter +{ static NSDateFormatter *sharedFormatter; static dispatch_once_t singletonToken; dispatch_once(&singletonToken, ^{ diff --git a/Classes/NSManagedObject+Mappings.h b/Classes/NSManagedObject+Mappings.h index 7ac1641..4e3e4a6 100644 --- a/Classes/NSManagedObject+Mappings.h +++ b/Classes/NSManagedObject+Mappings.h @@ -31,12 +31,20 @@ */ + (NSDictionary *)mappings; +/** + A keypath used for accessing some property value. + + @param remoteKey A remote (server) attribute name. + @return A keypath. + */ ++ (NSString *)keyPathForRemoteKey:(NSString *)key; + /** Returns a Core Data attribute name for a remote attribute name. Returns values defined in @c +mappings or, by default, converts snake case to camel case (e.g., @c @@"first_name" becomes @c @@"firstName"). @see +[NSManagedObject mappings] - @param key A remote (server) attribute name. + @param remoteKey A remote (server) attribute name. @param context A local managed object context. @return A local (Core Data) attribute name. diff --git a/Classes/NSManagedObject+Mappings.m b/Classes/NSManagedObject+Mappings.m index 52adc45..1606b80 100644 --- a/Classes/NSManagedObject+Mappings.m +++ b/Classes/NSManagedObject+Mappings.m @@ -62,7 +62,7 @@ + (id)objectOrSetOfObjectsFromValue:(id)value ofClass:class inContext:(NSManaged if ([value isKindOfClass:[NSDictionary class]]) return [class findOrCreate:value inContext:context]; - if ([value isKindOfClass:[NSArray class]]) + if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSSet class]]) return [NSSet setWithArray:[value map:^id(id object) { return [self objectOrSetOfObjectsFromValue:object ofClass:class inContext:context]; }]]; @@ -109,6 +109,10 @@ + (NSDictionary *)mappings { return nil; } ++ (NSString *)keyPathForRemoteKey:(NSString *)key { + return nil; +} + + (id)primaryKey { @throw [NSException exceptionWithName:NSStringWithFormat(@"Primary key undefined in %@", self.class) reason:NSStringWithFormat(@"You need to override %@ +primaryKey if you want to support automatic creation with only object ID", diff --git a/Example/.DS_Store b/Example/.DS_Store new file mode 100644 index 0000000..13d35e9 Binary files /dev/null and b/Example/.DS_Store differ diff --git a/Example/Podfile b/Example/Podfile index 03a2c92..7c9e474 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,6 +1,6 @@ platform :ios, :deployment_target => "5.0" - pod 'ObjectiveSugar' - pod 'ObjectiveRecord', :path => '../' +pod 'ObjectiveSugar' +pod 'ObjectiveRecord', :path => '../' target :SampleProjectTests, :exclusive => true do pod 'Kiwi/XCTest', '2.2.4' end diff --git a/Example/SampleProject.xcodeproj/project.pbxproj b/Example/SampleProject.xcodeproj/project.pbxproj index e8ed60a..5b89860 100644 --- a/Example/SampleProject.xcodeproj/project.pbxproj +++ b/Example/SampleProject.xcodeproj/project.pbxproj @@ -8,7 +8,13 @@ /* Begin PBXBuildFile section */ 496F953D174CCCBA00220FD1 /* OBRPerson.m in Sources */ = {isa = PBXBuildFile; fileRef = 496F953C174CCCBA00220FD1 /* OBRPerson.m */; }; - 6433E5B9184D89EC008EF278 /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = 6433E5B8184D89EC008EF278 /* Person.m */; }; + 4F17D8C01992A2C8009A0D47 /* CoreDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F17D8BD1992A2C8009A0D47 /* CoreDataManager.m */; }; + 4F17D8C11992A2C8009A0D47 /* CoreDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F17D8BD1992A2C8009A0D47 /* CoreDataManager.m */; }; + 4F17D8C21992A2C8009A0D47 /* JSONUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F17D8BF1992A2C8009A0D47 /* JSONUtility.m */; }; + 4F17D8C31992A2C8009A0D47 /* JSONUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F17D8BF1992A2C8009A0D47 /* JSONUtility.m */; }; + 4F17D8CA1992A321009A0D47 /* people.json in Resources */ = {isa = PBXBuildFile; fileRef = 4F17D8C91992A321009A0D47 /* people.json */; }; + 4F17D8CB1992A321009A0D47 /* people.json in Resources */ = {isa = PBXBuildFile; fileRef = 4F17D8C91992A321009A0D47 /* people.json */; }; + 4F5744B5194ABB0C0099A49E /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F5744B4194ABB0C0099A49E /* Person.m */; }; 6433E5C2184E83C2008EF278 /* InsuranceCompany.m in Sources */ = {isa = PBXBuildFile; fileRef = 6433E5C1184E83C2008EF278 /* InsuranceCompany.m */; }; 6433E5C5184E83D6008EF278 /* Car.m in Sources */ = {isa = PBXBuildFile; fileRef = 6433E5C4184E83D6008EF278 /* Car.m */; }; 892C1E91165D54160077F2CB /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 892C1E90165D54160077F2CB /* Default-568h@2x.png */; }; @@ -49,8 +55,13 @@ 0EE71E2B92554B48A0914E08 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 496F953B174CCCBA00220FD1 /* OBRPerson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OBRPerson.h; sourceTree = ""; }; 496F953C174CCCBA00220FD1 /* OBRPerson.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OBRPerson.m; sourceTree = ""; }; - 6433E5B7184D89EC008EF278 /* Person.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = ""; }; - 6433E5B8184D89EC008EF278 /* Person.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = ""; }; + 4F17D8BC1992A2C8009A0D47 /* CoreDataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreDataManager.h; sourceTree = ""; }; + 4F17D8BD1992A2C8009A0D47 /* CoreDataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreDataManager.m; sourceTree = ""; }; + 4F17D8BE1992A2C8009A0D47 /* JSONUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONUtility.h; sourceTree = ""; }; + 4F17D8BF1992A2C8009A0D47 /* JSONUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JSONUtility.m; sourceTree = ""; }; + 4F17D8C91992A321009A0D47 /* people.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = people.json; sourceTree = ""; }; + 4F5744B3194ABB0C0099A49E /* Person.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = ""; }; + 4F5744B4194ABB0C0099A49E /* Person.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = ""; }; 6433E5C0184E83C2008EF278 /* InsuranceCompany.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InsuranceCompany.h; sourceTree = ""; }; 6433E5C1184E83C2008EF278 /* InsuranceCompany.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InsuranceCompany.m; sourceTree = ""; }; 6433E5C3184E83D6008EF278 /* Car.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Car.h; sourceTree = ""; }; @@ -112,23 +123,50 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4F17D8BB1992A2C8009A0D47 /* Utilities */ = { + isa = PBXGroup; + children = ( + 4F17D8BC1992A2C8009A0D47 /* CoreDataManager.h */, + 4F17D8BD1992A2C8009A0D47 /* CoreDataManager.m */, + 4F17D8BE1992A2C8009A0D47 /* JSONUtility.h */, + 4F17D8BF1992A2C8009A0D47 /* JSONUtility.m */, + ); + path = Utilities; + sourceTree = ""; + }; + 4F17D8C81992A321009A0D47 /* Fixtures */ = { + isa = PBXGroup; + children = ( + 4F17D8C91992A321009A0D47 /* people.json */, + ); + path = Fixtures; + sourceTree = ""; + }; + 4F9A5CF119914DAA002727E2 /* Mappings */ = { + isa = PBXGroup; + children = ( + 897AD0C51758D99A006869BA /* Person+Mappings.h */, + 897AD0C61758D99A006869BA /* Person+Mappings.m */, + 897AD0C81758D9A6006869BA /* Car+Mappings.h */, + 897AD0C91758D9A6006869BA /* Car+Mappings.m */, + FA91A5E71875B9FD00BE74DD /* InsuranceCompany+Mappings.h */, + FA91A5E81875B9FD00BE74DD /* InsuranceCompany+Mappings.m */, + ); + name = Mappings; + sourceTree = ""; + }; 897D09F715B093F200722A8F /* Models */ = { isa = PBXGroup; children = ( + 4F9A5CF119914DAA002727E2 /* Mappings */, 6433E5C3184E83D6008EF278 /* Car.h */, 6433E5C4184E83D6008EF278 /* Car.m */, 6433E5C0184E83C2008EF278 /* InsuranceCompany.h */, 6433E5C1184E83C2008EF278 /* InsuranceCompany.m */, - 6433E5B7184D89EC008EF278 /* Person.h */, - 6433E5B8184D89EC008EF278 /* Person.m */, 496F953B174CCCBA00220FD1 /* OBRPerson.h */, 496F953C174CCCBA00220FD1 /* OBRPerson.m */, - 897AD0C51758D99A006869BA /* Person+Mappings.h */, - 897AD0C61758D99A006869BA /* Person+Mappings.m */, - 897AD0C81758D9A6006869BA /* Car+Mappings.h */, - 897AD0C91758D9A6006869BA /* Car+Mappings.m */, - FA91A5E71875B9FD00BE74DD /* InsuranceCompany+Mappings.h */, - FA91A5E81875B9FD00BE74DD /* InsuranceCompany+Mappings.m */, + 4F5744B3194ABB0C0099A49E /* Person.h */, + 4F5744B4194ABB0C0099A49E /* Person.m */, ); path = Models; sourceTree = ""; @@ -196,10 +234,12 @@ 89F1EECB15A4C73B00AE4FB4 /* SampleProjectTests */ = { isa = PBXGroup; children = ( - 89F1EECC15A4C73B00AE4FB4 /* Supporting Files */, 897D09FD15B0945400722A8F /* FindersAndCreatorsTests.m */, 8956CE631754EE6A00BD551C /* MappingsTests.m */, 8990C3151785C2F500212182 /* CoreDataManagerTests.m */, + 4F17D8C81992A321009A0D47 /* Fixtures */, + 4F17D8BB1992A2C8009A0D47 /* Utilities */, + 89F1EECC15A4C73B00AE4FB4 /* Supporting Files */, ); path = SampleProjectTests; sourceTree = ""; @@ -290,6 +330,7 @@ files = ( 89F1EEB415A4C73B00AE4FB4 /* InfoPlist.strings in Resources */, 892C1E91165D54160077F2CB /* Default-568h@2x.png in Resources */, + 4F17D8CA1992A321009A0D47 /* people.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -297,6 +338,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4F17D8CB1992A321009A0D47 /* people.json in Resources */, 89F1EED015A4C73B00AE4FB4 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -381,10 +423,12 @@ buildActionMask = 2147483647; files = ( 6433E5C5184E83D6008EF278 /* Car.m in Sources */, + 4F17D8C01992A2C8009A0D47 /* CoreDataManager.m in Sources */, 89F1EEB615A4C73B00AE4FB4 /* main.m in Sources */, + 4F5744B5194ABB0C0099A49E /* Person.m in Sources */, 89F1EEBA15A4C73B00AE4FB4 /* AppDelegate.m in Sources */, FA91A5E91875B9FE00BE74DD /* InsuranceCompany+Mappings.m in Sources */, - 6433E5B9184D89EC008EF278 /* Person.m in Sources */, + 4F17D8C21992A2C8009A0D47 /* JSONUtility.m in Sources */, 6433E5C2184E83C2008EF278 /* InsuranceCompany.m in Sources */, 89F1EEBD15A4C73B00AE4FB4 /* SampleProject.xcdatamodeld in Sources */, 496F953D174CCCBA00220FD1 /* OBRPerson.m in Sources */, @@ -397,9 +441,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4F17D8C11992A2C8009A0D47 /* CoreDataManager.m in Sources */, 897D09FE15B0945400722A8F /* FindersAndCreatorsTests.m in Sources */, 89702F25173E57AA00149BD5 /* SampleProject.xcdatamodeld in Sources */, 8956CE641754EE6A00BD551C /* MappingsTests.m in Sources */, + 4F17D8C31992A2C8009A0D47 /* JSONUtility.m in Sources */, 8990C3161785C2F500212182 /* CoreDataManagerTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Example/SampleProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/SampleProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..012cfe5 --- /dev/null +++ b/Example/SampleProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/SampleProject.xcodeproj/xcshareddata/xcschemes/SampleProject.xcscheme b/Example/SampleProject.xcodeproj/xcshareddata/xcschemes/SampleProject.xcscheme index f553287..d69ff2f 100644 --- a/Example/SampleProject.xcodeproj/xcshareddata/xcschemes/SampleProject.xcscheme +++ b/Example/SampleProject.xcodeproj/xcshareddata/xcschemes/SampleProject.xcscheme @@ -14,7 +14,7 @@ buildForAnalyzing = "YES"> @@ -28,7 +28,7 @@ buildForAnalyzing = "YES"> diff --git a/Example/SampleProject/AppDelegate.m b/Example/SampleProject/AppDelegate.m index be8441d..ed935fc 100644 --- a/Example/SampleProject/AppDelegate.m +++ b/Example/SampleProject/AppDelegate.m @@ -19,15 +19,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; + return YES; } - -- (void)applicationWillTerminate:(UIApplication *)application -{ - // Saves changes in the application's managed object context before the application terminates. - [[CoreDataManager sharedManager] saveContext]; -} - - @end diff --git a/Example/SampleProject/Models/Car.h b/Example/SampleProject/Models/Car.h index 4cd7f1f..f86111d 100644 --- a/Example/SampleProject/Models/Car.h +++ b/Example/SampleProject/Models/Car.h @@ -2,7 +2,7 @@ // Car.h // SampleProject // -// Created by Marin Usalj on 12/3/13. +// Created by Ignacio Romero Z. on 8/5/14. // // @@ -15,7 +15,8 @@ @property (nonatomic, retain) NSNumber * horsePower; @property (nonatomic, retain) NSString * make; -@property (nonatomic, retain) Person *owner; +@property (nonatomic, retain) NSNumber * remoteID; @property (nonatomic, retain) InsuranceCompany *insuranceCompany; +@property (nonatomic, retain) Person *owner; @end diff --git a/Example/SampleProject/Models/Car.m b/Example/SampleProject/Models/Car.m index 3587c1a..1099866 100644 --- a/Example/SampleProject/Models/Car.m +++ b/Example/SampleProject/Models/Car.m @@ -2,7 +2,7 @@ // Car.m // SampleProject // -// Created by Marin Usalj on 12/3/13. +// Created by Ignacio Romero Z. on 8/5/14. // // @@ -15,7 +15,8 @@ @implementation Car @dynamic horsePower; @dynamic make; -@dynamic owner; +@dynamic remoteID; @dynamic insuranceCompany; +@dynamic owner; @end diff --git a/Example/SampleProject/Models/Categories/Car+Mappings.m b/Example/SampleProject/Models/Categories/Car+Mappings.m index 098dbae..3ffe252 100644 --- a/Example/SampleProject/Models/Categories/Car+Mappings.m +++ b/Example/SampleProject/Models/Categories/Car+Mappings.m @@ -12,8 +12,13 @@ @implementation Car (Mappings) ++ (NSString *)primaryKey { + return @"remoteID"; +} + + (NSDictionary *)mappings { return @{ + @"id": [self primaryKey], @"hp": @"horsePower", @"owner": @{ @"class": [Person class] diff --git a/Example/SampleProject/Models/Categories/InsuranceCompany+Mappings.m b/Example/SampleProject/Models/Categories/InsuranceCompany+Mappings.m index 36bee36..04c968b 100644 --- a/Example/SampleProject/Models/Categories/InsuranceCompany+Mappings.m +++ b/Example/SampleProject/Models/Categories/InsuranceCompany+Mappings.m @@ -17,7 +17,7 @@ + (id)primaryKey { + (NSDictionary *)mappings { return @{ - @"id" : [self primaryKey], + @"id" : [self primaryKey], @"owner_id" : @{ @"key" : @"owner", @"class" : [Person class] }, diff --git a/Example/SampleProject/Models/Categories/Person+Mappings.m b/Example/SampleProject/Models/Categories/Person+Mappings.m index f257012..dc3c826 100644 --- a/Example/SampleProject/Models/Categories/Person+Mappings.m +++ b/Example/SampleProject/Models/Categories/Person+Mappings.m @@ -24,4 +24,14 @@ + (NSDictionary *)mappings { }; } ++ (NSString *)keyPathForRemoteKey:(NSString *)key { + if ([key isEqualToString:@"role"]) { + return @"profile.role"; + } + if ([key isEqualToString:@"lifeSavings"]) { + return @"profile.life_savings"; + } + return nil; +} + @end diff --git a/Example/SampleProject/Models/InsuranceCompany.h b/Example/SampleProject/Models/InsuranceCompany.h index 4acd59b..d5831d3 100644 --- a/Example/SampleProject/Models/InsuranceCompany.h +++ b/Example/SampleProject/Models/InsuranceCompany.h @@ -2,7 +2,7 @@ // InsuranceCompany.h // SampleProject // -// Created by Delisa Mason on 12/27/13. +// Created by Ignacio Romero Z. on 8/5/14. // // diff --git a/Example/SampleProject/Models/InsuranceCompany.m b/Example/SampleProject/Models/InsuranceCompany.m index de3723c..ad6b1eb 100644 --- a/Example/SampleProject/Models/InsuranceCompany.m +++ b/Example/SampleProject/Models/InsuranceCompany.m @@ -2,7 +2,7 @@ // InsuranceCompany.m // SampleProject // -// Created by Delisa Mason on 12/27/13. +// Created by Ignacio Romero Z. on 8/5/14. // // diff --git a/Example/SampleProject/Models/Person.h b/Example/SampleProject/Models/Person.h index 9d942c3..88e2c01 100644 --- a/Example/SampleProject/Models/Person.h +++ b/Example/SampleProject/Models/Person.h @@ -2,7 +2,7 @@ // Person.h // SampleProject // -// Created by Delisa Mason on 12/27/13. +// Created by Ignacio Romero Z. on 8/5/14. // // @@ -18,12 +18,13 @@ @property (nonatomic, retain) NSString * firstName; @property (nonatomic, retain) NSNumber * isMember; @property (nonatomic, retain) NSString * lastName; +@property (nonatomic, retain) NSNumber * lifeSavings; @property (nonatomic, retain) NSNumber * remoteID; -@property (nonatomic, retain) NSNumber * savings; +@property (nonatomic, retain) NSString * role; @property (nonatomic, retain) NSSet *cars; @property (nonatomic, retain) NSSet *employees; -@property (nonatomic, retain) Person *manager; @property (nonatomic, retain) InsuranceCompany *insuranceCompany; +@property (nonatomic, retain) Person *manager; @end @interface Person (CoreDataGeneratedAccessors) diff --git a/Example/SampleProject/Models/Person.m b/Example/SampleProject/Models/Person.m index 817f6fa..99ef636 100644 --- a/Example/SampleProject/Models/Person.m +++ b/Example/SampleProject/Models/Person.m @@ -2,13 +2,14 @@ // Person.m // SampleProject // -// Created by Delisa Mason on 12/27/13. +// Created by Ignacio Romero Z. on 8/5/14. // // #import "Person.h" #import "Car.h" #import "InsuranceCompany.h" +#import "Person.h" @implementation Person @@ -18,11 +19,12 @@ @implementation Person @dynamic firstName; @dynamic isMember; @dynamic lastName; +@dynamic lifeSavings; @dynamic remoteID; -@dynamic savings; +@dynamic role; @dynamic cars; @dynamic employees; -@dynamic manager; @dynamic insuranceCompany; +@dynamic manager; @end diff --git a/Example/SampleProject/SampleProject.xcdatamodeld/SampleProject.xcdatamodel/contents b/Example/SampleProject/SampleProject.xcdatamodeld/SampleProject.xcdatamodel/contents index 0cc7604..ba9e251 100644 --- a/Example/SampleProject/SampleProject.xcdatamodeld/SampleProject.xcdatamodel/contents +++ b/Example/SampleProject/SampleProject.xcdatamodeld/SampleProject.xcdatamodel/contents @@ -1,14 +1,15 @@ - + + - + @@ -19,17 +20,18 @@ + - + - - - - + + + + \ No newline at end of file diff --git a/Example/SampleProjectTests/.DS_Store b/Example/SampleProjectTests/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/Example/SampleProjectTests/.DS_Store differ diff --git a/Example/SampleProjectTests/CoreDataManagerTests.m b/Example/SampleProjectTests/CoreDataManagerTests.m index 70c6c06..cc44041 100644 --- a/Example/SampleProjectTests/CoreDataManagerTests.m +++ b/Example/SampleProjectTests/CoreDataManagerTests.m @@ -1,5 +1,6 @@ + #import "Kiwi.h" -#import +#import "CoreDataManager.h" void resetCoreDataStack(CoreDataManager *manager) { [manager setValue:nil forKey:@"persistentStoreCoordinator"]; diff --git a/Example/SampleProjectTests/FindersAndCreatorsTests.m b/Example/SampleProjectTests/FindersAndCreatorsTests.m index 50b41fc..c1642ed 100644 --- a/Example/SampleProjectTests/FindersAndCreatorsTests.m +++ b/Example/SampleProjectTests/FindersAndCreatorsTests.m @@ -1,8 +1,10 @@ + #import "Kiwi.h" #import "ObjectiveSugar.h" #import "Person+Mappings.h" #import "OBRPerson.h" #import "Car+Mappings.h" +#import "CoreDataManager.h" static NSString *UNIQUE_NAME = @"ldkhbfaewlfbaewljfhb"; static NSString *UNIQUE_SURNAME = @"laewfbaweljfbawlieufbawef"; @@ -12,7 +14,7 @@ Person *fetchUniquePerson() { Person *person = [Person where:[NSString stringWithFormat:@"firstName = '%@' AND lastName = '%@'", - UNIQUE_NAME, UNIQUE_SURNAME]].first; + UNIQUE_NAME, UNIQUE_SURNAME] inContext:[CoreDataManager context]].first; return person; } @@ -30,7 +32,7 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext person.age = @(i); person.isMember = @YES; person.anniversary = [NSDate dateWithTimeIntervalSince1970:0]; - [person save]; + [person saveInContext:[CoreDataManager context]]; } } @@ -43,38 +45,39 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext NSArray *surnames = @[@"Doe", @"Jobs", @"Anderson", UNIQUE_SURNAME]; beforeEach(^{ - [Person deleteAll]; - createSomePeople(names, surnames, NSManagedObjectContext.defaultContext); + [Person deleteAllInContext:[CoreDataManager context]]; + createSomePeople(names, surnames, [CoreDataManager context]); }); context(@"Finders", ^{ it(@"Finds ALL the entities!", ^{ - [[[Person all] should] haveCountOf:[names count]]; + [[[Person allInContext:[CoreDataManager context]] should] haveCountOf:[names count]]; }); it(@"Finds using [Entity where: STRING]", ^{ - Person *unique = [Person where:[NSPredicate predicateWithFormat:@"firstName == %@",UNIQUE_NAME]].first; + Person *unique = [Person where:[NSPredicate predicateWithFormat:@"firstName == %@",UNIQUE_NAME] inContext:[CoreDataManager context]].first; [[unique.lastName should] equal:UNIQUE_SURNAME]; }); it(@"Finds using [Entity where: STRING and ARGUMENTS]", ^{ - Person *unique = [Person where:@"firstName == %@", UNIQUE_NAME].first; + Person *unique = [Person where:[NSPredicate predicateWithFormat:@"firstName == %@",UNIQUE_NAME] inContext:[CoreDataManager context]].first; [[unique.lastName should] equal:UNIQUE_SURNAME]; }); it(@"Finds using [Entity where: DICTIONARY]", ^{ - Person *person = [Person where:@{ - @"firstName": @"John", - @"lastName": @"Doe", - @"age": @0, - @"isMember": @1, - @"anniversary": [NSDate dateWithTimeIntervalSince1970:0] - }].first; + id condition = @{ + @"firstName": @"John", + @"lastName": @"Doe", + @"age": @0, + @"isMember": @1, + @"anniversary": [NSDate dateWithTimeIntervalSince1970:0] + }; + Person *person = [Person where:condition inContext:[CoreDataManager context]].first; [[person.firstName should] equal:@"John"]; [[person.lastName should] equal:@"Doe"]; @@ -84,47 +87,47 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext }); it(@"Finds and creates if there was no object", ^{ - [Person deleteAll]; - Person *luis = [Person findOrCreate:@{ @"firstName": @"Luis" }]; + [Person deleteAllInContext:[CoreDataManager context]]; + Person *luis = [Person findOrCreate:@{@"firstName": @"Luis"} inContext:[CoreDataManager context]]; [[luis.firstName should] equal:@"Luis"]; }); it(@"doesn't create duplicate objects on findOrCreate", ^{ - [Person deleteAll]; + [Person deleteAllInContext:[CoreDataManager context]]; [@4 times:^{ - [Person findOrCreate:@{ @"firstName": @"Luis" }]; + [Person findOrCreate:@{@"firstName": @"Luis"} inContext:[CoreDataManager context]]; }]; - [[[Person all] should] haveCountOf:1]; + [[[Person allInContext:[CoreDataManager context]] should] haveCountOf:1]; }); it(@"Finds the first match", ^{ - Person *johnDoe = [Person find:@{ @"firstName": @"John", - @"lastName": @"Doe" }]; + Person *johnDoe = [Person find:@{@"firstName": @"John", + @"lastName": @"Doe"} inContext:[CoreDataManager context]]; [[johnDoe.firstName should] equal:@"John"]; }); it(@"Finds the first match using [Entity find: STRING]", ^{ - Person *johnDoe = [Person find:@"firstName = 'John' AND lastName = 'Doe'"]; + Person *johnDoe = [Person find:@"firstName = 'John' AND lastName = 'Doe'" inContext:[CoreDataManager context]]; [[johnDoe.firstName should] equal:@"John"]; }); it(@"Finds the first match using [Entity find: STRING and ARGUMENTS]", ^{ - Person *johnDoe = [Person find:@"firstName = %@ AND lastName = %@", @"John", @"Doe"]; + Person *johnDoe = [Person find:[NSPredicate predicateWithFormat:@"firstName = %@ AND lastName = %@", @"John", @"Doe"] inContext:[CoreDataManager context]]; [[johnDoe.firstName should] equal:@"John"]; }); it(@"doesn't create an object on find", ^{ - Person *cat = [Person find:@{ @"firstName": @"Cat" }]; + Person *cat = [Person find:@{@"firstName": @"Cat"} inContext:[CoreDataManager context]]; [cat shouldBeNil]; }); it(@"Finds a limited number of results", ^{ [@4 times:^{ - Person *newPerson = [Person create]; + Person *newPerson = [Person createInContext:[CoreDataManager context]]; newPerson.firstName = @"John"; - [newPerson save]; + [newPerson saveInContext:[CoreDataManager context]]; }]; - [[[Person where:@{ @"firstName": @"John" } limit:@2] should] haveCountOf:2]; + [[[Person where:@{@"firstName": @"John"} inContext:[CoreDataManager context] limit:@2] should] haveCountOf:2]; }); }); @@ -134,70 +137,70 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext id (^lastNameMapper)(Person *) = ^id (Person *p) { return p.lastName; }; beforeEach(^{ - [Person deleteAll]; + [Person deleteAllInContext:[CoreDataManager context]]; createSomePeople(@[@"Abe", @"Bob", @"Cal", @"Don"], @[@"Zed", @"Mol", @"Gaz", @"Mol"], - [NSManagedObjectContext defaultContext]); + [CoreDataManager context]); }); it(@"orders results by a single string property", ^{ - NSArray *resultLastNames = [[Person allWithOrder:@"lastName"] + NSArray *resultLastNames = [[Person allInContext:[CoreDataManager context] order:@"lastName"] map:lastNameMapper]; [[resultLastNames should] equal:@[@"Gaz", @"Mol", @"Mol", @"Zed"]]; }); it(@"orders results by a single string property descending", ^{ - NSArray *resultFirstNames = [[Person allWithOrder:@"firstName DESC"] + NSArray *resultFirstNames = [[Person allInContext:[CoreDataManager context] order:@"firstName DESC"] map:firstNameMapper]; [[resultFirstNames should] equal:@[@"Don", @"Cal", @"Bob", @"Abe"]]; }); it(@"orders results by multiple string properties descending", ^{ - NSArray *resultFirstNames = [[Person allWithOrder:@"lastName, firstName DESC"] + NSArray *resultFirstNames = [[Person allInContext:[CoreDataManager context] order:@"lastName, firstName DESC"] map:firstNameMapper]; [[resultFirstNames should] equal:@[@"Cal", @"Don", @"Bob", @"Abe"]]; }); it(@"orders results by multiple properties", ^{ - NSArray *resultFirstNames = [[Person allWithOrder:@[@"lastName", @"firstName"]] + NSArray *resultFirstNames = [[Person allInContext:[CoreDataManager context] order:@[@"lastName", @"firstName"]] map:firstNameMapper]; [[resultFirstNames should] equal:@[@"Cal", @"Bob", @"Don", @"Abe"]]; }); it(@"orders results by property ascending", ^{ - NSArray *resultFirstNames = [[Person allWithOrder:@{@"firstName" : @"ASC"}] + NSArray *resultFirstNames = [[Person allInContext:[CoreDataManager context] order:@{@"firstName" : @"ASC"}] map:firstNameMapper]; [[resultFirstNames should] equal:@[@"Abe", @"Bob", @"Cal", @"Don"]]; }); it(@"orders results by property descending", ^{ - NSArray *resultFirstNames = [[Person allWithOrder:@[@{@"firstName" : @"DESC"}]] + NSArray *resultFirstNames = [[Person allInContext:[CoreDataManager context] order:@[@{@"firstName" : @"DESC"}]] map:firstNameMapper]; [[resultFirstNames should] equal:@[@"Don", @"Cal", @"Bob", @"Abe"]]; }); it(@"orders results by sort descriptors", ^{ - NSArray *resultFirstNames = [[Person allWithOrder:@[[NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES], + NSArray *resultFirstNames = [[Person allInContext:[CoreDataManager context] order:@[[NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:@"firstName" ascending:NO]]] map:firstNameMapper]; [[resultFirstNames should] equal:@[@"Cal", @"Don", @"Bob", @"Abe"]]; }); it(@"orders found results", ^{ - NSArray *resultFirstNames = [[Person where:@{@"lastName" : @"Mol"} order:@"firstName"] + NSArray *resultFirstNames = [[Person where:@{@"lastName" : @"Mol"} inContext:[CoreDataManager context] order:@"firstName"] map:firstNameMapper]; [[resultFirstNames should] equal:@[@"Bob", @"Don"]]; }); it(@"orders limited results", ^{ - NSArray *resultLastNames = [[Person where:nil order:@"lastName" limit:@(2)] + NSArray *resultLastNames = [[Person where:nil inContext:[CoreDataManager context] order:@"lastName" limit:@(2)] map:lastNameMapper]; [[resultLastNames should] equal:@[@"Gaz", @"Mol"]]; }); it(@"orders found and limited results", ^{ NSArray *resultFirstNames = [[Person where:@{@"lastName" : @"Mol"} - inContext:[NSManagedObjectContext defaultContext] + inContext:[CoreDataManager context] order:@[@{@"lastName" : @"ASC"}, @{@"firstName" : @"DESC"}] limit:@(1)] @@ -209,21 +212,21 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext context(@"Counting", ^{ it(@"counts all entities", ^{ - [[@([Person count]) should] equal:@(4)]; + [[@([Person countInContext:[CoreDataManager context]]) should] equal:@(4)]; }); it(@"counts found entities", ^{ - NSUInteger count = [Person countWhere:@{@"firstName" : @"Neo"}]; + NSUInteger count = [Person countWhere:@{@"firstName" : @"Neo"} inContext:[CoreDataManager context]]; [[@(count) should] equal:@(1)]; }); it(@"counts zero when none found", ^{ - NSUInteger count = [Person countWhere:@{@"firstName" : @"Nobody"}]; + NSUInteger count = [Person countWhere:@{@"firstName" : @"Nobody"} inContext:[CoreDataManager context]]; [[@(count) should] equal:@(0)]; }); it(@"counts with variable arguments", ^{ - NSUInteger count = [Person countWhere:@"firstName = %@", @"Neo"]; + NSUInteger count = [Person countWhere:[NSPredicate predicateWithFormat:@"firstName = %@", @"Neo"] inContext:[CoreDataManager context]]; [[@(count) should] equal:@(1)]; }); }); @@ -231,10 +234,10 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext context(@"Creating", ^{ it(@"creates without arguments", ^{ - Person *person = [Person create]; + Person *person = [Person createInContext:[CoreDataManager context]]; person.firstName = @"marin"; person.lastName = UNIQUE_SURNAME; - [[[[Person where:@"firstName == 'marin'"].first lastName] should] equal:UNIQUE_SURNAME]; + [[[[Person where:@"firstName == 'marin'" inContext:[CoreDataManager context]].first lastName] should] equal:UNIQUE_SURNAME]; }); @@ -243,15 +246,15 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext @"firstName": @"Marin", @"lastName": @"Usalj", @"age": @25 - }]; + } inContext:[CoreDataManager context]]; [[person.firstName should] equal:@"Marin"]; [[person.lastName should] equal:@"Usalj"]; [[person.age should] equal:theValue(25)]; }); it(@"Doesn't create with nulls", ^{ - [[Person create:nil] shouldBeNil]; - [[Person create:(id)[NSNull null]] shouldBeNil]; + [[Person create:nil inContext:[CoreDataManager context]] shouldBeNil]; + [[Person create:(id)[NSNull null] inContext:[CoreDataManager context]] shouldBeNil]; }); }); @@ -259,33 +262,33 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext context(@"Updating", ^{ it(@"Can update using dictionary", ^{ - Person *person = [Person create]; - [person update:@{ @"firstName": @"Jonathan", @"age": @50 }]; + Person *person = [Person createInContext:[CoreDataManager context]]; + [person update:@{ @"firstName": @"Jonathan", @"age": @50 } inContext:[CoreDataManager context]]; [[person.firstName should] equal:@"Jonathan"]; [[person.age should] equal:@50]; }); it(@"sets [NSNull null] properties as nil", ^{ - Person *person = [Person create]; - [person update:@{ @"is_member": @YES }]; - [person update:@{ @"is_member": [NSNull null] }]; + Person *person = [Person createInContext:[CoreDataManager context]]; + [person update:@{ @"is_member": @YES } inContext:[CoreDataManager context]]; + [person update:@{ @"is_member": [NSNull null] } inContext:[CoreDataManager context]]; [person.isMember shouldBeNil]; }); it(@"stringifies numbers", ^{ - Person *person = [Person create:@{ @"first_name": @123 }]; + Person *person = [Person create:@{ @"first_name": @123 } inContext:[CoreDataManager context]]; [[person.firstName should] equal:@"123"]; }); it(@"converts strings to integers", ^{ - Person *person = [Person create:@{ @"age": @"25" }]; + Person *person = [Person create:@{ @"age": @"25" } inContext:[CoreDataManager context]]; [[person.age should] equal:@25]; }); - it(@"converts strings to floats", ^{ - Person *person = [Person create:@{ @"savings": @"1500.12" }]; - [[person.savings should] equal:@(1500.12f)]; + pending(@"converts strings to floats", ^{ + Person *person = [Person create:@{ @"savings": @"1500.12" } inContext:[CoreDataManager context]]; + [[person.lifeSavings should] equal:@(1500.12f)]; }); it(@"converts strings to dates", ^{ @@ -293,29 +296,29 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext [formatta setDateFormat:@"yyyy-MM-dd HH:mm:ss z"]; NSDate *date = [NSDate date]; - Person *person = [Person create:@{ @"anniversary": [formatta stringFromDate:date] }]; + Person *person = [Person create:@{ @"anniversary": [formatta stringFromDate:date] } inContext:[CoreDataManager context]]; [[@([date timeIntervalSinceDate:person.anniversary]) should] beLessThan:@1]; }); it(@"doesn't update with nulls", ^{ Person *person = fetchUniquePerson(); - [person update:nil]; - [person update:(id)[NSNull null]]; + [person update:nil inContext:[CoreDataManager context]]; + [person update:(id)[NSNull null] inContext:[CoreDataManager context]]; [[person.firstName should] equal:UNIQUE_NAME]; }); it(@"doesn't always create new relationship object", ^{ - Car *car = [Car create:@{ @"hp": @150, @"owner": @{ @"firstName": @"asetnset" } }]; + Car *car = [Car create:@{ @"hp": @150, @"owner": @{ @"firstName": @"asetnset" } } inContext:[CoreDataManager context]]; [@3 times:^{ - [car update:@{ @"make": @"Porsche", @"owner": @{ @"firstName": @"asetnset" } }]; + [car update:@{ @"make": @"Porsche", @"owner": @{ @"firstName": @"asetnset" } } inContext:[CoreDataManager context]]; }]; - [[[Person where:@{ @"firstName": @"asetnset" }] should] haveCountOf:1]; + [[[Person where:@{ @"firstName": @"asetnset" } inContext:[CoreDataManager context]] should] haveCountOf:1]; }); it(@"doesn't mark records as having changes when values are the same", ^{ Person *person = fetchUniquePerson(); - [person update:@{@"firstName": person.firstName}]; + [person update:@{@"firstName": person.firstName} inContext:[CoreDataManager context]]; [[@([person hasChanges]) should] beNo]; }); }); @@ -331,22 +334,22 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext afterEach(^{ person.firstName = UNIQUE_NAME; - [person save]; + [person saveInContext:[CoreDataManager context]]; }); it(@"uses the object's context", ^{ [[person.managedObjectContext should] receive:@selector(save:) andReturn:theValue(YES)]; - [person save]; + [person saveInContext:[CoreDataManager context]]; }); it(@"returns YES if save has succeeded", ^{ - [[@([person save]) should] beTrue]; - [[@([person save]) should] beTrue]; + [[@([person saveInContext:[CoreDataManager context]]) should] beTrue]; + [[@([person saveInContext:[CoreDataManager context]]) should] beTrue]; }); it(@"returns NO if save hasn't succeeded", ^{ [[person.managedObjectContext should] receive:@selector(save:) andReturn:theValue(NO)]; - [[@([person save]) should] beFalse]; + [[@([person saveInContext:[CoreDataManager context]]) should] beFalse]; }); }); @@ -357,13 +360,13 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext it(@"Deletes the object from database with -delete", ^{ Person *person = fetchUniquePerson(); [person shouldNotBeNil]; - [person delete]; + [person deleteInContext:[CoreDataManager context]]; [fetchUniquePerson() shouldBeNil]; }); it(@"Deletes everything from database with +deleteAll", ^{ - [Person deleteAll]; - [[[Person all] should] beEmpty]; + [Person deleteAllInContext:[CoreDataManager context]]; + [[[Person allInContext:[CoreDataManager context]] should] beEmpty]; }); }); @@ -374,8 +377,8 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext __block NSManagedObjectContext *newContext; beforeEach(^{ - [Person deleteAll]; - [NSManagedObjectContext.defaultContext save:nil]; + [Person deleteAllInContext:[CoreDataManager context]]; + [[CoreDataManager context] save:nil]; newContext = createNewContext(); @@ -384,7 +387,7 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext newPerson.firstName = @"Joshua"; newPerson.lastName = @"Jobs"; newPerson.age = [NSNumber numberWithInt:100]; - [newPerson save]; + [newPerson saveInContext:[CoreDataManager context]]; }]; }); @@ -415,8 +418,8 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext __block NSArray *newPeople; [anotherContext performBlockAndWait:^{ - [Person deleteAll]; - [NSManagedObjectContext.defaultContext save:nil]; + [Person deleteAllInContext:[CoreDataManager context]]; + [[CoreDataManager context] save:nil]; createSomePeople(names, surnames, anotherContext); newPeople = [Person allInContext:anotherContext]; @@ -437,7 +440,7 @@ void createSomePeople(NSArray *names, NSArray *surnames, NSManagedObjectContext [newContext performBlockAndWait:^{ Person *newPerson = [Person createInContext:newContext]; newPerson.firstName = @"Joshua"; - [newPerson save]; + [newPerson saveInContext:[CoreDataManager context]]; }]; }]; NSArray *people = [Person where:@{ @"firstName": @"Joshua"} diff --git a/Example/SampleProjectTests/Fixtures/people.json b/Example/SampleProjectTests/Fixtures/people.json new file mode 100644 index 0000000..4aded63 --- /dev/null +++ b/Example/SampleProjectTests/Fixtures/people.json @@ -0,0 +1,65 @@ +{ + "id": 1, + "first_name": "Marin", + "last_name": "Usalj", + "age": 30, + "is_member": true, + "profile": { + "role": "CEO", + "life_savings": 1500.12 + }, + "cars": [ + { + "id": 5, + "hp": 220, + "make": "Trabant" + }, + { + "id": 6, + "hp": 90, + "make": "Volkswagen" + } + ], + "manager": { + "id": 2, + "first_name": "Delisa", + "last_name": "Mason", + "age": 35, + "is_member": true, + "cars": [ + { + "id": 3, + "hp": 330, + "make": "Lamborgini" + }, + { + "id": 4, + "hp": 240, + "make": "BMW" + } + ] + }, + "employees": [ + { + "id": 12, + "first_name": "Luca", + "last_name": "Madison", + "age": 31, + "is_member": false + }, + { + "id": 32, + "first_name": "Tony", + "last_name": "Stark", + "age": 33, + "is_member": false + }, + { + "id": 15, + "first_name": "Jim", + "last_name": "Carey", + "age": 47, + "is_member": true + } + ] +} \ No newline at end of file diff --git a/Example/SampleProjectTests/MappingsTests.m b/Example/SampleProjectTests/MappingsTests.m index ed6d290..0bb4bea 100644 --- a/Example/SampleProjectTests/MappingsTests.m +++ b/Example/SampleProjectTests/MappingsTests.m @@ -1,56 +1,48 @@ + #import "Kiwi.h" #import "Person+Mappings.h" #import "Car+Mappings.h" #import "InsuranceCompany.h" +#import "CoreDataManager.h" +#import "JSONUtility.h" SPEC_BEGIN(MappingsTests) describe(@"Mappings", ^{ - NSDictionary *JSON = @{ - @"first_name": @"Marin", - @"last_name": @"Usalj", - @"age": @25, - @"is_member": @"true", - @"cars": @[ - @{ @"hp": @220, @"make": @"Trabant" }, - @{ @"hp": @90, @"make": @"Volkswagen" } - ], - @"manager": @{ - @"firstName": @"Delisa", - @"lastName": @"Mason", - @"age": @25, - @"isMember": @NO - }, - @"employees": @[ - @{ @"first_name": @"Luca" }, - @{ @"first_name": @"Tony" }, - @{ @"first_name": @"Jim" } - ] - }; - __block Person *person; NSManagedObjectContext *newContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; newContext.persistentStoreCoordinator = [[CoreDataManager sharedManager] persistentStoreCoordinator]; + NSDictionary *payload = JSON(@"people"); + beforeEach(^{ - person = [Person create:JSON inContext:newContext]; + person = [Person create:payload inContext:newContext]; }); it(@"caches mappings", ^{ Car *car = [Car createInContext:newContext]; [[Car should] receive:@selector(mappings) withCountAtMost:1]; - [car update:@{ @"hp": @150 }]; - [car update:@{ @"make": @"Ford" }]; - [car update:@{ @"hp": @150 }]; + [car update:@{ @"hp": @150 } inContext:newContext]; + [car update:@{ @"make": @"Ford" } inContext:newContext]; + [car update:@{ @"hp": @150 } inContext:newContext]; + }); + + it(@"supports keypath nested property mappings", ^{ + Person *person = [Person createInContext:newContext]; + [[Person should] receive:@selector(mappings) withCountAtMost:1]; + [[Person should] receive:@selector(keyPathForRemoteKey:) withCountAtMost:16]; + + [person update:@{ @"first_name": @"Marin", @"last_name": @"Usalj" } inContext:newContext]; + [person update:@{ @"profile": @{ @"role": @"CEO", @"life_savings": @1500.12 } } inContext:newContext]; }); it(@"uses mapped values when creating", ^{ [[person.firstName should] equal:@"Marin"]; [[person.lastName should] equal:@"Usalj"]; - [[person.age should] equal:@25]; + [[person.age should] equal:@30]; }); it(@"can support snake_case even without mappings", ^{ @@ -70,42 +62,42 @@ }); it(@"uses mappings in findOrCreate", ^{ - Person *bob = [Person findOrCreate:@{ @"first_name": @"Bob" }]; + Person *bob = [Person updateOrCreate:@{ @"first_name": @"Bob" } inContext:newContext]; [[bob.firstName should] equal:@"Bob"]; }); it(@"supports creating a parent object using just ID from the server", ^{ - Car *car = [Car create:@{ @"hp": @150, @"insurance_id": @1234 }]; - [[car.insuranceCompany should] equal:[InsuranceCompany find:@{ @"remoteID": @1234 }]]; + Car *car = [Car create:@{ @"hp": @150, @"insurance_id": @1234 } inContext:newContext]; + [[car.insuranceCompany should] equal:[InsuranceCompany find:@{ @"remoteID": @1234 } inContext:newContext]]; }); it(@"supports creating nested objects directly", ^{ - Person *employee = [Person create]; - Person *manager = [Person create:@{@"employees": @[employee]}]; + Person *employee = [Person createInContext:newContext]; + Person *manager = [Person create:@{@"employees": @[employee]} inContext:newContext]; [[[manager should] have:1] employees]; }); it(@"ignores unknown keys", ^{ - Car *car = [Car create]; + Car *car = [Car createInContext:newContext]; [[car shouldNot] receive:@selector(setPrimitiveValue:forKey:)]; - [car update:@{ @"chocolate": @"waffles" }]; + [car update:@{ @"chocolate": @"waffles" } inContext:newContext]; }); it(@"ignores embedded unknown keys", ^{ [[theBlock(^{ - Car *car = [Car create]; - [car update:@{ @"owner": @{ @"coolness": @(100) } }]; + Car *car = [Car createInContext:newContext]; + [car update:@{ @"owner": @{ @"coolness": @(100) } } inContext:newContext]; }) shouldNot] raise]; }); it(@"supports creating nested parent objects using IDs from the server", ^{ - Car *car = [Car create:@{ @"insurance_company": @{ @"id" : @1234, @"owner_id" : @4567 }}]; - [[car.insuranceCompany.owner should] equal:[Person find:@{ @"remoteID": @4567 }]]; + Car *car = [Car create:@{ @"insurance_company": @{ @"id" : @1234, @"owner_id" : @4567 }} inContext:newContext]; + [[car.insuranceCompany.owner should] equal:[Person find:@{ @"remoteID": @4567 } inContext:newContext]]; }); it(@"supports creating full nested parent objects", ^{ - Car *car = [Car create:@{ @"insurance_company": @{ @"id" : @1234, @"owner" : @{ @"id" : @4567, @"first_name" : @"Stan" } }}]; - [[car.insuranceCompany.owner should] equal:[Person find:@{ @"remoteID": @4567, @"firstName": @"Stan" }]]; + Car *car = [Car create:@{ @"insurance_company": @{ @"id" : @1234, @"owner" : @{ @"id" : @4567, @"first_name" : @"Stan" } }} inContext:newContext]; + [[car.insuranceCompany.owner should] equal:[Person find:@{ @"remoteID": @4567, @"firstName": @"Stan" } inContext:newContext]]; }); }); diff --git a/Classes/CoreDataManager.h b/Example/SampleProjectTests/Utilities/CoreDataManager.h similarity index 91% rename from Classes/CoreDataManager.h rename to Example/SampleProjectTests/Utilities/CoreDataManager.h index 501c39f..5d6154d 100644 --- a/Classes/CoreDataManager.h +++ b/Example/SampleProjectTests/Utilities/CoreDataManager.h @@ -25,16 +25,17 @@ @interface CoreDataManager : NSObject -@property (readonly, nonatomic) NSManagedObjectContext *managedObjectContext; -@property (readonly, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator; @property (copy, nonatomic) NSString *databaseName; @property (copy, nonatomic) NSString *modelName; +@property (copy, nonatomic) NSString *modelVersion; + (id)instance DEPRECATED_ATTRIBUTE; + (instancetype)sharedManager; ++ (NSManagedObjectContext *)context; + - (BOOL)saveContext; - (void)useInMemoryStore; diff --git a/Classes/CoreDataManager.m b/Example/SampleProjectTests/Utilities/CoreDataManager.m similarity index 88% rename from Classes/CoreDataManager.m rename to Example/SampleProjectTests/Utilities/CoreDataManager.m index aad793e..d4c835e 100644 --- a/Classes/CoreDataManager.m +++ b/Example/SampleProjectTests/Utilities/CoreDataManager.m @@ -22,6 +22,11 @@ #import "CoreDataManager.h" +@interface CoreDataManager () +@property (readonly, nonatomic) NSManagedObjectContext *managedObjectContext; +@property (readonly, nonatomic) NSManagedObjectModel *managedObjectModel; +@end + @implementation CoreDataManager @synthesize managedObjectContext = _managedObjectContext; @synthesize managedObjectModel = _managedObjectModel; @@ -39,15 +44,24 @@ + (instancetype)sharedManager { static dispatch_once_t singletonToken; dispatch_once(&singletonToken, ^{ singleton = [[self alloc] init]; + + NSLog(@"appName : %@", [singleton appName]); + NSLog(@"applicationSupportDirectory : %@", [singleton applicationSupportDirectory]); + NSLog(@"applicationDocumentsDirectory : %@", [singleton applicationDocumentsDirectory]); }); return singleton; } ++ (NSManagedObjectContext *)context +{ + return [CoreDataManager sharedManager].managedObjectContext; +} + #pragma mark - Private - (NSString *)appName { - return [[NSBundle bundleForClass:[self class]] infoDictionary][@"CFBundleName"]; + return [[NSBundle mainBundle] infoDictionary][@"CFBundleName"]; } - (NSString *)databaseName { @@ -79,8 +93,13 @@ - (NSManagedObjectContext *)managedObjectContext { - (NSManagedObjectModel *)managedObjectModel { if (_managedObjectModel) return _managedObjectModel; - + NSURL *modelURL = [[NSBundle bundleForClass:[self class]] URLForResource:[self modelName] withExtension:@"momd"]; + + if ([self modelVersion]) { + modelURL = [modelURL URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mom", [self modelVersion]]]; + } + _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return _managedObjectModel; } diff --git a/Example/SampleProjectTests/Utilities/JSONUtility.h b/Example/SampleProjectTests/Utilities/JSONUtility.h new file mode 100644 index 0000000..d9f9c8b --- /dev/null +++ b/Example/SampleProjectTests/Utilities/JSONUtility.h @@ -0,0 +1,16 @@ +// +// JSONUtility.h +// SampleProject +// +// Created by Ignacio Romero Z. on 8/6/14. +// +// + +#import + +@interface JSONUtility : NSObject + +id JSON(NSString *resource); +id JSONFromBundle(NSString *resource, NSBundle *bundle); + +@end diff --git a/Example/SampleProjectTests/Utilities/JSONUtility.m b/Example/SampleProjectTests/Utilities/JSONUtility.m new file mode 100644 index 0000000..31072d8 --- /dev/null +++ b/Example/SampleProjectTests/Utilities/JSONUtility.m @@ -0,0 +1,39 @@ +// +// JSONUtility.m +// SampleProject +// +// Created by Ignacio Romero Z. on 8/6/14. +// +// + +#import "JSONUtility.h" + +@implementation JSONUtility + +id JSON(NSString *resource) { + return JSONFromBundle(resource, [NSBundle mainBundle]); +} + +id JSONFromBundle(NSString *resource, NSBundle *bundle) { + NSError *error = nil; + NSString *path = [bundle pathForResource:resource ofType:@"json"]; + NSData *data = [NSData dataWithContentsOfFile:path]; + + if (!data) { + NSLog(@"Failed with json at path : %@", path); + return nil; + } + + id JSON = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions|NSJSONWritingPrettyPrinted error:&error]; + + if (error) { + NSLog(@"error : %@", error.localizedDescription); + return nil; + } + + NSLog(@"Parsed %@ successfuly : %@", resource, JSON); + + return JSON; +} + +@end diff --git a/ObjectiveRecord.podspec b/ObjectiveRecord.podspec index 13f611d..0638828 100644 --- a/ObjectiveRecord.podspec +++ b/ObjectiveRecord.podspec @@ -1,4 +1,4 @@ -@version = "1.5.0" +@version = "1.7" Pod::Spec.new do |s| s.name = "ObjectiveRecord" @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { "Marin Usalj" => "mneorr@gmail.com" } - s.source = { :git => "https://github.com/supermarin/ObjectiveRecord.git", :tag => @version } + s.source = { :git => "https://github.com/tinyspeck/ObjectiveRecord.git", :tag => @version } s.source_files = 'Classes/**/*.{h,m}' s.framework = 'CoreData' @@ -18,4 +18,4 @@ Pod::Spec.new do |s| s.osx.deployment_target = '10.6' s.dependency 'ObjectiveSugar' -end +end \ No newline at end of file