Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'release/0.10.1'

  • Loading branch information...
commit 55561809fd77a93da3d9b5374059dcd0c7fc58b7 2 parents 8d0d9fc + 8647fa5
@blakewatters blakewatters authored
Showing with 12,870 additions and 5,500 deletions.
  1. +2 −3 .gitignore
  2. +1 −1  .gitmodules
  3. +76 −29 CREDITS.md
  4. +15 −5 Code/CoreData/CoreData.h
  5. +93 −0 Code/CoreData/NSEntityDescription+RKAdditions.h
  6. +107 −0 Code/CoreData/NSEntityDescription+RKAdditions.m
  7. +43 −9 Code/CoreData/NSManagedObject+ActiveRecord.h
  8. +387 −355 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. +180 −0 Code/CoreData/RKEntityByAttributeCache.h
  14. +279 −0 Code/CoreData/RKEntityByAttributeCache.m
  15. +133 −0 Code/CoreData/RKEntityCache.h
  16. +141 −0 Code/CoreData/RKEntityCache.m
  17. +19 −0 Code/CoreData/RKFetchRequestManagedObjectCache.h
  18. +65 −0 Code/CoreData/RKFetchRequestManagedObjectCache.m
  19. +18 −0 Code/CoreData/RKInMemoryManagedObjectCache.h
  20. +77 −0 Code/CoreData/RKInMemoryManagedObjectCache.m
  21. +0 −33 Code/CoreData/RKManagedObjectCache.h
  22. +60 −0 Code/CoreData/RKManagedObjectCaching.h
  23. +15 −6 Code/CoreData/RKManagedObjectLoader.h
  24. +82 −50 Code/CoreData/RKManagedObjectLoader.m
  25. +97 −27 Code/CoreData/RKManagedObjectMapping.h
  26. +95 −35 Code/CoreData/RKManagedObjectMapping.m
  27. +5 −5 Code/CoreData/RKManagedObjectMappingOperation.h
  28. +76 −36 Code/CoreData/RKManagedObjectMappingOperation.m
  29. +51 −0 Code/CoreData/RKManagedObjectSearchEngine.h
  30. +100 −0 Code/CoreData/RKManagedObjectSearchEngine.m
  31. +5 −5 Code/CoreData/RKManagedObjectSeeder.h
  32. +55 −54 Code/CoreData/RKManagedObjectSeeder.m
  33. +64 −35 Code/CoreData/RKManagedObjectStore.h
  34. +278 −261 Code/CoreData/RKManagedObjectStore.m
  35. +4 −4 Code/CoreData/RKManagedObjectThreadSafeInvocation.h
  36. +29 −16 Code/CoreData/RKManagedObjectThreadSafeInvocation.m
  37. +54 −0 Code/CoreData/RKObjectMappingProvider+CoreData.h
  38. +31 −0 Code/CoreData/RKObjectMappingProvider+CoreData.m
  39. +4 −4 Code/CoreData/RKObjectPropertyInspector+CoreData.h
  40. +32 −11 Code/CoreData/RKObjectPropertyInspector+CoreData.m
  41. +40 −0 Code/CoreData/RKSearchWord.h
  42. +35 −0 Code/CoreData/RKSearchWord.m
  43. +34 −0 Code/CoreData/RKSearchWordObserver.h
  44. +70 −0 Code/CoreData/RKSearchWordObserver.m
  45. +119 −0 Code/CoreData/RKSearchableManagedObject.h
  46. +81 −0 Code/CoreData/RKSearchableManagedObject.m
  47. +14 −16 Specs/ObjectMapping/RKObjectiveCPlusPlusSpec.mm → Code/Network/NSData+RKAdditions.h
  48. +17 −17 Code/Network/{NSData+MD5.m → NSData+RKAdditions.m}
  49. +10 −18 Code/Network/NSDictionary+RKRequestSerialization.h
  50. +12 −49 Code/Network/NSDictionary+RKRequestSerialization.m
  51. +19 −0 Code/Network/NSObject+URLEncoding.h
  52. +24 −0 Code/Network/NSObject+URLEncoding.m
  53. +0 −48 Code/Network/NSString+MD5.m
  54. +4 −4 Code/Network/Network.h
  55. +599 −383 Code/Network/RKClient.h
  56. +194 −125 Code/Network/RKClient.m
  57. +18 −17 Code/Network/RKNotifications.h
  58. +11 −10 Code/Network/RKNotifications.m
  59. +235 −55 Code/Network/RKOAuthClient.h
  60. +38 −40 Code/Network/RKOAuthClient.m
  61. +140 −49 Code/Network/RKParams.h
  62. +113 −102 Code/Network/RKParams.m
  63. +99 −49 Code/Network/RKParamsAttachment.h
  64. +90 −91 Code/Network/RKParamsAttachment.m
  65. +204 −103 Code/Network/RKReachabilityObserver.h
  66. +124 −124 Code/Network/RKReachabilityObserver.m
  67. +588 −208 Code/Network/RKRequest.h
  68. +361 −255 Code/Network/RKRequest.m
  69. +166 −24 Code/Network/RKRequestCache.h
  70. +119 −272 Code/Network/RKRequestCache.m
  71. +223 −98 Code/Network/RKRequestQueue.h
  72. +224 −168 Code/Network/RKRequestQueue.m
  73. +55 −27 Code/Network/RKRequestSerializable.h
  74. +42 −16 Code/Network/RKRequestSerialization.h
  75. +11 −11 Code/Network/RKRequestSerialization.m
  76. +5 −4 Code/Network/RKRequest_Internals.h
  77. +211 −78 Code/Network/RKResponse.h
  78. +182 −157 Code/Network/RKResponse.m
  79. +210 −19 Code/Network/RKURL.h
  80. +117 −41 Code/Network/RKURL.m
  81. +4 −4 Code/ObjectMapping/ObjectMapping.h
  82. +39 −0 Code/ObjectMapping/RKConfigurationDelegate.h
  83. +14 −21 Code/ObjectMapping/RKDynamicObjectMapping.h
  84. +17 −62 Code/ObjectMapping/RKDynamicObjectMapping.m
  85. +30 −0 Code/ObjectMapping/RKDynamicObjectMappingMatcher.h
  86. +78 −0 Code/ObjectMapping/RKDynamicObjectMappingMatcher.m
  87. +4 −4 Code/ObjectMapping/RKErrorMessage.h
  88. +4 −4 Code/ObjectMapping/RKErrorMessage.m
  89. +7 −7 Code/ObjectMapping/RKMappingOperationQueue.h
  90. +2 −2 Code/ObjectMapping/RKMappingOperationQueue.m
  91. +6 −6 Code/ObjectMapping/RKObjectAttributeMapping.h
  92. +6 −6 Code/ObjectMapping/RKObjectAttributeMapping.m
  93. +132 −50 Code/ObjectMapping/RKObjectLoader.h
  94. +267 −146 Code/ObjectMapping/RKObjectLoader.m
  95. +7 −5 Code/ObjectMapping/RKObjectLoader_Internals.h
  96. +217 −143 Code/ObjectMapping/RKObjectManager.h
  97. +297 −192 Code/ObjectMapping/RKObjectManager.m
  98. +15 −18 Code/ObjectMapping/RKObjectMapper.h
  99. +160 −117 Code/ObjectMapping/RKObjectMapper.m
  100. +6 −6 Code/ObjectMapping/RKObjectMapperError.h
  101. +8 −8 Code/ObjectMapping/RKObjectMapper_Private.h
  102. +176 −147 Code/ObjectMapping/RKObjectMapping.h
  103. +109 −44 Code/ObjectMapping/RKObjectMapping.m
  104. +47 −7 Code/ObjectMapping/RKObjectMappingDefinition.h
  105. +16 −0 Code/ObjectMapping/RKObjectMappingDefinition.m
  106. +90 −28 Code/ObjectMapping/RKObjectMappingOperation.h
  107. +214 −114 Code/ObjectMapping/RKObjectMappingOperation.m
  108. +50 −0 Code/ObjectMapping/RKObjectMappingProvider+Contexts.h
  109. +131 −50 Code/ObjectMapping/RKObjectMappingProvider.h
  110. +227 −25 Code/ObjectMapping/RKObjectMappingProvider.m
  111. +19 −0 Code/ObjectMapping/RKObjectMappingProviderContextEntry.h
  112. +60 −0 Code/ObjectMapping/RKObjectMappingProviderContextEntry.m
  113. +4 −4 Code/ObjectMapping/RKObjectMappingResult.h
  114. +10 −10 Code/ObjectMapping/RKObjectMappingResult.m
  115. +281 −0 Code/ObjectMapping/RKObjectPaginator.h
  116. +220 −0 Code/ObjectMapping/RKObjectPaginator.m
  117. +11 −5 Code/ObjectMapping/RKObjectPropertyInspector.h
  118. +63 −63 Code/ObjectMapping/RKObjectPropertyInspector.m
  119. +8 −8 Code/ObjectMapping/RKObjectRelationshipMapping.h
  120. +7 −7 Code/ObjectMapping/RKObjectRelationshipMapping.m
  121. +22 −19 Code/ObjectMapping/RKObjectRouter.h
  122. +43 −24 Code/ObjectMapping/RKObjectRouter.m
  123. +4 −4 Code/ObjectMapping/RKObjectSerializer.h
  124. +19 −15 Code/ObjectMapping/RKObjectSerializer.m
  125. +48 −11 Code/ObjectMapping/RKParserRegistry.h
  126. +44 −19 Code/ObjectMapping/RKParserRegistry.m
  127. +4 −4 Code/ObjectMapping/RKRouter.h
  128. +5 −5 Code/RestKit.h
  129. +27 −0 Code/Support/NSArray+RKAdditions.h
  130. +57 −0 Code/Support/NSArray+RKAdditions.m
  131. +94 −0 Code/Support/NSBundle+RKAdditions.h
  132. +110 −0 Code/Support/NSBundle+RKAdditions.m
  133. +22 −7 Code/Support/NSDictionary+RKAdditions.h
  134. +52 −14 Code/Support/NSDictionary+RKAdditions.m
  135. +42 −26 Code/Support/{NSString+RestKit.h → NSString+RKAdditions.h}
  136. +70 −20 Code/Support/{NSString+RestKit.m → NSString+RKAdditions.m}
  137. +10 −10 Code/Support/{NSURL+RestKit.h → NSURL+RKAdditions.h}
  138. +10 −10 Code/Support/{NSURL+RestKit.m → NSURL+RKAdditions.m}
  139. +4 −4 Code/Support/Parsers/JSON/RKJSONParserJSONKit.h
  140. +13 −6 Code/Support/Parsers/JSON/RKJSONParserJSONKit.m
  141. +0 −37 Code/Support/Parsers/XML/RKXMLParserLibXML.h
  142. +0 −160 Code/Support/Parsers/XML/RKXMLParserLibXML.m
  143. +32 −0 Code/Support/Parsers/XML/RKXMLParserXMLReader.h
  144. +22 −0 Code/Support/Parsers/XML/RKXMLParserXMLReader.m
  145. +4 −4 Code/Support/RKAlert.h
  146. +6 −6 Code/Support/RKAlert.m
  147. +121 −0 Code/Support/RKBenchmark.h
  148. +119 −0 Code/Support/RKBenchmark.m
  149. +38 −0 Code/Support/RKCache.h
  150. +233 −0 Code/Support/RKCache.m
  151. +18 −3 Code/Support/RKDirectory.h
  152. +45 −43 Code/Support/RKDirectory.m
  153. +23 −11 Code/Support/RKDotNetDateFormatter.h
  154. +20 −2 Code/Support/RKDotNetDateFormatter.m
  155. +31 −10 Code/Support/{Errors.h → RKErrors.h}
  156. +9 −8 Code/Support/{Errors.m → RKErrors.m}
  157. +6 −6 Code/Support/RKFixCategoryBug.h
  158. +13 −0 Code/Support/RKISO8601DateFormatter.h
  159. +13 −0 Code/Support/RKISO8601DateFormatter.m
  160. +76 −24 Code/Support/RKLog.h
  161. +143 −5 Code/Support/RKLog.m
  162. +4 −4 Code/Support/RKMIMETypes.h
  163. +4 −4 Code/Support/RKMIMETypes.m
  164. +45 −0 Code/Support/RKMutableBlockDictionary.h
  165. +145 −0 Code/Support/RKMutableBlockDictionary.m
  166. +64 −0 Code/Support/RKOrderedDictionary.h
  167. +151 −0 Code/Support/RKOrderedDictionary.m
Sorry, we could not display the entire diff because too many files (895) changed.
View
5 .gitignore
@@ -7,6 +7,7 @@ Docs/API
# temp nibs and swap files
*~.nib
*.swp
+*.orig
# OS X folder attributes
.DS_Store
@@ -20,6 +21,4 @@ Docs/API
Examples/RKDiscussionBoardExample/discussion_board_backend/public/system/attachments/*
-# UISpecRunner cached build path
-.uispec.app
-Specs/Runner/UISpec
+test-reports/
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
20 Code/CoreData/CoreData.h
@@ -3,14 +3,14 @@
// 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.
// 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.
@@ -23,6 +23,16 @@
#import "NSManagedObject+ActiveRecord.h"
#import "RKManagedObjectStore.h"
#import "RKManagedObjectSeeder.h"
-#import "RKManagedObjectCache.h"
#import "RKManagedObjectMapping.h"
+#import "RKManagedObjectMappingOperation.h"
+#import "RKManagedObjectCaching.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
93 Code/CoreData/NSEntityDescription+RKAdditions.h
@@ -0,0 +1,93 @@
+//
+// 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;
+
+/**
+ The substitution variable used in predicateForPrimaryKeyAttribute.
+
+ **Value**: @"PRIMARY_KEY_VALUE"
+ @see predicateForPrimaryKeyAttribute
+ */
+extern NSString * const RKEntityDescriptionPrimaryKeyAttributeValuePredicateSubstitutionVariable;
+
+/**
+ 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 *primaryKeyAttributeName;
+
+/**
+ The attribute description object for the attribute designated as the primary key for the receiver.
+ */
+@property (nonatomic, readonly) NSAttributeDescription *primaryKeyAttribute;
+
+/**
+ The class representing the value of the attribute designated as the primary key for the receiver.
+ */
+@property (nonatomic, readonly) Class primaryKeyAttributeClass;
+
+/**
+ Returns a cached predicate specifying that the primary key attribute is equal to the $PRIMARY_KEY_VALUE
+ substitution variable.
+
+ This predicate is cached to avoid parsing overhead during object mapping operations
+ and must be evaluated using [NSPredicate predicateWithSubstitutionVariables:]
+
+ @return A cached predicate specifying the value of the primary key attribute is equal to the $PRIMARY_KEY_VALUE
+ substitution variable.
+ */
+- (NSPredicate *)predicateForPrimaryKeyAttribute;
+
+/**
+ Returns a predicate specifying that the value of the primary key attribute is equal to a given
+ value. This predicate is constructed by evaluating the cached predicate returned by the
+ predicateForPrimaryKeyAttribute with a dictionary of substitution variables specifying that
+ $PRIMARY_KEY_VALUE is equal to the given value.
+
+ **NOTE**: This method considers the type of the receiver's primary key attribute when constructing
+ the predicate. It will coerce the given value into either an NSString or an NSNumber as
+ appropriate. This behavior is a convenience to avoid annoying issues related to Core Data's
+ handling of predicates for NSString and NSNumber types that were not appropriately casted.
+
+ @return A predicate speciying that the value of the primary key attribute is equal to a given value.
+ */
+- (NSPredicate *)predicateForPrimaryKeyAttributeWithValue:(id)value;
+
+/**
+ Coerces the given value into the class representing the primary key. Currently support NSString
+ and NSNumber coercsions.
+
+ @bug **NOTE** This API is temporary and will be deprecated and replaced.
+ @since 0.10.1
+ */
+- (id)coerceValueForPrimaryKey:(id)primaryKeyValue;
+
+@end
View
107 Code/CoreData/NSEntityDescription+RKAdditions.m
@@ -0,0 +1,107 @@
+//
+// 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";
+NSString * const RKEntityDescriptionPrimaryKeyAttributeValuePredicateSubstitutionVariable = @"PRIMARY_KEY_VALUE";
+
+static char primaryKeyAttributeNameKey, primaryKeyPredicateKey;
+
+@implementation NSEntityDescription (RKAdditions)
+
+- (void)setPredicateForPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
+{
+ NSPredicate *predicate = (primaryKeyAttribute) ? [NSPredicate predicateWithFormat:@"%K == $PRIMARY_KEY_VALUE", primaryKeyAttribute] : nil;
+ objc_setAssociatedObject(self,
+ &primaryKeyPredicateKey,
+ predicate,
+ OBJC_ASSOCIATION_RETAIN);
+}
+
+#pragma mark - Public
+
+- (NSAttributeDescription *)primaryKeyAttribute
+{
+ return [[self attributesByName] valueForKey:[self primaryKeyAttributeName]];
+}
+
+- (Class)primaryKeyAttributeClass
+{
+ NSAttributeDescription *attributeDescription = [self primaryKeyAttribute];
+ if (attributeDescription) {
+ return NSClassFromString(attributeDescription.attributeValueClassName);
+ }
+
+ return nil;
+}
+
+- (NSString *)primaryKeyAttributeName
+{
+ // Check for an associative object reference
+ NSString *primaryKeyAttribute = (NSString *) objc_getAssociatedObject(self, &primaryKeyAttributeNameKey);
+
+ // Fall back to the userInfo dictionary
+ if (! primaryKeyAttribute) {
+ primaryKeyAttribute = [self.userInfo valueForKey:RKEntityDescriptionPrimaryKeyAttributeUserInfoKey];
+
+ // If we have loaded from the user info, ensure we have a predicate
+ if (! [self predicateForPrimaryKeyAttribute]) {
+ [self setPredicateForPrimaryKeyAttribute:primaryKeyAttribute];
+ }
+ }
+
+ return primaryKeyAttribute;
+}
+
+- (void)setPrimaryKeyAttributeName:(NSString *)primaryKeyAttributeName
+{
+ objc_setAssociatedObject(self,
+ &primaryKeyAttributeNameKey,
+ primaryKeyAttributeName,
+ OBJC_ASSOCIATION_RETAIN);
+ [self setPredicateForPrimaryKeyAttribute:primaryKeyAttributeName];
+}
+
+- (NSPredicate *)predicateForPrimaryKeyAttribute
+{
+ return (NSPredicate *) objc_getAssociatedObject(self, &primaryKeyPredicateKey);
+}
+
+- (id)coerceValueForPrimaryKey:(id)primaryKeyValue
+{
+ id searchValue = primaryKeyValue;
+ Class theClass = [self primaryKeyAttributeClass];
+ if (theClass) {
+ // TODO: This coercsion behavior should be pluggable and reused from the mapper
+ if ([theClass isSubclassOfClass:[NSNumber class]] && ![searchValue isKindOfClass:[NSNumber class]]) {
+ // Handle NSString -> NSNumber
+ if ([searchValue isKindOfClass:[NSString class]]) {
+ searchValue = [NSNumber numberWithDouble:[searchValue doubleValue]];
+ }
+ } else if ([theClass isSubclassOfClass:[NSString class]] && ![searchValue isKindOfClass:[NSString class]]) {
+ // Coerce to string
+ if ([searchValue respondsToSelector:@selector(stringValue)]) {
+ searchValue = [searchValue stringValue];
+ }
+ }
+ }
+
+ return searchValue;
+}
+
+- (NSPredicate *)predicateForPrimaryKeyAttributeWithValue:(id)value
+{
+ id substitutionValue = [self coerceValueForPrimaryKey:value];
+ NSDictionary *variables = [NSDictionary dictionaryWithObject:substitutionValue
+ forKey:RKEntityDescriptionPrimaryKeyAttributeValuePredicateSubstitutionVariable];
+ return [[self predicateForPrimaryKeyAttribute] predicateWithSubstitutionVariables:variables];
+}
+
+@end
View
52 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,22 +10,31 @@
#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
- * defaults to the subclass className, may be overridden
+ * The NSEntityDescription for the Subclass
+ * defaults to the subclass className, may be overridden
*/
+ (NSEntityDescription*)entity;
/**
- * Returns an initialized NSFetchRequest for the entity, with no predicate
+ * Returns an initialized NSFetchRequest for the entity, with no predicate
*/
+ (NSFetchRequest*)fetchRequest;
@@ -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.
*/
@@ -79,7 +94,7 @@
+ (NSUInteger)count DEPRECATED_ATTRIBUTE;
/**
- * Creates a new managed object and inserts it into the managedObjectContext.
+ * Creates a new managed object and inserts it into the managedObjectContext.
*/
+ (id)object;
@@ -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
742 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,86 +22,114 @@
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)
-+ (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];
+@implementation NSManagedObjectContext (ActiveRecord)
+
++ (NSManagedObjectContext *)defaultContext {
+ return defaultContext;
+}
+
++ (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]];
+ NSString* className = [NSString stringWithCString:class_getName([self class]) encoding:NSASCIIStringEncoding];
+ return [NSEntityDescription entityForName:className inManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]];
}
+ (NSFetchRequest*)fetchRequest {
- NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
- NSEntityDescription *entity = [self entity];
- [fetchRequest setEntity:entity];
- return fetchRequest;
+ NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
+ NSEntityDescription *entity = [self entity];
+ [fetchRequest setEntity:entity];
+ return fetchRequest;
}
+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest {
- NSError* error = nil;
- NSArray* objects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
- if (objects == nil) {
- RKLogError(@"Error: %@", [error localizedDescription]);
- }
- return objects;
+ NSError* error = nil;
+ 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) {
- [mutableObjectArray addObjectsFromArray:[self objectsWithFetchRequest:fetchRequest]];
- }
- NSArray* objects = [NSArray arrayWithArray:mutableObjectArray];
- [mutableObjectArray release];
- return objects;
+ NSMutableArray* mutableObjectArray = [[NSMutableArray alloc] init];
+ for (NSFetchRequest* fetchRequest in fetchRequests) {
+ [mutableObjectArray addObjectsFromArray:[self objectsWithFetchRequest:fetchRequest]];
+ }
+ NSArray* objects = [NSArray arrayWithArray:mutableObjectArray];
+ [mutableObjectArray release];
+ return objects;
}
+ (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest {
- [fetchRequest setFetchLimit:1];
- NSArray* objects = [self objectsWithFetchRequest:fetchRequest];
- if ([objects count] == 0) {
- return nil;
- } else {
- return [objects objectAtIndex:0];
- }
+ [fetchRequest setFetchLimit:1];
+ NSArray* objects = [self objectsWithFetchRequest:fetchRequest];
+ if ([objects count] == 0) {
+ return nil;
+ } else {
+ return [objects objectAtIndex:0];
+ }
}
+ (NSArray*)objectsWithPredicate:(NSPredicate*)predicate {
- NSFetchRequest* fetchRequest = [self fetchRequest];
- [fetchRequest setPredicate:predicate];
- return [self objectsWithFetchRequest:fetchRequest];
+ NSFetchRequest* fetchRequest = [self fetchRequest];
+ [fetchRequest setPredicate:predicate];
+ return [self objectsWithFetchRequest:fetchRequest];
}
+ (id)objectWithPredicate:(NSPredicate*)predicate {
- NSFetchRequest* fetchRequest = [self fetchRequest];
- [fetchRequest setPredicate:predicate];
- return [self objectWithFetchRequest:fetchRequest];
+ NSFetchRequest* fetchRequest = [self fetchRequest];
+ [fetchRequest setPredicate:predicate];
+ return [self objectWithFetchRequest:fetchRequest];
}
+ (NSArray*)allObjects {
- return [self objectsWithPredicate:nil];
+ return [self objectsWithPredicate:nil];
}
+ (NSUInteger)count:(NSError**)error {
- NSFetchRequest* fetchRequest = [self fetchRequest];
- return [[self managedObjectContext] countForFetchRequest:fetchRequest error:error];
+ NSFetchRequest* fetchRequest = [self fetchRequest];
+ return [[NSManagedObjectContext contextForCurrentThread] countForFetchRequest:fetchRequest error:error];
}
+ (NSUInteger)count {
- NSError *error = nil;
- return [self count:&error];
+ NSError *error = nil;
+ return [self count:&error];
}
+ (id)object {
- id object = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:[self managedObjectContext]];
- return [object autorelease];
+ id object = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]];
+ return [object autorelease];
}
- (BOOL)isNew {
@@ -110,104 +137,113 @@ - (BOOL)isNew {
return [vals count] == 0;
}
++ (id)findByPrimaryKey:(id)primaryKeyValue inContext:(NSManagedObjectContext *)context {
+ NSPredicate *predicate = [[self entityDescriptionInContext:context] predicateForPrimaryKeyAttributeWithValue:primaryKeyValue];
+ if (! predicate) {
+ RKLogWarning(@"Attempt to findByPrimaryKey for entity with nil primaryKeyAttribute. Set the primaryKeyAttributeName and try again! %@", self);
+ return nil;
+ }
+
+ return [self findFirstWithPredicate:predicate 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
{
- @synchronized(defaultBatchSize)
- {
- defaultBatchSize = [NSNumber numberWithUnsignedInteger:newBatchSize];
- }
+ @synchronized(defaultBatchSize)
+ {
+ defaultBatchSize = [NSNumber numberWithUnsignedInteger:newBatchSize];
+ }
}
+ (NSInteger)defaultBatchSize
{
- if (defaultBatchSize == nil)
- {
- [self setDefaultBatchSize:kActiveRecordDefaultBatchSize];
- }
- return [defaultBatchSize integerValue];
+ if (defaultBatchSize == nil)
+ {
+ [self setDefaultBatchSize:kActiveRecordDefaultBatchSize];
+ }
+ return [defaultBatchSize integerValue];
}
+ (void)handleErrors:(NSError *)error
{
- if (error)
- {
- NSDictionary *userInfo = [error userInfo];
- for (NSArray *detailedError in [userInfo allValues])
- {
- if ([detailedError isKindOfClass:[NSArray class]])
- {
- for (NSError *e in detailedError)
- {
- if ([e respondsToSelector:@selector(userInfo)])
- {
- RKLogError(@"Error Details: %@", [e userInfo]);
- }
- else
- {
- RKLogError(@"Error Details: %@", e);
- }
- }
- }
- else
- {
- RKLogError(@"Error: %@", detailedError);
- }
- }
- RKLogError(@"Error Domain: %@", [error domain]);
- RKLogError(@"Recovery Suggestion: %@", [error localizedRecoverySuggestion]);
- }
-}
-
-//- (void)handleErrors:(NSError *)error
-//{
-// [[self class] handleErrors:error];
-//}
+ if (error)
+ {
+ NSDictionary *userInfo = [error userInfo];
+ for (NSArray *detailedError in [userInfo allValues])
+ {
+ if ([detailedError isKindOfClass:[NSArray class]])
+ {
+ for (NSError *e in detailedError)
+ {
+ if ([e respondsToSelector:@selector(userInfo)])
+ {
+ RKLogError(@"Error Details: %@", [e userInfo]);
+ }
+ else
+ {
+ RKLogError(@"Error Details: %@", e);
+ }
+ }
+ }
+ else
+ {
+ RKLogError(@"Error: %@", detailedError);
+ }
+ }
+ RKLogError(@"Error Domain: %@", [error domain]);
+ RKLogError(@"Recovery Suggestion: %@", [error localizedRecoverySuggestion]);
+ }
+}
+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context
{
- NSError *error = nil;
-
- NSArray *results = [context executeFetchRequest:request error:&error];
- [self handleErrors:error];
- return results;
+ NSError *error = nil;
+
+ NSArray *results = [context executeFetchRequest:request error:&error];
+ [self handleErrors:error];
+ return results;
}
-+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request
++ (NSArray *)executeFetchRequest:(NSFetchRequest *)request
{
- return [self executeFetchRequest:request inContext:[self currentContext]];
+ 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)
- {
- return nil;
- }
- return [results objectAtIndex:0];
+ [request setFetchLimit:1];
+
+ NSArray *results = [self executeFetchRequest:request inContext:context];
+ if ([results count] == 0)
+ {
+ return nil;
+ }
+ return [results objectAtIndex:0];
}
+ (id)executeFetchRequestAndReturnFirstObject:(NSFetchRequest *)request
{
- return [self executeFetchRequestAndReturnFirstObject:request inContext:[self currentContext]];
+ return [self executeFetchRequestAndReturnFirstObject:request inContext:[self currentContext]];
}
#if TARGET_OS_IPHONE
+ (void)performFetch:(NSFetchedResultsController *)controller
{
- NSError *error = nil;
- if (![controller performFetch:&error])
- {
- [self handleErrors:error];
- }
+ NSError *error = nil;
+ if (![controller performFetch:&error])
+ {
+ [self handleErrors:error];
+ }
}
#endif
@@ -219,84 +255,84 @@ + (NSEntityDescription *)entityDescriptionInContext:(NSManagedObjectContext *)co
+ (NSEntityDescription *)entityDescription
{
- return [self entityDescriptionInContext:[self currentContext]];
+ return [self entityDescriptionInContext:[self currentContext]];
}
+ (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];
- if (property)
- {
- [propertiesWanted addObject:property];
- }
- else
- {
- RKLogError(@"Property '%@' not found in %@ properties for %@", propertyName, [propDict count], NSStringFromClass(self));
- }
- }
- }
- return propertiesWanted;
+ NSEntityDescription *description = [self entityDescription];
+ NSMutableArray *propertiesWanted = [NSMutableArray array];
+
+ if (properties)
+ {
+ NSDictionary *propDict = [description propertiesByName];
+
+ for (NSString *propertyName in properties)
+ {
+ NSPropertyDescription *property = [propDict objectForKey:propertyName];
+ if (property)
+ {
+ [propertiesWanted addObject:property];
+ }
+ else
+ {
+ RKLogError(@"Property '%@' not found in %@ properties for %@", propertyName, [propDict count], NSStringFromClass(self));
+ }
+ }
+ }
+ return propertiesWanted;
}
+ (NSArray *)sortAscending:(BOOL)ascending attributes:(id)attributesToSortBy, ...
{
- NSMutableArray *attributes = [NSMutableArray array];
-
- if ([attributesToSortBy isKindOfClass:[NSArray class]])
- {
- id attributeName;
- va_list variadicArguments;
- va_start(variadicArguments, attributesToSortBy);
- while ((attributeName = va_arg(variadicArguments, id))!= nil)
- {
- NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:attributeName ascending:ascending];
- [attributes addObject:sortDescriptor];
- [sortDescriptor release];
- }
- va_end(variadicArguments);
-
- }
- else if ([attributesToSortBy isKindOfClass:[NSString class]])
- {
- va_list variadicArguments;
- va_start(variadicArguments, attributesToSortBy);
- [attributes addObject:[[[NSSortDescriptor alloc] initWithKey:attributesToSortBy ascending:ascending] autorelease] ];
- va_end(variadicArguments);
- }
-
- return attributes;
+ NSMutableArray *attributes = [NSMutableArray array];
+
+ if ([attributesToSortBy isKindOfClass:[NSArray class]])
+ {
+ id attributeName;
+ va_list variadicArguments;
+ va_start(variadicArguments, attributesToSortBy);
+ while ((attributeName = va_arg(variadicArguments, id))!= nil)
+ {
+ NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:attributeName ascending:ascending];
+ [attributes addObject:sortDescriptor];
+ [sortDescriptor release];
+ }
+ va_end(variadicArguments);
+
+ }
+ else if ([attributesToSortBy isKindOfClass:[NSString class]])
+ {
+ va_list variadicArguments;
+ va_start(variadicArguments, attributesToSortBy);
+ [attributes addObject:[[[NSSortDescriptor alloc] initWithKey:attributesToSortBy ascending:ascending] autorelease] ];
+ va_end(variadicArguments);
+ }
+
+ return attributes;
}
+ (NSArray *)ascendingSortDescriptors:(id)attributesToSortBy, ...
{
- return [self sortAscending:YES attributes:attributesToSortBy];
+ return [self sortAscending:YES attributes:attributesToSortBy];
}
+ (NSArray *)descendingSortDescriptors:(id)attributesToSortyBy, ...
{
- return [self sortAscending:NO attributes:attributesToSortyBy];
+ return [self sortAscending:NO attributes:attributesToSortyBy];
}
+ (NSFetchRequest *)createFetchRequestInContext:(NSManagedObjectContext *)context
{
- NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
- [request setEntity:[self entityDescriptionInContext:context]];
-
- return request;
+ NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
+ [request setEntity:[self entityDescriptionInContext:context]];
+
+ return request;
}
+ (NSFetchRequest *)createFetchRequest
{
- return [self createFetchRequestInContext:[self currentContext]];
+ return [self createFetchRequestInContext:[self currentContext]];
}
#pragma mark -
@@ -304,34 +340,34 @@ + (NSFetchRequest *)createFetchRequest
+ (NSNumber *)numberOfEntitiesWithContext:(NSManagedObjectContext *)context
{
- NSError *error = nil;
- NSUInteger count = [context countForFetchRequest:[self createFetchRequestInContext:context] error:&error];
- [self handleErrors:error];
-
- return [NSNumber numberWithUnsignedInteger:count];
+ NSError *error = nil;
+ NSUInteger count = [context countForFetchRequest:[self createFetchRequestInContext:context] error:&error];
+ [self handleErrors:error];
+
+ return [NSNumber numberWithUnsignedInteger:count];
}
+ (NSNumber *)numberOfEntities
{
- return [self numberOfEntitiesWithContext:[self currentContext]];
+ return [self numberOfEntitiesWithContext:[self currentContext]];
}
+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context
{
- NSError *error = nil;
- NSFetchRequest *request = [self createFetchRequestInContext:context];
- [request setPredicate:searchTerm];
-
- NSUInteger count = [context countForFetchRequest:request error:&error];
- [self handleErrors:error];
-
- return [NSNumber numberWithUnsignedInteger:count];
+ NSError *error = nil;
+ NSFetchRequest *request = [self createFetchRequestInContext:context];
+ [request setPredicate:searchTerm];
+
+ NSUInteger count = [context countForFetchRequest:request error:&error];
+ [self handleErrors:error];
+
+ return [NSNumber numberWithUnsignedInteger:count];
}
+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm;
{
- return [self numberOfEntitiesWithPredicate:searchTerm
- inContext:[self currentContext]];
+ return [self numberOfEntitiesWithPredicate:searchTerm
+ inContext:[self currentContext]];
}
+ (BOOL)hasAtLeastOneEntityInContext:(NSManagedObjectContext *)context
@@ -348,19 +384,19 @@ + (BOOL)hasAtLeastOneEntity
#pragma mark Reqest Helpers
+ (NSFetchRequest *)requestAll
{
- return [self createFetchRequestInContext:[self currentContext]];
+ return [self createFetchRequestInContext:[self currentContext]];
}
+ (NSFetchRequest *)requestAllInContext:(NSManagedObjectContext *)context
{
- return [self createFetchRequestInContext:context];
+ return [self createFetchRequestInContext:context];
}
+ (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [self createFetchRequestInContext:context];
[request setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", property, value]];
-
+
return request;
}
@@ -374,7 +410,7 @@ + (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm inContex
NSFetchRequest *request = [self createFetchRequestInContext:context];
[request setPredicate:searchTerm];
[request setFetchLimit:1];
-
+
return request;
}
@@ -386,9 +422,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;
}
@@ -399,45 +434,45 @@ + (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 *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
- ascending:ascending
- inContext:[self currentContext]];
+ return [self requestAllSortedBy:sortTerm
+ ascending:ascending
+ inContext:[self currentContext]];
}
+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context
{
- NSFetchRequest *request = [self requestAllInContext:context];
- [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 *request = [self requestAllInContext:context];
+ [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
- ascending:ascending
- withPredicate:searchTerm
- inContext:[self currentContext]];
- return request;
+ NSFetchRequest *request = [self requestAllSortedBy:sortTerm
+ ascending:ascending
+ withPredicate:searchTerm
+ inContext:[self currentContext]];
+ return request;
}
@@ -446,44 +481,44 @@ + (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
{
- return [self findAllInContext:[self currentContext]];
+ return [self findAllInContext:[self currentContext]];
}
+ (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];
+ 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
- inContext:[self currentContext]];
+ 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
- ascending:ascending
- withPredicate:searchTerm
- inContext:context];
-
- return [self executeFetchRequest:request inContext:context];
+ 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
- ascending:ascending
- withPredicate:searchTerm
- inContext:[self currentContext]];
+ return [self findAllSortedBy:sortTerm
+ ascending:ascending
+ withPredicate:searchTerm
+ inContext:[self currentContext]];
}
#pragma mark -
@@ -493,73 +528,73 @@ + (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending with
+ (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
{
- NSString *cacheName = nil;
+ NSString *cacheName = nil;
#ifdef STORE_USE_CACHE
- cacheName = [NSString stringWithFormat:@"ActiveRecord-Cache-%@", NSStringFromClass(self)];
+ cacheName = [NSString stringWithFormat:@"ActiveRecord-Cache-%@", NSStringFromClass(self)];
#endif
-
- NSFetchRequest *request = [self requestAllSortedBy:sortTerm
- ascending:ascending
- withPredicate:searchTerm
- inContext:context];
-
- NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request
- managedObjectContext:context
- sectionNameKeyPath:group
- cacheName:cacheName];
- return [controller autorelease];
+
+ NSFetchRequest *request = [self requestAllSortedBy:sortTerm
+ ascending:ascending
+ withPredicate:searchTerm
+ inContext:context];
+
+ 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
- sortedBy:sortTerm
- ascending:ascending
- inContext:[self currentContext]];
+ return [self fetchRequestAllGroupedBy:group
+ withPredicate:searchTerm
+ sortedBy:sortTerm
+ ascending:ascending
+ inContext:[self currentContext]];
}
+ (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath inContext:(NSManagedObjectContext *)context
{
- NSFetchedResultsController *controller = [self fetchRequestAllGroupedBy:groupingKeyPath
- withPredicate:searchTerm
- sortedBy:sortTerm
- ascending:ascending
- inContext:context];
-
- [self performFetch:controller];
- return controller;
+ NSFetchedResultsController *controller = [self fetchRequestAllGroupedBy:groupingKeyPath
+ withPredicate:searchTerm
+ 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
- ascending:ascending
- withPredicate:searchTerm
- groupBy:groupingKeyPath
- inContext:[self currentContext]];
+ return [self fetchAllSortedBy:sortTerm
+ ascending:ascending
+ withPredicate:searchTerm
+ groupBy:groupingKeyPath
+ inContext:[self currentContext]];
}
+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group inContext:(NSManagedObjectContext *)context
{
- NSString *cacheName = nil;
+ NSString *cacheName = nil;
#ifdef STORE_USE_CACHE
- cacheName = [NSString stringWithFormat:@"ActiveRecord-Cache-%@", NSStringFromClass([self class])];
+ cacheName = [NSString stringWithFormat:@"ActiveRecord-Cache-%@", NSStringFromClass([self class])];
#endif
- NSFetchedResultsController *controller =
+ NSFetchedResultsController *controller =
[[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:group
cacheName:cacheName];
[self performFetch:controller];
- return [controller autorelease];
+ return [controller autorelease];
}
+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group
{
- return [self fetchRequest:request
- groupedBy:group
- inContext:[self currentContext]];
+ return [self fetchRequest:request
+ groupedBy:group
+ inContext:[self currentContext]];
}
#endif
@@ -567,50 +602,49 @@ + (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy
+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context
{
- NSFetchRequest *request = [self createFetchRequestInContext:context];
- [request setPredicate:searchTerm];
-
- return [self executeFetchRequest:request
- inContext:context];
+ NSFetchRequest *request = [self createFetchRequestInContext:context];
+ [request setPredicate:searchTerm];
+
+ return [self executeFetchRequest:request
+ inContext:context];
}
+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm
{
- return [self findAllWithPredicate:searchTerm
- inContext:[self currentContext]];
+ return [self findAllWithPredicate:searchTerm
+ inContext:[self currentContext]];
}
+ (id)findFirstInContext:(NSManagedObjectContext *)context
{
- NSFetchRequest *request = [self createFetchRequestInContext:context];
-
- return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
+ NSFetchRequest *request = [self createFetchRequestInContext:context];
+
+ return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
+ (id)findFirst
{
- return [self findFirstInContext:[self currentContext]];
+ return [self findFirstInContext:[self currentContext]];
}
+ (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];
+{
+ NSFetchRequest *request = [self requestFirstByAttribute:attribute withValue:searchValue inContext:context];
+
+ return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue
{
- return [self findFirstByAttribute:attribute
- withValue:searchValue
- inContext:[self currentContext]];
+ 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];
}
@@ -621,87 +655,85 @@ + (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];
+ 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
- inContext:[self currentContext]];
+ 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];
+ NSFetchRequest *request = [self createFetchRequestInContext:context];
+ [request setPredicate:searchTerm];
+
+ return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
}
+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes
{
- return [self findFirstWithPredicate:searchTerm
- andRetrieveAttributes:attributes
- inContext:[self currentContext]];
+ 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
- ascending:ascending
- withPredicate:searchTerm
- inContext:context];
- [request setPropertiesToFetch:[self propertiesNamed:attributes]];
-
- return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
+ NSFetchRequest *request = [self requestAllSortedBy:sortBy
+ ascending:ascending
+ withPredicate:searchTerm
+ inContext:context];
+
+ 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
+ return [self findFirstWithPredicate:searchTerm
+ sortedBy:sortBy
+ ascending:ascending
inContext:[self currentContext]
- andRetrieveAttributes:attributes];
+ 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];
+ 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
- inContext:[self currentContext]];
+ 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];
+ 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
- withValue:searchValue
- andOrderBy:sortTerm
- ascending:ascending
- inContext:[self currentContext]];
+ return [self findByAttribute:attribute
+ withValue:searchValue
+ andOrderBy:sortTerm
+ ascending:ascending
+ inContext:[self currentContext]];
}
+ (id)createInContext:(NSManagedObjectContext *)context
@@ -711,22 +743,22 @@ + (id)createInContext:(NSManagedObjectContext *)context
}
+ (id)createEntity
-{
- NSManagedObject *newEntity = [self createInContext:[self currentContext]];
-
- return newEntity;
+{
+ NSManagedObject *newEntity = [self createInContext:[self currentContext]];
+
+ return newEntity;
}
- (BOOL)deleteInContext:(NSManagedObjectContext *)context
{
- [context deleteObject:self];
- return YES;
+ [context deleteObject:self];
+ return YES;
}
- (BOOL)deleteEntity
{
- [self deleteInContext:[[self class] currentContext]];
- return YES;
+ [self deleteInContext:[[self class] currentContext]];
+ return YES;
}
+ (BOOL)truncateAllInContext:(NSManagedObjectContext *)context
@@ -747,25 +779,25 @@ + (BOOL)truncateAll
+ (NSNumber *)maxValueFor:(NSString *)property
{
- NSManagedObject *obj = [[self class] findFirstByAttribute:property
- withValue:[NSString stringWithFormat:@"max(%@)", property]];
-
- return [obj valueForKey: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];
+ 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]];
+ return [[self class] objectWithMinValueFor:property inContext:[self currentContext]];
}
@end
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
180 Code/CoreData/RKEntityByAttributeCache.h
@@ -0,0 +1,180 @@
+//
+// RKEntityByAttributeCache.h
+// RestKit
+//
+// Created by Blake Watters on 5/1/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+/**
+ Instances of RKEntityByAttributeCache provide an in-memory caching mechanism
+ for managed objects instances of an entity in a managed object context with
+ the value of one of the object's attributes acting as the cache key. When loaded,
+ the cache will retrieve all instances of an entity from the store and build a
+ dictionary mapping values for the given cache key attribute to the managed object
+ ID for all objects matching the value. The cache can then be used to quickly retrieve
+ objects by attribute value for the cache key without executing another fetch request
+ against the managed object context. This can provide a large performance improvement
+ when a large number of objects are being retrieved using a particular attribute as
+ the key.
+
+ RKEntityByAttributeCache instances are used by the RKEntityCache to provide
+ caching for multiple entities at once.
+
+ @see RKEntityCache
+ */
+@interface RKEntityByAttributeCache : NSObject
+
+///-----------------------------------------------------------------------------
+/// @name Creating a Cache
+///-----------------------------------------------------------------------------
+
+/**
+ Initializes the receiver with a given entity, attribute, and managed object context.
+
+ @param entity The Core Data entity description for the managed objects being cached.
+ @param attributeName The name of an attribute within the cached entity that acts as the cache key.
+ @param managedObjectContext The managed object context the cache retrieves the cached
+ objects from
+ @return The receiver, initialized with the given entity, attribute, and managed object
+ context.
+ */
+- (id)initWithEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName managedObjectContext:(NSManagedObjectContext *)context;
+
+///-----------------------------------------------------------------------------
+/// @name Getting Cache Identity
+///-----------------------------------------------------------------------------
+
+/**
+ The Core Data entity description for the managed objects being cached.
+ */
+@property (nonatomic, readonly) NSEntityDescription *entity;
+
+/**
+ An attribute that is part of the cached entity that acts as the cache key.
+ */
+@property (nonatomic, readonly) NSString *attribute;
+
+/**
+ The managed object context the receiver fetches cached objects from.
+ */
+@property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
+
+/**
+ A Boolean value determining if the receiever monitors the managed object context
+ for changes and updates the cache entries using the notifications emitted.
+ */
+@property (nonatomic, assign) BOOL monitorsContextForChanges;
+
+///-----------------------------------------------------------------------------
+/// @name Loading and Flushing the Cache
+///-----------------------------------------------------------------------------
+
+/**
+ Loads the cache by finding all instances of the configured entity and building
+ an association between the value of the cached attribute's value and the
+ managed object ID for the object.
+ */
+- (void)load;
+
+/**
+ Flushes the cache by releasing all cache attribute value to managed object ID
+ associations.
+ */
+- (void)flush;
+
+///-----------------------------------------------------------------------------
+/// @name Inspecting Cache State
+///-----------------------------------------------------------------------------
+
+/**
+ A Boolean value indicating if the cache has loaded associations between cache
+ attribute values and managed object ID's.
+ */
+- (BOOL)isLoaded;
+
+/**
+ Returns a count of the total number of cached objects.
+ */
+- (NSUInteger)count;
+
+/**
+ Returns the total number of cached objects with a given value for
+ the attribute acting as the cache key.
+
+ @param attributeValue The value for the cache key attribute to retrieve
+ a count of the objects with a matching value.
+ @return The number of objects in the cache with the given value for the cache
+ attribute of the receiver.
+ */
+- (NSUInteger)countWithAttributeValue:(id)attributeValue;
+
+/**
+ Returns the number of unique attribute values contained within the receiver.
+
+ @return The number of unique attribute values within the receiver.
+ */
+- (NSUInteger)countOfAttributeValues;
+
+/**
+ Returns a Boolean value that indicates whether a given object is present
+ in the cache.
+
+ @param object An object.
+ @return YES if object is present in the cache, otherwise NO.
+ */
+- (BOOL)containsObject:(NSManagedObject *)object;
+
+/**
+ Returns a Boolean value that indicates whether one of more objects is present
+ in the cache with a given value of the cache key attribute.
+
+ @param attributeValue The value with which to check the cache for objects with
+ a matching value.
+ @return YES if one or more objects with the given value for the cache key
+ attribute is present in the cache, otherwise NO.
+ */
+- (BOOL)containsObjectWithAttributeValue:(id)attributeValue;
+
+/**
+ Returns the first object with a matching value for the cache key attribute.
+
+ @param attributeValue A value for the cache key attribute.
+ @return An object with the value of attribute matching attributeValue or nil.
+ */
+- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue;
+
+/**
+ Returns the collection of objects with a matching value for the cache key attribute.
+
+ @param attributeValue A value for the cache key attribute.
+ @return An array of objects with the value of attribute matching attributeValue or
+ an empty array.
+ */
+- (NSArray *)objectsWithAttributeValue:(id)attributeValue;
+
+///-----------------------------------------------------------------------------
+/// @name Managing Cached Objects
+///-----------------------------------------------------------------------------
+
+/**
+ Adds a managed object to the cache.
+
+ The object must be an instance of the cached entity.
+
+ @param object The managed object to add to the cache.
+ */
+- (void)addObject:(NSManagedObject *)object;
+
+/**
+ Removes a managed object from the cache.
+
+ The object must be an instance of the cached entity.
+
+ @param object The managed object to remove from the cache.
+ */
+- (void)removeObject:(NSManagedObject *)object;
+
+@end
View
279 Code/CoreData/RKEntityByAttributeCache.m
@@ -0,0 +1,279 @@
+//
+// RKEntityByAttributeCache.m
+// RestKit
+//
+// Created by Blake Watters on 5/1/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#endif
+
+#import "RKEntityByAttributeCache.h"
+#import "RKLog.h"
+#import "RKObjectPropertyInspector.h"
+#import "RKObjectPropertyInspector+CoreData.h"
+
+// Set Logging Component
+#undef RKLogComponent
+#define RKLogComponent lcl_cRestKitCoreDataCache
+
+@interface RKEntityByAttributeCache ()
+@property (nonatomic, retain) NSMutableDictionary *attributeValuesToObjectIDs;
+@end
+
+@implementation RKEntityByAttributeCache
+
+@synthesize entity = _entity;
+@synthesize attribute = _attribute;
+@synthesize managedObjectContext = _managedObjectContext;
+@synthesize attributeValuesToObjectIDs = _attributeValuesToObjectIDs;
+@synthesize monitorsContextForChanges = _monitorsContextForChanges;
+
+- (id)initWithEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName managedObjectContext:(NSManagedObjectContext *)context
+{
+ self = [self init];
+ if (self) {
+ _entity = [entity retain];
+ _attribute = [attributeName retain];
+ _managedObjectContext = [context retain];
+ _monitorsContextForChanges = YES;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(managedObjectContextDidChange:)
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ object:context];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(managedObjectContextDidSave:)
+ name:NSManagedObjectContextDidSaveNotification
+ object:context];
+#if TARGET_OS_IPHONE
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(didReceiveMemoryWarning:)
+ name:UIApplicationDidReceiveMemoryWarningNotification
+ object:nil];
+#endif
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [_entity release];
+ [_attribute release];
+ [_managedObjectContext release];
+ [_attributeValuesToObjectIDs release];
+
+ [super dealloc];
+}
+
+- (NSUInteger)count
+{
+ return [[[self.attributeValuesToObjectIDs allValues] valueForKeyPath:@"@sum.@count"] integerValue];
+}
+
+- (NSUInteger)countOfAttributeValues
+{
+ return [self.attributeValuesToObjectIDs count];
+}
+
+- (NSUInteger)countWithAttributeValue:(id)attributeValue
+{
+ return [[self objectsWithAttributeValue:attributeValue] count];
+}
+
+- (BOOL)shouldCoerceAttributeToString:(NSString *)attributeValue
+{
+ if ([attributeValue isKindOfClass:[NSString class]] || [attributeValue isEqual:[NSNull null]]) {
+ return NO;
+ }
+
+ Class attributeType = [[RKObjectPropertyInspector sharedInspector] typeForProperty:self.attribute ofEntity:self.entity];
+ return [attributeType instancesRespondToSelector:@selector(stringValue)];
+}
+
+- (void)load
+{
+ RKLogDebug(@"Loading entity cache for Entity '%@' by attribute '%@'", self.entity.name, self.attribute);
+ NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
+ [fetchRequest setEntity:self.entity];
+ [fetchRequest setResultType:NSManagedObjectIDResultType];
+
+ NSError *error = nil;
+ NSArray *objectIDs = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
+ [fetchRequest release];
+ if (error) {
+ RKLogError(@"Failed to load entity cache: %@", error);
+ return;
+ }
+
+ self.attributeValuesToObjectIDs = [NSMutableDictionary dictionaryWithCapacity:[objectIDs count]];
+ for (NSManagedObjectID *objectID in objectIDs) {
+ NSError *error = nil;
+ NSManagedObject *object = [self.managedObjectContext existingObjectWithID:objectID error:&error];
+ if (! object && error) {
+ RKLogError(@"Failed to retrieve managed object with ID %@: %@", objectID, error);
+ }
+
+ [self addObject:object];
+ }
+}
+
+- (void)flush
+{
+ RKLogDebug(@"Flushing entity cache for Entity '%@' by attribute '%@'", self.entity.name, self.attribute);
+ self.attributeValuesToObjectIDs = nil;
+}
+
+- (void)reload
+{
+ [self flush];
+ [self load];
+}
+
+- (BOOL)isLoaded
+{
+ return (self.attributeValuesToObjectIDs != nil);
+}
+
+- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue
+{
+ NSArray *objects = [self objectsWithAttributeValue:attributeValue];
+ return ([objects count] > 0) ? [objects objectAtIndex:0] : nil;
+}
+
+- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID {
+ /*
+ 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 = [self.managedObjectContext existingObjectWithID:objectID error:&error];
+ if (! object && error) {
+ RKLogError(@"Failed to retrieve managed object with ID %@. Error %@\n%@", objectID, [error localizedDescription], [error userInfo]);
+ return nil;
+ }
+
+ return object;
+}
+
+- (NSArray *)objectsWithAttributeValue:(id)attributeValue
+{
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ NSMutableArray *objectIDs = [self.attributeValuesToObjectIDs objectForKey:attributeValue];
+ if (objectIDs) {
+ NSMutableArray *objects = [NSMutableArray arrayWithCapacity:[objectIDs count]];
+ for (NSManagedObjectID *objectID in objectIDs) {
+ NSManagedObject *object = [self objectWithID:objectID];
+ if (object) [objects addObject:object];
+ }
+
+ return objects;
+ }
+
+ return [NSArray array];
+}
+
+- (void)addObject:(NSManagedObject *)object
+{
+ NSAssert([object.entity isEqual:self.entity], @"Cannot add object with entity '%@' to cache with entity of '%@'", [[object entity] name], [self.entity name]);
+ id attributeValue = [object valueForKey:self.attribute];
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ if (attributeValue) {
+ NSManagedObjectID *objectID = [object objectID];
+ NSMutableArray *objectIDs = [self.attributeValuesToObjectIDs objectForKey:attributeValue];
+ if (objectIDs) {
+ if (! [objectIDs containsObject:objectID]) {
+ [objectIDs addObject:objectID];
+ }
+ } else {
+ objectIDs = [NSMutableArray arrayWithObject:objectID];
+ }
+
+ if (nil == self.attributeValuesToObjectIDs) self.attributeValuesToObjectIDs = [NSMutableDictionary dictionary];
+ [self.attributeValuesToObjectIDs setValue:objectIDs forKey:attributeValue];
+ } else {
+ RKLogWarning(@"Unable to add object with nil value for attribute '%@': %@", self.attribute, object);
+ }
+}
+
+- (void)removeObject:(NSManagedObject *)object
+{
+ NSAssert([object.entity isEqual:self.entity], @"Cannot remove object with entity '%@' from cache with entity of '%@'", [[object entity] name], [self.entity name]);
+ id attributeValue = [object valueForKey:self.attribute];
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ if (attributeValue) {
+ NSManagedObjectID *objectID = [object objectID];
+ NSMutableArray *objectIDs = [self.attributeValuesToObjectIDs objectForKey:attributeValue];
+ if (objectIDs && [objectIDs containsObject:objectID]) {
+ [objectIDs removeObject:objectID];
+ }
+ } else {
+ RKLogWarning(@"Unable to remove object with nil value for attribute '%@': %@", self.attribute, object);
+ }
+}
+
+- (BOOL)containsObjectWithAttributeValue:(id)attributeValue
+{
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ return [[self objectsWithAttributeValue:attributeValue] count] > 0;
+}
+
+- (BOOL)containsObject:(NSManagedObject *)object
+{
+ if (! [object.entity isEqual:self.entity]) return NO;
+ id attributeValue = [object valueForKey:self.attribute];
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ return [[self objectsWithAttributeValue:attributeValue] containsObject:object];
+}
+
+- (void)managedObjectContextDidChange:(NSNotification *)notification
+{
+ if (self.monitorsContextForChanges == NO) return;
+
+ NSDictionary *userInfo = notification.userInfo;
+ NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
+ NSSet *updatedObjects = [userInfo objectForKey:NSUpdatedObjectsKey];
+ NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
+ RKLogTrace(@"insertedObjects=%@, updatedObjects=%@, deletedObjects=%@", insertedObjects, updatedObjects, deletedObjects);
+
+ NSMutableSet *objectsToAdd = [NSMutableSet setWithSet:insertedObjects];
+ [objectsToAdd unionSet:updatedObjects];
+
+ for (NSManagedObject *object in objectsToAdd) {
+ if ([object.entity isEqual:self.entity]) {
+ [self addObject:object];
+ }
+ }
+
+ for (NSManagedObject *object in deletedObjects) {
+ if ([object.entity isEqual:self.entity]) {
+ [self removeObject:object];
+ }
+ }
+}
+
+- (void)managedObjectContextDidSave:(NSNotification *)notification
+{
+ // After the MOC has been saved, we flush to ensure any temporary
+ // objectID references are converted into permanent ID's on the next load.
+ [self flush];
+}
+
+- (void)didReceiveMemoryWarning:(NSNotification *)notification
+{
+ [self flush];
+}
+
+@end
View
133 Code/CoreData/RKEntityCache.h
@@ -0,0 +1,133 @@
+//
+// RKEntityCache.h
+// RestKit
+//
+// Created by Blake Watters on 5/2/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+@class RKEntityByAttributeCache;
+
+/**
+ 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 RKEntityCache : NSObject
+
+///-----------------------------------------------------------------------------