From 4c401de6bf20457bb2734180a28fa1c39a7ea575 Mon Sep 17 00:00:00 2001 From: Blake Watters Date: Sat, 29 Sep 2012 17:49:13 -0400 Subject: [PATCH] Convert `RKConnectionMapping` into a subclass of `RKPropertyMapping`. Add delegate callback for tracking the connection of relationships. --- ...KManagedObjectMappingOperationDataSource.m | 9 +++ .../RKRelationshipConnectionOperation.h | 62 +++++++++++-------- .../RKRelationshipConnectionOperation.m | 29 +++++---- Code/ObjectMapping/RKConnectionMapping.h | 8 +-- Code/ObjectMapping/RKConnectionMapping.m | 25 -------- Code/ObjectMapping/RKMappingOperation.h | 17 ++++- 6 files changed, 82 insertions(+), 68 deletions(-) diff --git a/Code/CoreData/RKManagedObjectMappingOperationDataSource.m b/Code/CoreData/RKManagedObjectMappingOperationDataSource.m index 49c4a9c8..79b2449a 100644 --- a/Code/CoreData/RKManagedObjectMappingOperationDataSource.m +++ b/Code/CoreData/RKManagedObjectMappingOperationDataSource.m @@ -149,10 +149,19 @@ extern NSString * const RKObjectMappingNestingAttributeKeyName; RKRelationshipConnectionOperation *operation = [[RKRelationshipConnectionOperation alloc] initWithManagedObject:mappingOperation.destinationObject connectionMapping:connectionMapping managedObjectCache:self.managedObjectCache]; + // TODO: This should really be done using dependencies... if (self.operationQueue) { [self.operationQueue addOperation:operation]; + [operation setCompletionBlock:^{ + if ([mappingOperation.delegate respondsToSelector:@selector(mappingOperation:didConnectRelationship:usingMapping:)]) { + [mappingOperation.delegate mappingOperation:mappingOperation didConnectRelationship:connectionMapping.relationship usingMapping:connectionMapping]; + } + }]; } else { [operation start]; + if ([mappingOperation.delegate respondsToSelector:@selector(mappingOperation:didConnectRelationship:usingMapping:)]) { + [mappingOperation.delegate mappingOperation:mappingOperation didConnectRelationship:connectionMapping.relationship usingMapping:connectionMapping]; + } } } } diff --git a/Code/CoreData/RKRelationshipConnectionOperation.h b/Code/CoreData/RKRelationshipConnectionOperation.h index e0e4efa3..78601160 100644 --- a/Code/CoreData/RKRelationshipConnectionOperation.h +++ b/Code/CoreData/RKRelationshipConnectionOperation.h @@ -5,6 +5,18 @@ // Created by Blake Watters on 7/12/12. // Copyright (c) 2012 RestKit. All rights reserved. // +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// #import @@ -12,36 +24,17 @@ @protocol RKManagedObjectCaching; /** - The RKRelationshipConnectionOperation class is a subclass of NSOperation that manages the connection - of NSManagedObject relationships as described by an RKConnectionMapping object. When executed, the - operation will find related objects by searching the associated managed object cache for a matching object - whose destination attribute value matches that of the associated managed object's source attribute. + The `RKRelationshipConnectionOperation` class is a subclass of `NSOperation` that manages the connection of `NSManagedObject` relationships as described by an `RKConnectionMapping` object. When executed, the operation will find related objects by searching the associated managed object cache for a matching object whose destination attribute value matches that of the associated managed object's source attribute. - For example, given a managed object for the `Employee` entity with a one-to-one relationship to a `Company` named `company` - (with an inverse relationship one-to-many relationship named `employees`) and a connection mapping specifying that - the relationship can be connected by finding the `Company` managed object whose `companyID` attribute matches the - `companyID` of the `Employee`, the operation would find the Company that employs the Employee by primary key and set - the Core Data relationship to reflect the relationship appropriately. + For example, given a managed object for the `Employee` entity with a one-to-one relationship to a `Company` named `company` (with an inverse relationship one-to-many relationship named `employees`) and a connection mapping specifying that the relationship can be connected by finding the `Company` managed object whose `companyID` attribute matches the `companyID` of the `Employee`, the operation would find the Company that employs the Employee by primary key and set the Core Data relationship to reflect the relationship appropriately. - @see RKConnectionMapping + @see `RKConnectionMapping` */ @interface RKRelationshipConnectionOperation : NSOperation -/** - The managed object the receiver will attempt to connect a relationship for. - */ -@property (nonatomic, strong, readonly) NSManagedObject *managedObject; - -/** - The connection mapping describing the relationship connection the receiver will attempt to connect. - */ -@property (nonatomic, strong, readonly) RKConnectionMapping *connectionMapping; - -/** - The managed object cache the receiver will use to fetch a related object satisfying the connection - mapping. - */ -@property (nonatomic, strong, readonly) id managedObjectCache; +///------------------------------------------------------- +/// @name Initializing a Relationship Connection Operation +///------------------------------------------------------- /** Initializes the receiver with a given managed object, connection mapping, and managed object cache. @@ -55,4 +48,23 @@ connectionMapping:(RKConnectionMapping *)connectionMapping managedObjectCache:(id)managedObjectCache; +///-------------------------------------------- +/// @name Accessing Details About the Operation +///-------------------------------------------- + +/** + The managed object the receiver will attempt to connect a relationship for. + */ +@property (nonatomic, strong, readonly) NSManagedObject *managedObject; + +/** + The connection mapping describing the relationship connection the receiver will attempt to connect. + */ +@property (nonatomic, strong, readonly) RKConnectionMapping *connectionMapping; + +/** + The managed object cache the receiver will use to fetch a related object satisfying the connection mapping. + */ +@property (nonatomic, strong, readonly) id managedObjectCache; + @end diff --git a/Code/CoreData/RKRelationshipConnectionOperation.m b/Code/CoreData/RKRelationshipConnectionOperation.m index f1cfd0ba..5b2dd426 100644 --- a/Code/CoreData/RKRelationshipConnectionOperation.m +++ b/Code/CoreData/RKRelationshipConnectionOperation.m @@ -5,6 +5,18 @@ // Created by Blake Watters on 7/12/12. // Copyright (c) 2012 RestKit. All rights reserved. // +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// #import #import "RKRelationshipConnectionOperation.h" @@ -12,6 +24,7 @@ #import "RKLog.h" #import "RKManagedObjectCaching.h" #import "RKDynamicMappingMatcher.h" +#import "RKErrors.h" // Set Logging Component #undef RKLogComponent @@ -21,6 +34,7 @@ @property (nonatomic, strong, readwrite) NSManagedObject *managedObject; @property (nonatomic, strong, readwrite) RKConnectionMapping *connectionMapping; @property (nonatomic, strong, readwrite) id managedObjectCache; +@property (nonatomic, strong, readwrite) NSError *error; // Helpers @property (weak, nonatomic, readonly) NSManagedObjectContext *managedObjectContext; @@ -169,24 +183,15 @@ RKLogTrace(@"Connecting relationship '%@' with mapping: %@", relationshipName, self.connectionMapping); [self.managedObjectContext performBlockAndWait:^{ id relatedObject = [self findConnected]; - if (relatedObject) { - [self.managedObject setValue:relatedObject forKeyPath:relationshipName]; - RKLogDebug(@"Connected relationship '%@' to object '%@'", relationshipName, relatedObject); - } else { - RKLogDebug(@"Failed to find instance of '%@' to connect relationship '%@'", [[self.connectionMapping.relationship destinationEntity] name], relationshipName); - } + [self.managedObject setValue:relatedObject forKeyPath:relationshipName]; + RKLogDebug(@"Connected relationship '%@' to object '%@'", relationshipName, relatedObject); }]; } - (void)main { if (self.isCancelled) return; - @try { - [self connectRelationship]; - } - @catch (NSException *exception) { - RKLogCritical(@"Caught exception: %@", exception); - } + [self connectRelationship]; } @end diff --git a/Code/ObjectMapping/RKConnectionMapping.h b/Code/ObjectMapping/RKConnectionMapping.h index b43fd3e9..e83aa88e 100644 --- a/Code/ObjectMapping/RKConnectionMapping.h +++ b/Code/ObjectMapping/RKConnectionMapping.h @@ -21,12 +21,11 @@ #import #import #import "RKMapping.h" +#import "RKPropertyMapping.h" @class RKConnectionMapping, RKDynamicMappingMatcher; @protocol RKManagedObjectCaching; -typedef id (^RKObjectConnectionBlock)(RKConnectionMapping *mapping, id source); - // Defines the rules for connecting relationsips /** Instructs RestKit to connect a relationship of the object being mapped to the @@ -93,11 +92,10 @@ typedef id (^RKObjectConnectionBlock)(RKConnectionMapping *mapping, id source); @see connectRelationship:withObjectForPrimaryKeyAttribute: */ -@interface RKConnectionMapping : NSObject +@interface RKConnectionMapping : RKPropertyMapping @property (nonatomic, strong, readonly) NSRelationshipDescription *relationship; -@property (nonatomic, strong, readonly) NSString *sourceKeyPath; -@property (nonatomic, strong, readonly) NSString *destinationKeyPath; + @property (nonatomic, strong, readonly) RKDynamicMappingMatcher *matcher; // Can be nil // Returns YES if the receiver describes a connection between entities that is established diff --git a/Code/ObjectMapping/RKConnectionMapping.m b/Code/ObjectMapping/RKConnectionMapping.m index 0e1739f8..bd2f7989 100644 --- a/Code/ObjectMapping/RKConnectionMapping.m +++ b/Code/ObjectMapping/RKConnectionMapping.m @@ -41,31 +41,6 @@ @implementation RKConnectionMapping -//+ (RKConnectionMapping *)connectionMappingForRelationship:(NSString *)relationshipName fromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath withMapping:(RKMapping *)objectOrDynamicMapping -//{ -// RKConnectionMapping *mapping = [[self alloc] initWithRelationshipName:relationshipName sourceKeyPath:sourceKeyPath destinationKeyPath:destinationKeyPath mapping:objectOrDynamicMapping matcher:nil]; -// return mapping; -//} -// -//+ (RKConnectionMapping*)connectionMappingForRelationship:(NSString *)relationshipName fromKeyPath:(NSString *)sourceKeyPath toKeyPath:(NSString *)destinationKeyPath withMapping:(RKMapping *)objectOrDynamicMapping matcher:(RKDynamicMappingMatcher *)matcher -//{ -// RKConnectionMapping *mapping = [[self alloc] initWithRelationshipName:relationshipName sourceKeyPath:sourceKeyPath destinationKeyPath:destinationKeyPath mapping:objectOrDynamicMapping matcher:matcher]; -// return mapping; -//} -// -//- (id)initWithRelationshipName:(NSString *)relationshipName sourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath mapping:(RKMapping *)objectOrDynamicMapping matcher:(RKDynamicMappingMatcher *)matcher -//{ -// self = [super init]; -// if (self) { -// self.relationshipName = relationshipName; -// self.sourceKeyPath = sourceKeyPath; -// self.destinationKeyPath = destinationKeyPath; -// self.mapping = objectOrDynamicMapping; -// self.matcher = matcher; -// } -// return self; -//} - - (Class)connectionMappingClassForRelationship:(NSRelationshipDescription *)relationship sourceKeyPath:(NSString *)sourceKeyPath destinationKeyPath:(NSString *)destinationKeyPath { NSEntityDescription *sourceEntity = relationship.entity; diff --git a/Code/ObjectMapping/RKMappingOperation.h b/Code/ObjectMapping/RKMappingOperation.h index b5138a74..495e3a4a 100644 --- a/Code/ObjectMapping/RKMappingOperation.h +++ b/Code/ObjectMapping/RKMappingOperation.h @@ -21,7 +21,7 @@ #import "RKObjectMapping.h" #import "RKAttributeMapping.h" -@class RKMappingOperation, RKDynamicMapping; +@class RKMappingOperation, RKDynamicMapping, RKConnectionMapping; @protocol RKMappingOperationDataSource; /** @@ -88,6 +88,21 @@ */ - (void)mappingOperation:(RKMappingOperation *)operation didSelectObjectMapping:(RKObjectMapping *)objectMapping forDynamicMapping:(RKDynamicMapping *)dynamicMapping; +#ifdef _COREDATADEFINES_H + +/** + Tells the delegate that the mapping operation has connected a relationship. + + Only sent when mapping an `RKEntityMapping` object that contains connection mappings. + + @param operation The mapping operation. + @param relationship The relationship that was connected. + @param connectionMapping The mappings that was used to connect the relationship. + */ +- (void)mappingOperation:(RKMappingOperation *)operation didConnectRelationship:(NSRelationshipDescription *)relationship usingMapping:(RKConnectionMapping *)connectionMapping; + +#endif + @end /**