Add new RKManagedObjectStore method for assisting in performing migrations when you have made dynamic modifications to the managed object model (such as when you leverage the Search indexing support)

This commit is contained in:
Blake Watters
2013-01-08 18:26:05 -05:00
parent 70180022be
commit 4c0e62a62a
12 changed files with 521 additions and 30 deletions

View File

@@ -244,6 +244,54 @@
*/
- (NSManagedObjectContext *)newChildManagedObjectContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrencyType;
///----------------------------
/// @name Performing Migrations
///----------------------------
/**
Performs a migration on a persistent store at a given URL to the model at the specified URL.
This method provides support for migrating persistent stores in which the source and destination models have been mutated after being loaded from the model archive on disk, such as when the RestKit managed object searching support is used. In a situation where the persistent store has been created with a dynamically modified managed object model. Core Data is unable to infer the mapping model because the metadata of the persistent store does not agree with that of the managed object model due to the dynamic modifications. In order to perform a migration, one must load the appropriate source model, apply the dynamic changes appropriate for that model, then infer a mapping model from the modified model. This method assists in this process by accepting a source store and a destination model as arguments and searching through all models in the .momd package and yielding each model to the given configuration block for processing. After the block is invoked, the metadata of the store is checked for compatibility with the modified managed object model to identify the source store. Once the source store is found, a mapping model is inferred and the migration proceeds. The migration is done against a copy of the given persistent store and if successful, the migrated store is moved to replace the original store.
To understand how this is used, consider the following example: Given a managed object model containing two entities 'Article' and 'Tag', the user wishes to configure managed object search indexing on the models and wishes to be able to migrate existing persistent stores across versions. The migration configuration would look something like this:
NSURL *storeURL = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"MyStore.sqlite"];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"VersionedModel" withExtension:@"momd"];
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:^(NSManagedObjectModel *model, NSURL *sourceURL) {
// Examine each model and configure search indexing appropriately based on the versionIdentifiers configured in the model
if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"1.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
} else if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"2.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title", @"body" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
} else if ([[model versionIdentifiers] containsObject:@"3.0"] || [[model versionIdentifiers] containsObject:@"4.0"]) {
// We index the same attributes on v3 and v4
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title", @"body", @"authorName" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
}
}];
@param storeType The type of store that given URL. May be `nil`.
@param storeURL A URL to the store that is to be migrated.
@param destinationModelURL A URL to the managed object model that the persistent store is to be updated to. This URL may target a specific model version with a .momd package or point to the .momd package itself, in which case the migration is performed to the current version of the model as configured on the .xcdatamodeld file used to the build the .momd package.
@param error A pointer to an error object that is set in the event that the migration is unsuccessful.
@param block A block object used to configure
@warning This method is only usable with a versioned Managed Object Model stored as a .momd package containing .mom managed object model archives.
*/
+ (BOOL)migratePersistentStoreOfType:(NSString *)storeType
atURL:(NSURL *)storeURL
toModelAtURL:(NSURL *)destinationModelURL
error:(NSError **)error
configuringModelsWithBlock:(void (^)(NSManagedObjectModel *model, NSURL *sourceURL))block;
@end
// Option containing the path to the seed database a SQLite store was initialized with

View File

@@ -31,6 +31,8 @@
#undef RKLogComponent
#define RKLogComponent RKlcl_cRestKitCoreData
extern NSString * const RKErrorDomain;
NSString * const RKSQLitePersistentStoreSeedDatabasePathOption = @"RKSQLitePersistentStoreSeedDatabasePathOption";
NSString * const RKManagedObjectStoreDidFailSaveNotification = @"RKManagedObjectStoreDidFailSaveNotification";
@@ -288,4 +290,101 @@ static RKManagedObjectStore *defaultStore = nil;
}];
}
+ (BOOL)migratePersistentStoreOfType:(NSString *)storeType
atURL:(NSURL *)storeURL
toModelAtURL:(NSURL *)destinationModelURL
error:(NSError **)error
configuringModelsWithBlock:(void (^)(NSManagedObjectModel *, NSURL *))block
{
BOOL isMomd = [[destinationModelURL pathExtension] isEqualToString:@"momd"]; // Momd contains a directory of versioned models
NSManagedObjectModel *destinationModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:destinationModelURL] mutableCopy];
// Yield the destination model for configuration (i.e. search indexing)
if (block) block(destinationModel, destinationModelURL);
// Check if the store is compatible with our model
NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType
URL:storeURL
error:error];
if (! storeMetadata) return NO;
if ([destinationModel isConfiguration:nil compatibleWithStoreMetadata:storeMetadata]) {
// Our store is compatible with the current model, no migration is necessary
return YES;
}
RKLogInfo(@"Determined that store at URL %@ has incompatible metadata for managed object model: performing migration...", storeURL);
NSURL *momdURL = isMomd ? destinationModelURL : [destinationModelURL URLByDeletingLastPathComponent];
// We can only do migrations within a versioned momd
if (![[momdURL pathExtension] isEqualToString:@"momd"]) {
NSString *errorDescription = [NSString stringWithFormat:@"Migration failed: Migrations can only be performed to versioned destination models contained in a .momd package. Incompatible destination model given at path '%@'", [momdURL path]];
*error = [NSError errorWithDomain:RKErrorDomain code:NSMigrationError userInfo:@{ NSLocalizedDescriptionKey: errorDescription }];
return NO;
}
NSArray *versionedModelURLs = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:momdURL
includingPropertiesForKeys:@[] // We only want the URLs
options:NSDirectoryEnumerationSkipsPackageDescendants|NSDirectoryEnumerationSkipsHiddenFiles
error:error];
if (! versionedModelURLs) {
return NO;
}
// Iterate across each model version and try to find a compatible store
NSManagedObjectModel *sourceModel = nil;
for (NSURL *versionedModelURL in versionedModelURLs) {
if (! [@[@"mom", @"momd"] containsObject:[versionedModelURL pathExtension]]) continue;
NSManagedObjectModel *model = [[[NSManagedObjectModel alloc] initWithContentsOfURL:versionedModelURL] mutableCopy];
if (! model) continue;
if (block) block(model, versionedModelURL);
if ([model isConfiguration:nil compatibleWithStoreMetadata:storeMetadata]) {
sourceModel = model;
break;
}
}
// Cannot complete the migration as we can't find a source model
if (! sourceModel) {
NSString *errorDescription = [NSString stringWithFormat:@"Migration failed: Unable to find the source managed object model used to create the %@ store at path '%@'", storeType, [storeURL path]];
*error = [NSError errorWithDomain:RKErrorDomain code:NSMigrationMissingSourceModelError userInfo:@{ NSLocalizedDescriptionKey: errorDescription }];
return NO;
}
// Infer a mapping model and complete the migration
NSMappingModel *mappingModel = [NSMappingModel inferredMappingModelForSourceModel:sourceModel
destinationModel:destinationModel
error:error];
if (!mappingModel) {
RKLogError(@"Failed to obtain inferred mapping model for source and destination models: aborting migration...");
RKLogError(@"%@", *error);
return NO;
}
NSString *UUID = (__bridge_transfer NSString*)CFUUIDCreateString(kCFAllocatorDefault, CFUUIDCreate(NULL));
NSString *migrationPath = [NSTemporaryDirectory() stringByAppendingFormat:@"Migration-%@.sqlite", UUID];
NSURL *migrationURL = [NSURL fileURLWithPath:migrationPath];
// Create a migration manager to perform the migration.
NSMigrationManager *manager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:destinationModel];
BOOL success = [manager migrateStoreFromURL:storeURL type:NSSQLiteStoreType
options:nil withMappingModel:mappingModel toDestinationURL:migrationURL
destinationType:NSSQLiteStoreType destinationOptions:nil error:error];
if (success) {
success = [[NSFileManager defaultManager] removeItemAtURL:storeURL error:error];
if (success) {
success = [[NSFileManager defaultManager] moveItemAtURL:migrationURL toURL:storeURL error:error];
if (success) RKLogInfo(@"Successfully migrated existing store to managed object model at path '%@'...", [destinationModelURL path]);
} else {
RKLogError(@"Failed to remove existing store at path '%@': unable to complete migration...", [storeURL path]);
RKLogError(@"%@", *error);
}
} else {
RKLogError(@"Failed migration with error: %@", *error);
}
return success;
}
@end

View File

@@ -475,6 +475,8 @@
25A226D81618A57500952D72 /* RKObjectUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A226D51618A57500952D72 /* RKObjectUtilities.m */; };
25A226D91618A57500952D72 /* RKObjectUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A226D51618A57500952D72 /* RKObjectUtilities.m */; };
25A34245147D8AAA0009758D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25A34244147D8AAA0009758D /* Security.framework */; };
25A73362169C8C230090A930 /* VersionedModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 25A73360169C8C230090A930 /* VersionedModel.xcdatamodeld */; };
25A73363169C8C230090A930 /* VersionedModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 25A73360169C8C230090A930 /* VersionedModel.xcdatamodeld */; };
25A763DB15C7240200A9DF31 /* RKSearchTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 25A763D915C7240100A9DF31 /* RKSearchTokenizer.h */; settings = {ATTRIBUTES = (Public, ); }; };
25A763DC15C7240200A9DF31 /* RKSearchTokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 25A763D915C7240100A9DF31 /* RKSearchTokenizer.h */; settings = {ATTRIBUTES = (Public, ); }; };
25A763DD15C7240200A9DF31 /* RKSearchTokenizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A763DA15C7240100A9DF31 /* RKSearchTokenizer.m */; };
@@ -884,6 +886,10 @@
25A226D41618A57500952D72 /* RKObjectUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKObjectUtilities.h; sourceTree = "<group>"; };
25A226D51618A57500952D72 /* RKObjectUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectUtilities.m; sourceTree = "<group>"; };
25A34244147D8AAA0009758D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
25A73361169C8C230090A930 /* VersionedModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = VersionedModel.xcdatamodel; sourceTree = "<group>"; };
25A73365169C8CD80090A930 /* VersionedModel 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "VersionedModel 2.xcdatamodel"; sourceTree = "<group>"; };
25A73366169C8CF30090A930 /* VersionedModel 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "VersionedModel 3.xcdatamodel"; sourceTree = "<group>"; };
25A73367169C8D500090A930 /* VersionedModel 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "VersionedModel 4.xcdatamodel"; sourceTree = "<group>"; };
25A763D915C7240100A9DF31 /* RKSearchTokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKSearchTokenizer.h; sourceTree = "<group>"; };
25A763DA15C7240100A9DF31 /* RKSearchTokenizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKSearchTokenizer.m; sourceTree = "<group>"; };
25A763E015C72B9D00A9DF31 /* RKSearchTokenizerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKSearchTokenizerTest.m; sourceTree = "<group>"; };
@@ -1447,6 +1453,7 @@
251610081456F2330060A5C5 /* RKParent.m */,
251610091456F2330060A5C5 /* RKResident.h */,
2516100A1456F2330060A5C5 /* RKResident.m */,
25A73360169C8C230090A930 /* VersionedModel.xcdatamodeld */,
);
path = Models;
sourceTree = "<group>";
@@ -2372,6 +2379,7 @@
2551338F167838590017E4B6 /* RKHTTPRequestOperationTest.m in Sources */,
255133CF167AC7600017E4B6 /* RKManagedObjectRequestOperationTest.m in Sources */,
25B639CC16961EFA0065EB7B /* RKMappingTestTest.m in Sources */,
25A73362169C8C230090A930 /* VersionedModel.xcdatamodeld in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2523,6 +2531,7 @@
2536D1FE167270F100DF9BB0 /* RKRouterTest.m in Sources */,
25513390167838590017E4B6 /* RKHTTPRequestOperationTest.m in Sources */,
25B639CD16961EFA0065EB7B /* RKMappingTestTest.m in Sources */,
25A73363169C8C230090A930 /* VersionedModel.xcdatamodeld in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2835,6 +2844,22 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
25A73360169C8C230090A930 /* VersionedModel.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
25A73365169C8CD80090A930 /* VersionedModel 2.xcdatamodel */,
25A73366169C8CF30090A930 /* VersionedModel 3.xcdatamodel */,
25A73367169C8D500090A930 /* VersionedModel 4.xcdatamodel */,
25A73361169C8C230090A930 /* VersionedModel.xcdatamodel */,
);
currentVersion = 25A73367169C8D500090A930 /* VersionedModel 4.xcdatamodel */;
path = VersionedModel.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = 25160D0D14564E810060A5C5 /* Project object */;
}

View File

@@ -21,6 +21,21 @@
#import "RKTestEnvironment.h"
#import "RKHuman.h"
#import "RKPathUtilities.h"
#import "RKSearchIndexer.h"
// TODO: Does this become `RKManagedObjectStore managedObjectModelWithName:version:inBundle:` ??? URLForManagedObjectModel
static NSURL *RKURLForManagedObjectModelWithNameAtVersion(NSString *modelName, NSUInteger version)
{
NSString *modelNameAndVersion = (version == 1) ? modelName : [NSString stringWithFormat:@"%@ %ld", modelName, (unsigned long) version];
return [[RKTestFixture fixtureBundle] URLForResource:[NSString stringWithFormat:@"%@.momd/%@", modelName, modelNameAndVersion] withExtension:@"mom"];
}
static NSManagedObjectModel *RKManagedObjectModelWithNameAtVersion(NSString *modelName, NSUInteger version)
{
NSURL *URL = RKURLForManagedObjectModelWithNameAtVersion(modelName, version);
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:URL];
return model;
}
@interface RKManagedObjectStoreTest : RKTestCase
@@ -52,7 +67,9 @@
- (void)testAdditionOfSQLiteStoreRetainsPathOfSeedDatabase
{
// Create a store with a SQLite database to use as our store
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSString *seedPath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Seed.sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [seedStore addSQLitePersistentStoreAtPath:seedPath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
@@ -61,7 +78,7 @@
expect(fileExists).to.beTruthy();
// Create a secondary store using the seed
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] init];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Test.sqlite"];
persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
expect(persistentStore).notTo.beNil();
@@ -77,7 +94,9 @@
- (void)testAddingPersistentSQLiteStoreFromSeedDatabase
{
// Create a store with an object to serve as our seed database
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *seedPath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Seed.sqlite"];
NSPersistentStore *seedPersistentStore = [seedStore addSQLitePersistentStoreAtPath:seedPath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
@@ -91,7 +110,7 @@
// Create a secondary store using the first store as the seed
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"SeededStore.sqlite"];
RKManagedObjectStore *seededStore = [[RKManagedObjectStore alloc] init];
RKManagedObjectStore *seededStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSPersistentStore *persistentStore = [seededStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[seededStore createManagedObjectContexts];
@@ -107,7 +126,9 @@
- (void)testResetPersistentStoresRecreatesInMemoryStoreThusDeletingAllManagedObjects
{
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
assertThat(persistentStore, is(notNilValue()));
@@ -136,7 +157,9 @@
- (void)testResetPersistentStoresRecreatesSQLiteStoreThusDeletingAllManagedObjects
{
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Test.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
@@ -163,7 +186,9 @@
- (void)testResetPersistentStoreRecreatesSQLiteStoreThusRecreatingTheStoreFileOnDisk
{
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Test.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
@@ -192,7 +217,9 @@
- (void)testResetPersistentStoreForSQLiteStoreSeededWithDatabaseReclonesTheSeedDatabaseToTheStoreLocation
{
// Create a store with an object to serve as our seed database
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *seedStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *seedPath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"Seed.sqlite"];
NSPersistentStore *seedPersistentStore = [seedStore addSQLitePersistentStoreAtPath:seedPath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
@@ -206,7 +233,7 @@
// Create a secondary store using the first store as the seed
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"SeededStore.sqlite"];
RKManagedObjectStore *seededStore = [[RKManagedObjectStore alloc] init];
RKManagedObjectStore *seededStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSPersistentStore *persistentStore = [seededStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
assertThat(persistentStore, is(notNilValue()));
[seededStore createManagedObjectContexts];
@@ -239,7 +266,9 @@
#if __IPHONE_OS_VERSION_MIN_REQUIRED
- (void)testThatAddingASQLiteStoreExcludesThePathFromiCloudBackups
{
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestBackupExclusion.sqlite"];
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
@@ -273,7 +302,9 @@
- (void)testCleanupOfExternalStorageDirectoryOnReset
{
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSEntityDescription *humanEntity = [managedObjectStore.managedObjectModel entitiesByName][@"Human"];
NSAttributeDescription *photoAttribute = [[NSAttributeDescription alloc] init];
[photoAttribute setName:@"photo"];
@@ -310,7 +341,9 @@
- (void)testThatPersistentStoreWithLongNameHasExternalStorageResetCorrectly
{
// Create a store with an object to serve as our seed database
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] init];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error = nil;
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"This is the Store.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
@@ -337,4 +370,197 @@
expect([creationDate laterDate:newCreationDate]).to.equal(newCreationDate);
}
#pragma mark - Versioning Tests
- (void)testThatAttemptToMigrateStoreAtNonExistantFileURLReturnsError
{
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"NonExistantStore.sqlite"];
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSError *error = nil;
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(NO);
expect(error.code).to.equal(0);
}
- (void)testThatAttemptingToMigrateToANonVersionedModelReturnsError
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Attempting to update to `modelURL` will fail because this is an unversioned model
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(NO);
expect(error).notTo.beNil();
expect(error.code).to.equal(NSMigrationError);
assertThat([error localizedDescription], startsWith(@"Migration failed: Migrations can only be performed to versioned destination models contained in a .momd package. Incompatible destination model given at path"));
}
- (void)testThatAttemptingToMigrateFromAnUnidentifiableSourceModelReturnsError
{
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
NSEntityDescription *extraEntity = [NSEntityDescription new];
[extraEntity setName:@"Extra"];
[model_v1 setEntities:[[model_v1 entities] arrayByAddingObject:extraEntity]];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Attempting to upgrade to v2 returns NO, because it can't find the source model (due to the added entity)
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 2);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(NO);
expect(error).notTo.beNil();
expect(error.code).to.equal(NSMigrationMissingSourceModelError);
assertThat([error localizedDescription], startsWith(@"Migration failed: Unable to find the source managed object model used to create the SQLite store at path"));
}
- (void)testMigrationToCompatibleVersionReturnsYes
{
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Attempting to upgrade to v1 returns YES, because its already compatible
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelAt1_0to2_0
{
// Create a v1 Store
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to v2
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 2);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelAt1_0to_4_0
{
// Create a v1 Store
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to v4
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 4);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:nil];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelWithSearchAttributesAt2_0to_3_0
{
// Create a v2 Store
NSManagedObjectModel *model_v2 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 2);
// Add search indexing on the title attribute
NSEntityDescription *articleEntity = [[model_v2 entitiesByName] objectForKey:@"Article"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v2];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to v3
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = RKURLForManagedObjectModelWithNameAtVersion(@"VersionedModel", 4);
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:^(NSManagedObjectModel *model, NSURL *sourceURL) {
if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"2.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
}
}];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
- (void)testUpgradingFromVersionedModelWithSearchAttributesAt_1_0toLatest
{
// Create a v1 Store
NSManagedObjectModel *model_v1 = RKManagedObjectModelWithNameAtVersion(@"VersionedModel", 1);
// Add search indexing on the title attribute
NSEntityDescription *articleEntity = [[model_v1 entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model_v1 entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model_v1];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"TestStore.sqlite"];
NSError *error = nil;
NSDictionary *options = @{ NSMigratePersistentStoresAutomaticallyOption: @(NO), NSInferMappingModelAutomaticallyOption: @(NO) };
[managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:options error:&error];
[managedObjectStore createManagedObjectContexts];
managedObjectStore = nil;
// Now upgrade it to the latest version
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"VersionedModel" withExtension:@"momd"];
BOOL success = [RKManagedObjectStore migratePersistentStoreOfType:NSSQLiteStoreType atURL:storeURL toModelAtURL:modelURL error:&error configuringModelsWithBlock:^(NSManagedObjectModel *model, NSURL *sourceURL) {
if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"1.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
} else if ([[model versionIdentifiers] isEqualToSet:[NSSet setWithObject:@"2.0"]]) {
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title", @"body" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
} else if ([[model versionIdentifiers] containsObject:@"3.0"] || [[model versionIdentifiers] containsObject:@"4.0"]) {
// We index the same attributes on v3 and v4
NSEntityDescription *articleEntity = [[model entitiesByName] objectForKey:@"Article"];
NSEntityDescription *tagEntity = [[model entitiesByName] objectForKey:@"Tag"];
[RKSearchIndexer addSearchIndexingToEntity:articleEntity onAttributes:@[ @"title", @"body", @"authorName" ]];
[RKSearchIndexer addSearchIndexingToEntity:tagEntity onAttributes:@[ @"name" ]];
}
}];
expect(success).to.equal(YES);
expect(error).to.beNil();
}
@end

View File

@@ -14,6 +14,12 @@
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@end
static NSManagedObjectModel *RKManagedObjectModel()
{
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
return [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
}
@interface RKSearchIndexerTest : RKTestCase
@end
@@ -22,7 +28,7 @@
- (void)testAddingSearchIndexingToEntity
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
NSEntityDescription *searchWordEntity = [managedObjectModel.entitiesByName objectForKey:@"RKSearchWord"];
assertThat(searchWordEntity, is(nilValue()));
@@ -49,7 +55,7 @@
- (void)testAddingSearchIndexingToEntityWithMixtureOfNSAttributeDescriptionAndStringNames
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
NSEntityDescription *searchWordEntity = [managedObjectModel.entitiesByName objectForKey:@"RKSearchWord"];
assertThat(searchWordEntity, is(nilValue()));
@@ -77,7 +83,7 @@
- (void)testAddingSearchIndexingToNonStringAttributeTypeRaisesException
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
NSEntityDescription *searchWordEntity = [managedObjectModel.entitiesByName objectForKey:@"RKSearchWord"];
assertThat(searchWordEntity, is(nilValue()));
@@ -97,7 +103,7 @@
- (void)testAddingSearchIndexingToNonExistantAttributeRaisesException
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
NSEntityDescription *searchWordEntity = [managedObjectModel.entitiesByName objectForKey:@"RKSearchWord"];
assertThat(searchWordEntity, is(nilValue()));
@@ -117,7 +123,7 @@
- (void)testAddingSearchIndexingToTwoEntitiesManipulatesTheSameSearchWordEntity
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *humanEntity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
NSEntityDescription *catEntity = [[managedObjectModel entitiesByName] objectForKey:@"Cat"];
@@ -136,7 +142,7 @@
- (void)testIndexingManagedObject
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -161,7 +167,7 @@
- (void)testIndexingManagedObjectUsingIndexingContext
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -195,7 +201,7 @@
- (void)testIndexingOnManagedObjectContextSave
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -223,7 +229,7 @@
- (void)testIndexingOnManagedObjectContextSaveUsingIndexingContext
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -268,7 +274,7 @@
- (void)testIndexingChangesInManagedObjectContextWithoutSave
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -294,7 +300,7 @@
- (void)testIndexingChangesInManagedObjectContextWithoutSaveUsingIndexingContext
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -328,7 +334,7 @@
- (void)testCancellationOfIndexingInAnIndexingContext
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -370,7 +376,7 @@
- (void)testThatDelegateCanDenyCreationOfSearchWordForWord
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -403,7 +409,7 @@
- (void)testThatDelegateIsInformedWhenSearchWordIsCreated
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -432,7 +438,7 @@
- (void)testThatDelegateCanBeUsedToFetchExistingSearchWords
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -463,7 +469,7 @@
- (void)testThatTheDelegateCanDeclineIndexingOfAnObject
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];
@@ -490,7 +496,7 @@
- (void)testThatTheDelegateIsNotifiedAfterIndexingHasCompleted
{
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSManagedObjectModel *managedObjectModel = RKManagedObjectModel();
NSEntityDescription *entity = [[managedObjectModel entitiesByName] objectForKey:@"Human"];
[RKSearchIndexer addSearchIndexingToEntity:entity onAttributes:@[ @"name", @"nickName" ]];

View File

@@ -19,7 +19,8 @@
- (void)testSearchingForManagedObjects
{
__block NSError *error;
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
[managedObjectStore addSearchIndexingToEntityForName:@"Cat" onAttributes:@[ @"name" ]];
[managedObjectStore addInMemoryPersistentStore:&error];

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>VersionedModel 4.xcdatamodel</string>
</dict>
</plist>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="2.0" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="12C60" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="Article" syncable="YES">
<attribute name="authorName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="body" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="Tag" syncable="YES">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<elements>
<element name="Article" positionX="160" positionY="192" width="128" height="90"/>
<element name="Tag" positionX="160" positionY="192" width="128" height="60"/>
</elements>
</model>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="3.0" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="12C60" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="Article" syncable="YES">
<attribute name="authorName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="body" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="createdAt" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="Tag" syncable="YES">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<elements>
<element name="Article" positionX="160" positionY="192" width="128" height="105"/>
<element name="Tag" positionX="160" positionY="192" width="128" height="60"/>
</elements>
</model>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="4.0" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="12C60" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="Article" syncable="YES">
<attribute name="authorName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="body" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="Tag" syncable="YES">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<elements>
<element name="Article" positionX="160" positionY="192" width="128" height="90"/>
<element name="Tag" positionX="160" positionY="192" width="128" height="60"/>
</elements>
</model>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model name="" userDefinedModelVersionIdentifier="1.0" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1811" systemVersion="12C60" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="Article" syncable="YES">
<attribute name="body" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="Tag" syncable="YES">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<elements>
<element name="Article" positionX="160" positionY="192" width="128" height="75"/>
<element name="Tag" positionX="160" positionY="192" width="128" height="60"/>
</elements>
</model>

View File

@@ -47,6 +47,24 @@
// Configure logging from the environment variable. See RKLog.h for details
RKLogConfigureFromEnvironment();
// Configure the Test Factory to use a specific model file
[RKTestFactory defineFactory:RKTestFactoryDefaultNamesManagedObjectStore withBlock:^id {
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:RKTestFactoryDefaultStoreFilename];
NSURL *modelURL = [[RKTestFixture fixtureBundle] URLForResource:@"Data Model" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:model];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
if (persistentStore) {
BOOL success = [managedObjectStore resetPersistentStores:&error];
if (! success) {
RKLogError(@"Failed to reset persistent store: %@", error);
}
}
return managedObjectStore;
}];
}
@end