Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions Classes/NSManagedObject+Foundry.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

#import "NSManagedObject+Foundry.h"

@interface NSObject (FoundryPrivate)
+(NSDictionary *)foundryAttributesWithSpec:(NSDictionary*)spec;
@end



@implementation NSManagedObject (Foundry)

+ (instancetype)foundryBuild
Expand All @@ -17,18 +23,44 @@ + (instancetype)foundryBuild
userInfo:nil];
}

+ (instancetype)foundryBuildWithContext:(NSManagedObjectContext *)context
+ (instancetype)foundryBuildWithContext:(NSManagedObjectContext *)context {
[self foundryWillBuildObject];
id instance = [self foundryBuildWithContext:context usingSpec:[self foundryBuildSpecs]];
[self foundryDidBuildObject:instance];

return instance;
}

+ (instancetype)foundryBuildWithContext:(NSManagedObjectContext *)context usingSpec:(NSDictionary*)spec
{
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context];
NSDictionary *attributes = [self foundryAttributes];
NSDictionary *attributes = [self foundryAttributesWithSpec:spec];
NSDictionary *propertiesByName = object.entity.propertiesByName;
for (NSString *propertyName in [propertiesByName allKeys]) {
if (attributes[propertyName]) {
if ([propertiesByName[propertyName] isKindOfClass:[NSAttributeDescription class]]) {
[object setValue:attributes[propertyName] forKey:propertyName];
} else if ([propertiesByName[propertyName] isKindOfClass:[NSRelationshipDescription class]]) {

if ([propertiesByName[propertyName] isKindOfClass:[NSAttributeDescription class]] &&
attributes[propertyName]) {
[object setValue:attributes[propertyName] forKey:propertyName];
} else if ([propertiesByName[propertyName] isKindOfClass:[NSRelationshipDescription class]] &&
spec[propertyName]) {
NSRelationshipDescription* relationship = propertiesByName[propertyName];
id relatedObject;
FoundryPropertyType type = [spec[propertyName] integerValue];
if (type == FoundryPropertyTypeAnyRelationship) {
NSEntityDescription* relatedEntity = [relationship destinationEntity];
Class targetClass = NSClassFromString([relatedEntity managedObjectClassName]);
NSRelationshipDescription* inverse = [relationship inverseRelationship];
NSMutableDictionary* targetSpec = [[targetClass foundryBuildSpecs] mutableCopy];
if (inverse) {
[targetSpec removeObjectForKey:[inverse name]];
}
relatedObject = [targetClass foundryBuildWithContext:context usingSpec:targetSpec];
relatedObject = [relationship isToMany]? [NSSet setWithObject:relatedObject] : relatedObject;
}
else if (type == FoundryPropertyTypeSpecificRelationship) {
relatedObject = [self foundryRelatedObjectForProperty:propertyName
inContext:context];
}
[object setValue:relatedObject forKey:propertyName];
}
}

Expand Down
7 changes: 5 additions & 2 deletions Classes/NSObject+Foundry.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,13 @@ + (NSDictionary *)foundryBuildSpecs
userInfo:nil];
}

+ (NSDictionary *)foundryAttributes
+ (NSDictionary *)foundryAttributes {
return [self foundryAttributesWithSpec:[self foundryBuildSpecs]];
}

+ (NSDictionary *)foundryAttributesWithSpec:(NSDictionary*)spec
{
NSMutableDictionary *attributesDict = [NSMutableDictionary new];
NSDictionary *spec = [self foundryBuildSpecs];
for (NSString *key in [spec allKeys]) {
FoundryPropertyType type = [spec[key] integerValue];

Expand Down
30 changes: 30 additions & 0 deletions Classes/TGFoundryObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,26 @@ typedef NS_ENUM(NSUInteger, FoundryPropertyType) {

*/
FoundryPropertyTypeCustom,

/**

* (Managed objects ONLY) Property represents a relationship where the destination entity's class also conforms to `TGFoundryObject` protocol. The related object will be built using `+foundryBuildWithContext:`.
* @note If the relationship is to-many, a factory object will be wrapped in `NSSet`.

*/
FoundryPropertyTypeAnyRelationship,

/**

* (Managed objects ONLY) Property represents a relationship. The related object will be set using `+foundryRelatedObjectForKey:inContext:` method on the `TGFoundryObject` protocol.

*/
FoundryPropertyTypeSpecificRelationship,
};


@class NSManagedObjectContext;

/**
* Protocol that an object must adopt in order to be manufactured using Foundry.
*/
Expand Down Expand Up @@ -198,6 +216,18 @@ typedef NS_ENUM(NSUInteger, FoundryPropertyType) {

+ (id)foundryAttributeForProperty:(NSString *)property;

/**
* If you assign a relationship property the `FoundryPropertyTypeSpecificRelationship` value in the build specs, this method will be called looking for the related object.
*
* @param property Name of the relationship key that Foundry is seeking a value for.
*
* @param context The managed object context that the receiver's new instance is being created in.
*
* @return The managed object to set for the relationship.
*/

+ (id)foundryRelatedObjectForProperty:(NSString*)property inContext:(NSManagedObjectContext*)context;

/**
* Callback when an object of the `TGFoundryObject` class is about to be built.
*/
Expand Down
4 changes: 2 additions & 2 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ xcodeproj 'Tests/FoundryTests.xcodeproj'
inhibit_all_warnings!

target :iostests do
platform :ios, '7.0'
platform :ios, '8.0'
pod 'MagicalRecord'
pod 'Gizou', :git => 'https://github.com/smyrgl/Gizou.git'
xcodeproj 'Tests/FoundryTests.xcodeproj'
Expand All @@ -15,4 +15,4 @@ target :osxtests do
pod 'MagicalRecord'
pod 'Gizou', :git => 'https://github.com/smyrgl/Gizou.git'
xcodeproj 'Tests/FoundryTests.xcodeproj'
end
end
13 changes: 10 additions & 3 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PODS:
- Gizou (0.1.3)
- MagicalRecord (2.2):
- MagicalRecord/Core
- MagicalRecord/Core (= 2.2)
- MagicalRecord/Core (2.2)

DEPENDENCIES:
Expand All @@ -12,8 +12,15 @@ EXTERNAL SOURCES:
Gizou:
:git: https://github.com/smyrgl/Gizou.git

CHECKOUT OPTIONS:
Gizou:
:commit: 09d10c2c49bfe52a714d5b182799d27cc79bbcbe
:git: https://github.com/smyrgl/Gizou.git

SPEC CHECKSUMS:
Gizou: 5d17a448abd3f2b7ce5002f28073cdc717dc76c3
MagicalRecord: 95d49d74ef752cd52f6ad87bcb474817fc3978cf
MagicalRecord: 2dc87179ae7c1f2a274442624ec65a07d4357a6f

PODFILE CHECKSUM: a8bf7158851c7d4952a122893aefe8fb2705eb03

COCOAPODS: 0.32.1
COCOAPODS: 1.2.0
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ What if you want to manually assign the value yourself during the build process

### Relationships

Right now you can set these using the custom property types but very soon Foundry will add the ability to nest factories so that you can assign a factory to a relationship attribute as part of your build spec. Stay tuned!
Relationships are supported for `NSManagedObject`s. You have two options:

- `FoundryPropertyTypeAnyRelationship`: Assuming the related object is another `NSManagedObject` that conforms to `TGFoundryObject`, using this type will build an instance of that class using `foundryBuildWithContext:`.

- `FoundryPropertyTypeSpecificRelationship`: If you want to provide a specific object (or set, for to-many) relationships, specify this type in the build specs, then implement `foundryRelatedObjectForProperty:inContext:` method.

## Requirements

Expand Down
21 changes: 20 additions & 1 deletion Tests/FoundryManagedObjectTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//

#import <XCTest/XCTest.h>
#import "ManagedAnimal+ManagedAnimalTests.h"
#import "ManagedPerson+ManagedPersonTests.h"

@interface FoundryManagedObjectTests : XCTestCase

Expand All @@ -32,7 +34,6 @@ - (void)testBuildManagedObject
ManagedPerson *newPerson = [ManagedPerson foundryBuildWithContext:[NSManagedObjectContext MR_defaultContext]];
XCTAssert(newPerson, @"There must be a new person built");
XCTAssert(newPerson.objectID.isTemporaryID, @"The object should have a temporary ID");

}

- (void)testCreateManagedObject
Expand Down Expand Up @@ -72,4 +73,22 @@ - (void)testFailObjectBuildBatch
XCTAssertThrows([ManagedPerson foundryBuildNumber:10], @"Trying to use the batch build method on a managed object should throw an exception");
}

- (void)testBuildManagedObjectsWithAnyRelationship
{
NSArray *animals = [ManagedAnimal foundryCreateNumber:5 withContext:[NSManagedObjectContext MR_defaultContext]];
for (ManagedAnimal* animal in animals) {
XCTAssertNotNil(animal.owner, @"Creating a new managed object should also create related objects.");
}
}

-(void)testBuildManagedObjectsWithSpecificRelationship
{
NSManagedObjectContext* context = [NSManagedObjectContext MR_defaultContext];
NSArray *animals = [[ManagedAnimal foundryCreateNumber:5 withContext:context] sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]];
ManagedPerson *person = [ManagedPerson foundryCreateWithContext:context];
XCTAssert([person.pets count] == 1, @"To-many relationships created with `FoundryPropertyTypeAnyRelationship` should have a single object.");
ManagedAnimal* thePet = [person.pets anyObject];
XCTAssert([thePet.name isEqualToString:[[animals firstObject] name]], @"`FoundryPropertyTypeSpecificRelationship` should set the right object.");
}

@end
Loading