mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-01-12 22:51:50 +08:00
Add support for deletion of mapped objects by predicate. closes #1109
This commit is contained in:
@@ -177,6 +177,17 @@
|
||||
*/
|
||||
- (RKConnectionDescription *)connectionForRelationship:(id)relationshipOrName;
|
||||
|
||||
///------------------------------------
|
||||
/// @name Flagging Objects for Deletion
|
||||
///------------------------------------
|
||||
|
||||
/**
|
||||
A predicate that identifies objects for the receiver's entity that are to be deleted from the local store.
|
||||
|
||||
This property provides support for local deletion of managed objects mapped as a 'tombstone' record from the source representation.
|
||||
*/
|
||||
@property (nonatomic, copy) NSPredicate *deletionPredicate;
|
||||
|
||||
///------------------------------------------
|
||||
/// @name Retrieving Default Attribute Values
|
||||
///------------------------------------------
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import "RKManagedObjectMappingOperationDataSource.h"
|
||||
#import "RKObjectMapping.h"
|
||||
#import "RKEntityMapping.h"
|
||||
@@ -34,6 +35,8 @@
|
||||
|
||||
extern NSString * const RKObjectMappingNestingAttributeKeyName;
|
||||
|
||||
static char kRKManagedObjectMappingOperationDataSourceAssociatedObjectKey;
|
||||
|
||||
id RKTransformedValueWithClass(id value, Class destinationType, NSValueTransformer *dateToStringValueTransformer);
|
||||
NSArray *RKApplyNestingAttributeValueToMappings(NSString *attributeName, id value, NSArray *propertyMappings);
|
||||
|
||||
@@ -117,6 +120,58 @@ static NSDictionary *RKEntityIdentificationAttributesForEntityMappingWithReprese
|
||||
return entityIdentifierAttributes;
|
||||
}
|
||||
|
||||
@interface RKManagedObjectDeletionOperation : NSOperation
|
||||
|
||||
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
|
||||
- (void)addEntityMapping:(RKEntityMapping *)entityMapping;
|
||||
@end
|
||||
|
||||
@interface RKManagedObjectDeletionOperation ()
|
||||
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
|
||||
@property (nonatomic, strong) NSMutableSet *entityMappings;
|
||||
@end
|
||||
|
||||
@implementation RKManagedObjectDeletionOperation
|
||||
|
||||
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
|
||||
{
|
||||
self = [self init];
|
||||
if (self) {
|
||||
self.managedObjectContext = managedObjectContext;
|
||||
self.entityMappings = [NSMutableSet new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addEntityMapping:(RKEntityMapping *)entityMapping
|
||||
{
|
||||
if (! entityMapping.deletionPredicate) return;
|
||||
[self.entityMappings addObject:entityMapping];
|
||||
}
|
||||
|
||||
- (void)main
|
||||
{
|
||||
[self.managedObjectContext performBlockAndWait:^{
|
||||
NSMutableSet *objectsToDelete = [NSMutableSet set];
|
||||
for (RKEntityMapping *entityMapping in self.entityMappings) {
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest alloc];
|
||||
[fetchRequest setEntity:entityMapping.entity];
|
||||
[fetchRequest setPredicate:entityMapping.deletionPredicate];
|
||||
NSError *error = nil;
|
||||
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (fetchedObjects) {
|
||||
[objectsToDelete addObjectsFromArray:fetchedObjects];
|
||||
}
|
||||
}
|
||||
|
||||
for (NSManagedObject *managedObject in objectsToDelete) {
|
||||
[self.managedObjectContext deleteObject:managedObject];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Set Logging Component
|
||||
#undef RKLogComponent
|
||||
#define RKLogComponent RKlcl_cRestKitCoreData
|
||||
@@ -126,6 +181,7 @@ extern NSString * const RKObjectMappingNestingAttributeKeyName;
|
||||
@interface RKManagedObjectMappingOperationDataSource ()
|
||||
@property (nonatomic, strong, readwrite) NSManagedObjectContext *managedObjectContext;
|
||||
@property (nonatomic, strong, readwrite) id<RKManagedObjectCaching> managedObjectCache;
|
||||
@property (nonatomic, strong) NSMutableArray *deletionPredicates;
|
||||
@end
|
||||
|
||||
@implementation RKManagedObjectMappingOperationDataSource
|
||||
@@ -169,7 +225,7 @@ extern NSString * const RKObjectMappingNestingAttributeKeyName;
|
||||
"Unable to update existing object instances by identification attributes. Duplicate objects may be created.");
|
||||
}
|
||||
|
||||
// If we have found the entity identifier attributes, try to find an existing instance to update
|
||||
// If we have found the entity identification attributes, try to find an existing instance to update
|
||||
NSEntityDescription *entity = [entityMapping entity];
|
||||
NSManagedObject *managedObject = nil;
|
||||
if ([entityIdentifierAttributes count]) {
|
||||
@@ -246,6 +302,27 @@ extern NSString * const RKObjectMappingNestingAttributeKeyName;
|
||||
[operationQueue addOperation:operation];
|
||||
RKLogTrace(@"Enqueued %@ dependent upon parent operation %@ to operation queue %@", operation, self.parentOperation, operationQueue);
|
||||
}
|
||||
|
||||
// Handle tombstone deletion by predicate
|
||||
if ([(RKEntityMapping *)mappingOperation.objectMapping deletionPredicate]) {
|
||||
RKManagedObjectDeletionOperation *deletionOperation = nil;
|
||||
if (self.parentOperation) {
|
||||
// Attach a deletion operation for execution after the parent operation completes
|
||||
deletionOperation = (RKManagedObjectDeletionOperation *)objc_getAssociatedObject(self.parentOperation, &kRKManagedObjectMappingOperationDataSourceAssociatedObjectKey);
|
||||
if (! deletionOperation) {
|
||||
deletionOperation = [[RKManagedObjectDeletionOperation alloc] initWithManagedObjectContext:self.managedObjectContext];
|
||||
objc_setAssociatedObject(self.parentOperation, &kRKManagedObjectMappingOperationDataSourceAssociatedObjectKey, deletionOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
[deletionOperation addDependency:self.parentOperation];
|
||||
NSOperationQueue *operationQueue = self.operationQueue ?: [NSOperationQueue currentQueue];
|
||||
[operationQueue addOperation:deletionOperation];
|
||||
}
|
||||
[deletionOperation addEntityMapping:(RKEntityMapping *)mappingOperation.objectMapping];
|
||||
} else {
|
||||
deletionOperation = [[RKManagedObjectDeletionOperation alloc] initWithManagedObjectContext:self.managedObjectContext];
|
||||
[deletionOperation addEntityMapping:(RKEntityMapping *)mappingOperation.objectMapping];
|
||||
[deletionOperation start];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
|
||||
@@ -1064,6 +1064,50 @@
|
||||
|
||||
}
|
||||
|
||||
- (void)testDeletionOfTombstoneRecords
|
||||
{
|
||||
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
|
||||
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
RKEntityMapping *mapping = [[RKEntityMapping alloc] initWithEntity:entity];
|
||||
[mapping addAttributeMappingsFromArray:@[ @"name" ]];
|
||||
mapping.deletionPredicate = [NSPredicate predicateWithFormat:@"sex = %@", @"female"];
|
||||
|
||||
RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
human.sex = @"female";
|
||||
|
||||
RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
|
||||
cache:nil];
|
||||
NSDictionary *representation = @{ @"name": @"Whatever" };
|
||||
RKMappingOperation *operation = [[RKMappingOperation alloc] initWithSourceObject:representation destinationObject:human mapping:mapping];
|
||||
operation.dataSource = dataSource;
|
||||
NSError *error = nil;
|
||||
BOOL success = [operation performMapping:&error];
|
||||
assertThatBool(success, is(equalToBool(YES)));
|
||||
expect([human isDeleted]).to.equal(YES);
|
||||
}
|
||||
|
||||
- (void)testDeletionOfTombstoneRecordsInMapperOperation
|
||||
{
|
||||
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
|
||||
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
RKEntityMapping *mapping = [[RKEntityMapping alloc] initWithEntity:entity];
|
||||
[mapping addAttributeMappingsFromArray:@[ @"name" ]];
|
||||
mapping.deletionPredicate = [NSPredicate predicateWithFormat:@"sex = %@", @"female"];
|
||||
|
||||
RKHuman *human = [NSEntityDescription insertNewObjectForEntityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
|
||||
human.sex = @"female";
|
||||
|
||||
RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext
|
||||
cache:nil];
|
||||
NSDictionary *representation = @{ @"name": @"Whatever" };
|
||||
NSError *error = nil;
|
||||
RKMapperOperation *mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:representation mappingsDictionary:@{ [NSNull null]: mapping }];
|
||||
mapperOperation.mappingOperationDataSource = dataSource;
|
||||
BOOL success = [mapperOperation execute:&error];
|
||||
assertThatBool(success, is(equalToBool(YES)));
|
||||
expect([human isDeleted]).to.equal(YES);
|
||||
}
|
||||
|
||||
// TODO: Import bencharmk utility somehow...
|
||||
//- (void)testMappingAPayloadContainingRepeatedObjectsPerformsAcceptablyWithFetchRequestMappingCache
|
||||
//{
|
||||
|
||||
Reference in New Issue
Block a user