Browse files

Merge branch 'release/0.10.0'

  • Loading branch information...
2 parents 8d0d9fc + 587fc73 commit 69adee9a701d34ee22effe8a8a0d95fb2818f2b9 @blakewatters blakewatters committed Apr 4, 2012
Showing with 9,896 additions and 3,439 deletions.
  1. +4 −3 .gitignore
  2. +1 −1 .gitmodules
  3. +76 −29 CREDITS.md
  4. +12 −2 Code/CoreData/CoreData.h
  5. +39 −0 Code/CoreData/NSEntityDescription+RKAdditions.h
  6. +38 −0 Code/CoreData/NSEntityDescription+RKAdditions.m
  7. +40 −6 Code/CoreData/NSManagedObject+ActiveRecord.h
  8. +137 −104 Code/CoreData/NSManagedObject+ActiveRecord.m
  9. +23 −0 Code/CoreData/NSManagedObject+RKAdditions.h
  10. +19 −0 Code/CoreData/NSManagedObject+RKAdditions.m
  11. +23 −0 Code/CoreData/NSManagedObjectContext+RKAdditions.h
  12. +26 −0 Code/CoreData/NSManagedObjectContext+RKAdditions.m
  13. +19 −0 Code/CoreData/RKFetchRequestManagedObjectCache.h
  14. +44 −0 Code/CoreData/RKFetchRequestManagedObjectCache.m
  15. +83 −0 Code/CoreData/RKInMemoryEntityCache.h
  16. +291 −0 Code/CoreData/RKInMemoryEntityCache.m
  17. +19 −0 Code/CoreData/RKInMemoryManagedObjectCache.h
  18. +43 −0 Code/CoreData/RKInMemoryManagedObjectCache.m
  19. +0 −33 Code/CoreData/RKManagedObjectCache.h
  20. +11 −2 Code/CoreData/RKManagedObjectLoader.h
  21. +60 −32 Code/CoreData/RKManagedObjectLoader.m
  22. +87 −17 Code/CoreData/RKManagedObjectMapping.h
  23. +65 −21 Code/CoreData/RKManagedObjectMapping.m
  24. +35 −0 Code/CoreData/RKManagedObjectMappingCache.h
  25. +1 −1 Code/CoreData/RKManagedObjectMappingOperation.h
  26. +44 −21 Code/CoreData/RKManagedObjectMappingOperation.m
  27. +51 −0 Code/CoreData/RKManagedObjectSearchEngine.h
  28. +100 −0 Code/CoreData/RKManagedObjectSearchEngine.m
  29. +1 −1 Code/CoreData/RKManagedObjectSeeder.h
  30. +4 −3 Code/CoreData/RKManagedObjectSeeder.m
  31. +51 −22 Code/CoreData/RKManagedObjectStore.h
  32. +171 −155 Code/CoreData/RKManagedObjectStore.m
  33. +1 −1 Code/CoreData/RKManagedObjectThreadSafeInvocation.h
  34. +2 −1 Code/CoreData/RKManagedObjectThreadSafeInvocation.m
  35. +54 −0 Code/CoreData/RKObjectMappingProvider+CoreData.h
  36. +31 −0 Code/CoreData/RKObjectMappingProvider+CoreData.m
  37. +1 −1 Code/CoreData/RKObjectPropertyInspector+CoreData.h
  38. +23 −2 Code/CoreData/RKObjectPropertyInspector+CoreData.m
  39. +40 −0 Code/CoreData/RKSearchWord.h
  40. +35 −0 Code/CoreData/RKSearchWord.m
  41. +34 −0 Code/CoreData/RKSearchWordObserver.h
  42. +70 −0 Code/CoreData/RKSearchWordObserver.m
  43. +119 −0 Code/CoreData/RKSearchableManagedObject.h
  44. +81 −0 Code/CoreData/RKSearchableManagedObject.m
  45. +10 −12 Specs/ObjectMapping/RKObjectiveCPlusPlusSpec.mm → Code/Network/NSData+RKAdditions.h
  46. +4 −4 Code/Network/{NSData+MD5.m → NSData+RKAdditions.m}
  47. +5 −13 Code/Network/NSDictionary+RKRequestSerialization.h
  48. +8 −45 Code/Network/NSDictionary+RKRequestSerialization.m
  49. +19 −0 Code/Network/NSObject+URLEncoding.h
  50. +24 −0 Code/Network/NSObject+URLEncoding.m
  51. +0 −48 Code/Network/NSString+MD5.m
  52. +1 −1 Code/Network/Network.h
  53. +525 −318 Code/Network/RKClient.h
  54. +110 −49 Code/Network/RKClient.m
  55. +15 −14 Code/Network/RKNotifications.h
  56. +8 −7 Code/Network/RKNotifications.m
  57. +228 −48 Code/Network/RKOAuthClient.h
  58. +14 −16 Code/Network/RKOAuthClient.m
  59. +134 −43 Code/Network/RKParams.h
  60. +2 −2 Code/Network/RKParams.m
  61. +92 −42 Code/Network/RKParamsAttachment.h
  62. +3 −4 Code/Network/RKParamsAttachment.m
  63. +179 −78 Code/Network/RKReachabilityObserver.h
  64. +1 −1 Code/Network/RKReachabilityObserver.m
  65. +567 −186 Code/Network/RKRequest.h
  66. +217 −104 Code/Network/RKRequest.m
  67. +153 −21 Code/Network/RKRequestCache.h
  68. +76 −229 Code/Network/RKRequestCache.m
  69. +218 −93 Code/Network/RKRequestQueue.h
  70. +146 −92 Code/Network/RKRequestQueue.m
  71. +52 −24 Code/Network/RKRequestSerializable.h
  72. +38 −12 Code/Network/RKRequestSerialization.h
  73. +5 −5 Code/Network/RKRequestSerialization.m
  74. +1 −1 Code/Network/RKRequest_Internals.h
  75. +207 −74 Code/Network/RKResponse.h
  76. +29 −19 Code/Network/RKResponse.m
  77. +207 −16 Code/Network/RKURL.h
  78. +113 −37 Code/Network/RKURL.m
  79. +1 −1 Code/ObjectMapping/ObjectMapping.h
  80. +39 −0 Code/ObjectMapping/RKConfigurationDelegate.h
  81. +7 −14 Code/ObjectMapping/RKDynamicObjectMapping.h
  82. +7 −52 Code/ObjectMapping/RKDynamicObjectMapping.m
  83. +30 −0 Code/ObjectMapping/RKDynamicObjectMappingMatcher.h
  84. +78 −0 Code/ObjectMapping/RKDynamicObjectMappingMatcher.m
  85. +1 −1 Code/ObjectMapping/RKErrorMessage.h
  86. +1 −1 Code/ObjectMapping/RKErrorMessage.m
  87. +1 −1 Code/ObjectMapping/RKMappingOperationQueue.h
  88. +1 −1 Code/ObjectMapping/RKMappingOperationQueue.m
  89. +1 −1 Code/ObjectMapping/RKObjectAttributeMapping.h
  90. +1 −1 Code/ObjectMapping/RKObjectAttributeMapping.m
  91. +116 −28 Code/ObjectMapping/RKObjectLoader.h
  92. +216 −102 Code/ObjectMapping/RKObjectLoader.m
  93. +4 −2 Code/ObjectMapping/RKObjectLoader_Internals.h
  94. +181 −107 Code/ObjectMapping/RKObjectManager.h
  95. +221 −162 Code/ObjectMapping/RKObjectManager.m
  96. +12 −15 Code/ObjectMapping/RKObjectMapper.h
  97. +140 −97 Code/ObjectMapping/RKObjectMapper.m
  98. +3 −3 Code/ObjectMapping/RKObjectMapperError.h
  99. +5 −5 Code/ObjectMapping/RKObjectMapper_Private.h
  100. +140 −111 Code/ObjectMapping/RKObjectMapping.h
  101. +99 −34 Code/ObjectMapping/RKObjectMapping.m
  102. +44 −4 Code/ObjectMapping/RKObjectMappingDefinition.h
  103. +16 −0 Code/ObjectMapping/RKObjectMappingDefinition.m
  104. +3 −3 Code/ObjectMapping/RKObjectMappingOperation.h
  105. +156 −75 Code/ObjectMapping/RKObjectMappingOperation.m
  106. +50 −0 Code/ObjectMapping/RKObjectMappingProvider+Contexts.h
  107. +93 −12 Code/ObjectMapping/RKObjectMappingProvider.h
  108. +224 −22 Code/ObjectMapping/RKObjectMappingProvider.m
  109. +19 −0 Code/ObjectMapping/RKObjectMappingProviderContextEntry.h
  110. +61 −0 Code/ObjectMapping/RKObjectMappingProviderContextEntry.m
  111. +1 −1 Code/ObjectMapping/RKObjectMappingResult.h
  112. +2 −2 Code/ObjectMapping/RKObjectMappingResult.m
  113. +281 −0 Code/ObjectMapping/RKObjectPaginator.h
  114. +220 −0 Code/ObjectMapping/RKObjectPaginator.m
  115. +7 −1 Code/ObjectMapping/RKObjectPropertyInspector.h
  116. +3 −3 Code/ObjectMapping/RKObjectPropertyInspector.m
  117. +5 −5 Code/ObjectMapping/RKObjectRelationshipMapping.h
  118. +3 −3 Code/ObjectMapping/RKObjectRelationshipMapping.m
  119. +12 −9 Code/ObjectMapping/RKObjectRouter.h
  120. +39 −20 Code/ObjectMapping/RKObjectRouter.m
  121. +1 −1 Code/ObjectMapping/RKObjectSerializer.h
  122. +7 −3 Code/ObjectMapping/RKObjectSerializer.m
  123. +45 −8 Code/ObjectMapping/RKParserRegistry.h
  124. +36 −11 Code/ObjectMapping/RKParserRegistry.m
  125. +1 −1 Code/ObjectMapping/RKRouter.h
  126. +1 −1 Code/RestKit.h
  127. +94 −0 Code/Support/NSBundle+RKAdditions.h
  128. +110 −0 Code/Support/NSBundle+RKAdditions.m
  129. +18 −3 Code/Support/NSDictionary+RKAdditions.h
  130. +41 −3 Code/Support/NSDictionary+RKAdditions.m
  131. +22 −6 Code/Support/{NSString+RestKit.h → NSString+RKAdditions.h}
  132. +61 −11 Code/Support/{NSString+RestKit.m → NSString+RKAdditions.m}
  133. +4 −4 Code/Support/{NSURL+RestKit.h → NSURL+RKAdditions.h}
  134. +7 −7 Code/Support/{NSURL+RestKit.m → NSURL+RKAdditions.m}
  135. +1 −1 Code/Support/Parsers/JSON/RKJSONParserJSONKit.h
  136. +9 −2 Code/Support/Parsers/JSON/RKJSONParserJSONKit.m
  137. +0 −37 Code/Support/Parsers/XML/RKXMLParserLibXML.h
  138. +0 −160 Code/Support/Parsers/XML/RKXMLParserLibXML.m
  139. +32 −0 Code/Support/Parsers/XML/RKXMLParserXMLReader.h
  140. +22 −0 Code/Support/Parsers/XML/RKXMLParserXMLReader.m
  141. +1 −1 Code/Support/RKAlert.h
  142. +1 −1 Code/Support/RKAlert.m
  143. +38 −0 Code/Support/RKCache.h
  144. +233 −0 Code/Support/RKCache.m
  145. +16 −1 Code/Support/RKDirectory.h
  146. +35 −33 Code/Support/RKDirectory.m
  147. +13 −1 Code/Support/RKDotNetDateFormatter.h
  148. +14 −2 Code/Support/RKDotNetDateFormatter.m
  149. +31 −10 Code/Support/{Errors.h → RKErrors.h}
  150. +9 −8 Code/Support/{Errors.m → RKErrors.m}
  151. +2 −2 Code/Support/RKFixCategoryBug.h
  152. +13 −0 Code/Support/RKISO8601DateFormatter.h
  153. +13 −0 Code/Support/RKISO8601DateFormatter.m
  154. +36 −2 Code/Support/RKLog.h
  155. +96 −1 Code/Support/RKLog.m
  156. +1 −1 Code/Support/RKMIMETypes.h
  157. +1 −1 Code/Support/RKMIMETypes.m
  158. +45 −0 Code/Support/RKMutableBlockDictionary.h
  159. +145 −0 Code/Support/RKMutableBlockDictionary.m
  160. +64 −0 Code/Support/RKOrderedDictionary.h
  161. +151 −0 Code/Support/RKOrderedDictionary.m
  162. +23 −9 Code/Support/RKParser.h
  163. +3 −3 Code/Support/RKPathMatcher.h
  164. +14 −5 Code/Support/RKPathMatcher.m
  165. +75 −35 Code/Support/RKSearchEngine.h
  166. +44 −34 Code/Support/RKSearchEngine.m
  167. +8 −4 Code/Support/Support.h
  168. +15 −9 Code/Support/lcl_config_components.h
  169. +1 −1 Code/Support/lcl_config_extensions.h
  170. +1 −1 Code/Support/lcl_config_logger.h
  171. +178 −0 Code/Testing/RKMappingTest.h
Sorry, we could not display the entire diff because too many files (860) changed.
View
7 .gitignore
@@ -7,6 +7,7 @@ Docs/API
# temp nibs and swap files
*~.nib
*.swp
+*.orig
# OS X folder attributes
.DS_Store
@@ -20,6 +21,6 @@ Docs/API
Examples/RKDiscussionBoardExample/discussion_board_backend/public/system/attachments/*
-# UISpecRunner cached build path
-.uispec.app
-Specs/Runner/UISpec
+# Thin
+log
+tmp
View
2 .gitmodules
@@ -1,3 +1,3 @@
[submodule "Examples/RKCatalog/Server"]
path = Examples/RKCatalog/Server
- url = git://github.com/twotoasters/RKCatalog-Server.git
+ url = git://github.com/RestKit/RKCatalog-Server.git
View
105 CREDITS.md
@@ -6,32 +6,79 @@ as a Ruby on Rails specific object mapper for XML data. In early 2010 the framew
rebranded as RestKit and evolved into a general purpose HTTP toolkit and object mapping
system.
-RestKit is a production of Two Toasters and available as an Open Source package under
-the terms of the Apache License (see LICENSE for details).
-
-Original Author
----------------
-* Blake Watters (blakewatters) @blakewatters
-
-Core Team
----------
-* Jeremy Ellison (jeremyellison)
-* Daniel Hammond (danielrhammond)
-* Jeff Arena (jeffarena)
-
-Web Designer
-------------
-* Adit Shukla (aditshukla)
-
-Contributors
-------------
-* Marc Weil (aspir)
-* Pat Shields (pashields)
-* Tim Kerchmar (timkerchmar)
-* Rachit Shukla (rachitshukla)
-* Adam Hinz (ahinz)
-* Stefan Eletzhofer (seletz)
-* Peter Marks (tassock)
-* Chad Podoski (chadpod)
-* Andras Hatvani (andrashatvani)
-* Ed McManus (emcmanus)
+RestKit is available as an Open Source package under the terms of the Apache License (see
+LICENSE for details).
+
+## Original Author
+* Blake Watters (blakewatters) @blakewatters
+
+## Core Team
+* Jeff Arena (jeffarena)
+* Gregory Combs (grgcombs)
+* Brian Morton (bmorton)
+
+## Web Designer
+* Adit Shukla (aditshukla)
+
+## Contributors
+#### Version 0.9.3 and earlier
+* Jeremy Ellison (jeremyellison)
+* Daniel Hammond (danielrhammond)
+* Marc Weil (aspir)
+* Pat Shields (pashields)
+* Tim Kerchmar (timkerchmar)
+* Rachit Shukla (rachitshukla)
+* Adam Hinz (ahinz)
+* Stefan Eletzhofer (seletz)
+* Peter Marks (tassock)
+* Chad Podoski (chadpod)
+* Andras Hatvani (andrashatvani)
+* Ed McManus (emcmanus)
+
+#### Version 0.10.0
+* Christopher Swasey (endash)
+* Aneil Mallavarapu (amallavarapu)
+* Rui D Lopes (ruidlopes)
+* Robert Altman (inquinity)
+* Beat Besmer (besi)
+* Scott Penrose (spenrose)
+* Charlie Savage (cfis)
+* Jawwad Ahmad (jawwad)
+* John Stallings (jstallings)
+* Bob Spryn (sprynmr)
+* Ray Fix (rayfix)
+* Marlon Andrade (marlonandrade)
+* David Young-Chan Kay (DavidYKay)
+* Chethan Reddy (creddy)
+* Julien Grimault (juliengrimault)
+* Matthias Bartelmeß
+* Nolan Waite (nolanw)
+* Michael Fleet (fantasticmf)
+* Tony Lee (hktonylee)
+* Aaron Crespo (aaroncrespo)
+* James Sullivan (jsullivanlive)
+* Marco Pesenti Gritti (marcopg)
+* Brad Phelan (bradphelan)
+* Ivan Vučica (ivucica)
+* Felix Holmgren (Felixyz)
+* Open Thread (OpenFibers)
+* Sergej Tatarincev (SevInf)
+* Ben Einstein (beinstein)
+* Johan Bilien (jobi)
+* Björn Jonsson (bjornjonsson)
+* Ralf van der Zanden (ralfvdz)
+* Parker Boundy (parkerboundy)
+* Jeremy Mack (mutewinter)
+* Allen Wei (allenwei)
+* Robin Eggenkamp (Edubits)
+* Emil Wojtaszek (emilwojtaszek)
+* Victor Kryukov (victorkryukov)
+* Cody Rayment (crayment)
+* Arne Harren (aharren)
+* Cameron Royal (cammm)
+
+## Honorable Mentions
+RestKit would like to thank the following companies for aiding in the support of this product:
+
+* GateGuru - http://www.gateguru.com
+* Two Toasters - http://www.twotoasters.com
View
14 Code/CoreData/CoreData.h
@@ -3,7 +3,7 @@
// RestKit
//
// Created by Blake Watters on 9/30/10.
-// Copyright 2010 Two Toasters
+// Copyright (c) 2009-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.
@@ -23,6 +23,16 @@
#import "NSManagedObject+ActiveRecord.h"
#import "RKManagedObjectStore.h"
#import "RKManagedObjectSeeder.h"
-#import "RKManagedObjectCache.h"
#import "RKManagedObjectMapping.h"
+#import "RKManagedObjectMappingOperation.h"
+#import "RKManagedObjectMappingCache.h"
+#import "RKInMemoryManagedObjectCache.h"
+#import "RKFetchRequestManagedObjectCache.h"
+#import "RKSearchableManagedObject.h"
+#import "RKSearchWord.h"
+
#import "RKObjectPropertyInspector+CoreData.h"
+#import "RKObjectMappingProvider+CoreData.h"
+#import "NSManagedObjectContext+RKAdditions.h"
+#import "NSManagedObject+RKAdditions.h"
+#import "NSEntityDescription+RKAdditions.h"
View
39 Code/CoreData/NSEntityDescription+RKAdditions.h
@@ -0,0 +1,39 @@
+//
+// NSEntityDescription+RKAdditions.h
+// RestKit
+//
+// Created by Blake Watters on 3/22/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+/**
+ The key for retrieving the name of the attribute that acts as
+ the primary key from the user info dictionary of the receiving NSEntityDescription.
+
+ **Value**: @"primaryKeyAttribute"
+ */
+extern NSString * const RKEntityDescriptionPrimaryKeyAttributeUserInfoKey;
+
+/**
+ Provides extensions to NSEntityDescription for various common tasks.
+ */
+@interface NSEntityDescription (RKAdditions)
+
+/**
+ The name of the attribute that acts as the primary key for the receiver.
+
+ The primary key attribute can be configured in two ways:
+ 1. From within the Xcode Core Data editing view by
+ adding the desired attribute's name as the value for the
+ key `primaryKeyAttribute` to the user info dictionary.
+ 1. Programmatically, by retrieving the NSEntityDescription instance and
+ setting the property's value.
+
+ Programmatically configured values take precedence over the user info
+ dictionary.
+ */
+@property(nonatomic, retain) NSString *primaryKeyAttribute;
+
+@end
View
38 Code/CoreData/NSEntityDescription+RKAdditions.m
@@ -0,0 +1,38 @@
+//
+// NSEntityDescription+RKAdditions.m
+// RestKit
+//
+// Created by Blake Watters on 3/22/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import <objc/runtime.h>
+#import "NSEntityDescription+RKAdditions.h"
+
+NSString * const RKEntityDescriptionPrimaryKeyAttributeUserInfoKey = @"primaryKeyAttribute";
+static char primaryKeyAttributeKey;
+
+@implementation NSEntityDescription (RKAdditions)
+
+- (NSString *)primaryKeyAttribute
+{
+ // Check for an associative object reference
+ NSString *primaryKeyAttribute = (NSString *) objc_getAssociatedObject(self, &primaryKeyAttributeKey);
+
+ // Fall back to the userInfo dictionary
+ if (! primaryKeyAttribute) {
+ primaryKeyAttribute = [self.userInfo valueForKey:RKEntityDescriptionPrimaryKeyAttributeUserInfoKey];
+ }
+
+ return primaryKeyAttribute;
+}
+
+- (void)setPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
+{
+ objc_setAssociatedObject(self,
+ &primaryKeyAttributeKey,
+ primaryKeyAttribute,
+ OBJC_ASSOCIATION_RETAIN);
+}
+
+@end
View
46 Code/CoreData/NSManagedObject+ActiveRecord.h
@@ -1,5 +1,5 @@
//
-// RKManagedObject+ActiveRecord.h
+// NSManagedObject+ActiveRecord.h
//
// Adapted from https://github.com/magicalpanda/MagicalRecord
// Created by Saul Mora on 11/15/09.
@@ -10,16 +10,25 @@
#import <CoreData/CoreData.h>
-@interface NSManagedObject (ActiveRecord)
+/**
+ Extensions to NSManagedObjectContext for RestKit's Active Record pattern implementation
+ */
+@interface NSManagedObjectContext (ActiveRecord)
+
++ (NSManagedObjectContext *)defaultContext;
++ (void)setDefaultContext:(NSManagedObjectContext *)context;
++ (NSManagedObjectContext *)contextForCurrentThread;
+
+@end
/**
- * The Core Data managed object context from the RKObjectManager's objectStore
- * that is managing this model
+ Provides extensions to NSManagedObject implementing a low-ceremony querying
+ interface.
*/
-+ (NSManagedObjectContext*)managedObjectContext;
+@interface NSManagedObject (ActiveRecord)
/**
- * The NSEntityDescription for the Subclass
+ * The NSEntityDescription for the Subclass
* defaults to the subclass className, may be overridden
*/
+ (NSEntityDescription*)entity;
@@ -35,6 +44,12 @@
+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest;
/**
+ * Retrieves the number of objects that would be retrieved by the fetchRequest,
+ * if executed
+ */
++ (NSUInteger)countOfObjectsWithFetchRequest:(NSFetchRequest*)fetchRequest;
+
+/**
* Fetches all objects from the persistent store via a set of fetch requests and
* returns all results in a single array.
*/
@@ -88,6 +103,25 @@
*/
- (BOOL)isNew;
+/**
+ Finds the instance of the receiver's entity with the given value for the primary key attribute
+ in the managed object context for the current thread.
+
+ @param primaryKeyValue The value for the receiving entity's primary key attribute.
+ @return The object with the primary key attribute equal to the given value or nil.
+ */
++ (id)findByPrimaryKey:(id)primaryKeyValue;
+
+/**
+ Finds the instance of the receiver's entity with the given value for the primary key attribute in
+ the given managed object context.
+
+ @param primaryKeyValue The value for the receiving entity's primary key attribute.
+ @param context The managed object context to find the instance in.
+ @return The object with the primary key attribute equal to the given value or nil.
+ */
++ (id)findByPrimaryKey:(id)primaryKeyValue inContext:(NSManagedObjectContext *)context;
+
////////////////////////////////////////////////////////////////////////////////////////////////////
+ (NSManagedObjectContext*)currentContext;
View
241 Code/CoreData/NSManagedObject+ActiveRecord.m
@@ -10,11 +10,10 @@
#import <objc/runtime.h>
#import "NSManagedObject+ActiveRecord.h"
-#import "RKObjectManager.h"
+#import "RKManagedObjectStore.h"
#import "RKLog.h"
#import "RKFixCategoryBug.h"
-
-RK_FIX_CATEGORY_BUG(NSManagedObject_ActiveRecord)
+#import "NSEntityDescription+RKAdditions.h"
// Set Logging Component
#undef RKLogComponent
@@ -23,19 +22,38 @@
static NSUInteger const kActiveRecordDefaultBatchSize = 10;
static NSNumber *defaultBatchSize = nil;
-@implementation NSManagedObject (ActiveRecord)
+static NSManagedObjectContext *defaultContext = nil;
-#pragma mark - RKManagedObject methods
+RK_FIX_CATEGORY_BUG(NSManagedObjectContext_ActiveRecord)
+
+@implementation NSManagedObjectContext (ActiveRecord)
+
++ (NSManagedObjectContext *)defaultContext {
+ return defaultContext;
+}
-+ (NSManagedObjectContext*)managedObjectContext {
- NSAssert([RKObjectManager sharedManager], @"[RKObjectManager sharedManager] cannot be nil");
- NSAssert([RKObjectManager sharedManager].objectStore, @"[RKObjectManager sharedManager].objectStore cannot be nil");
- return [[[RKObjectManager sharedManager] objectStore] managedObjectContext];
++ (void)setDefaultContext:(NSManagedObjectContext *)newDefaultContext {
+ [newDefaultContext retain];
+ [defaultContext release];
+ defaultContext = newDefaultContext;
}
++ (NSManagedObjectContext *)contextForCurrentThread {
+ NSAssert([RKManagedObjectStore defaultObjectStore], @"[RKManagedObjectStore defaultObjectStore] cannot be nil");
+ return [[RKManagedObjectStore defaultObjectStore] managedObjectContextForCurrentThread];
+}
+
+@end
+
+RK_FIX_CATEGORY_BUG(NSManagedObject_ActiveRecord)
+
+@implementation NSManagedObject (ActiveRecord)
+
+#pragma mark - RKManagedObject methods
+
+ (NSEntityDescription*)entity {
NSString* className = [NSString stringWithCString:class_getName([self class]) encoding:NSASCIIStringEncoding];
- return [NSEntityDescription entityForName:className inManagedObjectContext:[self managedObjectContext]];
+ return [NSEntityDescription entityForName:className inManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]];
}
+ (NSFetchRequest*)fetchRequest {
@@ -47,13 +65,22 @@ + (NSFetchRequest*)fetchRequest {
+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest {
NSError* error = nil;
- NSArray* objects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
+ NSArray* objects = [[NSManagedObjectContext contextForCurrentThread] executeFetchRequest:fetchRequest error:&error];
if (objects == nil) {
RKLogError(@"Error: %@", [error localizedDescription]);
}
return objects;
}
++ (NSUInteger)countOfObjectsWithFetchRequest:(NSFetchRequest*)fetchRequest {
+ NSError* error = nil;
+ NSUInteger objectCount = [[NSManagedObjectContext contextForCurrentThread] countForFetchRequest:fetchRequest error:&error];
+ if (objectCount == NSNotFound) {
+ RKLogError(@"Error: %@", [error localizedDescription]);
+ }
+ return objectCount;
+}
+
+ (NSArray*)objectsWithFetchRequests:(NSArray*)fetchRequests {
NSMutableArray* mutableObjectArray = [[NSMutableArray alloc] init];
for (NSFetchRequest* fetchRequest in fetchRequests) {
@@ -92,7 +119,7 @@ + (NSArray*)allObjects {
+ (NSUInteger)count:(NSError**)error {
NSFetchRequest* fetchRequest = [self fetchRequest];
- return [[self managedObjectContext] countForFetchRequest:fetchRequest error:error];
+ return [[NSManagedObjectContext contextForCurrentThread] countForFetchRequest:fetchRequest error:error];
}
+ (NSUInteger)count {
@@ -101,7 +128,7 @@ + (NSUInteger)count {
}
+ (id)object {
- id object = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:[self managedObjectContext]];
+ id object = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]];
return [object autorelease];
}
@@ -110,10 +137,25 @@ - (BOOL)isNew {
return [vals count] == 0;
}
++ (id)findByPrimaryKey:(id)primaryKeyValue inContext:(NSManagedObjectContext *)context {
+ NSEntityDescription *entity = [self entityDescriptionInContext:context];
+ NSString *primaryKeyAttribute = entity.primaryKeyAttribute;
+ if (! primaryKeyAttribute) {
+ RKLogWarning(@"Attempt to findByPrimaryKey for entity with nil primaryKeyAttribute. Set the primaryKeyAttribute and try again! %@", entity);
+ return nil;
+ }
+
+ return [self findFirstByAttribute:primaryKeyAttribute withValue:primaryKeyValue inContext:context];
+}
+
++ (id)findByPrimaryKey:(id)primaryKeyValue {
+ return [self findByPrimaryKey:primaryKeyValue inContext:[NSManagedObjectContext contextForCurrentThread]];
+}
+
#pragma mark - MagicalRecord Ported Methods
+ (NSManagedObjectContext*)currentContext; {
- return [self managedObjectContext];
+ return [NSManagedObjectContext contextForCurrentThread];
}
+ (void)setDefaultBatchSize:(NSUInteger)newBatchSize
@@ -160,33 +202,28 @@ + (void)handleErrors:(NSError *)error
}
}
RKLogError(@"Error Domain: %@", [error domain]);
- RKLogError(@"Recovery Suggestion: %@", [error localizedRecoverySuggestion]);
+ RKLogError(@"Recovery Suggestion: %@", [error localizedRecoverySuggestion]);
}
}
-//- (void)handleErrors:(NSError *)error
-//{
-// [[self class] handleErrors:error];
-//}
-
+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context
{
NSError *error = nil;
-
+
NSArray *results = [context executeFetchRequest:request error:&error];
[self handleErrors:error];
- return results;
+ return results;
}
-+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request
++ (NSArray *)executeFetchRequest:(NSFetchRequest *)request
{
return [self executeFetchRequest:request inContext:[self currentContext]];
}
+ (id)executeFetchRequestAndReturnFirstObject:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context
{
[request setFetchLimit:1];
-
+
NSArray *results = [self executeFetchRequest:request inContext:context];
if ([results count] == 0)
{
@@ -226,11 +263,11 @@ + (NSArray *)propertiesNamed:(NSArray *)properties
{
NSEntityDescription *description = [self entityDescription];
NSMutableArray *propertiesWanted = [NSMutableArray array];
-
+
if (properties)
{
NSDictionary *propDict = [description propertiesByName];
-
+
for (NSString *propertyName in properties)
{
NSPropertyDescription *property = [propDict objectForKey:propertyName];
@@ -250,7 +287,7 @@ + (NSArray *)propertiesNamed:(NSArray *)properties
+ (NSArray *)sortAscending:(BOOL)ascending attributes:(id)attributesToSortBy, ...
{
NSMutableArray *attributes = [NSMutableArray array];
-
+
if ([attributesToSortBy isKindOfClass:[NSArray class]])
{
id attributeName;
@@ -263,7 +300,7 @@ + (NSArray *)sortAscending:(BOOL)ascending attributes:(id)attributesToSortBy, ..
[sortDescriptor release];
}
va_end(variadicArguments);
-
+
}
else if ([attributesToSortBy isKindOfClass:[NSString class]])
{
@@ -272,7 +309,7 @@ + (NSArray *)sortAscending:(BOOL)ascending attributes:(id)attributesToSortBy, ..
[attributes addObject:[[[NSSortDescriptor alloc] initWithKey:attributesToSortBy ascending:ascending] autorelease] ];
va_end(variadicArguments);
}
-
+
return attributes;
}
@@ -290,8 +327,8 @@ + (NSFetchRequest *)createFetchRequestInContext:(NSManagedObjectContext *)contex
{
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[self entityDescriptionInContext:context]];
-
- return request;
+
+ return request;
}
+ (NSFetchRequest *)createFetchRequest
@@ -307,8 +344,8 @@ + (NSNumber *)numberOfEntitiesWithContext:(NSManagedObjectContext *)context
NSError *error = nil;
NSUInteger count = [context countForFetchRequest:[self createFetchRequestInContext:context] error:&error];
[self handleErrors:error];
-
- return [NSNumber numberWithUnsignedInteger:count];
+
+ return [NSNumber numberWithUnsignedInteger:count];
}
+ (NSNumber *)numberOfEntities
@@ -321,7 +358,7 @@ + (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm inContext:
NSError *error = nil;
NSFetchRequest *request = [self createFetchRequestInContext:context];
[request setPredicate:searchTerm];
-
+
NSUInteger count = [context countForFetchRequest:request error:&error];
[self handleErrors:error];
@@ -330,7 +367,7 @@ + (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm inContext:
+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm;
{
- return [self numberOfEntitiesWithPredicate:searchTerm
+ return [self numberOfEntitiesWithPredicate:searchTerm
inContext:[self currentContext]];
}
@@ -360,7 +397,7 @@ + (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value inC
{
NSFetchRequest *request = [self createFetchRequestInContext:context];
[request setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", property, value]];
-
+
return request;
}
@@ -374,7 +411,7 @@ + (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm inContex
NSFetchRequest *request = [self createFetchRequestInContext:context];
[request setPredicate:searchTerm];
[request setFetchLimit:1];
-
+
return request;
}
@@ -386,9 +423,8 @@ + (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm
+ (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context;
{
NSFetchRequest *request = [self createFetchRequestInContext:context];
- [request setPropertiesToFetch:[self propertiesNamed:[NSArray arrayWithObject:attribute]]];
[request setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", attribute, searchValue]];
-
+
return request;
}
@@ -400,17 +436,17 @@ + (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)
+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self requestAllInContext:context];
-
+
NSSortDescriptor *sortBy = [[NSSortDescriptor alloc] initWithKey:sortTerm ascending:ascending];
[request setSortDescriptors:[NSArray arrayWithObject:sortBy]];
[sortBy release];
-
+
return request;
}
+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending
{
- return [self requestAllSortedBy:sortTerm
+ return [self requestAllSortedBy:sortTerm
ascending:ascending
inContext:[self currentContext]];
}
@@ -421,21 +457,21 @@ + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)asce
[request setPredicate:searchTerm];
[request setIncludesSubentities:NO];
[request setFetchBatchSize:[self defaultBatchSize]];
-
+
if (sortTerm != nil){
NSSortDescriptor *sortBy = [[NSSortDescriptor alloc] initWithKey:sortTerm ascending:ascending];
[request setSortDescriptors:[NSArray arrayWithObject:sortBy]];
[sortBy release];
}
-
+
return request;
}
+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm;
{
- NSFetchRequest *request = [self requestAllSortedBy:sortTerm
+ NSFetchRequest *request = [self requestAllSortedBy:sortTerm
ascending:ascending
- withPredicate:searchTerm
+ withPredicate:searchTerm
inContext:[self currentContext]];
return request;
}
@@ -446,7 +482,7 @@ + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)asce
+ (NSArray *)findAllInContext:(NSManagedObjectContext *)context
{
- return [self executeFetchRequest:[self requestAllInContext:context] inContext:context];
+ return [self executeFetchRequest:[self requestAllInContext:context] inContext:context];
}
+ (NSArray *)findAll
@@ -457,32 +493,32 @@ + (NSArray *)findAll
+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self requestAllSortedBy:sortTerm ascending:ascending inContext:context];
-
+
return [self executeFetchRequest:request inContext:context];
}
+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending
{
- return [self findAllSortedBy:sortTerm
- ascending:ascending
+ return [self findAllSortedBy:sortTerm
+ ascending:ascending
inContext:[self currentContext]];
}
+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context
{
- NSFetchRequest *request = [self requestAllSortedBy:sortTerm
+ NSFetchRequest *request = [self requestAllSortedBy:sortTerm
ascending:ascending
withPredicate:searchTerm
inContext:context];
-
+
return [self executeFetchRequest:request inContext:context];
}
+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm
{
- return [self findAllSortedBy:sortTerm
+ return [self findAllSortedBy:sortTerm
ascending:ascending
- withPredicate:searchTerm
+ withPredicate:searchTerm
inContext:[self currentContext]];
}
@@ -497,20 +533,20 @@ + (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withP
#ifdef STORE_USE_CACHE
cacheName = [NSString stringWithFormat:@"ActiveRecord-Cache-%@", NSStringFromClass(self)];
#endif
-
- NSFetchRequest *request = [self requestAllSortedBy:sortTerm
- ascending:ascending
+
+ NSFetchRequest *request = [self requestAllSortedBy:sortTerm
+ ascending:ascending
withPredicate:searchTerm
inContext:context];
-
- NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request
+
+ NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:group
cacheName:cacheName];
return [controller autorelease];
}
-+ (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending
++ (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending
{
return [self fetchRequestAllGroupedBy:group
withPredicate:searchTerm
@@ -521,22 +557,22 @@ + (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withP
+ (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath inContext:(NSManagedObjectContext *)context
{
- NSFetchedResultsController *controller = [self fetchRequestAllGroupedBy:groupingKeyPath
+ NSFetchedResultsController *controller = [self fetchRequestAllGroupedBy:groupingKeyPath
withPredicate:searchTerm
- sortedBy:sortTerm
+ sortedBy:sortTerm
ascending:ascending
inContext:context];
-
+
[self performFetch:controller];
return controller;
}
+ (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath
{
- return [self fetchAllSortedBy:sortTerm
+ return [self fetchAllSortedBy:sortTerm
ascending:ascending
- withPredicate:searchTerm
- groupBy:groupingKeyPath
+ withPredicate:searchTerm
+ groupBy:groupingKeyPath
inContext:[self currentContext]];
}
@@ -557,7 +593,7 @@ + (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy
+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group
{
- return [self fetchRequest:request
+ return [self fetchRequest:request
groupedBy:group
inContext:[self currentContext]];
}
@@ -569,21 +605,21 @@ + (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManaged
{
NSFetchRequest *request = [self createFetchRequestInContext:context];
[request setPredicate:searchTerm];
-
- return [self executeFetchRequest:request
+
+ return [self executeFetchRequest:request
inContext:context];
}
+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm
{
- return [self findAllWithPredicate:searchTerm
+ return [self findAllWithPredicate:searchTerm
inContext:[self currentContext]];
}
+ (id)findFirstInContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self createFetchRequestInContext:context];
-
+
return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
@@ -593,24 +629,23 @@ + (id)findFirst
}
+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context
-{
+{
NSFetchRequest *request = [self requestFirstByAttribute:attribute withValue:searchValue inContext:context];
- [request setPropertiesToFetch:[self propertiesNamed:[NSArray arrayWithObject:attribute]]];
-
+
return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue
{
- return [self findFirstByAttribute:attribute
- withValue:searchValue
+ return [self findFirstByAttribute:attribute
+ withValue:searchValue
inContext:[self currentContext]];
}
+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self requestFirstWithPredicate:searchTerm];
-
+
return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
@@ -622,85 +657,83 @@ + (id)findFirstWithPredicate:(NSPredicate *)searchTerm
+ (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self requestAllSortedBy:property ascending:ascending withPredicate:searchterm inContext:context];
-
+
return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
+ (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending
{
- return [self findFirstWithPredicate:searchterm
- sortedBy:property
- ascending:ascending
+ return [self findFirstWithPredicate:searchterm
+ sortedBy:property
+ ascending:ascending
inContext:[self currentContext]];
}
+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self createFetchRequestInContext:context];
[request setPredicate:searchTerm];
- [request setPropertiesToFetch:[self propertiesNamed:attributes]];
-
+
return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes
{
- return [self findFirstWithPredicate:searchTerm
- andRetrieveAttributes:attributes
+ return [self findFirstWithPredicate:searchTerm
+ andRetrieveAttributes:attributes
inContext:[self currentContext]];
}
+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context andRetrieveAttributes:(id)attributes, ...
{
- NSFetchRequest *request = [self requestAllSortedBy:sortBy
+ NSFetchRequest *request = [self requestAllSortedBy:sortBy
ascending:ascending
withPredicate:searchTerm
inContext:context];
- [request setPropertiesToFetch:[self propertiesNamed:attributes]];
-
+
return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending andRetrieveAttributes:(id)attributes, ...
{
return [self findFirstWithPredicate:searchTerm
- sortedBy:sortBy
- ascending:ascending
+ sortedBy:sortBy
+ ascending:ascending
inContext:[self currentContext]
andRetrieveAttributes:attributes];
}
+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self createFetchRequestInContext:context];
-
+
[request setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", attribute, searchValue]];
-
+
return [self executeFetchRequest:request inContext:context];
}
+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue
{
- return [self findByAttribute:attribute
- withValue:searchValue
+ return [self findByAttribute:attribute
+ withValue:searchValue
inContext:[self currentContext]];
}
+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
NSPredicate *searchTerm = [NSPredicate predicateWithFormat:@"%K = %@", attribute, searchValue];
NSFetchRequest *request = [self requestAllSortedBy:sortTerm ascending:ascending withPredicate:searchTerm inContext:context];
-
+
return [self executeFetchRequest:request];
}
+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending
{
- return [self findByAttribute:attribute
+ return [self findByAttribute:attribute
withValue:searchValue
- andOrderBy:sortTerm
- ascending:ascending
+ andOrderBy:sortTerm
+ ascending:ascending
inContext:[self currentContext]];
}
@@ -711,9 +744,9 @@ + (id)createInContext:(NSManagedObjectContext *)context
}
+ (id)createEntity
-{
+{
NSManagedObject *newEntity = [self createInContext:[self currentContext]];
-
+
return newEntity;
}
@@ -749,21 +782,21 @@ + (NSNumber *)maxValueFor:(NSString *)property
{
NSManagedObject *obj = [[self class] findFirstByAttribute:property
withValue:[NSString stringWithFormat:@"max(%@)", property]];
-
+
return [obj valueForKey:property];
}
+ (id)objectWithMinValueFor:(NSString *)property inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [[self class] createFetchRequestInContext:context];
-
+
NSPredicate *searchFor = [NSPredicate predicateWithFormat:@"SELF = %@ AND %K = min(%@)", self, property, property];
[request setPredicate:searchFor];
-
+
return [[self class] executeFetchRequestAndReturnFirstObject:request inContext:context];
}
-+ (id)objectWithMinValueFor:(NSString *)property
++ (id)objectWithMinValueFor:(NSString *)property
{
return [[self class] objectWithMinValueFor:property inContext:[self currentContext]];
}
View
23 Code/CoreData/NSManagedObject+RKAdditions.h
@@ -0,0 +1,23 @@
+//
+// NSManagedObject+RKAdditions.h
+// RestKit
+//
+// Created by Blake Watters on 3/14/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+@class RKManagedObjectStore, RKManagedObjectMapping;
+
+/**
+ Provides extensions to NSManagedObject for various common tasks.
+ */
+@interface NSManagedObject (RKAdditions)
+
+/**
+ The receiver's managed object store.
+ */
+- (RKManagedObjectStore *)managedObjectStore;
+
+@end
View
19 Code/CoreData/NSManagedObject+RKAdditions.m
@@ -0,0 +1,19 @@
+//
+// NSManagedObject+RKAdditions.m
+// RestKit
+//
+// Created by Blake Watters on 3/14/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import "NSManagedObject+RKAdditions.h"
+#import "NSManagedObjectContext+RKAdditions.h"
+
+@implementation NSManagedObject (RKAdditions)
+
+- (RKManagedObjectStore *)managedObjectStore
+{
+ return self.managedObjectContext.managedObjectStore;
+}
+
+@end
View
23 Code/CoreData/NSManagedObjectContext+RKAdditions.h
@@ -0,0 +1,23 @@
+//
+// NSManagedObjectContext+RKAdditions.h
+// RestKit
+//
+// Created by Blake Watters on 3/14/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+@class RKManagedObjectStore;
+
+/**
+ Provides extensions to NSManagedObjectContext for various common tasks.
+ */
+@interface NSManagedObjectContext (RKAdditions)
+
+/**
+ The receiver's managed object store.
+ */
+@property (nonatomic, assign) RKManagedObjectStore *managedObjectStore;
+
+@end
View
26 Code/CoreData/NSManagedObjectContext+RKAdditions.m
@@ -0,0 +1,26 @@
+//
+// NSManagedObjectContext+RKAdditions.m
+// RestKit
+//
+// Created by Blake Watters on 3/14/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import <objc/runtime.h>
+#import "NSManagedObjectContext+RKAdditions.h"
+
+static char NSManagedObject_RKManagedObjectStoreAssociatedKey;
+
+@implementation NSManagedObjectContext (RKAdditions)
+
+- (RKManagedObjectStore *)managedObjectStore
+{
+ return (RKManagedObjectStore *) objc_getAssociatedObject(self, &NSManagedObject_RKManagedObjectStoreAssociatedKey);
+}
+
+- (void)setManagedObjectStore:(RKManagedObjectStore *)managedObjectStore
+{
+ objc_setAssociatedObject(self, &NSManagedObject_RKManagedObjectStoreAssociatedKey, managedObjectStore, OBJC_ASSOCIATION_ASSIGN);
+}
+
+@end
View
19 Code/CoreData/RKFetchRequestManagedObjectCache.h
@@ -0,0 +1,19 @@
+//
+// RKFetchRequestManagedObjectCache.h
+// RestKit
+//
+// Created by Jeff Arena on 1/24/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import "RKManagedObjectMappingCache.h"
+
+/**
+ Provides a simple managed object cache strategy in which every request for an object
+ is satisfied by dispatching an NSFetchRequest against the Core Data persistent store.
+ Performance can be disappointing for data sets with a large amount of redundant data
+ being mapped and connected together, but the memory footprint stays flat.
+ */
+@interface RKFetchRequestManagedObjectCache : NSObject <RKManagedObjectCacheing>
+
+@end
View
44 Code/CoreData/RKFetchRequestManagedObjectCache.m
@@ -0,0 +1,44 @@
+//
+// RKFetchRequestMappingCache.m
+// RestKit
+//
+// Created by Jeff Arena on 1/24/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import "RKFetchRequestManagedObjectCache.h"
+#import "NSManagedObject+ActiveRecord.h"
+#import "NSEntityDescription+RKAdditions.h"
+#import "RKLog.h"
+
+// Set Logging Component
+#undef RKLogComponent
+#define RKLogComponent lcl_cRestKitCoreData
+
+@implementation RKFetchRequestManagedObjectCache
+
+- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
+ withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
+ value:(id)primaryKeyValue
+ inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
+{
+ NSAssert(entity, @"Cannot find existing managed object without a target class");
+ NSAssert(primaryKeyAttribute, @"Cannot find existing managed object instance without mapping that defines a primaryKeyAttribute");
+ NSAssert(primaryKeyValue, @"Cannot find existing managed object by primary key without a value");
+ NSAssert(managedObjectContext, @"Cannot find existing managed object with a context");
+
+ NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
+ [fetchRequest setEntity:entity];
+ [fetchRequest setFetchLimit:1];
+ [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", primaryKeyAttribute, primaryKeyValue]];
+ NSArray *objects = [NSManagedObject executeFetchRequest:fetchRequest];
+ RKLogDebug(@"Found objects '%@' using fetchRequest '%@'", objects, fetchRequest);
+
+ NSManagedObject *object = nil;
+ if ([objects count] > 0) {
+ object = [objects objectAtIndex:0];
+ }
+ return object;
+}
+
+@end
View
83 Code/CoreData/RKInMemoryEntityCache.h
@@ -0,0 +1,83 @@
+//
+// RKInMemoryEntityCache.h
+// RestKit
+//
+// Created by Jeff Arena on 1/24/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+/**
+ Instances of RKInMemoryEntityCache provide an in-memory caching mechanism for
+ objects in a Core Data managed object context. Managed objects can be cached by
+ attribute for fast retrieval without repeatedly hitting the Core Data persistent store.
+ This can provide a substantial speed advantage over issuing fetch requests
+ in cases where repeated look-ups of the same data are performed using a small set
+ of attributes as the query key. Internally, the cache entries are maintained as
+ references to the NSManagedObjectID of corresponding cached objects.
+ */
+@interface RKInMemoryEntityCache : NSObject
+
+/**
+ The managed object context from which objects will be cached.
+ */
+@property(nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
+
+/// @name Initializing the Cache
+
+/**
+ Initializes the receiver with a managed object context containing the entity instances to be cached.
+
+ @param context The managed object context containing objects to be cached.
+ @returns self, initialized with context.
+ */
+- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
+
+/// @name Cacheing Objects by Attribute
+
+/**
+ Retrieves all objects within the cache
+ */
+- (NSMutableDictionary *)cachedObjectsForEntity:(NSEntityDescription *)entity
+ byAttribute:(NSString *)attributeName
+ inContext:(NSManagedObjectContext *)managedObjectContext;
+
+/**
+ */
+- (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity
+ withAttribute:(NSString *)attributeName
+ value:(id)attributeValue
+ inContext:(NSManagedObjectContext *)managedObjectContext;
+
+/**
+ Caches all instances of an entity in a given managed object context by the value
+ */
+- (void)cacheObjectsForEntity:(NSEntityDescription *)entity
+ byAttribute:(NSString *)attributeName
+ inContext:(NSManagedObjectContext *)managedObjectContext;
+
+/**
+ */
+- (void)cacheObject:(NSManagedObject *)managedObject
+ byAttribute:(NSString *)attributeName
+ inContext:(NSManagedObjectContext *)managedObjectContext;
+
+/**
+ */
+- (void)cacheObject:(NSEntityDescription *)entity
+ byAttribute:(NSString *)attributeName
+ value:(id)primaryKeyValue
+ inContext:(NSManagedObjectContext *)managedObjectContext;
+
+/**
+ */
+- (void)expireCacheEntryForObject:(NSManagedObject *)managedObject
+ byAttribute:(NSString *)attributeName
+ inContext:(NSManagedObjectContext *)managedObjectContext;
+
+/**
+ */
+- (void)expireCacheEntriesForEntity:(NSEntityDescription *)entity;
+
+@end
View
291 Code/CoreData/RKInMemoryEntityCache.m
@@ -0,0 +1,291 @@
+//
+// RKInMemoryEntityCache.m
+// RestKit
+//
+// Created by Jeff Arena on 1/24/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#endif
+
+#import "RKInMemoryEntityCache.h"
+#import "NSManagedObject+ActiveRecord.h"
+#import "../ObjectMapping/RKObjectPropertyInspector.h"
+#import "RKObjectPropertyInspector+CoreData.h"
+#import "RKLog.h"
+
+// Set Logging Component
+#undef RKLogComponent
+#define RKLogComponent lcl_cRestKitCoreData
+
+@interface RKInMemoryEntityCache ()
+@property(nonatomic, retain) NSMutableArray *attributeCaches;
+
+@property(nonatomic, retain) NSMutableDictionary *entityCache;
+
+- (BOOL)shouldCoerceAttributeToString:(NSString *)attribute forEntity:(NSEntityDescription *)entity;
+- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)managedObjectContext;
+@end
+
+@implementation RKInMemoryEntityCache
+
+@synthesize entityCache = _entityCache;
+@synthesize managedObjectContext = _managedObjectContext;
+@synthesize attributeCaches = _attributeCaches;
+
+- (id)init {
+ self = [super init];
+ if (self) {
+ _entityCache = [[NSMutableDictionary alloc] init];
+#if TARGET_OS_IPHONE
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(didReceiveMemoryWarning)
+ name:UIApplicationDidReceiveMemoryWarningNotification
+ object:nil];
+#endif
+ }
+ return self;
+}
+
+- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
+{
+ self = [self init];
+ if (self) {
+ _managedObjectContext = [managedObjectContext retain];
+ }
+
+ return self;
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [_entityCache release];
+ [_managedObjectContext release];
+ [_attributeCaches release];
+
+ [super dealloc];
+}
+
+- (void)didReceiveMemoryWarning {
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ object:nil];
+ [_entityCache removeAllObjects];
+}
+
+- (NSMutableDictionary *)cachedObjectsForEntity:(NSEntityDescription *)entity
+ byAttribute:(NSString *)attributeName
+ inContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(entity, @"Cannot retrieve cached objects without an entity");
+ NSAssert(attributeName, @"Cannot retrieve cached objects without an attributeName");
+ NSAssert(managedObjectContext, @"Cannot retrieve cached objects without a managedObjectContext");
+
+ NSMutableDictionary *cachedObjectsForEntity = [_entityCache objectForKey:entity.name];
+ if (cachedObjectsForEntity == nil) {
+ [self cacheObjectsForEntity:entity byAttribute:attributeName inContext:managedObjectContext];
+ cachedObjectsForEntity = [_entityCache objectForKey:entity.name];
+ }
+ return cachedObjectsForEntity;
+}
+
+- (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity
+ withAttribute:(NSString *)attributeName
+ value:(id)attributeValue
+ inContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(entity, @"Cannot retrieve a cached object without an entity");
+ NSAssert(attributeName, @"Cannot retrieve a cached object without a mapping");
+ NSAssert(attributeValue, @"Cannot retrieve a cached object without a primaryKeyValue");
+ NSAssert(managedObjectContext, @"Cannot retrieve a cached object without a managedObjectContext");
+
+ NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
+ byAttribute:attributeName
+ inContext:managedObjectContext];
+
+ // NOTE: We coerce the primary key into a string (if possible) for convenience. Generally
+ // primary keys are expressed either as a number of a string, so this lets us support either case interchangeably
+ id lookupValue = [attributeValue respondsToSelector:@selector(stringValue)] ? [attributeValue stringValue] : attributeValue;
+ NSManagedObjectID *objectID = [cachedObjectsForEntity objectForKey:lookupValue];
+ NSManagedObject *object = nil;
+ if (objectID) {
+ object = [self objectWithID:objectID inContext:managedObjectContext];
+ }
+ return object;
+}
+
+- (void)cacheObjectsForEntity:(NSEntityDescription *)entity
+ byAttribute:(NSString *)attributeName
+ inContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(entity, @"Cannot cache objects without an entity");
+ NSAssert(attributeName, @"Cannot cache objects without an attributeName");
+ NSAssert(managedObjectContext, @"Cannot cache objects without a managedObjectContext");
+
+ NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
+ [fetchRequest setEntity:entity];
+ [fetchRequest setResultType:NSManagedObjectIDResultType];
+
+ NSArray *objectIds = [NSManagedObject executeFetchRequest:fetchRequest inContext:managedObjectContext];
+ [fetchRequest release];
+
+ RKLogInfo(@"Caching all %ld %@ objectsIDs to thread local storage", (long) [objectIds count], entity.name);
+ NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
+ if ([objectIds count] > 0) {
+ BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity];
+ for (NSManagedObjectID* theObjectID in objectIds) {
+ NSManagedObject* theObject = [self objectWithID:theObjectID inContext:managedObjectContext];
+ id attributeValue = [theObject valueForKey:attributeName];
+ // Coerce to a string if possible
+ attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
+ if (attributeValue) {
+ [dictionary setObject:theObjectID forKey:attributeValue];
+ }
+ }
+ }
+ [_entityCache setObject:dictionary forKey:entity.name];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(objectsDidChange:)
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ object:managedObjectContext];
+}
+
+- (void)cacheObject:(NSManagedObject *)managedObject byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(managedObject, @"Cannot cache an object without a managedObject");
+ NSAssert(attributeName, @"Cannot cache an object without a mapping");
+ NSAssert(managedObjectContext, @"Cannot cache an object without a managedObjectContext");
+
+ NSManagedObjectID *objectID = [managedObject objectID];
+ if (objectID) {
+ NSEntityDescription *entity = managedObject.entity;
+ BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity];
+ id attributeValue = [managedObject valueForKey:attributeName];
+ // Coerce to a string if possible
+ attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
+ if (attributeValue) {
+ NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
+ byAttribute:attributeName
+ inContext:managedObjectContext];
+ [cachedObjectsForEntity setObject:objectID forKey:attributeValue];
+ }
+ }
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(objectsDidChange:)
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ object:managedObjectContext];
+}
+
+- (void)cacheObject:(NSEntityDescription *)entity byAttribute:(NSString *)attributeName value:(id)attributeValue inContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(entity, @"Cannot cache an object without an entity");
+ NSAssert(attributeName, @"Cannot cache an object without a mapping");
+ NSAssert(managedObjectContext, @"Cannot cache an object without a managedObjectContext");
+
+ // NOTE: We coerce the primary key into a string (if possible) for convenience. Generally
+ // primary keys are expressed either as a number or a string, so this lets us support either case interchangeably
+ id lookupValue = [attributeValue respondsToSelector:@selector(stringValue)] ? [attributeValue stringValue] : attributeValue;
+
+ NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
+ [fetchRequest setEntity:entity];
+ [fetchRequest setFetchLimit:1];
+ [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", attributeName, lookupValue]];
+ [fetchRequest setResultType:NSManagedObjectIDResultType];
+
+ NSArray *objectIds = [NSManagedObject executeFetchRequest:fetchRequest inContext:managedObjectContext];
+ [fetchRequest release];
+
+ NSManagedObjectID *objectID = nil;
+ if ([objectIds count] > 0) {
+ objectID = [objectIds objectAtIndex:0];
+ if (objectID && lookupValue) {
+ NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
+ byAttribute:attributeName
+ inContext:managedObjectContext];
+ [cachedObjectsForEntity setObject:objectID forKey:lookupValue];
+ }
+ }
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(objectsDidChange:)
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ object:managedObjectContext];
+}
+
+- (void)expireCacheEntryForObject:(NSManagedObject *)managedObject byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(managedObject, @"Cannot expire cache entry for an object without a managedObject");
+ NSAssert(attributeName, @"Cannot expire cache entry for an object without a mapping");
+ NSAssert(managedObjectContext, @"Cannot expire cache entry for an object without a managedObjectContext");
+
+ NSEntityDescription *entity = managedObject.entity;
+ BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity];
+ id attributeValue = [managedObject valueForKey:attributeName];
+ // Coerce to a string if possible
+ attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
+ if (attributeValue) {
+ NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
+ byAttribute:attributeName
+ inContext:managedObjectContext];
+ [cachedObjectsForEntity removeObjectForKey:attributeValue];
+
+ if ([cachedObjectsForEntity count] == 0) {
+ [self expireCacheEntriesForEntity:entity];
+ }
+ }
+}
+
+- (void)expireCacheEntriesForEntity:(NSEntityDescription *)entity {
+ NSAssert(entity, @"Cannot expire cache entry for an entity without an entity");
+ RKLogTrace(@"About to expire cache for entity name=%@", entity.name);
+ [_entityCache removeObjectForKey:entity.name];
+}
+
+
+#pragma mark Helper Methods
+
+- (BOOL)shouldCoerceAttributeToString:(NSString *)attribute forEntity:(NSEntityDescription *)entity {
+ Class attributeType = [[RKObjectPropertyInspector sharedInspector] typeForProperty:attribute ofEntity:entity];
+ return [attributeType instancesRespondToSelector:@selector(stringValue)];
+}
+
+- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(objectID, @"Cannot fetch a managedObject with a nil objectID");
+ NSAssert(managedObjectContext, @"Cannot fetch a managedObject with a nil managedObjectContext");
+ /*
+ NOTE:
+ We use existingObjectWithID: as opposed to objectWithID: as objectWithID: can return us a fault
+ that will raise an exception when fired. existingObjectWithID:error: will return nil if the ID has been
+ deleted. objectRegisteredForID: is also an acceptable approach.
+ */
+ NSError *error = nil;
+ NSManagedObject *object = [managedObjectContext existingObjectWithID:objectID error:&error];
+ if (! object && error) {
+ RKLogError(@"Failed to retrieve managed object with ID %@: %@", objectID, error);
+ }
+
+ return object;
+}
+
+
+#pragma mark Notifications
+
+- (void)objectsDidChange:(NSNotification *)notification {
+ NSDictionary *userInfo = notification.userInfo;
+ NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
+ NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
+ RKLogTrace(@"insertedObjects=%@, deletedObjects=%@", insertedObjects, deletedObjects);
+
+ NSMutableSet *entitiesToExpire = [NSMutableSet set];
+ for (NSManagedObject *object in insertedObjects) {
+ [entitiesToExpire addObject:object.entity];
+ }
+
+ for (NSManagedObject *object in deletedObjects) {
+ [entitiesToExpire addObject:object.entity];
+ }
+
+ for (NSEntityDescription *entity in entitiesToExpire) {
+ [self expireCacheEntriesForEntity:entity];
+ }
+}
+
+@end
View
19 Code/CoreData/RKInMemoryManagedObjectCache.h
@@ -0,0 +1,19 @@
+//
+// RKInMemoryManagedObjectCache.h
+// RestKit
+//
+// Created by Jeff Arena on 1/24/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import "RKManagedObjectMappingCache.h"
+#import "RKInMemoryEntityCache.h"
+
+/**
+ Provides a fast managed object cache where-in object instances are retained in
+ memory to avoid hitting the Core Data persistent store. Performance is greatly
+ increased over fetch request based strategy at the expense of memory consumption.
+ */
+@interface RKInMemoryManagedObjectCache : NSObject <RKManagedObjectCacheing>
+
+@end
View
43 Code/CoreData/RKInMemoryManagedObjectCache.m
@@ -0,0 +1,43 @@
+//
+// RKInMemoryManagedObjectCache.m
+// RestKit
+//
+// Created by Jeff Arena on 1/24/12.
+// Copyright (c) 2009-2012 RestKit. All rights reserved.
+//
+
+#import "RKInMemoryManagedObjectCache.h"
+#import "RKLog.h"
+
+// Set Logging Component
+#undef RKLogComponent
+#define RKLogComponent lcl_cRestKitCoreData
+
+static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey = @"RKInMemoryObjectManagedObjectCacheThreadDictionaryKey";
+
+@implementation RKInMemoryManagedObjectCache
+
+- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
+ withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
+ value:(id)primaryKeyValue
+ inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext {
+ NSAssert(entity, @"Cannot find existing managed object without a target class");
+ NSAssert(primaryKeyAttribute, @"Cannot find existing managed object instance without mapping");
+ NSAssert(primaryKeyValue, @"Cannot find existing managed object by primary key without a value");
+ NSAssert(managedObjectContext, @"Cannot find existing managed object with a context");
+ NSMutableDictionary *contextDictionary = [[[NSThread currentThread] threadDictionary] objectForKey:RKInMemoryObjectManagedObjectCacheThreadDictionaryKey];
+ if (! contextDictionary) {
+ contextDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
+ [[[NSThread currentThread] threadDictionary] setObject:contextDictionary forKey:RKInMemoryObjectManagedObjectCacheThreadDictionaryKey];
+ }
+ NSNumber *hashNumber = [NSNumber numberWithUnsignedInteger:[managedObjectContext hash]];
+ RKInMemoryEntityCache *cache = [contextDictionary objectForKey:hashNumber];
+ if (! cache) {
+ cache = [[RKInMemoryEntityCache alloc] initWithManagedObjectContext:managedObjectContext];
+ [contextDictionary setObject:cache forKey:hashNumber];
+ [cache release];
+ }
+ return [cache cachedObjectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue inContext:managedObjectContext];
+}
+
+@end
View
33 Code/CoreData/RKManagedObjectCache.h
@@ -1,33 +0,0 @@
-/*
- * RKManagedObjectCache.h
- * RestKit
- *
- * Created by Jeff Arena on 10/15/10.
- * Copyright 2010 GateGuru. All rights reserved.
- *
- */
-
-/**
- * Class used for determining the set of NSFetchRequest objects that
- * map to a given request URL.
- */
-@protocol RKManagedObjectCache
-@required
-
-/**
- * Must return an array containing NSFetchRequests for use in retrieving locally
- * cached objects associated with a given request resourcePath.
- */
-- (NSArray*)fetchRequestsForResourcePath:(NSString*)resourcePath;
-
-@optional
-
-/**
- * When the managed object cache is compared to objects from a resource path
- * payload, objects that are in the cache and not returned by the resource
- * path are normally deleted. By returning NO from this method you can prevent
- * the deletion of a given object.
- */
-- (BOOL)shouldDeleteOrphanedObject:(NSManagedObject*)managedObject;
-
-@end
View
13 Code/CoreData/RKManagedObjectLoader.h
@@ -3,7 +3,7 @@
// RestKit
//
// Created by Blake Watters on 2/13/11.
-// Copyright 2011 Two Toasters
+// Copyright (c) 2009-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.
@@ -28,11 +28,20 @@
concerns imposed by Core Data.
*/
@interface RKManagedObjectLoader : RKObjectLoader {
+ RKManagedObjectStore *_objectStore;
NSManagedObjectID* _targetObjectID;
NSMutableSet* _managedObjectKeyPaths;
BOOL _deleteObjectOnFailure;
}
-@property (nonatomic, readonly) RKManagedObjectStore* objectStore;
+/**
+ A reference to a RestKit managed object store for interacting with Core Data
+
+ @see RKManagedObjectStore
+ */
+@property (nonatomic, retain) RKManagedObjectStore* objectStore;
+
++ (id)loaderWithURL:(RKURL *)URL mappingProvider:(RKObjectMappingProvider *)mappingProvider objectStore:(RKManagedObjectStore *)objectStore;
+- (id)initWithURL:(RKURL *)URL mappingProvider:(RKObjectMappingProvider *)mappingProvider objectStore:(RKManagedObjectStore *)objectStore;
@end
View
92 Code/CoreData/RKManagedObjectLoader.m
@@ -3,7 +3,7 @@
// RestKit
//
// Created by Blake Watters on 2/13/11.
-// Copyright 2011 Two Toasters
+// Copyright (c) 2009-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.
@@ -26,15 +26,36 @@
#import "NSManagedObject+ActiveRecord.h"
#import "RKObjectLoader_Internals.h"
#import "RKRequest_Internals.h"
+#import "RKObjectMappingProvider+CoreData.h"
#import "RKLog.h"
+// Set Logging Component
+#undef RKLogComponent
+#define RKLogComponent lcl_cRestKitCoreData
+
@implementation RKManagedObjectLoader
-- (id)init {
- self = [super init];
+@synthesize objectStore = _objectStore;
+
++ (id)loaderWithURL:(RKURL *)URL mappingProvider:(RKObjectMappingProvider *)mappingProvider objectStore:(RKManagedObjectStore *)objectStore {
+ return [[[self alloc] initWithURL:URL mappingProvider:mappingProvider objectStore:objectStore] autorelease];
+}
+
+- (id)initWithURL:(RKURL *)URL mappingProvider:(RKObjectMappingProvider *)mappingProvider objectStore:(RKManagedObjectStore *)objectStore {
+ self = [self initWithURL:URL mappingProvider:mappingProvider];
+ if (self) {
+ _objectStore = [objectStore retain];
+ }
+
+ return self;
+}
+
+- (id)initWithURL:(RKURL *)URL mappingProvider:(RKObjectMappingProvider *)mappingProvider {
+ self = [super initWithURL:URL mappingProvider:mappingProvider];
if (self) {
_managedObjectKeyPaths = [[NSMutableSet alloc] init];
}
+
return self;
}
@@ -43,6 +64,7 @@ - (void)dealloc {
_targetObjectID = nil;
_deleteObjectOnFailure = NO;
[_managedObjectKeyPaths release];
+ [_objectStore release];
[super dealloc];
}
@@ -53,10 +75,6 @@ - (void)reset {
_targetObjectID = nil;
}
-- (RKManagedObjectStore*)objectStore {
- return self.objectManager.objectStore;
-}
-
#pragma mark - RKObjectMapperDelegate methods
- (void)objectMapper:(RKObjectMapper*)objectMapper didMapFromObject:(id)sourceObject toObject:(id)destinationObject atKeyPath:(NSString*)keyPath usingMapping:(RKObjectMapping*)objectMapping {
@@ -95,38 +113,35 @@ - (BOOL)prepareURLRequest {
// set before the managed object store.
if (self.targetObject && [self.targetObject isKindOfClass:[NSManagedObject class]]) {
_deleteObjectOnFailure = [(NSManagedObject*)self.targetObject isNew];
- [self.objectStore save];
+ [self.objectStore save:nil];
_targetObjectID = [[(NSManagedObject*)self.targetObject objectID] retain];
}
return [super prepareURLRequest];
}
+- (NSArray *)cachedObjects {
+ NSFetchRequest *fetchRequest = [self.mappingProvider fetchRequestForResourcePath:self.resourcePath];
+ if (fetchRequest) {
+ return [NSManagedObject objectsWithFetchRequest:fetchRequest];
+ }
+
+ return nil;
+}
+
- (void)deleteCachedObjectsMissingFromResult:(RKObjectMappingResult*)result {
if (! [self isGET]) {
RKLogDebug(@"Skipping cleanup of objects via managed object cache: only used for GET requests.");
return;
}
if ([self.URL isKindOfClass:[RKURL class]]) {
- RKURL* rkURL = (RKURL*)self.URL;
-
- NSArray* results = [result asCollection];
- NSArray* cachedObjects = [self.objectStore objectsForResourcePath:rkURL.resourcePath];
- NSObject<RKManagedObjectCache>* managedObjectCache = self.objectStore.managedObjectCache;
- BOOL queryForDeletion = [managedObjectCache respondsToSelector:@selector(shouldDeleteOrphanedObject:)];
-
+ NSArray *results = [result asCollection];
+ NSArray *cachedObjects = [self cachedObjects];
for (id object in cachedObjects) {
if (NO == [results containsObject:object]) {
- if (queryForDeletion && [managedObjectCache shouldDeleteOrphanedObject:object] == NO)
- {
- RKLogTrace(@"Sparing orphaned object %@ even though not returned in result set", object);
- }
- else
- {
RKLogTrace(@"Deleting orphaned object %@: not found in result set and expected at this resource path", object);
- [[self.objectStore managedObjectContext] deleteObject:object];
- }
+ [[self.objectStore managedObjectContextForCurrentThread] deleteObject:object];
}
}
} else {
@@ -140,21 +155,21 @@ - (void)processMappingResult:(RKObjectMappingResult*)result {
if (_targetObjectID && self.targetObject && self.method == RKRequestMethodDELETE) {
NSManagedObject* backgroundThreadObject = [self.objectStore objectWithID:_targetObjectID];
RKLogInfo(@"Deleting local object %@ due to DELETE request", backgroundThreadObject);
- [[self.objectStore managedObjectContext] deleteObject:backgroundThreadObject];
+ [[self.objectStore managedObjectContextForCurrentThread] deleteObject:backgroundThreadObject];
}
// If the response was successful, save the store...
if ([self.response isSuccessful]) {
[self deleteCachedObjectsMissingFromResult:result];
- NSError* error = [self.objectStore save];
- if (error) {
+ NSError *error = nil;
+ BOOL success = [self.objectStore save:&error];
+ if (! success) {
RKLogError(@"Failed to save managed object context after mapping completed: %@", [error localizedDescription]);
- NSMethodSignature* signature = [self.delegate methodSignatureForSelector:@selector(objectLoader:didFailWithError:)];
+ NSMethodSignature* signature = [(NSObject *)self methodSignatureForSelector:@selector(informDelegateOfError:)];
RKManagedObjectThreadSafeInvocation* invocation = [RKManagedObjectThreadSafeInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self.delegate];
- [invocation setSelector:@selector(objectLoader:didFailWithError:)];
- [invocation setArgument:&self atIndex:2];
- [invocation setArgument:&error atIndex:3];
+ [invocation setSelector:@selector(informDelegateOfError:)];
+ [invocation setArgument:&error atIndex:2];
[invocation invokeOnMainThread];
return;
}
@@ -180,8 +195,8 @@ - (void)handleResponseError {
RKLogInfo(@"Error response encountered: Deleting existing managed object with ID: %@", _targetObjectID);
NSManagedObject* objectToDelete = [self.objectStore objectWithID:_targetObjectID];
if (objectToDelete) {
- [[self.objectStore managedObjectContext] deleteObject:objectToDelete];
- [self.objectStore save];
+ [[self.objectStore managedObjectContextForCurrentThread] deleteObject:objectToDelete];
+ [self.objectStore save:nil];
} else {
RKLogWarning(@"Unable to delete existing managed object with ID: %@. Object not found in the store.", _targetObjectID);
}
@@ -191,4 +206,17 @@ - (void)handleResponseError {
}
}
+- (BOOL)isResponseMappable {
+ if ([self.response wasLoadedFromCache]) {
+ NSArray* cachedObjects = [self cachedObjects];
+ if (! cachedObjects) {
+ RKLogDebug(@"Skipping managed object mapping optimization -> Managed object cache returned nil cachedObjects for resourcePath: %@", self.resourcePath);
+ return [super isResponseMappable];
+ }
+ [self informDelegateOfObjectLoadWithResultDictionary:[NSDictionary dictionaryWithObject:cachedObjects forKey:@""]];
+ return NO;
+ }
+ return [super isResponseMappable];
+}
+
@end
View
104 Code/CoreData/RKManagedObjectMapping.h
@@ -3,7 +3,7 @@
// RestKit
//
// Created by Blake Watters on 5/31/11.
-// Copyright 2011 Two Toasters
+// Copyright (c) 2009-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.
@@ -20,41 +20,69 @@
#import <CoreData/CoreData.h>
#import "RKObjectMapping.h"
+//#import "RKManagedObjectStore.h"
+@class RKManagedObjectStore;
+
+/**
+ An RKManagedObjectMapping defines an object mapping with a Core Data destination
+ entity.
+ */
@interface RKManagedObjectMapping : RKObjectMapping {
- NSEntityDescription* _entity;