Continued cleanup related to managed object context work

This commit is contained in:
Blake Watters
2012-07-17 13:44:50 -04:00
parent b25a1833b3
commit a8f64e7982
21 changed files with 350 additions and 366 deletions

View File

@@ -36,7 +36,7 @@
/**
The Core Data entity description used for this object mapping
*/
@property (nonatomic, readonly) NSEntityDescription *entity;
@property (nonatomic, retain, readonly) NSEntityDescription *entity;
/**
The name of the attribute on the destination entity that acts as the primary key for instances

View File

@@ -33,6 +33,7 @@
BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
@interface RKEntityMapping ()
@property (nonatomic, retain, readwrite) NSEntityDescription *entity;
@property (nonatomic, retain) NSMutableArray *mutableConnections;
@end
@@ -67,8 +68,8 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
NSAssert(objectClass, @"The managedObjectClass for an object mapped entity cannot be nil.");
self = [self init];
if (self) {
_objectClass = [objectClass retain];
_entity = [entity retain];
self.objectClass = objectClass;
self.entity = entity;
[self addObserver:self forKeyPath:@"entity" options:NSKeyValueObservingOptionInitial context:nil];
[self addObserver:self forKeyPath:@"primaryKeyAttribute" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
@@ -159,71 +160,6 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
return [desc defaultValue];
}
// TODO: Gut this method from superclass...
- (id)mappableObjectForData:(id)mappableData
{
NSAssert(false, @"DISABLED!!!");
NSAssert(mappableData, @"Mappable data cannot be nil");
return nil;
// id object = nil;
// id primaryKeyValue = nil;
// NSString *primaryKeyAttribute;
//
// NSEntityDescription *entity = [self entity];
// RKObjectAttributeMapping *primaryKeyAttributeMapping = nil;
//
// primaryKeyAttribute = [self primaryKeyAttribute];
// if (primaryKeyAttribute) {
// // If a primary key has been set on the object mapping, find the attribute mapping
// // so that we can extract any existing primary key from the mappable data
// for (RKObjectAttributeMapping *attributeMapping in self.attributeMappings) {
// if ([attributeMapping.destinationKeyPath isEqualToString:primaryKeyAttribute]) {
// primaryKeyAttributeMapping = attributeMapping;
// break;
// }
// }
//
// // Get the primary key value out of the mappable data (if any)
// if ([primaryKeyAttributeMapping isMappingForKeyOfNestedDictionary]) {
// RKLogDebug(@"Detected use of nested dictionary key as primaryKey attribute...");
// primaryKeyValue = [[mappableData allKeys] lastObject];
// } else {
// NSString *keyPathForPrimaryKeyElement = primaryKeyAttributeMapping.sourceKeyPath;
// if (keyPathForPrimaryKeyElement) {
// primaryKeyValue = [mappableData valueForKeyPath:keyPathForPrimaryKeyElement];
// } else {
// RKLogWarning(@"Unable to find source attribute for primaryKeyAttribute '%@': unable to find existing object instances by primary key.", primaryKeyAttribute);
// }
// }
// }
//
// // If we have found the primary key attribute & value, try to find an existing instance to update
// if (primaryKeyAttribute && primaryKeyValue && NO == [primaryKeyValue isEqual:[NSNull null]]) {
// object = [self.objectStore.cacheStrategy findInstanceOfEntity:entity
// withPrimaryKeyAttribute:primaryKeyAttribute
// value:primaryKeyValue
// inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]];
//
// if (object && [self.objectStore.cacheStrategy respondsToSelector:@selector(didFetchObject:)]) {
// [self.objectStore.cacheStrategy didFetchObject:object];
// }
// }
//
// if (object == nil) {
// object = [[[NSManagedObject alloc] initWithEntity:entity
// insertIntoManagedObjectContext:[_objectStore managedObjectContextForCurrentThread]] autorelease];
// if (primaryKeyAttribute && primaryKeyValue && ![primaryKeyValue isEqual:[NSNull null]]) {
// id coercedPrimaryKeyValue = [entity coerceValueForPrimaryKey:primaryKeyValue];
// [object setValue:coercedPrimaryKeyValue forKey:primaryKeyAttribute];
// }
//
// if ([self.objectStore.cacheStrategy respondsToSelector:@selector(didCreateObject:)]) {
// [self.objectStore.cacheStrategy didCreateObject:object];
// }
// }
// return object;
}
- (Class)classForProperty:(NSString *)propertyName
{
Class propertyClass = [super classForProperty:propertyName];

View File

@@ -96,14 +96,11 @@
RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:self.managedObjectContext
cache:self.managedObjectStore.cacheStrategy];
dataSource.operationQueue = [[NSOperationQueue new] autorelease];
[dataSource.operationQueue setSuspended:YES];
[dataSource.operationQueue setMaxConcurrentOperationCount:1];
dataSource.tracksInsertedObjects = YES; // We need to be able to obtain permanent object ID's
self.mappingOperationDataSource = dataSource;
// // Merge changes from the primary MOC back into our MOC
// [[NSNotificationCenter defaultCenter] addObserver:self
// selector:@selector(handlePrimaryManagedObjectContextDidSaveNotification:)
// name:NSManagedObjectContextDidSaveNotification
// object:self.managedObjectStore.primaryManagedObjectContext];
self.mappingOperationDataSource = dataSource;
} else if ([keyPath isEqualToString:@"targetObject"]) {
if ([self.targetObject isKindOfClass:[NSManagedObject class]]) {
self.targetObjectID = [(NSManagedObject *)self.targetObject objectID];
@@ -113,16 +110,20 @@
}
}
//- (void)handlePrimaryManagedObjectContextDidSaveNotification:(NSNotification *)notification
//{
// [self.managedObjectContext performBlock:^{
// [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
// }];
//}
#pragma mark - RKObjectMapperDelegate methods
// TODO: Figure out how to eliminate this...
// TODO: Figure out how to eliminate the dependence on the delegate
- (void)mapperDidFinishMapping:(RKObjectMapper *)mapper
{
if ([self.mappingOperationDataSource isKindOfClass:[RKManagedObjectMappingOperationDataSource class]]) {
// Allow any enqueued connection operations to execute once mapping is complete
NSOperationQueue *operationQueue = [(RKManagedObjectMappingOperationDataSource *)self.mappingOperationDataSource operationQueue];
[operationQueue setSuspended:NO];
[operationQueue waitUntilAllOperationsAreFinished];
}
}
- (void)mapper:(RKObjectMapper *)objectMapper didMapFromObject:(id)sourceObject toObject:(id)destinationObject atKeyPath:(NSString *)keyPath usingMapping:(RKObjectMapping *)objectMapping
{
if ([destinationObject isKindOfClass:[NSManagedObject class]]) {
@@ -137,7 +138,11 @@
{
if ([NSThread isMainThread] == NO && _targetObjectID) {
NSAssert(self.managedObjectContext, @"Expected managedObjectContext not to be nil.");
return [self.managedObjectContext objectWithID:self.targetObjectID];
__block NSManagedObject *localTargetObject;
[self.managedObjectContext performBlockAndWait:^{
localTargetObject = [self.managedObjectContext objectWithID:self.targetObjectID];
}];
return localTargetObject;
}
return [super targetObject];
@@ -153,22 +158,6 @@
return mappingResult;
}
//- (BOOL)prepareURLRequest
//{
// // NOTE: There is an important sequencing issue here. You MUST save the
// // managed object context before retaining the objectID or you will run
// // into an error where the object context cannot be saved. We do this
// // right before send to avoid sequencing issues where the target object is
// // set before the managed object store.
// if (self.targetObject && [self.targetObject isKindOfClass:[NSManagedObject class]]) {
// self.deleteObjectOnFailure = [(NSManagedObject *)self.targetObject isNew];
// [self.objectStore save:nil];
// self.targetObjectID = [[(NSManagedObject *)self.targetObject objectID] retain];
// }
//
// return [super prepareURLRequest];
//}
- (NSArray *)cachedObjects
{
NSFetchRequest *fetchRequest = [self.mappingProvider fetchRequestForResourcePath:self.resourcePath];
@@ -230,7 +219,6 @@
[self.managedObjectContext performBlockAndWait:^{
success = [self.managedObjectContext save:&error];
NSLog(@"Saved MOC success = %d. Error: %@", success, error);
}];
if (! success) {
RKLogError(@"Failed to save managed object context after mapping completed: %@", [error localizedDescription]);
@@ -251,12 +239,9 @@
NSDictionary *dictionary = [result asDictionary];
NSMethodSignature *signature = [self methodSignatureForSelector:@selector(informDelegateOfObjectLoadWithResultDictionary:)];
if (self.managedObjectContext.parentContext) {
NSLog(@"Saving parent context...");
[self.managedObjectContext.parentContext performBlockAndWait:^{
NSError *error = nil;
if (! [self.managedObjectContext.parentContext save:&error]) {
NSLog(@"Failed to save parent context. Error: %@", error);
if ([[error domain] isEqualToString:@"NSCocoaErrorDomain"]) {
NSDictionary *userInfo = [error userInfo];
NSArray *errors = [userInfo valueForKey:@"NSDetailedErrors"];

View File

@@ -28,7 +28,7 @@
@synthesize managedObjectCache = _managedObjectCache;
@synthesize operationQueue = _operationQueue;
@synthesize tracksInsertedObjects = _tracksInsertedObjects;
@synthesize insertedObjects = _insertedObjects;
@synthesize mutableInsertedObjects = _mutableInsertedObjects;
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext cache:(id<RKManagedObjectCaching>)managedObjectCache
{
@@ -150,10 +150,10 @@
- (void)commitChangesForMappingOperation:(RKMappingOperation *)mappingOperation
{
if ([mappingOperation.objectMapping isKindOfClass:[RKEntityMapping class]]) {
if ([mappingOperation.mapping isKindOfClass:[RKEntityMapping class]]) {
[self emitDeadlockWarningIfNecessary];
for (RKConnectionMapping *connectionMapping in [(RKEntityMapping *)mappingOperation.objectMapping connections]) {
for (RKConnectionMapping *connectionMapping in [(RKEntityMapping *)mappingOperation.mapping connections]) {
RKRelationshipConnectionOperation *operation = [[RKRelationshipConnectionOperation alloc] initWithManagedObject:mappingOperation.destinationObject
connectionMapping:connectionMapping
managedObjectCache:self.managedObjectCache];

View File

@@ -65,16 +65,6 @@ typedef RKObjectMapping *(^RKDynamicMappingDelegateBlock)(id);
*/
+ (RKDynamicMapping *)dynamicMapping;
#if NS_BLOCKS_AVAILABLE
/**
Return a new auto-released dynamic object mapping after yielding it to the block for configuration
*/
+ (RKDynamicMapping *)dynamicMappingUsingBlock:(void(^)(RKDynamicMapping *dynamicMapping))block;
+ (RKDynamicMapping *)dynamicMappingWithBlock:(void(^)(RKDynamicMapping *dynamicMapping))block DEPRECATED_ATTRIBUTE;
#endif
/**
Defines a dynamic mapping rule stating that when the value of the key property matches the specified
value, the objectMapping should be used.

View File

@@ -1,5 +1,5 @@
//
// RKObjectMappingOperation.h
// RKMappingOperation.h
// RestKit
//
// Created by Blake Watters on 4/30/11.
@@ -21,7 +21,7 @@
#import "RKObjectMapping.h"
#import "RKAttributeMapping.h"
@class RKMappingOperation;
@class RKMappingOperation, RKDynamicMapping;
@protocol RKMappingOperationDataSource;
/**
@@ -76,16 +76,30 @@
- (void)mappingOperation:(RKMappingOperation *)operation didNotSetUnchangedValue:(id)value forKeyPath:(NSString *)keyPath usingMapping:(RKAttributeMapping *)mapping;
/**
Tells the delegate that the object mapping operation has failed due to an error.
Tells the delegate that the mapping operation has failed due to an error.
@param operation The object mapping operation that has failed.
@param error An error object indicating the reason for the failure.
*/
- (void)mappingOperation:(RKMappingOperation *)operation didFailWithError:(NSError *)error;
/**
Tells the delegate that the mapping operation has selected a concrete object mapping with which to map the source object.
Only sent if the receiver was initialized with an instance of RKDynamicMapping as the mapping.
@param operation The mapping operation.
@param objectMapping The concrete object mapping with which to perform the mapping.
@param dynamicMapping The dynamic source mapping from which the object mapping was determined.
@since 0.11.0
*/
- (void)mappingOperation:(RKMappingOperation *)operation didSelectObjectMapping:(RKObjectMapping *)objectMapping forDynamicMapping:(RKDynamicMapping *)dynamicMapping;
@end
/**
Instances of RKObjectMappingOperation perform transformation between object
Instances of RKMappingOperation perform transformation between object
representations according to the rules express in RKObjectMapping objects. Mapping
operations provide the foundation for the RestKit object mapping engine and
perform the work of inspecting the attributes and relationships of a source object
@@ -97,41 +111,33 @@
/**
A dictionary of mappable elements containing simple values or nested object structures.
*/
@property (nonatomic, readonly) id sourceObject;
@property (nonatomic, retain, readonly) id sourceObject;
/**
The target object for this operation. Mappable values in elements will be applied to object
using key-value coding.
*/
@property (nonatomic, readonly) id destinationObject;
@property (nonatomic, retain, readonly) id destinationObject;
/**
The object mapping defining how values contained in the source object should be transformed to the destination object via key-value coding
The mapping defining how values contained in the source object should be transformed to the destination object via key-value coding.
Will either be an instance of RKObjectMapping or RKDynamicMapping.
*/
@property (nonatomic, readonly) RKObjectMapping *objectMapping;
@property (nonatomic, retain, readonly) RKMapping *mapping;
/**
The delegate to inform of interesting events during the mapping operation
*/
@property (nonatomic, assign) id<RKMappingOperationDelegate> delegate;
/**
An operation queue for deferring portions of the mapping process until later
Defaults to nil. If this mapping operation was configured by an instance of RKObjectMapper, then
an instance of the operation queue will be configured and assigned for use. If the queue is nil,
the mapping operation will perform all its operations within the body of performMapping. If a queue
is present, it may elect to defer portions of the mapping operation using the queue.
*/
@property (nonatomic, retain) NSOperationQueue *queue;
/**
The data source is responsible for providing the mapping operation with an appropriate target object for
mapping when the destination is nil.
@see RKMappingOperationDataSource
*/
@property (nonatomic, retain) id<RKMappingOperationDataSource> dataSource;
@property (nonatomic, assign) id<RKMappingOperationDataSource> dataSource;
/**
Creates and returns a new mapping operation configured to transform the object representation
@@ -142,11 +148,11 @@
@param sourceObject The source object to be mapped. Cannot be nil.
@param destinationObject The destination object the results are to be mapped onto. May be nil,
in which case a new object will be constructed during the mapping.
@param mapping An instance of RKObjectMapping or RKDynamicMapping defining how the
@param objectOrDynamicMapping An instance of RKObjectMapping or RKDynamicMapping defining how the
mapping is to be performed.
@return An instance of RKObjectMappingOperation or RKManagedObjectMappingOperation for performing the mapping.
*/
+ (id)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(RKMapping *)mapping;
+ (id)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(RKMapping *)objectOrDynamicMapping;
/**
Initializes the receiver with a source and destination objects and an object mapping
@@ -155,11 +161,11 @@
@param sourceObject The source object to be mapped. Cannot be nil.
@param destinationObject The destination object the results are to be mapped onto. May be nil,
in which case a new object will be constructed during the mapping.
@param mapping An instance of RKObjectMapping or RKDynamicMapping defining how the
@param objectOrDynamicMapping An instance of RKObjectMapping or RKDynamicMapping defining how the
mapping is to be performed.
@return The receiver, initialized with a source object, a destination object, and a mapping.
*/
- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(RKMapping *)mapping;
- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(RKMapping *)objectOrDynamicMapping;
/**
Process all mappable values from the mappable dictionary and assign them to the target object

View File

@@ -1,5 +1,5 @@
//
// RKObjectMappingOperation.m
// RKMappingOperation.m
// RestKit
//
// Created by Blake Watters on 4/30/11.
@@ -64,48 +64,40 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) {
}
@interface RKMappingOperation ()
@property (nonatomic, retain, readwrite) RKMapping *mapping;
@property (nonatomic, retain, readwrite) id sourceObject;
@property (nonatomic, retain, readwrite) id destinationObject;
@property (nonatomic, retain) NSDictionary *nestedAttributeSubstitution;
@property (nonatomic, retain) NSError *validationError;
@property (nonatomic, retain) RKObjectMapping *objectMapping; // The concrete mapping
@end
@implementation RKMappingOperation
@synthesize sourceObject = _sourceObject;
@synthesize destinationObject = _destinationObject;
@synthesize objectMapping = _objectMapping;
@synthesize mapping = _mapping;
@synthesize delegate = _delegate;
@synthesize queue = _queue;
@synthesize nestedAttributeSubstitution = _nestedAttributeSubstitution;
@synthesize validationError = _validationError;
+ (id)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(RKMapping *)objectMapping
+ (id)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(RKMapping *)objectOrDynamicMapping
{
// Check for availability of ManagedObjectMappingOperation. Better approach for handling?
Class targetClass = NSClassFromString(@"RKManagedObjectMappingOperation");
if (targetClass == nil) targetClass = [RKMappingOperation class];
return [[[targetClass alloc] initWithSourceObject:sourceObject destinationObject:destinationObject mapping:objectMapping] autorelease];
return [[[self alloc] initWithSourceObject:sourceObject destinationObject:destinationObject mapping:objectOrDynamicMapping] autorelease];
}
- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(RKMapping *)objectMapping
- (id)initWithSourceObject:(id)sourceObject destinationObject:(id)destinationObject mapping:(RKMapping *)objectOrDynamicMapping
{
NSAssert(sourceObject != nil, @"Cannot perform a mapping operation without a sourceObject object");
NSAssert(destinationObject != nil, @"Cannot perform a mapping operation without a destinationObject");
NSAssert(objectMapping != nil, @"Cannot perform a mapping operation without a mapping");
NSAssert(objectOrDynamicMapping != nil, @"Cannot perform a mapping operation without a mapping");
self = [super init];
if (self) {
_sourceObject = [sourceObject retain];
_destinationObject = [destinationObject retain];
self.sourceObject = sourceObject;
self.destinationObject = destinationObject;
self.dataSource = [[RKObjectMappingOperationDataSource new] autorelease];
if ([objectMapping isKindOfClass:[RKDynamicMapping class]]) {
_objectMapping = [[(RKDynamicMapping *)objectMapping objectMappingForDictionary:_sourceObject] retain];
RKLogDebug(@"RKObjectMappingOperation was initialized with a dynamic mapping. Determined concrete mapping = %@", _objectMapping);
} else if ([objectMapping isKindOfClass:[RKObjectMapping class]]) {
_objectMapping = (RKObjectMapping *)[objectMapping retain];
}
NSAssert(_objectMapping, @"Cannot perform a mapping operation with an object mapping");
self.mapping = objectOrDynamicMapping;
}
return self;
@@ -115,10 +107,8 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) {
{
[_sourceObject release];
[_destinationObject release];
[_objectMapping release];
[_mapping release];
[_nestedAttributeSubstitution release];
[_queue release];
self.dataSource = nil;
[super dealloc];
}
@@ -454,8 +444,8 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) {
RKLogTrace(@"Performing nested object mapping using mapping %@ for data: %@", relationshipMapping, anObject);
RKMappingOperation *subOperation = [RKMappingOperation mappingOperationFromObject:anObject toObject:anotherObject withMapping:relationshipMapping.mapping];
subOperation.dataSource = self.dataSource;
subOperation.delegate = self.delegate;
subOperation.queue = self.queue;
if (NO == [subOperation performMapping:&error]) {
RKLogWarning(@"WARNING: Failed mapping nested object: %@", [error localizedDescription]);
}
@@ -659,6 +649,19 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue) {
{
RKLogDebug(@"Starting mapping operation...");
RKLogTrace(@"Performing mapping operation: %@", self);
// Determine the concrete mapping if we were initialized with a dynamic mapping
if ([self.mapping isKindOfClass:[RKDynamicMapping class]]) {
self.objectMapping = [(RKDynamicMapping *)self.mapping objectMappingForDictionary:self.sourceObject];
RKLogDebug(@"RKObjectMappingOperation was initialized with a dynamic mapping. Determined concrete mapping = %@", self.objectMapping);
if ([self.delegate respondsToSelector:@selector(mappingOperation:didSelectObjectMapping:forDynamicMapping:)]) {
[self.delegate mappingOperation:self didSelectObjectMapping:self.objectMapping forDynamicMapping:(RKDynamicMapping *)self.mapping];
}
} else if ([self.mapping isKindOfClass:[RKObjectMapping class]]) {
self.objectMapping = (RKObjectMapping *)self.mapping;
}
NSAssert(self.objectMapping, @"Cannot perform a mapping operation with an object mapping");
[self applyNestedMappings];
BOOL mappedAttributes = [self applyAttributeMappings];

View File

@@ -234,9 +234,8 @@
NSError *error = nil;
RKMappingOperation *mappingOperation = [RKMappingOperation mappingOperationFromObject:mappableObject
toObject:destinationObject
withMapping:mapping];
mappingOperation.queue = self.operationQueue;
toObject:destinationObject
withMapping:mapping];
mappingOperation.dataSource = self.mappingOperationDataSource;
if ([self.delegate respondsToSelector:@selector(mapper:willPerformMappingOperation:)]) {
[self.delegate mapper:self willPerformMappingOperation:mappingOperation];

View File

@@ -43,10 +43,7 @@ relationship. Relationships are processed using an object mapping as well.
Instances of RKObjectMapping are used to configure RKObjectMappingOperation instances, which actually
perform the mapping work. Both object loading and serialization are defined in terms of object mappings.
*/
@interface RKObjectMapping : RKMapping <NSCopying> {
Class _objectClass;
NSMutableArray *_mappings;
}
@interface RKObjectMapping : RKMapping <NSCopying>
/**
The target class this object mapping is defining rules for
@@ -482,14 +479,6 @@ relationship. Relationships are processed using an object mapping as well.
*/
- (id)defaultValueForMissingAttribute:(NSString *)attributeName;
/**
Returns an auto-released object that can be used to apply this object mapping
given a set of mappable data. For transient objects, this generally returns an
instance of the objectClass. For Core Data backed persistent objects, mappableData
will be inspected to search for primary key data to lookup existing object instances.
*/
- (id)mappableObjectForData:(id)mappableData;
/**
Returns the class of the attribute or relationship property of the target objectClass
@@ -499,14 +488,6 @@ relationship. Relationships are processed using an object mapping as well.
*/
- (Class)classForProperty:(NSString *)propertyName;
/**
Returns an auto-released object that can be used to apply this object mapping
given a set of mappable data. For transient objects, this generally returns an
instance of the objectClass. For Core Data backed persistent objects, mappableData
will be inspected to search for primary key data to lookup existing object instances.
*/
- (id)mappableObjectForData:(id)mappableData;
/**
Returns YES if the receiever and the other mapping target the same object class
and contain an equivalent set of attribute and relationship mappings.

View File

@@ -27,6 +27,11 @@
// Constants
NSString * const RKObjectMappingNestingAttributeKeyName = @"<RK_NESTING_ATTRIBUTE>";
@interface RKObjectMapping () {
NSMutableArray *_mappings;
}
@end
@implementation RKObjectMapping
@synthesize objectClass = _objectClass;
@@ -377,11 +382,6 @@ NSString * const RKObjectMappingNestingAttributeKeyName = @"<RK_NESTING_ATTRIBUT
return nil;
}
- (id)mappableObjectForData:(id)mappableData
{
return [[self.objectClass new] autorelease];
}
- (Class)classForProperty:(NSString *)propertyName
{
return [[RKPropertyInspector sharedInspector] typeForProperty:propertyName ofClass:self.objectClass];

View File

@@ -22,6 +22,8 @@
#import "RKMappingOperation.h"
#import "RKMappingTestExpectation.h"
@protocol RKMappingOperationDataSource;
/**
An RKMappingTest object provides support for unit testing
a RestKit object mapping operation by evaluation expectations
@@ -153,6 +155,13 @@
*/
@property (nonatomic, strong, readonly) RKObjectMapping *mapping;
/**
A data source for the mapping operation.
Defaults to an instance of RKObjectMappingOperationDataSource.
*/
@property (nonatomic, strong) id<RKMappingOperationDataSource> mappingOperationDataSource;
/**
A key path to apply to the source object to specify the location of the root
of the data under test. Useful when testing subsets of a larger payload or
@@ -171,10 +180,10 @@
The destionation object being mapped to.
If nil, the mapping test will instantiate a destination object to perform the mapping
by invoking `[self.mapping mappableObjectForData:self.sourceObject]` and set the
new object as the value for the destinationObject property.
by invoking `[self.mappingOperationDataSource objectForMappableContent:self.sourceObject mapping:self.mapping]`
to obtain a new object from the data source and then assign the object as the value for the destinationObject property.
@see [RKObjectMapping mappableObjectForData:]
@see RKMappingOperationDataSource
*/
@property (nonatomic, strong, readonly) id destinationObject;

View File

@@ -19,6 +19,8 @@
//
#import "RKMappingTest.h"
#import "RKEntityMapping.h"
#import "RKObjectMappingOperationDataSource.h"
BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
@@ -44,8 +46,8 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
@implementation RKMappingTestEvent
@synthesize value;
@synthesize mapping;
@synthesize value = _value;
@synthesize mapping = _mapping;
+ (RKMappingTestEvent *)eventWithMapping:(RKAttributeMapping *)mapping value:(id)value
{
@@ -101,6 +103,7 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
@synthesize events = _events;
@synthesize verifiesOnExpect = _verifiesOnExpect;
@synthesize performedMapping = _performedMapping;
@synthesize mappingOperationDataSource = _mappingOperationDataSource;
+ (RKMappingTest *)testForMapping:(RKObjectMapping *)mapping object:(id)sourceObject
{
@@ -119,13 +122,14 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
self = [super init];
if (self) {
_sourceObject = sourceObject;
_destinationObject = destinationObject;
_mapping = mapping;
_expectations = [NSMutableArray new];
_events = [NSMutableArray new];
_verifiesOnExpect = NO;
_performedMapping = NO;
self.sourceObject = sourceObject;
self.destinationObject = destinationObject;
self.mapping = mapping;
self.expectations = [NSMutableArray new];
self.events = [NSMutableArray new];
self.verifiesOnExpect = NO;
self.performedMapping = NO;
self.mappingOperationDataSource = [RKObjectMappingOperationDataSource new];
}
return self;
@@ -224,9 +228,10 @@ BOOL RKObjectIsValueEqualToValue(id sourceValue, id destinationValue);
if (! self.hasPerformedMapping) {
id sourceObject = self.rootKeyPath ? [self.sourceObject valueForKeyPath:self.rootKeyPath] : self.sourceObject;
if (nil == self.destinationObject) {
self.destinationObject = [self.mapping mappableObjectForData:self.sourceObject];
self.destinationObject = [self.mappingOperationDataSource objectForMappableContent:self.sourceObject mapping:self.mapping];
}
RKMappingOperation *mappingOperation = [RKMappingOperation mappingOperationFromObject:sourceObject toObject:self.destinationObject withMapping:self.mapping];
mappingOperation.dataSource = self.mappingOperationDataSource;
NSError *error = nil;
mappingOperation.delegate = self;
BOOL success = [mappingOperation performMapping:&error];

View File

@@ -461,6 +461,31 @@ static NSString *lastUpdatedDateDictionaryKey = @"lastUpdatedDateDictionaryKey";
#pragma mark - UITableViewDataSource methods
- (UITableViewCell *)cellFromCellMapping:(RKTableViewCellMapping *)cellMapping
{
RKLogTrace(@"About to dequeue reusable cell using self.reuseIdentifier=%@", cellMapping.reuseIdentifier);
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellMapping.reuseIdentifier];
if (cell) {
RKLogTrace(@"Dequeued existing cell object for reuse identifier '%@': %@", cellMapping.reuseIdentifier, cell);
} else {
cell = [[[cellMapping.objectClass alloc] initWithStyle:cellMapping.style
reuseIdentifier:cellMapping.reuseIdentifier] autorelease];
RKLogTrace(@"Failed to dequeue existing cell object for reuse identifier '%@', instantiated new cell: %@", cellMapping.reuseIdentifier, cell);
}
if (cellMapping.managesCellAttributes) {
cell.accessoryType = cellMapping.accessoryType;
cell.selectionStyle = cellMapping.selectionStyle;
}
// Fire the prepare callbacks
for (void (^block)(UITableViewCell *) in cellMapping.prepareCellBlocks) {
block(cell);
}
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSAssert(theTableView == self.tableView, @"tableView:cellForRowAtIndexPath: invoked with inappropriate tableView: %@", theTableView);
@@ -471,7 +496,7 @@ static NSString *lastUpdatedDateDictionaryKey = @"lastUpdatedDateDictionaryKey";
RKTableViewCellMapping* cellMapping = [self.cellMappings cellMappingForObject:mappableObject];
NSAssert(cellMapping, @"Cannot build a tableView cell for object %@: No cell mapping defined for objects of type '%@'", mappableObject, NSStringFromClass([mappableObject class]));
UITableViewCell *cell = [cellMapping mappableObjectForData:self.tableView];
UITableViewCell *cell = [self cellFromCellMapping:cellMapping];
NSAssert(cell, @"Cell mapping failed to dequeue or allocate a tableViewCell for object: %@", mappableObject);
// Map the object state into the cell
@@ -1390,17 +1415,17 @@ static NSString *lastUpdatedDateDictionaryKey = @"lastUpdatedDateDictionaryKey";
return YES;
}
- (void)loadTableWithObjectLoader:(RKObjectLoader *)theObjectLoader
- (void)loadTableWithObjectLoader:(RKObjectLoader *)objectLoader
{
NSAssert(theObjectLoader, @"Cannot perform a network load without an object loader");
if (! [self.objectLoader isEqual:theObjectLoader]) {
NSAssert(objectLoader, @"Cannot perform a network load without an object loader");
if (! [self.objectLoader isEqual:objectLoader]) {
if (self.objectLoader) {
RKLogDebug(@"Cancelling in progress table load: asked to load with a new object loader.");
[self.objectLoader.queue cancelRequest:self.objectLoader];
}
theObjectLoader.delegate = self;
self.objectLoader = theObjectLoader;
objectLoader.delegate = self;
self.objectLoader = objectLoader;
}
if ([self.delegate respondsToSelector:@selector(tableController:willLoadTableWithObjectLoader:)]) {
[self.delegate tableController:self willLoadTableWithObjectLoader:self.objectLoader];

View File

@@ -18,6 +18,7 @@
// limitations under the License.
//
#import <CoreData/CoreData.h>
#import "RKAbstractTableController.h"
typedef UIView *(^RKFetchedResultsTableViewViewForHeaderInSectionBlock)(NSUInteger sectionIndex, NSString *sectionTitle);
@@ -36,27 +37,31 @@ typedef UIView *(^RKFetchedResultsTableViewViewForHeaderInSectionBlock)(NSUInteg
/**
Instances of RKFetchedResultsTableController provide an interface for driving a UITableView
*/
@interface RKFetchedResultsTableController : RKAbstractTableController <NSFetchedResultsControllerDelegate> {
@private
NSArray *_arraySortedFetchedObjects;
BOOL _isEmptyBeforeAnimation;
}
@interface RKFetchedResultsTableController : RKAbstractTableController <NSFetchedResultsControllerDelegate>
// Delegate
@property (nonatomic, assign) id<RKFetchedResultsTableControllerDelegate> delegate;
@property (nonatomic, retain, readonly) NSFetchedResultsController *fetchedResultsController;
// Fetched Results Controller
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, copy) NSString *resourcePath;
@property (nonatomic, retain) NSFetchRequest *fetchRequest;
@property (nonatomic, assign) CGFloat heightForHeaderInSection;
@property (nonatomic, copy) RKFetchedResultsTableViewViewForHeaderInSectionBlock onViewForHeaderInSection;
@property (nonatomic, retain) NSPredicate *predicate;
@property (nonatomic, retain) NSArray *sortDescriptors;
@property (nonatomic, copy) NSString *sectionNameKeyPath;
@property (nonatomic, copy) NSString *cacheName;
@property (nonatomic, retain, readonly) NSFetchedResultsController *fetchedResultsController;
// Configuring Headers and Sections
@property (nonatomic, assign) CGFloat heightForHeaderInSection;
@property (nonatomic, copy) RKFetchedResultsTableViewViewForHeaderInSectionBlock onViewForHeaderInSection;
@property (nonatomic, assign) BOOL showsSectionIndexTitles;
// Sorting
@property (nonatomic, assign) SEL sortSelector;
@property (nonatomic, copy) NSComparator sortComparator;
- (void)setObjectMappingForClass:(Class)objectClass;
- (void)setObjectMappingForClass:(Class)objectClass; // TODO: Kill this API... mapping descriptors will cover use case.
- (void)loadTable;
- (void)loadTableFromNetwork;

View File

@@ -31,7 +31,10 @@
#define RKLogComponent lcl_cRestKitUI
@interface RKFetchedResultsTableController ()
@property (nonatomic, assign) BOOL isEmptyBeforeAnimation;
@property (nonatomic, retain, readwrite) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, retain) NSArray *arraySortedFetchedObjects;
- (BOOL)performFetch:(NSError **)error;
- (void)updateSortedArray;
@@ -52,6 +55,8 @@
@synthesize sortSelector = _sortSelector;
@synthesize sortComparator = _sortComparator;
@synthesize fetchRequest = _fetchRequest;
@synthesize arraySortedFetchedObjects = _arraySortedFetchedObjects;
@synthesize isEmptyBeforeAnimation = _isEmptyBeforeAnimation;
- (void)dealloc
{
@@ -81,9 +86,10 @@
- (BOOL)performFetch:(NSError **)error
{
// TODO: We could be doing a KVO on the predicate/sortDescriptors/sectionKeyPath and intelligently deleting the cache
[NSFetchedResultsController deleteCacheWithName:_fetchedResultsController.cacheName];
BOOL success = [_fetchedResultsController performFetch:error];
NSAssert(self.fetchedResultsController, @"Cannot perform a fetch: self.fetchedResultsController is nil.");
[NSFetchedResultsController deleteCacheWithName:self.fetchedResultsController.cacheName];
BOOL success = [self.fetchedResultsController performFetch:error];
if (!success) {
RKLogError(@"performFetch failed with error: %@", [*error localizedDescription]);
return NO;
@@ -109,17 +115,16 @@
- (void)updateSortedArray
{
[_arraySortedFetchedObjects release];
_arraySortedFetchedObjects = nil;
self.arraySortedFetchedObjects = nil;
if (_sortSelector || _sortComparator) {
if (_sortSelector) {
_arraySortedFetchedObjects = [[_fetchedResultsController.fetchedObjects sortedArrayUsingSelector:_sortSelector] retain];
} else if (_sortComparator) {
_arraySortedFetchedObjects = [[_fetchedResultsController.fetchedObjects sortedArrayUsingComparator:_sortComparator] retain];
if (self.sortSelector || self.sortComparator) {
if (self.sortSelector) {
self.arraySortedFetchedObjects = [self.fetchedResultsController.fetchedObjects sortedArrayUsingSelector:self.sortSelector];
} else if (self.sortComparator) {
self.arraySortedFetchedObjects = [self.fetchedResultsController.fetchedObjects sortedArrayUsingComparator:self.sortComparator];
}
NSAssert(_arraySortedFetchedObjects.count == _fetchedResultsController.fetchedObjects.count,
NSAssert(self.arraySortedFetchedObjects.count == self.fetchedResultsController.fetchedObjects.count,
@"sortSelector or sortComparator sort resulted in fewer objects than expected");
}
}
@@ -159,7 +164,7 @@
- (BOOL)isFooterRow:(NSUInteger)row
{
NSUInteger sectionIndex = ([self sectionCount] - 1);
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:sectionIndex];
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:sectionIndex];
NSUInteger nonFooterRowCount = [sectionInfo numberOfObjects];
if (sectionIndex == 0) {
nonFooterRowCount += (![self isEmpty] || self.showsHeaderRowsWhenEmpty) ? [self.headerItems count] : 0;
@@ -249,30 +254,30 @@
- (NSFetchRequest *)fetchRequest
{
return _fetchRequest ? _fetchRequest : _fetchedResultsController.fetchRequest;
return _fetchRequest ? _fetchRequest : self.fetchedResultsController.fetchRequest;
}
- (void)loadTable
{
NSFetchRequest *fetchRequest = nil;
if (_resourcePath) {
if (self.resourcePath) {
fetchRequest = [self.objectManager.mappingProvider fetchRequestForResourcePath:self.resourcePath];
} else {
fetchRequest = _fetchRequest;
fetchRequest = self.fetchRequest;
}
NSAssert(fetchRequest != nil, @"Attempted to load RKFetchedResultsTableController with nil fetchRequest for resourcePath %@, fetchRequest %@", _resourcePath, _fetchRequest);
if (_predicate) {
[fetchRequest setPredicate:_predicate];
if (self.predicate) {
[fetchRequest setPredicate:self.predicate];
}
if (_sortDescriptors) {
[fetchRequest setSortDescriptors:_sortDescriptors];
if (self.sortDescriptors) {
[fetchRequest setSortDescriptors:self.sortDescriptors];
}
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[NSManagedObjectContext contextForCurrentThread]
sectionNameKeyPath:_sectionNameKeyPath
cacheName:_cacheName];
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:self.sectionNameKeyPath
cacheName:self.cacheName];
[self.fetchedResultsController release];
self.fetchedResultsController.delegate = self;
@@ -299,17 +304,17 @@
- (void)setSortSelector:(SEL)sortSelector
{
NSAssert(_sectionNameKeyPath == nil, @"Attempted to sort fetchedObjects across multiple sections");
NSAssert(_sortComparator == nil, @"Attempted to sort fetchedObjects with a sortSelector when a sortComparator already exists");
_sortSelector = sortSelector;
NSAssert(self.sectionNameKeyPath == nil, @"Attempted to sort fetchedObjects across multiple sections");
NSAssert(self.sortComparator == nil, @"Attempted to sort fetchedObjects with a sortSelector when a sortComparator already exists");
self.sortSelector = sortSelector;
}
- (void)setSortComparator:(NSComparator)sortComparator
{
NSAssert(_sectionNameKeyPath == nil, @"Attempted to sort fetchedObjects across multiple sections");
NSAssert(_sortSelector == nil, @"Attempted to sort fetchedObjects with a sortComparator when a sortSelector already exists");
if (_sortComparator) {
Block_release(_sortComparator);
NSAssert(self.sectionNameKeyPath == nil, @"Attempted to sort fetchedObjects across multiple sections");
NSAssert(self.sortSelector == nil, @"Attempted to sort fetchedObjects with a sortComparator when a sortSelector already exists");
if (self.sortComparator) {
Block_release(self.sortComparator);
_sortComparator = nil;
}
_sortComparator = Block_copy(sortComparator);
@@ -317,8 +322,8 @@
- (void)setSectionNameKeyPath:(NSString *)sectionNameKeyPath
{
NSAssert(_sortSelector == nil, @"Attempted to create a sectioned fetchedResultsController when a sortSelector is present");
NSAssert(_sortComparator == nil, @"Attempted to create a sectioned fetchedResultsController when a sortComparator is present");
NSAssert(self.sortSelector == nil, @"Attempted to create a sectioned fetchedResultsController when a sortSelector is present");
NSAssert(self.sortComparator == nil, @"Attempted to create a sectioned fetchedResultsController when a sortComparator is present");
[sectionNameKeyPath retain];
[_sectionNameKeyPath release];
_sectionNameKeyPath = sectionNameKeyPath;
@@ -344,12 +349,12 @@
- (NSUInteger)sectionCount
{
return [[_fetchedResultsController sections] count];
return [[self.fetchedResultsController sections] count];
}
- (NSUInteger)rowCount
{
NSUInteger fetchedItemCount = [[_fetchedResultsController fetchedObjects] count];
NSUInteger fetchedItemCount = [[self.fetchedResultsController fetchedObjects] count];
NSUInteger nonFetchedItemCount = 0;
if (fetchedItemCount == 0) {
nonFetchedItemCount += self.emptyItem ? 1 : 0;
@@ -365,7 +370,7 @@
- (NSIndexPath *)indexPathForObject:(id)object
{
if ([object isKindOfClass:[NSManagedObject class]]) {
return [self indexPathForFetchedResultsIndexPath:[_fetchedResultsController indexPathForObject:object]];
return [self indexPathForFetchedResultsIndexPath:[self.fetchedResultsController indexPathForObject:object]];
} else if ([object isKindOfClass:[RKTableItem class]]) {
if ([object isEqual:self.emptyItem]) {
return ([self isEmpty]) ? [self emptyItemIndexPath] : nil;
@@ -376,7 +381,7 @@
return [NSIndexPath indexPathForRow:row inSection:[self headerSectionIndex]];
} else if ([self.footerItems containsObject:object]) {
NSUInteger footerSectionIndex = [self sectionCount] - 1;
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:footerSectionIndex];
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:footerSectionIndex];
NSUInteger numberOfFetchedResults = sectionInfo.numberOfObjects;
NSUInteger objectIndex = [self.footerItems indexOfObject:object];
NSUInteger row = numberOfFetchedResults + objectIndex;
@@ -405,15 +410,15 @@
- (NSInteger)numberOfSectionsInTableView:(UITableView *)theTableView
{
NSAssert(theTableView == self.tableView, @"numberOfSectionsInTableView: invoked with inappropriate tableView: %@", theTableView);
RKLogTrace(@"numberOfSectionsInTableView: %d (%@)", [[_fetchedResultsController sections] count], [[_fetchedResultsController sections] valueForKey:@"name"]);
return [[_fetchedResultsController sections] count];
RKLogTrace(@"numberOfSectionsInTableView: %d (%@)", [[self.fetchedResultsController sections] count], [[self.fetchedResultsController sections] valueForKey:@"name"]);
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section
{
NSAssert(theTableView == self.tableView, @"tableView:numberOfRowsInSection: invoked with inappropriate tableView: %@", theTableView);
RKLogTrace(@"%@ numberOfRowsInSection:%d = %d", self, section, self.sectionCount);
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSUInteger numberOfRows = [sectionInfo numberOfObjects];
if ([self isHeaderSection:section]) {
@@ -450,7 +455,7 @@
- (NSInteger)tableView:(UITableView *)theTableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
if (theTableView.style == UITableViewStylePlain && self.showsSectionIndexTitles) {
return [_fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
return 0;
}
@@ -470,13 +475,16 @@
[[RKObjectManager sharedManager] deleteObject:managedObject delegate:self];
} else {
RKLogTrace(@"About to locally delete managedObject: %@", managedObject);
[managedObject.managedObjectContext deleteObject:managedObject];
NSError *error = nil;
[managedObject.managedObjectContext save:&error];
if (error) {
RKLogError(@"Failed to save managedObjectContext after a delete with error: %@", error);
}
NSManagedObjectContext *managedObjectContext = managedObject.managedObjectContext;
[managedObjectContext performBlock:^{
[managedObjectContext deleteObject:managedObject];
NSError *error = nil;
[managedObjectContext save:&error];
if (error) {
RKLogError(@"Failed to save managedObjectContext after a delete with error: %@", error);
}
}];
}
}
}
@@ -504,7 +512,7 @@
- (CGFloat)tableView:(UITableView *)theTableView heightForHeaderInSection:(NSInteger)section
{
NSAssert(theTableView == self.tableView, @"heightForHeaderInSection: invoked with inappropriate tableView: %@", theTableView);
return _heightForHeaderInSection;
return self.heightForHeaderInSection;
}
- (CGFloat)tableView:(UITableView *)theTableView heightForFooterInSection:(NSInteger)sectionIndex
@@ -516,10 +524,10 @@
- (UIView *)tableView:(UITableView *)theTableView viewForHeaderInSection:(NSInteger)section
{
NSAssert(theTableView == self.tableView, @"viewForHeaderInSection: invoked with inappropriate tableView: %@", theTableView);
if (_onViewForHeaderInSection) {
if (self.onViewForHeaderInSection) {
NSString *sectionTitle = [self tableView:self.tableView titleForHeaderInSection:section];
if (sectionTitle) {
return _onViewForHeaderInSection(section, sectionTitle);
return self.onViewForHeaderInSection(section, sectionTitle);
}
}
return nil;
@@ -541,7 +549,7 @@
NSUInteger row = ([self isEmpty] && self.emptyItem) ? (indexPath.row - 1) : indexPath.row;
return [self.headerItems objectAtIndex:row];
} else if ([self isFooterIndexPath:indexPath]) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:indexPath.section];
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:indexPath.section];
NSUInteger footerRow = (indexPath.row - sectionInfo.numberOfObjects);
if (indexPath.section == 0) {
footerRow -= (![self isEmpty] || self.showsHeaderRowsWhenEmpty) ? [self.headerItems count] : 0;
@@ -549,14 +557,14 @@
}
return [self.footerItems objectAtIndex:footerRow];
} else if (_sortSelector || _sortComparator) {
return [_arraySortedFetchedObjects objectAtIndex:[self fetchedResultsIndexPathForIndexPath:indexPath].row];
} else if (self.sortSelector || self.sortComparator) {
return [self.arraySortedFetchedObjects objectAtIndex:[self fetchedResultsIndexPathForIndexPath:indexPath].row];
}
NSIndexPath *fetchedResultsIndexPath = [self fetchedResultsIndexPathForIndexPath:indexPath];
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:fetchedResultsIndexPath.section];
if (fetchedResultsIndexPath.row < [sectionInfo numberOfObjects]) {
return [_fetchedResultsController objectAtIndexPath:fetchedResultsIndexPath];
return [self.fetchedResultsController objectAtIndexPath:fetchedResultsIndexPath];
} else {
return nil;
}
@@ -588,10 +596,10 @@
{
RKLogTrace(@"Beginning updates for fetchedResultsController (%@). Current section count = %d (resource path: %@)", controller, [[controller sections] count], _resourcePath);
if (_sortSelector) return;
if (self.sortSelector) return;
[self.tableView beginUpdates];
_isEmptyBeforeAnimation = [self isEmpty];
self.isEmptyBeforeAnimation = [self isEmpty];
}
- (void)controller:(NSFetchedResultsController *)controller
@@ -682,7 +690,7 @@
[self updateSortedArray];
if (_sortSelector) {
if (self.sortSelector) {
[self.tableView reloadData];
} else {
[self.tableView endUpdates];

View File

@@ -24,6 +24,7 @@
#import "RKFormSection.h"
#import "NSArray+RKAdditions.h"
#import "RKMappingOperation.h"
#import "RKMappingOperationDataSource.h"
// Define logging component
#undef RKLogComponent

View File

@@ -222,6 +222,7 @@
The block will be invoked each time a cell is either initialized or dequeued for reuse.
*/
- (void)addPrepareCellBlock:(void (^)(UITableViewCell *cell))block;
@property (nonatomic, readonly) NSArray *prepareCellBlocks;
/** @name Configuring Control Actions */
// TODO: Docs!!!

View File

@@ -83,7 +83,7 @@ typedef void(^RKControlBlockActionBlock)(id sender);
@end
@interface RKTableViewCellMapping ()
@property (nonatomic, retain) NSMutableArray *prepareCellBlocks;
@property (nonatomic, retain) NSMutableArray *mutablePrepareCellBlocks;
@end
@implementation RKTableViewCellMapping
@@ -103,7 +103,7 @@ typedef void(^RKControlBlockActionBlock)(id sender);
@synthesize rowHeight = _rowHeight;
@synthesize deselectsRowOnSelection = _deselectsRowOnSelection;
@synthesize managesCellAttributes = _managesCellAttributes;
@synthesize prepareCellBlocks = _prepareCellBlocks;
@synthesize mutablePrepareCellBlocks = _mutablePrepareCellBlocks;
+ (id)cellMapping
{
@@ -142,7 +142,7 @@ typedef void(^RKControlBlockActionBlock)(id sender);
_selectionStyle = UITableViewCellSelectionStyleBlue;
self.rowHeight = 44;
self.deselectsRowOnSelection = YES;
_prepareCellBlocks = [NSMutableArray new];
self.mutablePrepareCellBlocks = [NSMutableArray array];
}
return self;
@@ -158,7 +158,7 @@ typedef void(^RKControlBlockActionBlock)(id sender);
- (void)dealloc
{
[_reuseIdentifier release];
[_prepareCellBlocks release];
[_mutablePrepareCellBlocks release];
Block_release(_onSelectCell);
Block_release(_onSelectCellForObjectAtIndexPath);
Block_release(_onCellWillAppearForObjectAtIndexPath);
@@ -170,11 +170,6 @@ typedef void(^RKControlBlockActionBlock)(id sender);
[super dealloc];
}
- (NSMutableArray *)prepareCellBlocks
{
return _prepareCellBlocks;
}
- (id)copyWithZone:(NSZone *)zone
{
RKTableViewCellMapping *copy = [super copyWithZone:zone];
@@ -192,8 +187,8 @@ typedef void(^RKControlBlockActionBlock)(id sender);
copy.targetIndexPathForMove = self.targetIndexPathForMove;
copy.rowHeight = self.rowHeight;
@synchronized(_prepareCellBlocks) {
for (void (^block)(UITableViewCell *) in _prepareCellBlocks) {
@synchronized(_mutablePrepareCellBlocks) {
for (void (^block)(UITableViewCell *) in _mutablePrepareCellBlocks) {
void (^blockCopy)(UITableViewCell *cell) = [block copy];
[copy addPrepareCellBlock:blockCopy];
[blockCopy release];
@@ -203,33 +198,6 @@ typedef void(^RKControlBlockActionBlock)(id sender);
return copy;
}
- (id)mappableObjectForData:(UITableView *)tableView
{
NSAssert([tableView isKindOfClass:[UITableView class]], @"Expected to be invoked with a tableView as the data. Got %@", tableView);
RKLogTrace(@"About to dequeue reusable cell using self.reuseIdentifier=%@", self.reuseIdentifier);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.reuseIdentifier];
if (cell) {
RKLogTrace(@"Dequeued existing cell object for reuse identifier '%@': %@", self.reuseIdentifier, cell);
} else {
cell = [[[self.objectClass alloc] initWithStyle:self.style
reuseIdentifier:self.reuseIdentifier] autorelease];
RKLogTrace(@"Failed to dequeue existing cell object for reuse identifier '%@', instantiated new cell: %@", self.reuseIdentifier, cell);
}
if (self.managesCellAttributes) {
cell.accessoryType = self.accessoryType;
cell.selectionStyle = self.selectionStyle;
}
// Fire the prepare callbacks
for (void (^block)(UITableViewCell *) in _prepareCellBlocks) {
block(cell);
}
return cell;
}
- (void)setSelectionStyle:(UITableViewCellSelectionStyle)selectionStyle
{
self.managesCellAttributes = YES;
@@ -278,10 +246,15 @@ typedef void(^RKControlBlockActionBlock)(id sender);
- (void)addPrepareCellBlock:(void (^)(UITableViewCell *cell))block
{
void (^blockCopy)(UITableViewCell *cell) = [block copy];
[_prepareCellBlocks addObject:blockCopy];
[self.mutablePrepareCellBlocks addObject:blockCopy];
[blockCopy release];
}
- (NSArray *)prepareCellBlocks
{
return [NSArray arrayWithArray:self.mutablePrepareCellBlocks];
}
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents toControlAtKeyPath:(NSString *)keyPath
{
[self addPrepareCellBlock:^(UITableViewCell *cell) {

View File

@@ -9,7 +9,7 @@
#import "RKTestEnvironment.h"
#import "RKFetchedResultsTableController.h"
#import "RKManagedObjectStore.h"
#import "RKManagedObjectMapping.h"
#import "RKEntityMapping.h"
#import "RKHuman.h"
#import "RKEvent.h"
#import "RKAbstractTableController_Internals.h"
@@ -39,6 +39,7 @@
@end
@interface RKFetchedResultsTableControllerTest : RKTestCase
@property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
@end
@implementation RKFetchedResultsTableControllerTest
@@ -57,8 +58,8 @@
- (void)bootstrapStoreAndCache
{
RKManagedObjectStore *store = [RKTestFactory managedObjectStore];
RKManagedObjectMapping *humanMapping = [RKManagedObjectMapping mappingForEntityWithName:@"RKHuman" inManagedObjectStore:store];
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityWithName:@"RKHuman" inManagedObjectContext:managedObjectStore.mainQueueManagedObjectContext];
[humanMapping mapKeyPath:@"id" toAttribute:@"railsID"];
[humanMapping mapAttributes:@"name", nil];
humanMapping.primaryKeyAttribute = @"railsID";
@@ -72,13 +73,13 @@
other.railsID = [NSNumber numberWithInt:5678];
other.name = @"other";
NSError *error = nil;
[store save:&error];
[managedObjectStore save:&error];
assertThat(error, is(nilValue()));
assertThatInt([RKHuman count:nil], is(equalToInt(2)));
RKObjectManager *objectManager = [RKTestFactory objectManager];
[objectManager.mappingProvider setMapping:humanMapping forKeyPath:@"human"];
objectManager.managedObjectStore = store;
objectManager.managedObjectStore = managedObjectStore;
[objectManager.mappingProvider setObjectMapping:humanMapping forResourcePathPattern:@"/JSON/humans/all\\.json" withFetchRequestBlock:^NSFetchRequest *(NSString *resourcePath) {
return [RKHuman requestAllSortedBy:@"name" ascending:YES];
@@ -89,10 +90,15 @@
}];
}
- (NSManagedObjectContext *)managedObjectContext
{
return [[RKObjectManager sharedManager].managedObjectStore mainQueueManagedObjectContext];
}
- (void)bootstrapNakedObjectStoreAndCache
{
RKManagedObjectStore *store = [RKTestFactory managedObjectStore];
RKManagedObjectMapping *eventMapping = [RKManagedObjectMapping mappingForClass:[RKEvent class] inManagedObjectStore:store];
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
RKEntityMapping *eventMapping = [RKEntityMapping mappingForEntityWithName:@"RKEvent" inManagedObjectContext:managedObjectStore.mainQueueManagedObjectContext];
[eventMapping mapKeyPath:@"event_id" toAttribute:@"eventID"];
[eventMapping mapKeyPath:@"type" toAttribute:@"eventType"];
[eventMapping mapAttributes:@"location", @"summary", nil];
@@ -106,13 +112,13 @@
nakedEvent.location = @"Performance Hall";
nakedEvent.summary = @"Shindig";
NSError *error = nil;
[store save:&error];
[managedObjectStore save:&error];
assertThat(error, is(nilValue()));
assertThatInt([RKEvent count:nil], is(equalToInt(1)));
RKObjectManager *objectManager = [RKTestFactory objectManager];
[objectManager.mappingProvider addObjectMapping:eventMapping];
objectManager.managedObjectStore = store;
objectManager.managedObjectStore = managedObjectStore;
id mockMappingProvider = [OCMockObject partialMockForObject:objectManager.mappingProvider];
[[[mockMappingProvider stub] andReturn:[RKEvent requestAllSortedBy:@"eventType" ascending:YES]] fetchRequestForResourcePath:@"/JSON/NakedEvents.json"];
@@ -120,8 +126,8 @@
- (void)bootstrapEmptyStoreAndCache
{
RKManagedObjectStore *store = [RKTestFactory managedObjectStore];
RKManagedObjectMapping *humanMapping = [RKManagedObjectMapping mappingForEntityWithName:@"RKHuman" inManagedObjectStore:store];
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
RKEntityMapping *humanMapping = [RKEntityMapping mappingForEntityWithName:@"RKHuman" inManagedObjectContext:managedObjectStore.mainQueueManagedObjectContext];
[humanMapping mapKeyPath:@"id" toAttribute:@"railsID"];
[humanMapping mapAttributes:@"name", nil];
humanMapping.primaryKeyAttribute = @"railsID";
@@ -131,7 +137,7 @@
RKObjectManager *objectManager = [RKTestFactory objectManager];
[objectManager.mappingProvider setMapping:humanMapping forKeyPath:@"human"];
objectManager.managedObjectStore = store;
objectManager.managedObjectStore = managedObjectStore;
id mockMappingProvider = [OCMockObject partialMockForObject:objectManager.mappingProvider];
[[[mockMappingProvider stub] andReturn:[RKHuman requestAllSortedBy:@"name" ascending:YES]] fetchRequestForResourcePath:@"/JSON/humans/all.json"];
@@ -154,6 +160,7 @@
[self bootstrapStoreAndCache];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
@@ -168,6 +175,7 @@
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.resourcePath = @"/JSON/NakedEvents.json";
tableController.managedObjectContext = self.managedObjectContext;
[tableController setObjectMappingForClass:[RKEvent class]];
[tableController loadTable];
@@ -194,6 +202,7 @@
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name"
ascending:YES]];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.predicate = predicate;
tableController.sortDescriptors = sortDescriptors;
@@ -211,6 +220,7 @@
[self bootstrapStoreAndCache];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
tableController.cacheName = @"allHumansCache";
@@ -231,6 +241,7 @@
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name"
ascending:YES]];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.predicate = predicate;
tableController.sortDescriptors = sortDescriptors;
@@ -252,9 +263,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
@@ -268,9 +279,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
[tableController loadTable];
@@ -282,9 +293,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
assertThatInt([tableController rowCount], is(equalToInt(2)));
@@ -298,6 +309,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -317,6 +329,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Empty";
@@ -336,6 +349,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Empty";
@@ -355,6 +369,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -378,6 +393,7 @@
[self bootstrapEmptyStoreAndCache];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -401,6 +417,7 @@
[self bootstrapStoreAndCache];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -442,9 +459,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
[tableController loadTable];
@@ -457,9 +474,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
@@ -471,9 +488,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
[tableController loadTable];
@@ -486,9 +503,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
@@ -509,9 +526,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
@@ -532,6 +549,7 @@
method:RKRequestMethodDELETE]];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController = [RKFetchedResultsTableController tableControllerForTableViewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.canEditRows = YES;
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
@@ -569,9 +587,9 @@
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.canEditRows = YES;
[tableController loadTable];
@@ -607,9 +625,9 @@
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
@@ -640,6 +658,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.canEditRows = YES;
[tableController loadTable];
@@ -666,9 +685,9 @@
[self bootstrapStoreAndCache];
UITableView *tableView = [UITableView new];
RKFetchedResultsTableControllerSpecViewController *viewController = [RKFetchedResultsTableControllerSpecViewController new];
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
RKFetchedResultsTableController *tableController = [[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.canMoveRows = YES;
[tableController loadTable];
@@ -700,6 +719,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
assertThatBool([tableController isHeaderSection:0], is(equalToBool(YES)));
@@ -715,6 +735,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -736,6 +757,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Footer";
@@ -757,6 +779,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
[tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
@@ -779,6 +802,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Footer";
@@ -800,6 +824,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
assertThatBool([tableController isEmptySection:0], is(equalToBool(YES)));
@@ -815,6 +840,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController loadTable];
assertThatBool([tableController isEmptyRow:0], is(equalToBool(YES)));
@@ -830,6 +856,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -854,6 +881,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Footer";
@@ -878,6 +906,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Empty";
@@ -902,6 +931,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -924,6 +954,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Footer";
@@ -945,6 +976,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
[tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
@@ -968,6 +1000,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController setEmptyItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Empty";
@@ -990,6 +1023,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1018,6 +1052,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
@@ -1048,6 +1083,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1083,6 +1119,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.sectionNameKeyPath = @"name";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
@@ -1118,6 +1155,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1145,6 +1183,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
RKTableItem *headerRow = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1175,6 +1214,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
RKTableItem *footerRow = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Footer";
@@ -1205,6 +1245,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1229,6 +1270,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addFooterRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Footer";
@@ -1253,6 +1295,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1289,6 +1332,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
RKTableItem *headerRow = [RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
@@ -1337,6 +1381,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1373,6 +1418,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
[tableController addHeaderRowForItem:[RKTableItem tableItemUsingBlock:^(RKTableItem *tableItem) {
tableItem.text = @"Header";
@@ -1415,6 +1461,7 @@
UIImage *image = [RKTestFixture imageWithContentsOfFixture:@"blake.png"];
tableController.imageForEmpty = image;
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/empty/array";
tableController.autoRefreshFromNetwork = YES;
[tableController.cache invalidateAll];
@@ -1436,6 +1483,7 @@
RKFetchedResultsTableController *tableController =
[[RKFetchedResultsTableController alloc] initWithTableView:tableView
viewController:viewController];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/NakedEvents.json";
[tableController setObjectMappingForClass:[RKEvent class]];
@@ -1457,6 +1505,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.cacheName = @"allHumansCache";
@@ -1480,6 +1529,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.cacheName = @"allHumansCache";
@@ -1500,6 +1550,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.cacheName = @"allHumansCache";
@@ -1520,6 +1571,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
tableController.cacheName = @"allHumansCache";
@@ -1542,6 +1594,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"];
@@ -1580,6 +1633,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"];
@@ -1617,6 +1671,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
RKTableItem *headerItem = [RKTableItem tableItemWithText:@"Header"];
@@ -1654,6 +1709,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/all.json";
RKTableItem *emptyItem = [RKTableItem tableItemWithText:@"Empty!"];
@@ -1696,6 +1752,7 @@
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
[cellMapping mapKeyPath:@"name" toAttribute:@"textLabel.text"];
[tableController mapObjectsWithClass:[RKHuman class] toTableCellsWithMapping:cellMapping];
tableController.managedObjectContext = self.managedObjectContext;
tableController.resourcePath = @"/JSON/humans/empty.json";
tableController.showsFooterRowsWhenEmpty = NO;
tableController.showsHeaderRowsWhenEmpty = NO;

View File

@@ -177,9 +177,9 @@
RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:objectStore.primaryManagedObjectContext
cache:managedObjectCache];
mappingOperationDataSource.operationQueue = [[NSOperationQueue new] autorelease];
RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
operation.dataSource = mappingOperationDataSource;
operation.queue = [[NSOperationQueue new] autorelease];
NSError *error = nil;
[operation performMapping:&error];
@@ -751,9 +751,9 @@
RKFetchRequestManagedObjectCache *managedObjectCache = [RKFetchRequestManagedObjectCache new];
RKManagedObjectMappingOperationDataSource *mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:objectStore.primaryManagedObjectContext
cache:managedObjectCache];
mappingOperationDataSource.operationQueue = [[NSOperationQueue new] autorelease];
RKMappingOperation* operation = [[RKMappingOperation alloc] initWithSourceObject:mappableData destinationObject:human mapping:humanMapping];
operation.dataSource = mappingOperationDataSource;
operation.queue = [[NSOperationQueue new] autorelease];
NSError* error = nil;
[operation performMapping:&error];

View File

@@ -111,9 +111,9 @@
assertThat(human.railsID, is(equalToInt(1)));
}
- (void)testShouldDeleteACoreDataBackedTargetObjectOnError
- (void)testShouldNotPersistTemporaryEntityToPersistentStoreOnError
{
RKHuman *temporaryHuman = [[RKHuman alloc] initWithEntity:[NSEntityDescription entityForName:@"RKHuman" inManagedObjectContext:_objectManager.managedObjectStore.primaryManagedObjectContext] insertIntoManagedObjectContext:_objectManager.managedObjectStore.primaryManagedObjectContext];
RKHuman *temporaryHuman = [NSEntityDescription insertNewObjectForEntityForName:@"RKHuman" inManagedObjectContext:_objectManager.managedObjectStore.primaryManagedObjectContext];
temporaryHuman.name = @"My Name";
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[mapping mapAttributes:@"name", nil];
@@ -128,7 +128,7 @@
[objectLoader send];
[loader waitForResponse];
assertThatBool([temporaryHuman hasBeenDeleted], is(equalToBool(YES)));
assertThatBool([temporaryHuman isNew], is(equalToBool(YES)));
}
- (void)testShouldNotDeleteACoreDataBackedTargetObjectOnErrorIfItWasAlreadySaved