Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'release/0.20.0-pre4'

  • Loading branch information...
commit 3331ff11973396a4d2ecd2e5d729a6e60343088f 2 parents 8d0d9fc + 753d796
@blakewatters blakewatters authored
Showing with 12,491 additions and 11,398 deletions.
  1. +5 −3 .gitignore
  2. +3 −3 .gitmodules
  3. +1 −1  .rvmrc
  4. +76 −29 CREDITS.md
  5. +33 −0 Code/CoreData.h
  6. +0 −183 Code/CoreData/NSManagedObject+ActiveRecord.h
  7. +0 −771 Code/CoreData/NSManagedObject+ActiveRecord.m
  8. +38 −0 Code/CoreData/NSManagedObject+RKAdditions.h
  9. +28 −0 Code/CoreData/NSManagedObject+RKAdditions.m
  10. +79 −0 Code/CoreData/NSManagedObjectContext+RKAdditions.h
  11. +64 −0 Code/CoreData/NSManagedObjectContext+RKAdditions.m
  12. +152 −0 Code/CoreData/RKConnectionDescription.h
  13. +141 −0 Code/CoreData/RKConnectionDescription.m
  14. +180 −0 Code/CoreData/RKEntityByAttributeCache.h
  15. +365 −0 Code/CoreData/RKEntityByAttributeCache.m
  16. +145 −0 Code/CoreData/RKEntityCache.h
  17. +146 −0 Code/CoreData/RKEntityCache.m
  18. +229 −0 Code/CoreData/RKEntityMapping.h
  19. +296 −0 Code/CoreData/RKEntityMapping.m
  20. +31 −0 Code/CoreData/RKFetchRequestManagedObjectCache.h
  21. +87 −0 Code/CoreData/RKFetchRequestManagedObjectCache.m
  22. +40 −0 Code/CoreData/RKInMemoryManagedObjectCache.h
  23. +88 −0 Code/CoreData/RKInMemoryManagedObjectCache.m
  24. +0 −33 Code/CoreData/RKManagedObjectCache.h
  25. +72 −0 Code/CoreData/RKManagedObjectCaching.h
  26. +152 −0 Code/CoreData/RKManagedObjectImporter.h
  27. +301 −0 Code/CoreData/RKManagedObjectImporter.m
  28. +0 −38 Code/CoreData/RKManagedObjectLoader.h
  29. +0 −194 Code/CoreData/RKManagedObjectLoader.m
  30. +0 −93 Code/CoreData/RKManagedObjectMapping.h
  31. +0 −160 Code/CoreData/RKManagedObjectMapping.m
  32. +0 −30 Code/CoreData/RKManagedObjectMappingOperation.h
  33. +0 −96 Code/CoreData/RKManagedObjectMappingOperation.m
  34. +84 −0 Code/CoreData/RKManagedObjectMappingOperationDataSource.h
  35. +242 −0 Code/CoreData/RKManagedObjectMappingOperationDataSource.m
  36. +0 −84 Code/CoreData/RKManagedObjectSeeder.h
  37. +0 −182 Code/CoreData/RKManagedObjectSeeder.m
  38. +155 −77 Code/CoreData/RKManagedObjectStore.h
  39. +203 −331 Code/CoreData/RKManagedObjectStore.m
  40. +0 −38 Code/CoreData/RKManagedObjectThreadSafeInvocation.h
  41. +0 −126 Code/CoreData/RKManagedObjectThreadSafeInvocation.m
  42. +0 −32 Code/CoreData/RKObjectPropertyInspector+CoreData.h
  43. +0 −66 Code/CoreData/RKObjectPropertyInspector+CoreData.m
  44. +45 −0 Code/CoreData/RKPropertyInspector+CoreData.h
  45. +110 −0 Code/CoreData/RKPropertyInspector+CoreData.m
  46. +90 −0 Code/CoreData/RKRelationshipConnectionOperation.h
  47. +186 −0 Code/CoreData/RKRelationshipConnectionOperation.m
  48. +11 −13 Code/{Network → }/Network.h
  49. +0 −45 Code/Network/NSData+MD5.m
  50. +0 −45 Code/Network/NSDictionary+RKRequestSerialization.h
  51. +0 −77 Code/Network/NSDictionary+RKRequestSerialization.m
  52. +0 −26 Code/Network/NSString+MD5.h
  53. +0 −48 Code/Network/NSString+MD5.m
  54. +0 −634 Code/Network/RKClient.h
  55. +0 −416 Code/Network/RKClient.m
  56. +61 −0 Code/Network/RKHTTPRequestOperation.h
  57. +244 −0 Code/Network/RKHTTPRequestOperation.m
  58. +178 −0 Code/Network/RKManagedObjectRequestOperation.h
  59. +491 −0 Code/Network/RKManagedObjectRequestOperation.m
  60. +0 −36 Code/Network/RKNotifications.h
  61. +0 −28 Code/Network/RKNotifications.m
  62. +0 −137 Code/Network/RKOAuthClient.h
  63. +0 −180 Code/Network/RKOAuthClient.m
  64. +829 −0 Code/Network/RKObjectManager.h
  65. +708 −0 Code/Network/RKObjectManager.m
  66. +44 −0 Code/Network/RKObjectParameterization.h
  67. +133 −0 Code/Network/RKObjectParameterization.m
  68. +198 −0 Code/Network/RKObjectRequestOperation.h
  69. +315 −0 Code/Network/RKObjectRequestOperation.m
  70. +248 −0 Code/Network/RKPaginator.h
  71. +290 −0 Code/Network/RKPaginator.m
  72. +0 −120 Code/Network/RKParams.h
  73. +0 −278 Code/Network/RKParams.m
  74. +0 −115 Code/Network/RKParamsAttachment.h
  75. +0 −223 Code/Network/RKParamsAttachment.m
  76. +0 −217 Code/Network/RKReachabilityObserver.h
  77. +0 −383 Code/Network/RKReachabilityObserver.m
  78. +0 −499 Code/Network/RKRequest.h
  79. +0 −727 Code/Network/RKRequest.m
  80. +0 −72 Code/Network/RKRequestCache.h
  81. +0 −397 Code/Network/RKRequestCache.m
  82. +84 −0 Code/Network/RKRequestDescriptor.h
  83. +79 −0 Code/Network/RKRequestDescriptor.m
  84. +45 −0 Code/Network/RKRequestOperationSubclass.h
  85. +0 −241 Code/Network/RKRequestQueue.h
  86. +0 −539 Code/Network/RKRequestQueue.m
  87. +0 −65 Code/Network/RKRequestSerializable.h
  88. +0 −50 Code/Network/RKRequestSerialization.h
  89. +0 −64 Code/Network/RKRequestSerialization.m
  90. +0 −273 Code/Network/RKResponse.h
  91. +0 −439 Code/Network/RKResponse.m
  92. +132 −0 Code/Network/RKResponseDescriptor.h
  93. +120 −0 Code/Network/RKResponseDescriptor.m
  94. +207 −0 Code/Network/RKResponseMapperOperation.h
  95. +387 −0 Code/Network/RKResponseMapperOperation.m
  96. +140 −0 Code/Network/RKRoute.h
  97. +153 −0 Code/Network/RKRoute.m
  98. +155 −0 Code/Network/RKRouteSet.h
  99. +201 −0 Code/Network/RKRouteSet.m
  100. +116 −0 Code/Network/RKRouter.h
  101. +85 −0 Code/Network/RKRouter.m
  102. +0 −41 Code/Network/RKURL.h
  103. +0 −76 Code/Network/RKURL.m
  104. +10 −10 Code/{ObjectMapping → }/ObjectMapping.h
  105. +60 −0 Code/ObjectMapping/RKAttributeMapping.h
  106. +40 −0 Code/ObjectMapping/RKAttributeMapping.m
  107. +76 −0 Code/ObjectMapping/RKDynamicMapping.h
  108. +87 −0 Code/ObjectMapping/RKDynamicMapping.m
  109. +67 −0 Code/ObjectMapping/RKDynamicMappingMatcher.h
  110. +49 −0 Code/ObjectMapping/RKDynamicMappingMatcher.m
  111. +0 −117 Code/ObjectMapping/RKDynamicObjectMapping.h
  112. +0 −156 Code/ObjectMapping/RKDynamicObjectMapping.m
  113. +23 −10 Code/ObjectMapping/RKErrorMessage.h
  114. +8 −14 Code/ObjectMapping/RKErrorMessage.m
  115. +125 −0 Code/ObjectMapping/RKHTTPUtilities.h
  116. +514 −0 Code/ObjectMapping/RKHTTPUtilities.m
  117. +224 −0 Code/ObjectMapping/RKMapperOperation.h
  118. +379 −0 Code/ObjectMapping/RKMapperOperation.m
  119. +10 −10 Code/ObjectMapping/{RKObjectMapper_Private.h → RKMapperOperation_Private.h}
  120. +60 −0 Code/ObjectMapping/RKMapping.h
  121. +22 −0 Code/ObjectMapping/RKMapping.m
  122. +36 −0 Code/ObjectMapping/RKMappingErrors.h
  123. +215 −0 Code/ObjectMapping/RKMappingOperation.h
  124. +728 −0 Code/ObjectMapping/RKMappingOperation.m
  125. +59 −0 Code/ObjectMapping/RKMappingOperationDataSource.h
  126. +0 −64 Code/ObjectMapping/RKMappingOperationQueue.h
  127. +0 −50 Code/ObjectMapping/RKMappingOperationQueue.m
  128. +91 −0 Code/ObjectMapping/RKMappingResult.h
  129. +87 −0 Code/ObjectMapping/RKMappingResult.m
  130. +0 −49 Code/ObjectMapping/RKObjectAttributeMapping.h
  131. +0 −70 Code/ObjectMapping/RKObjectAttributeMapping.m
  132. +0 −208 Code/ObjectMapping/RKObjectLoader.h
  133. +0 −385 Code/ObjectMapping/RKObjectLoader.m
  134. +0 −31 Code/ObjectMapping/RKObjectLoader_Internals.h
  135. +0 −380 Code/ObjectMapping/RKObjectManager.h
  136. +0 −341 Code/ObjectMapping/RKObjectManager.m
  137. +0 −72 Code/ObjectMapping/RKObjectMapper.h
  138. +0 −328 Code/ObjectMapping/RKObjectMapper.m
  139. +0 −29 Code/ObjectMapping/RKObjectMapperError.h
Sorry, we could not display the entire diff because too many files (844) changed.
View
8 .gitignore
@@ -7,6 +7,7 @@ Docs/API
# temp nibs and swap files
*~.nib
*.swp
+*.orig
# OS X folder attributes
.DS_Store
@@ -20,6 +21,7 @@ Docs/API
Examples/RKDiscussionBoardExample/discussion_board_backend/public/system/attachments/*
-# UISpecRunner cached build path
-.uispec.app
-Specs/Runner/UISpec
+test-reports/
+
+# CocoaPods
+Pods
View
6 .gitmodules
@@ -1,3 +1,3 @@
-[submodule "Examples/RKCatalog/Server"]
- path = Examples/RKCatalog/Server
- url = git://github.com/twotoasters/RKCatalog-Server.git
+[submodule "Vendor/AFNetworking"]
+ path = Vendor/AFNetworking
+ url = https://github.com/AFNetworking/AFNetworking.git
View
2  .rvmrc
@@ -1 +1 @@
-rvm use 1.9.2@RestKit
+rvm use 1.9.3@RestKit
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
33 Code/CoreData.h
@@ -0,0 +1,33 @@
+//
+// CoreData.h
+// RestKit
+//
+// Created by Blake Watters on 9/30/10.
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <CoreData/CoreData.h>
+#import "ObjectMapping.h"
+#import "RKManagedObjectStore.h"
+#import "RKManagedObjectImporter.h"
+#import "RKManagedObjectMappingOperationDataSource.h"
+#import "RKEntityMapping.h"
+#import "RKManagedObjectCaching.h"
+#import "RKInMemoryManagedObjectCache.h"
+#import "RKFetchRequestManagedObjectCache.h"
+
+#import "RKPropertyInspector+CoreData.h"
+#import "NSManagedObjectContext+RKAdditions.h"
+#import "NSManagedObject+RKAdditions.h"
View
0  Code/CoreData/CoreData.h
Sorry, we could not display the changes to this file because there were too many other changes to display.
View
183 Code/CoreData/NSManagedObject+ActiveRecord.h
@@ -1,183 +0,0 @@
-//
-// RKManagedObject+ActiveRecord.h
-//
-// Adapted from https://github.com/magicalpanda/MagicalRecord
-// Created by Saul Mora on 11/15/09.
-// Copyright 2010 Magical Panda Software, LLC All rights reserved.
-//
-// Created by Chad Podoski on 3/18/11.
-//
-
-#import <CoreData/CoreData.h>
-
-@interface NSManagedObject (ActiveRecord)
-
-/**
- * The Core Data managed object context from the RKObjectManager's objectStore
- * that is managing this model
- */
-+ (NSManagedObjectContext*)managedObjectContext;
-
-/**
- * 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
- */
-+ (NSFetchRequest*)fetchRequest;
-
-/**
- * Fetches all objects from the persistent store identified by the fetchRequest
- */
-+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest;
-
-/**
- * Fetches all objects from the persistent store via a set of fetch requests and
- * returns all results in a single array.
- */
-+ (NSArray*)objectsWithFetchRequests:(NSArray*)fetchRequests;
-
-/**
- * Fetches the first object identified by the fetch request. A limit of one will be
- * applied to the fetch request before dispatching.
- */
-+ (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest;
-
-/**
- * Fetches all objects from the persistent store by constructing a fetch request and
- * applying the predicate supplied. A short-cut for doing filtered searches on the objects
- * of this class under management.
- */
-+ (NSArray*)objectsWithPredicate:(NSPredicate*)predicate;
-
-/**
- * Fetches the first object matching a predicate from the persistent store. A fetch request
- * will be constructed for you and a fetch limit of 1 will be applied.
- */
-+ (id)objectWithPredicate:(NSPredicate*)predicate;
-
-/**
- * Fetches all managed objects of this class from the persistent store as an array
- */
-+ (NSArray*)allObjects;
-
-/**
- * Returns a count of all managed objects of this class in the persistent store. On
- * error, will populate the error argument
- */
-+ (NSUInteger)count:(NSError**)error;
-
-/**
- * Returns a count of all managed objects of this class in the persistent store. Deprecated
- * use the error form above
- *
- * @deprecated
- */
-+ (NSUInteger)count DEPRECATED_ATTRIBUTE;
-
-/**
- * Creates a new managed object and inserts it into the managedObjectContext.
- */
-+ (id)object;
-
-/**
- * Returns YES when an object has not been saved to the managed object context yet
- */
-- (BOOL)isNew;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-+ (NSManagedObjectContext*)currentContext;
-
-+ (void)handleErrors:(NSError *)error;
-
-+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request;
-+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context;
-+ (NSFetchRequest *)createFetchRequest;
-+ (NSFetchRequest *)createFetchRequestInContext:(NSManagedObjectContext *)context;
-+ (NSEntityDescription *)entityDescription;
-+ (NSEntityDescription *)entityDescriptionInContext:(NSManagedObjectContext *)context;
-+ (NSArray *)propertiesNamed:(NSArray *)properties;
-
-+ (id)createEntity;
-+ (id)createInContext:(NSManagedObjectContext *)context;
-- (BOOL)deleteEntity;
-- (BOOL)deleteInContext:(NSManagedObjectContext *)context;
-
-+ (BOOL)truncateAll;
-+ (BOOL)truncateAllInContext:(NSManagedObjectContext *)context;
-
-+ (NSArray *)ascendingSortDescriptors:(id)attributesToSortBy, ...;
-+ (NSArray *)descendingSortDescriptors:(id)attributesToSortyBy, ...;
-
-+ (NSNumber *)numberOfEntities;
-+ (NSNumber *)numberOfEntitiesWithContext:(NSManagedObjectContext *)context;
-+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm;
-+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
-
-+ (BOOL) hasAtLeastOneEntity;
-+ (BOOL) hasAtLeastOneEntityInContext:(NSManagedObjectContext *)context;
-
-+ (NSFetchRequest *)requestAll;
-+ (NSFetchRequest *)requestAllInContext:(NSManagedObjectContext *)context;
-+ (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value;
-+ (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value inContext:(NSManagedObjectContext *)context;
-+ (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm;
-+ (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
-+ (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue;
-+ (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context;
-+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending;
-+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context;
-+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm;
-+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
-
-+ (NSArray *)findAll;
-+ (NSArray *)findAllInContext:(NSManagedObjectContext *)context;
-+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending;
-+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context;
-+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm;
-+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
-+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
-
-+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm;
-+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
-
-+ (NSNumber *)maxValueFor:(NSString *)property;
-+ (id) objectWithMinValueFor:(NSString *)property;
-+ (id) objectWithMinValueFor:(NSString *)property inContext:(NSManagedObjectContext *)context;
-
-+ (id)findFirst;
-+ (id)findFirstInContext:(NSManagedObjectContext *)context;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes inContext:(NSManagedObjectContext *)context;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending andRetrieveAttributes:(id)attributes, ...;
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context andRetrieveAttributes:(id)attributes, ...;
-
-+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue;
-+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context;
-+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue;
-+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context;
-+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending;
-+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context;
-
-#if TARGET_OS_IPHONE
-
-+ (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath;
-+ (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath inContext:(NSManagedObjectContext *)context;
-
-+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group;
-+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group inContext:(NSManagedObjectContext *)context;
-
-+ (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 inContext:(NSManagedObjectContext *)context;
-
-#endif
-
-@end
View
771 Code/CoreData/NSManagedObject+ActiveRecord.m
@@ -1,771 +0,0 @@
-//
-// NSManagedObject+ActiveRecord.m
-//
-// Adapted from https://github.com/magicalpanda/MagicalRecord
-// Created by Saul Mora on 11/15/09.
-// Copyright 2010 Magical Panda Software, LLC All rights reserved.
-//
-// Created by Chad Podoski on 3/18/11.
-//
-
-#import <objc/runtime.h>
-#import "NSManagedObject+ActiveRecord.h"
-#import "RKObjectManager.h"
-#import "RKLog.h"
-#import "RKFixCategoryBug.h"
-
-RK_FIX_CATEGORY_BUG(NSManagedObject_ActiveRecord)
-
-// Set Logging Component
-#undef RKLogComponent
-#define RKLogComponent lcl_cRestKitCoreData
-
-static NSUInteger const kActiveRecordDefaultBatchSize = 10;
-static NSNumber *defaultBatchSize = nil;
-
-@implementation NSManagedObject (ActiveRecord)
-
-#pragma mark - RKManagedObject methods
-
-+ (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];
-}
-
-+ (NSEntityDescription*)entity {
- NSString* className = [NSString stringWithCString:class_getName([self class]) encoding:NSASCIIStringEncoding];
- return [NSEntityDescription entityForName:className inManagedObjectContext:[self managedObjectContext]];
-}
-
-+ (NSFetchRequest*)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;
-}
-
-+ (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;
-}
-
-+ (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest {
- [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];
-}
-
-+ (id)objectWithPredicate:(NSPredicate*)predicate {
- NSFetchRequest* fetchRequest = [self fetchRequest];
- [fetchRequest setPredicate:predicate];
- return [self objectWithFetchRequest:fetchRequest];
-}
-
-+ (NSArray*)allObjects {
- return [self objectsWithPredicate:nil];
-}
-
-+ (NSUInteger)count:(NSError**)error {
- NSFetchRequest* fetchRequest = [self fetchRequest];
- return [[self managedObjectContext] countForFetchRequest:fetchRequest error:error];
-}
-
-+ (NSUInteger)count {
- NSError *error = nil;
- return [self count:&error];
-}
-
-+ (id)object {
- id object = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:[self managedObjectContext]];
- return [object autorelease];
-}
-
-- (BOOL)isNew {
- NSDictionary *vals = [self committedValuesForKeys:nil];
- return [vals count] == 0;
-}
-
-#pragma mark - MagicalRecord Ported Methods
-
-+ (NSManagedObjectContext*)currentContext; {
- return [self managedObjectContext];
-}
-
-+ (void)setDefaultBatchSize:(NSUInteger)newBatchSize
-{
- @synchronized(defaultBatchSize)
- {
- defaultBatchSize = [NSNumber numberWithUnsignedInteger:newBatchSize];
- }
-}
-
-+ (NSInteger)defaultBatchSize
-{
- 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];
-//}
-
-+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context
-{
- NSError *error = nil;
-
- NSArray *results = [context executeFetchRequest:request error:&error];
- [self handleErrors:error];
- return results;
-}
-
-+ (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)
- {
- return nil;
- }
- return [results objectAtIndex:0];
-}
-
-+ (id)executeFetchRequestAndReturnFirstObject:(NSFetchRequest *)request
-{
- 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];
- }
-}
-#endif
-
-+ (NSEntityDescription *)entityDescriptionInContext:(NSManagedObjectContext *)context
-{
- NSString *entityName = NSStringFromClass([self class]);
- return [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
-}
-
-+ (NSEntityDescription *)entityDescription
-{
- 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;
-}
-
-+ (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;
-}
-
-+ (NSArray *)ascendingSortDescriptors:(id)attributesToSortBy, ...
-{
- return [self sortAscending:YES attributes:attributesToSortBy];
-}
-
-+ (NSArray *)descendingSortDescriptors:(id)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 *)createFetchRequest
-{
- return [self createFetchRequestInContext:[self currentContext]];
-}
-
-#pragma mark -
-#pragma mark Number of Entities
-
-+ (NSNumber *)numberOfEntitiesWithContext:(NSManagedObjectContext *)context
-{
- 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]];
-}
-
-+ (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];
-}
-
-+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm;
-{
- return [self numberOfEntitiesWithPredicate:searchTerm
- inContext:[self currentContext]];
-}
-
-+ (BOOL)hasAtLeastOneEntityInContext:(NSManagedObjectContext *)context
-{
- return [[self numberOfEntitiesWithContext:context] intValue] > 0;
-}
-
-+ (BOOL)hasAtLeastOneEntity
-{
- return [self hasAtLeastOneEntityInContext:[self currentContext]];
-}
-
-#pragma mark -
-#pragma mark Reqest Helpers
-+ (NSFetchRequest *)requestAll
-{
- return [self createFetchRequestInContext:[self currentContext]];
-}
-
-+ (NSFetchRequest *)requestAllInContext:(NSManagedObjectContext *)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;
-}
-
-+ (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value
-{
- return [self requestAllWhere:property isEqualTo:value inContext:[self currentContext]];
-}
-
-+ (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context
-{
- NSFetchRequest *request = [self createFetchRequestInContext:context];
- [request setPredicate:searchTerm];
- [request setFetchLimit:1];
-
- return request;
-}
-
-+ (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm
-{
- return [self requestFirstWithPredicate:searchTerm inContext:[self currentContext]];
-}
-
-+ (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;
-}
-
-+ (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue;
-{
- return [self requestFirstByAttribute:attribute withValue:searchValue inContext:[self currentContext]];
-}
-
-+ (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
- 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 *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm;
-{
- NSFetchRequest *request = [self requestAllSortedBy:sortTerm
- ascending:ascending
- withPredicate:searchTerm
- inContext:[self currentContext]];
- return request;
-}
-
-
-#pragma mark Finding Data
-#pragma mark -
-
-+ (NSArray *)findAllInContext:(NSManagedObjectContext *)context
-{
- return [self executeFetchRequest:[self requestAllInContext:context] inContext:context];
-}
-
-+ (NSArray *)findAll
-{
- 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];
-}
-
-+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)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
- 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]];
-}
-
-#pragma mark -
-#pragma mark NSFetchedResultsController helpers
-
-#if TARGET_OS_IPHONE
-
-+ (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context
-{
- NSString *cacheName = nil;
-#ifdef STORE_USE_CACHE
- 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];
-}
-
-+ (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]];
-}
-
-+ (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 *)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]];
-}
-
-+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group inContext:(NSManagedObjectContext *)context
-{
- NSString *cacheName = nil;
-#ifdef STORE_USE_CACHE
- cacheName = [NSString stringWithFormat:@"ActiveRecord-Cache-%@", NSStringFromClass([self class])];
-#endif
- NSFetchedResultsController *controller =
- [[NSFetchedResultsController alloc] initWithFetchRequest:request
- managedObjectContext:context
- sectionNameKeyPath:group
- cacheName:cacheName];
- [self performFetch:controller];
- return [controller autorelease];
-}
-
-+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group
-{
- return [self fetchRequest:request
- groupedBy:group
- inContext:[self currentContext]];
-}
-#endif
-
-#pragma mark -
-
-+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)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]];
-}
-
-+ (id)findFirstInContext:(NSManagedObjectContext *)context
-{
- NSFetchRequest *request = [self createFetchRequestInContext:context];
-
- return [self executeFetchRequestAndReturnFirstObject:request inContext:context];
-}
-
-+ (id)findFirst
-{
- 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];
-}
-
-+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)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];
-}
-
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm
-{
- return [self findFirstWithPredicate:searchTerm inContext:[self currentContext]];
-}
-
-+ (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
- 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
- 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];
-}
-
-+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending andRetrieveAttributes:(id)attributes, ...
-{
- return [self findFirstWithPredicate:searchTerm
- 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
- 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
- withValue:searchValue
- andOrderBy:sortTerm
- ascending:ascending
- inContext:[self currentContext]];
-}
-
-+ (id)createInContext:(NSManagedObjectContext *)context
-{
- NSString *entityName = NSStringFromClass([self class]);
- return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
-}
-
-+ (id)createEntity
-{
- NSManagedObject *newEntity = [self createInContext:[self currentContext]];
-
- return newEntity;
-}
-
-- (BOOL)deleteInContext:(NSManagedObjectContext *)context
-{
- [context deleteObject:self];
- return YES;
-}
-
-- (BOOL)deleteEntity
-{
- [self deleteInContext:[[self class] currentContext]];
- return YES;
-}
-
-+ (BOOL)truncateAllInContext:(NSManagedObjectContext *)context
-{
- NSArray *allEntities = [self findAllInContext:context];
- for (NSManagedObject *obj in allEntities)
- {
- [obj deleteInContext:context];
- }
- return YES;
-}
-
-+ (BOOL)truncateAll
-{
- [self truncateAllInContext:[self currentContext]];
- return YES;
-}
-
-+ (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
-{
- return [[self class] objectWithMinValueFor:property inContext:[self currentContext]];
-}
-
-@end
View
38 Code/CoreData/NSManagedObject+RKAdditions.h
@@ -0,0 +1,38 @@
+//
+// 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, RKEntityMapping;
+
+/**
+ Provides extensions to NSManagedObject for various common tasks.
+ */
+@interface NSManagedObject (RKAdditions)
+
+///--------------------------------------
+/// @name Inspecting Managed Object State
+///--------------------------------------
+
+/**
+ Determines if the receiver has been deleted from the persistent store
+ and removed from the object graph.
+
+ Unlike isDeleted, will return YES after a save event or if the managed object was deleted
+ in another managed object context that was then merged to the persistent store.
+
+ @return YES if the object has been deleted from the persistent store, else NO.
+ */
+- (BOOL)hasBeenDeleted;
+
+/**
+ * Returns YES when an object has not been saved to the managed object context yet
+ */
+- (BOOL)isNew;
+
+@end
View
28 Code/CoreData/NSManagedObject+RKAdditions.m
@@ -0,0 +1,28 @@
+//
+// 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"
+#import "RKLog.h"
+#import "RKManagedObjectStore.h"
+
+@implementation NSManagedObject (RKAdditions)
+
+- (BOOL)hasBeenDeleted
+{
+ NSManagedObject *managedObjectClone = [[self managedObjectContext] existingObjectWithID:[self objectID] error:nil];
+ return (managedObjectClone == nil) ? YES : NO;
+}
+
+- (BOOL)isNew
+{
+ NSDictionary *vals = [self committedValuesForKeys:nil];
+ return [vals count] == 0;
+}
+
+@end
View
79 Code/CoreData/NSManagedObjectContext+RKAdditions.h
@@ -0,0 +1,79 @@
+//
+// NSManagedObjectContext+RKAdditions.h
+// RestKit
+//
+// Created by Blake Watters on 3/14/12.
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <CoreData/CoreData.h>
+
+@class RKManagedObjectStore;
+
+/**
+ Provides extensions to `NSManagedObjectContext` for various common tasks.
+ */
+@interface NSManagedObjectContext (RKAdditions)
+
+///---------------------------------
+/// @name Inserting a Managed Object
+///---------------------------------
+
+/**
+ Inserts a new managed object for the entity for the given name.
+
+ This method is functionally equivalent to the follow code example.
+
+ [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self];
+
+ @param entityName The name of an entity.
+ @return A new, autoreleased, fully configured instance of the class for the entity named entityName. The instance has its entity description set and is inserted into the receiver.
+ */
+- (id)insertNewObjectForEntityForName:(NSString *)entityName;
+
+///-------------------------------
+/// @name Counting Managed Objects
+///-------------------------------
+
+/**
+ Convenience method for performing a count of the number of instances of an entity with the given name.
+
+ This method is functionally equivalent to the following code example.
+
+ NSError *error;
+ NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName];
+ fetchRequest.predicate = predicate;
+ NSUInteger count = [managedObjectContext countForFetchRequest:fetchRequest error:&error];
+
+ @param entityName The name of an entity.
+ @param predicate A predicate to limit the search. May be nil.
+ @param error If there is a problem executing the fetch, upon return contains an instance of NSError that describes the problem.
+ @return The number of objects a fetch request for the given entity name with the given predicate would have returned if it had been passed to executeFetchRequest:error:, or NSNotFound if an error occurs.
+ */
+- (NSUInteger)countForEntityForName:(NSString *)entityName predicate:(NSPredicate *)predicate error:(NSError **)error;
+
+///-------------------------------------------------
+/// @name Saving the Context to the Persistent Store
+///-------------------------------------------------
+
+/**
+ Saves the receiver and then traverses up the parent context chain until a parent managed object context with a nil parent is found. If the final ancestor context does not have a reference to the persistent store coordinator, then a warning is generated and the method returns NO.
+
+ @param error If there is a problem saving the receiver or any of its ancestor contexts, upon return contains an pointer to an instance of NSError that describes the problem.
+ @return YES if the save to the persistent store was successful, else NO.
+ */
+- (BOOL)saveToPersistentStore:(NSError **)error;
+
+@end
View
64 Code/CoreData/NSManagedObjectContext+RKAdditions.m
@@ -0,0 +1,64 @@
+//
+// NSManagedObjectContext+RKAdditions.m
+// RestKit
+//
+// Created by Blake Watters on 3/14/12.
+// 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.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <objc/runtime.h>
+#import "NSManagedObjectContext+RKAdditions.h"
+#import "RKLog.h"
+
+@implementation NSManagedObjectContext (RKAdditions)
+
+- (id)insertNewObjectForEntityForName:(NSString *)entityName
+{
+ return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self];
+}
+
+- (NSUInteger)countForEntityForName:(NSString *)entityName predicate:(NSPredicate *)predicate error:(NSError **)error
+{
+ NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName];
+ fetchRequest.predicate = predicate;
+ return [self countForFetchRequest:fetchRequest error:error];
+}
+
+- (BOOL)saveToPersistentStore:(NSError **)error
+{
+ __block NSError *localError = nil;
+ NSManagedObjectContext *contextToSave = self;
+ while (contextToSave) {
+ __block BOOL success;
+ [contextToSave performBlockAndWait:^{
+ success = [contextToSave save:&localError];
+ }];
+
+ if (! success) {
+ if (error) *error = localError;
+ return NO;
+ }
+
+ if (! contextToSave.parentContext && contextToSave.persistentStoreCoordinator == nil) {
+ RKLogWarning(@"Reached the end of the chain of nested managed object contexts without encountering a persistent store coordinator. Objects are not fully persisted.");
+ return NO;
+ }
+ contextToSave = contextToSave.parentContext;
+ }
+
+ return YES;
+}
+
+@end
View
152 Code/CoreData/RKConnectionDescription.h
@@ -0,0 +1,152 @@
+//
+// RKConnectionDescription.h
+// RestKit
+//
+// Created by Blake Watters on 11/20/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <CoreData/CoreData.h>
+
+/**
+ The `RKConnectionDescription` class describes a means for connecting a Core Data relationship. Connections can be established either by foreign key, in which case one or more attribute values on the source entity correspond to matching values on the destination entity, or by key path, in which case a key path is evaluated on the object graph to obtain a value for the relationship. Connection objects are used by instances of `RKRelationshipConnectionOperation` to connect a relationship of a given managed object.
+
+ ## Foreign Key Connections
+
+ A foreign key connection is established by identifying managed objects within a context which have corresponding values on the source and destination objects. This is typically used to model relationships in the same way one would within a relational database.
+
+ For example, consider the example of a `User` entity that has a to-many relationship named 'projects' for the `Project` entity. Within the `User` entity, there is an attribute named 'userID' that models the value for a given user's primary key as provided to the application by the remote backend API with which it is communicating. Within the `Project` entity, a corresponding 'userID' attribute exists specifying the value of the primary key for the `User` that owns the project. The applications loads each of these object representations independently from the '/me/profile' and '/projects' resources. The JSON representation returned for a given `Project` entity looks something like:
+
+ { "project":
+ { "id": 12345,
+ "name": "My Project",
+ "userID": 1
+ }
+ }
+
+ When this representation is mapped to a managed object for the `Project` entity, the 'user' relationship cannot be mapped directly because there is no nested representation -- only the primary key is available. In this case, the relationship can be connected by describing the association between the entities with an `RKConnectionDescription` object:
+
+ NSEntityDescription *projectEntity = [NSEntityDescription entityForName:@"Project" inManagedObjectContext:managedObjectContext];
+ NSRelationshipDescription *userRelationship = [projectEntity relationshipsByName][@"user"];
+ RKConnectionDescription *connection = [[RKConnectionDescription alloc] initWithRelationship:relationship attributes:@{ @"userID": @"userID" }];
+
+ Note that the value for the `attributes` argument is provided as a dictionary. Each pair within the dictionary correspond to an attribute pair in which the key is an attribute on the source entity (in this case, the `Project`) and the value is the destination entity (in this case, the `User`).
+
+ Any number of attribute pairs may be specified, but all values must match for the connection to be satisfied and the relationship's value to be set.
+
+ ### Connecting with Collection Values
+
+ Connections can be established by a collection of values. For example, imagine that the previously described project representation has been extended to include a list of team members who are working on the project:
+
+ { "project":
+ { "id": 12345,
+ "name": "My Project",
+ "userID": 1,
+ "teamMemberIDs": [1, 2, 3, 4]
+ }
+ }
+
+ The 'teamMemberIDs' contains an array specifying the ID's of the `User` objects who are collaborating on the project, which corresponds to a to-many relationship named 'teamMembers' on the `Project` entity. In this case, the 'teamMemberIDs' could be mapped on to an `NSArray` or `NSSet` property on the `Project` entity and then connected:
+
+ NSEntityDescription *projectEntity = [NSEntityDescription entityForName:@"Project" inManagedObjectContext:managedObjectContext];
+ NSRelationshipDescription *teamMembers = [projectEntity relationshipsByName][@"teamMembers"]; // To many relationship for the `User` entity
+ RKConnectionDescription *connection = [[RKConnectionDescription alloc] initWithRelationship:relationship attributes:@{ @"teamMemberIDs": @"userID" }];
+
+ When evaluating the above JSON, the connection would be established for the 'teamMembers' relationship to the `User` entities whose userID's are 1, 2, 3 or 4.
+
+ Note that collections of attribute values are always interpetted as logic OR's, but compound connections are aggregated as a logical AND. For example, if we were to add a second connecting attribute for the "gender" property and include `"gender": "male"` in the JSON, the connection would be made to all `User` managed objects whose ID is 1, 2, 3, OR 4 AND whose gender is "male".
+
+ ## Key Path Connections
+
+ A key path connection is established by evaluating the key path of the connection against the managed object being connected. The returned value has type transformation applied and is then assigned to the relationship.
+
+ @see `RKManagedObjectMappingOperationDataSource`
+ @see `RKRelationshipConnectionOperation`
+ */
+@interface RKConnectionDescription : NSObject <NSCopying>
+
+///-----------------------------------------------
+/// @name Connecting Relationships by Foreign Keys
+///-----------------------------------------------
+
+/**
+ Initializes the receiver with a given relationship and a dictionary of attributes specifying how to connect the relationship.
+
+ @param relationship The relationship to be connected.
+ @param sourceToDestinationEntityAttributes A dictionary specifying how attributes on the source entity correspond to attributes on the destination entity.
+ @return The receiver, initialized with the given relationship and attributes.
+ */
+- (id)initWithRelationship:(NSRelationshipDescription *)relationship attributes:(NSDictionary *)sourceToDestinationEntityAttributes;
+
+/**
+ The dictionary of attributes specifying how attributes on the source entity for the relationship correspond to attributes on the destination entity.
+
+ This attribute is `nil` unless the value of `isForeignKeyConnection` is `YES`.
+ */
+@property (nonatomic, copy, readonly) NSDictionary *attributes;
+
+/**
+ Returns a Boolean value indicating if the receiver describes a foreign key connection.
+
+ @return `YES` if the receiver describes a foreign key connection, else `NO`.
+ */
+- (BOOL)isForeignKeyConnection;
+
+///-------------------------------------------
+/// @name Connecting Relationships by Key Path
+///-------------------------------------------
+
+/**
+ Initializes the receiver with a given relationship and key path.
+
+ @param relationship The relationship to be connected.
+ @param keyPath The key path from which to read the value that is to be set for the relationship.
+ @return The receiver, initialized with the given relationship and key path.
+ */
+- (id)initWithRelationship:(NSRelationshipDescription *)relationship keyPath:(NSString *)keyPath;
+
+/**
+ The key path that is to be evaluated to obtain the value for the relationship.
+
+ This attribute is `nil` unless the value of `isKeyPathConnection` is `YES`.
+ */
+@property (nonatomic, copy, readonly) NSString *keyPath;
+
+/**
+ Returns a Boolean value indicating if the receiver describes a key path connection.
+
+ @return `YES` if the receiver describes a key path connection, else `NO`.
+ */
+- (BOOL)isKeyPathConnection;
+
+///-------------------------------------------------
+/// @name Accessing the Relationship to be Connected
+///-------------------------------------------------
+
+/**
+ Returns the relationship that is to be connected.
+ */
+@property (nonatomic, strong, readonly) NSRelationshipDescription *relationship;
+
+///----------------------------
+/// @name Setting the Predicate
+///----------------------------
+
+/**
+ An optional predicate for filtering objects to be connected.
+ */
+@property (nonatomic, copy) NSPredicate *predicate;
+
+@end
View
141 Code/CoreData/RKConnectionDescription.m
@@ -0,0 +1,141 @@
+//
+// RKConnectionDescription.m
+// RestKit
+//
+// Created by Blake Watters on 11/20/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "RKConnectionDescription.h"
+
+static NSSet *RKSetWithInvalidAttributesForEntity(NSArray *attributes, NSEntityDescription *entity)
+{
+ NSMutableSet *attributesSet = [NSMutableSet setWithArray:attributes];
+ NSSet *validAttributeNames = [NSSet setWithArray:[[entity attributesByName] allKeys]];
+ [attributesSet minusSet:validAttributeNames];
+ return attributesSet;
+}
+
+// Provides support for connecting a relationship by
+@interface RKForeignKeyConnectionDescription : RKConnectionDescription
+@end
+
+// Provides support for connecting a relationship by traversing the object graph
+@interface RKKeyPathConnectionDescription : RKConnectionDescription
+@end
+
+@interface RKConnectionDescription ()
+@property (nonatomic, strong, readwrite) NSRelationshipDescription *relationship;
+@property (nonatomic, copy, readwrite) NSDictionary *attributes;
+@property (nonatomic, copy, readwrite) NSString *keyPath;
+@end
+
+@implementation RKConnectionDescription
+
+- (id)initWithRelationship:(NSRelationshipDescription *)relationship attributes:(NSDictionary *)attributes
+{
+ NSParameterAssert(relationship);
+ NSParameterAssert(attributes);
+ NSAssert([attributes count], @"Cannot connect a relationship without at least one pair of attributes describing the connection");
+ NSSet *invalidSourceAttributes = RKSetWithInvalidAttributesForEntity([attributes allKeys], [relationship entity]);
+ NSAssert([invalidSourceAttributes count] == 0, @"Cannot connect relationship: invalid attributes given for source entity '%@': %@", [[relationship entity] name], [[invalidSourceAttributes allObjects] componentsJoinedByString:@", "]);
+ NSSet *invalidDestinationAttributes = RKSetWithInvalidAttributesForEntity([attributes allValues], [relationship destinationEntity]);
+ NSAssert([invalidDestinationAttributes count] == 0, @"Cannot connect relationship: invalid attributes given for destination entity '%@': %@", [[relationship destinationEntity] name], [[invalidDestinationAttributes allObjects] componentsJoinedByString:@", "]);
+
+ self = [[RKForeignKeyConnectionDescription alloc] init];
+ if (self) {
+ self.relationship = relationship;
+ self.attributes = attributes;
+ }
+ return self;
+}
+
+- (id)initWithRelationship:(NSRelationshipDescription *)relationship keyPath:(NSString *)keyPath
+{
+ NSParameterAssert(relationship);
+ NSParameterAssert(keyPath);
+ self = [[RKKeyPathConnectionDescription alloc] init];
+ if (self) {
+ self.relationship = relationship;
+ self.keyPath = keyPath;
+ }
+ return self;
+}
+
+- (id)init
+{
+ if ([self class] == [RKConnectionDescription class]) {
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException
+ reason:[NSString stringWithFormat:@"%@ Failed to call designated initializer. "
+ "Invoke initWithRelationship:sourceKeyPath:destinationKeyPath:matcher: instead.",
+ NSStringFromClass([self class])]
+ userInfo:nil];
+ }
+ return [super init];
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ if ([self isForeignKeyConnection]) {
+ return [[[self class] allocWithZone:zone] initWithRelationship:self.relationship attributes:self.attributes];
+ } else if ([self isKeyPathConnection]) {
+ return [[[self class] allocWithZone:zone] initWithRelationship:self.relationship keyPath:self.keyPath];
+ }
+
+ return nil;
+}
+
+- (BOOL)isForeignKeyConnection
+{
+ return NO;
+}
+
+- (BOOL)isKeyPathConnection
+{
+ return NO;
+}
+
+@end
+
+@implementation RKForeignKeyConnectionDescription
+
+- (BOOL)isForeignKeyConnection
+{
+ return YES;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@:%p connecting Relationship '%@' from Entity '%@' to Destination Entity '%@' with attributes=%@>",
+ NSStringFromClass([self class]), self, [self.relationship name], [[self.relationship entity] name],
+ [[self.relationship destinationEntity] name], self.attributes];
+}
+
+@end
+
+@implementation RKKeyPathConnectionDescription
+
+- (BOOL)isKeyPathConnection
+{
+ return YES;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@:%p connecting Relationship '%@' of Entity '%@' with keyPath=%@>",
+ NSStringFromClass([self class]), self, [self.relationship name], [[self.relationship entity] name], self.keyPath];
+}
+
+@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.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <CoreData/CoreData.h>
+
+/**
+ The `RKEntityByAttributeCache` class provides 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.
+
+ @bug Please note that the `RKEntityByAttribute` cache is implemented using a `NSFetchRequest` with a result type of `NSDictionaryResultType`. This means that the cache **cannot** load pending object instances via a fetch from the `load` method. Pending objects must be manually added to the cache via `addObject:` if it is desirable for the pending objects to be retrieved by subsequent invocations of `objectWithAttributeValue:inContext:` and `objectsWithAttributeValue:inContext:` prior to a save.
+
+ This is a limitation imposed by Core Data. The dictionary result type implementation is leveraged instead a normal fetch request because it offers very large performance and memory utilization improvements by avoiding construction of managed object instances and faulting.
+
+ @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 attributeNames An array of attribute names used as the cache keys.
+ @param context 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 attributes:(NSArray *)attributeNames managedObjectContext:(NSManagedObjectContext *)context;
+
+///-----------------------------
+/// @name Getting Cache Identity
+///-----------------------------
+
+/**
+ The Core Data entity description for the managed objects being cached.
+ */
+@property (nonatomic, readonly) NSEntityDescription *entity;
+
+/**
+ An array of attribute names specifying attributes of the cached entity that act as the cache key.
+ */
+@property (nonatomic, readonly) NSArray *attributes;
+
+/**
+ 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 whose attributes match the values in the given dictionary of attribute values.
+
+ @param attributeValues 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)countWithAttributeValues:(NSDictionary *)attributeValues;
+
+/**
+ 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)containsObjectWithAttributeValues:(NSDictionary *)attributeValues;
+
+/**
+ Returns the first object with a matching value for the cache key attributes in a given managed object context.
+
+ @param attributeValues A value for the cache key attribute.
+ @param context The managed object context to retrieve the object from.
+ @return An object with the value of attribute matching attributeValue or nil.
+ */
+- (NSManagedObject *)objectWithAttributeValues:(NSDictionary *)attributeValues inContext:(NSManagedObjectContext *)context;
+
+/**
+ Returns the collection of objects with a matching value for the cache key attribute in a given managed object context.
+
+ @param attributeValue A value for the cache key attribute.
+ @param context The managed object context to retrieve the objects from.
+ @return An array of objects with the value of attribute matching attributeValue or an empty array.
+ */
+- (NSSet *)objectsWithAttributeValues:(NSDictionary *)attributeValues inContext:(NSManagedObjectContext *)context;
+
+///------------------------------
+/// @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
365 Code/CoreData/RKEntityByAttributeCache.m
@@ -0,0 +1,365 @@
+//
+// RKEntityByAttributeCache.m
+// RestKit
+//
+// Created by Blake Watters on 5/1/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#endif
+
+#import "RKEntityByAttributeCache.h"
+#import "RKLog.h"
+#import "RKPropertyInspector.h"
+#import "RKPropertyInspector+CoreData.h"
+#import "NSManagedObject+RKAdditions.h"
+#import "RKObjectUtilities.h"
+
+// Set Logging Component
+#undef RKLogComponent
+#define RKLogComponent RKlcl_cRestKitCoreDataCache
+
+static id RKCacheKeyValueForEntityAttributeWithValue(NSEntityDescription *entity, NSString *attribute, id value)
+{
+ if ([value isKindOfClass:[NSString class]] || [value isEqual:[NSNull null]]) {
+ return value;
+ }
+
+ Class attributeType = [[RKPropertyInspector sharedInspector] classForPropertyNamed:attribute ofEntity:entity];
+ return [attributeType instancesRespondToSelector:@selector(stringValue)] ? [value stringValue] : value;
+}
+
+static NSString *RKCacheKeyForEntityWithAttributeValues(NSEntityDescription *entity, NSDictionary *attributeValues)
+{
+ NSArray *sortedAttributes = [[attributeValues allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
+ NSMutableArray *sortedValues = [NSMutableArray arrayWithCapacity:[sortedAttributes count]];
+ [sortedAttributes enumerateObjectsUsingBlock:^(NSString *attributeName, NSUInteger idx, BOOL *stop) {
+ id cacheKeyValue = RKCacheKeyValueForEntityAttributeWithValue(entity, attributeName, [attributeValues objectForKey:attributeName]);
+ [sortedValues addObject:cacheKeyValue];
+ }];
+
+ return [sortedValues componentsJoinedByString:@":"];
+}
+
+/*
+ This function recursively calculates a set of cache keys given a dictionary of attribute values. The basic premise is that we wish to decompose all arrays of values within the dictionary into a distinct cache key, as each object within the cache will appear for only one key.
+ */
+static NSArray *RKCacheKeysForEntityFromAttributeValues(NSEntityDescription *entity, NSDictionary *attributeValues)
+{
+ NSMutableArray *cacheKeys = [NSMutableArray array];
+ NSSet *collectionKeys = [attributeValues keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
+ return RKObjectIsCollection(obj);
+ }];
+
+ if ([collectionKeys count] > 0) {
+ for (NSString *attributeName in collectionKeys) {
+ id attributeValue = [attributeValues objectForKey:attributeName];
+ for (id value in attributeValue) {
+ NSMutableDictionary *mutableAttributeValues = [attributeValues mutableCopy];
+ [mutableAttributeValues setValue:value forKey:attributeName];
+ [cacheKeys addObjectsFromArray:RKCacheKeysForEntityFromAttributeValues(entity, mutableAttributeValues)];
+ }
+ }
+ } else {
+ [cacheKeys addObject:RKCacheKeyForEntityWithAttributeValues(entity, attributeValues)];
+ }
+
+ return cacheKeys;
+}
+
+@interface RKEntityByAttributeCache ()
+@property (nonatomic, strong) NSMutableDictionary *cacheKeysToObjectIDs;
+@end
+
+@implementation RKEntityByAttributeCache
+
+- (id)initWithEntity:(NSEntityDescription *)entity attributes:(NSArray *)attributeNames managedObjectContext:(NSManagedObjectContext *)context
+{
+ NSParameterAssert(entity);
+ NSParameterAssert(attributeNames);
+ NSParameterAssert(context);
+
+ self = [self init];
+ if (self) {
+ _entity = entity;
+ _attributes = attributeNames;
+ _managedObjectContext = context;
+ _monitorsContextForChanges = YES;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(managedObjectContextDidChange:)
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ 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];
+}
+
+- (NSUInteger)count
+{
+ return [[[self.cacheKeysToObjectIDs allValues] valueForKeyPath:@"@sum.@count"] integerValue];
+}
+
+- (NSUInteger)countOfAttributeValues
+{
+ return [self.cacheKeysToObjectIDs count];
+}
+
+- (NSUInteger)countWithAttributeValues:(NSDictionary *)attributeValues
+{
+ return [[self objectsWithAttributeValues:attributeValues inContext:self.managedObjectContext] count];
+}
+
+- (void)load
+{
+ RKLogDebug(@"Loading entity cache for Entity '%@' by attributes '%@' in managed object context %@ (concurrencyType = %ld)",
+ self.entity.name, self.attributes, self.managedObjectContext, (unsigned long)self.managedObjectContext.concurrencyType);
+ @synchronized(self.cacheKeysToObjectIDs) {
+ self.cacheKeysToObjectIDs = [NSMutableDictionary dictionary];
+
+ NSExpressionDescription* objectIDExpression = [NSExpressionDescription new];
+ objectIDExpression.name = @"objectID";
+ objectIDExpression.expression = [NSExpression expressionForEvaluatedObject];
+ objectIDExpression.expressionResultType = NSObjectIDAttributeType;
+
+ // NOTE: `NSDictionaryResultType` does NOT support fetching pending changes. Pending objects must be manually added to the cache via `addObject:`.
+ NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
+ fetchRequest.entity = self.entity;
+ fetchRequest.resultType = NSDictionaryResultType;
+ fetchRequest.propertiesToFetch = [self.attributes arrayByAddingObject:objectIDExpression];
+
+ [self.managedObjectContext performBlockAndWait:^{
+ NSError *error = nil;
+ NSArray *dictionaries = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
+ if (dictionaries) {
+ RKLogDebug(@"Retrieved %ld dictionaries for cachable `NSManagedObjectID` objects with fetch request: %@", (long) [dictionaries count], fetchRequest);
+ } else {
+ RKLogWarning(@"Failed to load entity cache. Failed to execute fetch request: %@", fetchRequest);
+ RKLogCoreDataError(error);
+ }
+
+ for (NSDictionary *dictionary in dictionaries) {
+ NSManagedObjectID *objectID = [dictionary objectForKey:@"objectID"];
+ NSDictionary *attributeValues = [dictionary dictionaryWithValuesForKeys:self.attributes];
+ [self setObjectID:objectID forAttributeValues:attributeValues];
+ }
+ }];
+ }
+}
+
+- (void)flush
+{
+ @synchronized(self.cacheKeysToObjectIDs) {
+ RKLogDebug(@"Flushing entity cache for Entity '%@' by attributes '%@'", self.entity.name, self.attributes);
+ self.cacheKeysToObjectIDs = nil;
+ }
+}
+
+- (void)reload
+{
+ [self flush];
+ [self load];
+}
+
+- (BOOL)isLoaded
+{
+ return (self.cacheKeysToObjectIDs != nil);
+}
+
+- (NSManagedObject *)objectForObjectID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)context
+{
+ /**
+ 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.
+ */
+ __block NSError *error = nil;
+ __block NSManagedObject *object;
+ [context performBlockAndWait:^{
+ object = [context existingObjectWithID:objectID error:&error];
+ }];
+ if (! object) {
+ // Referential integrity errors often indicates that the temporary objectID does not exist in the specified context
+ if (error && !([objectID isTemporaryID] && [error code] == NSManagedObjectReferentialIntegrityError)) {
+ RKLogError(@"Failed to retrieve managed object with ID %@. Error %@\n%@", objectID, [error localizedDescription], [error userInfo]);
+ }
+
+ return nil;
+ }
+
+ return object;
+}
+
+- (NSManagedObject *)objectWithAttributeValues:(NSDictionary *)attributeValues inContext:(NSManagedObjectContext *)context
+{
+ NSSet *objects = [self objectsWithAttributeValues:attributeValues inContext:context];
+ return ([objects count] > 0) ? [objects anyObject] : nil;
+}
+
+- (NSSet *)objectsWithAttributeValues:(NSDictionary *)attributeValues inContext:(NSManagedObjectContext *)context
+{
+ // TODO: Assert that the attribute values contains all of the cache attributes!!!
+ NSMutableSet *objects = [NSMutableSet set];
+ NSArray *cacheKeys = RKCacheKeysForEntityFromAttributeValues(self.entity, attributeValues);
+ for (NSString *cacheKey in cacheKeys) {
+ NSArray *objectIDs = nil;
+ @synchronized(self.cacheKeysToObjectIDs) {
+ objectIDs = [[NSArray alloc] initWithArray:[self.cacheKeysToObjectIDs objectForKey:cacheKey] copyItems:YES];
+ }
+ if ([objectIDs count]) {
+ /**
+ NOTE:
+ In my benchmarking, retrieving the objects one at a time using existingObjectWithID: is significantly faster
+ than issuing a single fetch request against all object ID's.
+ */
+ for (NSManagedObjectID *objectID in objectIDs) {
+ NSManagedObject *object = [self objectForObjectID:objectID inContext:context];
+ if (object) {
+ [objects addObject:object];
+ } else {
+ RKLogDebug(@"Evicting objectID association for attributes %@ of Entity '%@': %@", attributeValues, self.entity.name, objectID);
+ [self removeObjectID:objectID forAttributeValues:attributeValues];
+ }
+ }
+ }
+ }
+
+ return objects;
+}
+
+- (void)setObjectID:(NSManagedObjectID *)objectID forAttributeValues:(NSDictionary *)attributeValues
+{
+ @synchronized(self.cacheKeysToObjectIDs) {
+ if (attributeValues && [attributeValues count]) {
+ NSString *cacheKey = RKCacheKeyForEntityWithAttributeValues(self.entity, attributeValues);
+ NSMutableArray *objectIDs = [self.cacheKeysToObjectIDs objectForKey:cacheKey];
+ if (objectIDs) {
+ if (! [objectIDs containsObject:objectID]) {
+ [objectIDs addObject:objectID];
+ }
+ } else {
+ objectIDs = [NSMutableArray arrayWithObject:objectID];
+ }
+
+ if (nil == self.cacheKeysToObjectIDs) self.cacheKeysToObjectIDs = [NSMutableDictionary dictionary];
+ [self.cacheKeysToObjectIDs setValue:objectIDs forKey:cacheKey];
+ } else {
+ RKLogWarning(@"Unable to add object for object ID %@: empty values dictionary for attributes '%@'", objectID, self.attributes);
+ }
+ }
+}
+
+- (void)removeObjectID:(NSManagedObjectID *)objectID forAttributeValues:(NSDictionary *)attributeValues
+{
+ @synchronized(self.cacheKeysToObjectIDs) {
+ if (attributeValues && [attributeValues count]) {
+ NSString *cacheKey = RKCacheKeyForEntityWithAttributeValues(self.entity, attributeValues);
+ NSMutableArray *objectIDs = [self.cacheKeysToObjectIDs objectForKey:cacheKey];
+ if (objectIDs && [objectIDs containsObject:objectID]) {
+ [objectIDs removeObject:objectID];
+ }
+ } else {
+ RKLogWarning(@"Unable to remove object for object ID %@: empty values dictionary for attributes '%@'", objectID, self.attributes);
+ }
+ }
+}
+
+- (void)addObject:(NSManagedObject *)object
+{
+ __block NSEntityDescription *entity;
+ __block NSDictionary *attributeValues;
+ __block NSManagedObjectID *objectID;
+ [object.managedObjectContext performBlockAndWait:^{
+ entity = object.entity;
+ objectID = [object objectID];
+ attributeValues = [object dictionaryWithValuesForKeys:self.attributes];
+ }];
+ NSAssert([entity isKindOfEntity:self.entity], @"Cannot add object with entity '%@' to cache for entity of '%@'", [entity name], [self.entity name]);
+ [self setObjectID:objectID forAttributeValues:attributeValues];
+}
+
+- (void)removeObject:(NSManagedObject *)object
+{
+ __block NSEntityDescription *entity;
+ __block NSDictionary *attributeValues;
+ __block NSManagedObjectID *objectID;
+ [object.managedObjectContext performBlockAndWait:^{
+ entity = object.entity;
+ objectID = [object objectID];
+ attributeValues = [object dictionaryWithValuesForKeys:self.attributes];
+ }];
+ NSAs