From 88ae415e35890a6d2fe4286dfcd132c83f55c95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Mon, 11 Nov 2013 17:22:21 +0100 Subject: [PATCH 1/3] Change import file to remove warning objc_sendMsg is not in obj/runtime.h but in objc/message.h. --- Source/Factory/TyphoonDefinitionRegisterer.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Factory/TyphoonDefinitionRegisterer.m b/Source/Factory/TyphoonDefinitionRegisterer.m index 5071287e6..7b6288d23 100644 --- a/Source/Factory/TyphoonDefinitionRegisterer.m +++ b/Source/Factory/TyphoonDefinitionRegisterer.m @@ -8,7 +8,7 @@ #import "TyphoonDefinition.h" #import "TyphoonComponentFactory.h" #import "TyphoonComponentFactory+TyphoonDefinitionRegisterer.h" -#import +#import #import "OCLogTemplate.h" @implementation TyphoonDefinitionRegisterer From b1b51a6fec5cb8c4b5a59ece0c8b6f50681fa361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Mon, 11 Nov 2013 21:34:39 +0100 Subject: [PATCH 2/3] Import UIKit to be able to referece UIViewController. --- .../Factory/Config/Resolver/TyphoonViewControllerNibResolver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ios/Factory/Config/Resolver/TyphoonViewControllerNibResolver.h b/Source/ios/Factory/Config/Resolver/TyphoonViewControllerNibResolver.h index 9a213c08a..9e61b99ef 100644 --- a/Source/ios/Factory/Config/Resolver/TyphoonViewControllerNibResolver.h +++ b/Source/ios/Factory/Config/Resolver/TyphoonViewControllerNibResolver.h @@ -9,7 +9,7 @@ // //////////////////////////////////////////////////////////////////////////////// -#import +#import #import "TyphoonComponentFactoryPostProcessor.h" /** From 229b6a4f79eff1bea865c503f2793e6800db76cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Mon, 11 Nov 2013 21:47:51 +0100 Subject: [PATCH 3/3] TyphoonFactoryProvider implementation and tests TyphoonFactoryProvider simplifies the task of creating factories for your classes, removing a lot of boilerplate code and typing. --- .../TyphoonAssistedFactoryBase.h | 32 +++ .../TyphoonAssistedFactoryBase.m | 43 +++ .../TyphoonAssistedFactoryDefinition.h | 54 ++++ .../TyphoonAssistedFactoryDefinition.m | 54 ++++ .../FactoryProvider/TyphoonFactoryProvider.h | 113 ++++++++ .../FactoryProvider/TyphoonFactoryProvider.m | 248 ++++++++++++++++++ Source/Typhoon.h | 1 + Tests/Component/FactoryProvider/AuthService.h | 16 ++ .../FactoryProvider/AuthServiceImpl.h | 17 ++ .../FactoryProvider/AuthServiceImpl.m | 16 ++ .../Component/FactoryProvider/CreditService.h | 16 ++ .../FactoryProvider/CreditServiceImpl.h | 17 ++ .../FactoryProvider/CreditServiceImpl.m | 16 ++ Tests/Component/FactoryProvider/Payment.h | 21 ++ .../FactoryProvider/PaymentFactory.h | 24 ++ Tests/Component/FactoryProvider/PaymentImpl.h | 24 ++ Tests/Component/FactoryProvider/PaymentImpl.m | 41 +++ Tests/Component/FactoryProvider/Pizza.h | 20 ++ .../Component/FactoryProvider/PizzaFactory.h | 25 ++ Tests/Component/FactoryProvider/PizzaImpl.h | 22 ++ Tests/Component/FactoryProvider/PizzaImpl.m | 39 +++ .../TyphoonAssistedFactoryBaseTest.m | 51 ++++ .../TyphoonAssistedFactoryDefinitionTest.m | 104 ++++++++ .../TyphoonFactoryProviderTest.m | 168 ++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 94 ++++++- Typhoon.xcodeproj/project.pbxproj | 44 ++++ 26 files changed, 1319 insertions(+), 1 deletion(-) create mode 100644 Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.h create mode 100644 Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.m create mode 100644 Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.h create mode 100644 Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.m create mode 100644 Source/Component/FactoryProvider/TyphoonFactoryProvider.h create mode 100644 Source/Component/FactoryProvider/TyphoonFactoryProvider.m create mode 100644 Tests/Component/FactoryProvider/AuthService.h create mode 100644 Tests/Component/FactoryProvider/AuthServiceImpl.h create mode 100644 Tests/Component/FactoryProvider/AuthServiceImpl.m create mode 100644 Tests/Component/FactoryProvider/CreditService.h create mode 100644 Tests/Component/FactoryProvider/CreditServiceImpl.h create mode 100644 Tests/Component/FactoryProvider/CreditServiceImpl.m create mode 100644 Tests/Component/FactoryProvider/Payment.h create mode 100644 Tests/Component/FactoryProvider/PaymentFactory.h create mode 100644 Tests/Component/FactoryProvider/PaymentImpl.h create mode 100644 Tests/Component/FactoryProvider/PaymentImpl.m create mode 100644 Tests/Component/FactoryProvider/Pizza.h create mode 100644 Tests/Component/FactoryProvider/PizzaFactory.h create mode 100644 Tests/Component/FactoryProvider/PizzaImpl.h create mode 100644 Tests/Component/FactoryProvider/PizzaImpl.m create mode 100644 Tests/Component/FactoryProvider/TyphoonAssistedFactoryBaseTest.m create mode 100644 Tests/Component/FactoryProvider/TyphoonAssistedFactoryDefinitionTest.m create mode 100644 Tests/Component/FactoryProvider/TyphoonFactoryProviderTest.m diff --git a/Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.h b/Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.h new file mode 100644 index 000000000..85f7313ca --- /dev/null +++ b/Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.h @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import + +/** + * Internal base class for all Typhoon assisted factories. Users should not use + * this class directly. + */ +@interface TyphoonAssistedFactoryBase : NSObject + +/** Used internally by the setters of the properties in the subclasses */ +- (void)setInjectionValue:(id)value forProperty:(NSString *)property; + +/** Used internally by the getters of the properties in the subclasses */ +- (id)injectionValueForProperty:(NSString *)property; + +/** Used to get the type encoding during the construction of subclasses */ +- (id)_dummyGetter; + +/** Used to get the type encoding during the construction of subclasses */ +- (void)_setDummySetter:(id)value; + +@end diff --git a/Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.m b/Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.m new file mode 100644 index 000000000..5016151f2 --- /dev/null +++ b/Source/Component/FactoryProvider/TyphoonAssistedFactoryBase.m @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import "TyphoonAssistedFactoryBase.h" + +@implementation TyphoonAssistedFactoryBase +{ + NSMutableDictionary *_injections; +} + +- (instancetype)init +{ + self = [super init]; + if (self) + { + _injections = [[NSMutableDictionary alloc] init]; + } + + return self; +} + +- (void)setInjectionValue:(id)value forProperty:(NSString *)property +{ + [_injections setObject:value forKey:property]; +} + +- (id)injectionValueForProperty:(NSString *)property +{ + return [_injections objectForKey:property]; +} + +- (id)_dummyGetter { return nil; } +- (void)_setDummySetter:(id)value {} + +@end diff --git a/Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.h b/Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.h new file mode 100644 index 000000000..a1ceff94c --- /dev/null +++ b/Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import + +@class TyphoonAssistedFactoryDefinition; + +/** Used to configure the TyphoonAssistedFactoryDefinition passed as argument */ +typedef void(^TyphoonAssistedFactoryDefinitionBlock)(TyphoonAssistedFactoryDefinition *definition); + +/** Used to enumerate over factory method selectors and their associated body blocks */ +typedef void(^TyphoonAssistedFactoryMethodsEnumerationBlock)(SEL name, id body); + +@interface TyphoonAssistedFactoryDefinition : NSObject + +/** + * Define a new factory method with the given selector and associating the given + * block. The block should return a value. The arguments of the block are the + * arguments of the factory method in the same order, but the factory itself is + * prefixed as first argument, so the factory method arguments are the second + * and following arguments. + */ +- (void)factoryMethod:(SEL)name body:(id)bodyBlock; + +#pragma mark - Internal methods + +/** + * The number of factory methods defined for this assisted factory. Users should + * not invoke this method directly. + */ +@property (nonatomic, assign, readonly) NSUInteger countOfFactoryMethods; + +/** + * Configure this assisted factory definition inside the block provider as + * argument. Users should not invoke this method directly. + */ +- (void)configure:(TyphoonAssistedFactoryDefinitionBlock)configurationBlock; + +/** + * Enumerate over all the defined factory method. The block will be invoked once + * per factory method, receiving the selector and the body block associated for + * each factory method. Users should not invoke this method directly. + */ +- (void)enumerateFactoryMethods:(TyphoonAssistedFactoryMethodsEnumerationBlock)enumerationBlock; + +@end diff --git a/Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.m b/Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.m new file mode 100644 index 000000000..a7048af92 --- /dev/null +++ b/Source/Component/FactoryProvider/TyphoonAssistedFactoryDefinition.m @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import "TyphoonAssistedFactoryDefinition.h" + +@implementation TyphoonAssistedFactoryDefinition + +{ + NSMutableArray *_factoryMethods; +} + +- (instancetype)init +{ + self = [super init]; + if (self) + { + _factoryMethods = [[NSMutableArray alloc] init]; + } + + return self; +} + +- (NSUInteger)countOfFactoryMethods +{ + return [_factoryMethods count]; +} + +- (void)configure:(TyphoonAssistedFactoryDefinitionBlock)configurationBlock +{ + configurationBlock(self); +} + +- (void)factoryMethod:(SEL)name body:(id)bodyBlock +{ + [_factoryMethods addObject:@[NSStringFromSelector(name), bodyBlock]]; +} + +- (void)enumerateFactoryMethods:(TyphoonAssistedFactoryMethodsEnumerationBlock)enumerationBlock +{ + for (NSArray *factoryMethodPair in _factoryMethods) + { + enumerationBlock(NSSelectorFromString(factoryMethodPair[0]), factoryMethodPair[1]); + } +} + +@end diff --git a/Source/Component/FactoryProvider/TyphoonFactoryProvider.h b/Source/Component/FactoryProvider/TyphoonFactoryProvider.h new file mode 100644 index 000000000..e06d155e1 --- /dev/null +++ b/Source/Component/FactoryProvider/TyphoonFactoryProvider.h @@ -0,0 +1,113 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import + +#import "TyphoonDefinition.h" +#import "TyphoonAssistedFactoryDefinition.h" + +/** +* Provides a factory that combines the convenience method arguments with the +* assembly-supplied dependencies to construct objects. +* +* To create a factory you must define a protocol for the factory, wire its +* dependencies (defined as readonly properties), and provide implementation +* blocks for the body of the class methods. It is not as automatic as we will +* like, but at least it avoids a lot of tedius and repetitive boilerplate. +* +* # Example +* +* Imagine you have defined this Payment class with two dependencies that should +* be injected, and two parameters that must be provided at runtime. +* +* @interface Payment : NSObject +* +* - (instancetype)initWithCreditService:(id)creditService +* authService:(id)authService +* startDate:(NSDate *)date +* amout:(NSUInteger)amount; +* +* @end +* +* You also define a protocol for the factory of the Payment objects. The factory +* must declare the dependencies as readonly properties, and the factory methods +* should be the only ones existing. +* +* @protocol PaymentFactory +* +* @property (nonatomic, strong, readonly) id creditService; +* @property (nonatomic, strong, readonly) id authService; +* +* + (Payment *)paymentWithStartDate:(NSDate *)startDate +* amount:(NSUInteger)amount; +* +* @end +* +* Then, in your assembly file, you define the PaymentFactory component using the +* TyphoonFactoryProvider as the following code: +* +* - (id)paymentFactory { +* return [TyphoonFactoryProvider withProtocol:@protocol(PaymentFactory) dependencies:^(TyphoonDefinition *definition) { +* [definition injectProperty:@selector(creditService)]; +* [definition injectProperty:@selector(authService)]; +* } factories:^(TyphoonAssistedFactoryDefinition *definition) { +* [definition factoryMethod:@selector(paymentWithStartDate:amount:) body:^id (id factory, NSDate *startDate, NSUInteger amount) { +* return [[Payment alloc] initWithCreditService:factory.creditService authService:factory.authService startDate:startDate amount:amount]; +* }]; +* }]; +* } +* +* In the dependencies block you can use any `injectProperty:` method that you +* will use for your normal definitions. It is a standard TyphoonDefinitionBlock +* that get passed directly to the TyphoonDefinition constructor used internally. +* +* For the factories block you must provide one body block for each class method +* of your factory protocol. The block used as the body receives the factory +* itself as the first argument, so you can use the factory properties, and then +* the rest of the class method arguments in the second and following positions. +* +* In the case of the protocol having only one factory method you can use a +* shorter version of the method. For example the equivalent shorter version for +* the above definition will be the following one: +* +* - (id)paymentFactory { +* return [TyphoonFactoryProvider withProtocol:@protocol(PaymentFactory) dependencies:^(TyphoonDefinition *definition) { +* [definition injectProperty:@selector(creditService)]; +* [definition injectProperty:@selector(authService)]; +* } factory^id (id factory, NSDate *startDate, NSUInteger amount) { +* return [[Payment alloc] initWithCreditService:factory.creditService authService:factory.authService startDate:startDate amount:amount]; +* }]; +* } +* +* Know limitation: You can only create one factory for a given protocol. +*/ +@interface TyphoonFactoryProvider : NSObject + +/** +* Creates a factory definition for a given protocol, dependencies and factory +* block. The protocol is supposed to only have one class method, otherwise this +* method will fail during runtime. +*/ ++ (TyphoonDefinition *)withProtocol:(Protocol *)protocol + dependencies:(TyphoonDefinitionBlock)dependenciesBlock + factory:(id)factoryBlock; + +/** + * Creates a factor definition for a given protocol, dependencies and a list of + * factory methods. The protocol is supposed to have the same number of class + * methods, and with the same selectors as defined in the factories block, + * otherwise this method will fail during runtime. + */ ++ (TyphoonDefinition *)withProtocol:(Protocol *)protocol + dependencies:(TyphoonDefinitionBlock)dependenciesBlock + factories:(TyphoonAssistedFactoryDefinitionBlock)definitionBlock; + +@end diff --git a/Source/Component/FactoryProvider/TyphoonFactoryProvider.m b/Source/Component/FactoryProvider/TyphoonFactoryProvider.m new file mode 100644 index 000000000..f768da421 --- /dev/null +++ b/Source/Component/FactoryProvider/TyphoonFactoryProvider.m @@ -0,0 +1,248 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import "TyphoonFactoryProvider.h" + +#import + +#import "TyphoonAssistedFactoryBase.h" + +@implementation TyphoonFactoryProvider + +static dispatch_queue_t sQueue; + +static NSString *GetFactoryClassName(Protocol *protocol) +{ + return [NSString stringWithFormat:@"%s__TyphoonAssistedFactoryImpl", + protocol_getName(protocol)]; +} + +static void AssertValidProtocolForFactory(Protocol *protocol, TyphoonAssistedFactoryDefinition *factoryDefinition) +{ + unsigned int methodCount = 0; + unsigned int propertiesCount = 0; + + struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount); + objc_property_t *properties = protocol_copyPropertyList(protocol, &propertiesCount); + free(methodDescriptions); + free(properties); + + // The readonly properties are returned also as their getter methods, so we + // need to remove those to check that there are only n factory methods left. + NSUInteger factoryMethodCount = [factoryDefinition countOfFactoryMethods]; + NSCAssert(methodCount - propertiesCount == factoryMethodCount, + @"protocol factory method count (%u) differs from factory defintion method count (%lu)", + methodCount - propertiesCount, (unsigned long)factoryMethodCount); +} + +static void AddPropertyGetter(Class factoryClass, objc_property_t property) +{ + // This dummy will give us the type encodings of the properties. + // Only object properties are supported. + Method getter = class_getInstanceMethod([TyphoonAssistedFactoryBase class], @selector(_dummyGetter)); + + const char *cName = property_getName(property); + NSString *name = [NSString stringWithCString:cName encoding:NSASCIIStringEncoding]; + SEL getterSEL = sel_registerName(cName); + + IMP getterIMP = imp_implementationWithBlock(^id (TyphoonAssistedFactoryBase *_self) { + return [_self injectionValueForProperty:name]; + }); + class_addMethod(factoryClass, getterSEL, getterIMP, method_getTypeEncoding(getter)); +} + +static void AddPropertySetter(Class factoryClass, objc_property_t property) +{ + // This dummy will give us the type encodings of the properties. + // Only object properties are supported. + Method setter = class_getInstanceMethod([TyphoonAssistedFactoryBase class], @selector(_setDummySetter:)); + + const char *cName = property_getName(property); + NSString *name = [NSString stringWithCString:cName encoding:NSASCIIStringEncoding]; + NSString *setterName = [NSString stringWithFormat:@"set%@%@:", + [[name substringToIndex:1] uppercaseString], + [name substringFromIndex:1]]; + SEL setterSEL = sel_registerName([setterName cStringUsingEncoding:NSASCIIStringEncoding]); + + IMP setterIMP = imp_implementationWithBlock(^(TyphoonAssistedFactoryBase *_self, id value) { + [_self setInjectionValue:value forProperty:name]; + }); + class_addMethod(factoryClass, setterSEL, setterIMP, method_getTypeEncoding(setter)); +} + +static void AddProperty(Class factoryClass, objc_property_t property) +{ + unsigned int propertyAttributesCount = 0; + const char *cName = property_getName(property); + objc_property_attribute_t *propertyAttributes = property_copyAttributeList(property, &propertyAttributesCount); + class_addProperty(factoryClass, cName, propertyAttributes, propertyAttributesCount); +} + +static void AddPropertiesToFactory(Class factoryClass, Protocol *protocol) +{ + unsigned int propertiesCount = 0; + objc_property_t *properties = protocol_copyPropertyList(protocol, &propertiesCount); + for (unsigned int idx = 0; idx < propertiesCount; idx++) + { + objc_property_t property = properties[idx]; + AddPropertyGetter(factoryClass, property); + AddPropertySetter(factoryClass, property); + AddProperty(factoryClass, property); + } + free(properties); +} + +static void AddFactoryMethodsToFactory(Class factoryClass, Protocol *protocol, TyphoonAssistedFactoryDefinition *definition) +{ + unsigned int methodCount = 0; + struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount); + + NSCAssert(methodCount > 0, @"protocol method count must be at least one"); + [definition enumerateFactoryMethods:^(SEL name, id body) { + // Search for the right obcj_method_description + struct objc_method_description methodDescription; + BOOL found = NO; + for (unsigned int idx = 0; idx < methodCount; idx++) + { + methodDescription = methodDescriptions[idx]; + if (methodDescription.name == name) + { + found = YES; + break; + } + } + NSCAssert(found, @"protocol doesn't support factory method with name %@", NSStringFromSelector(name)); + + // Here the method description is valid + IMP methodIMP = imp_implementationWithBlock(body); + class_addMethod(factoryClass, methodDescription.name, methodIMP, methodDescription.types); + }]; + + free(methodDescriptions); +} + +static SEL GuessFactoryMethodForProtocol(Protocol *protocol) +{ + // Lets create two sets: the property getters and all the methods (including + // those getters). The difference must be only one, and must be our method. + NSMutableSet *propertyNames = [NSMutableSet set]; + NSMutableSet *methodNames = [NSMutableSet set]; + + unsigned int methodCount = 0; + struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount); + for (unsigned int idx = 0; idx < methodCount; idx++) + { + struct objc_method_description methodDescription = methodDescriptions[idx]; + [methodNames addObject:NSStringFromSelector(methodDescription.name)]; + } + free(methodDescriptions); + + unsigned int propertiesCount = 0; + objc_property_t *properties = protocol_copyPropertyList(protocol, &propertiesCount); + for (unsigned int idx = 0; idx < propertiesCount; idx++) + { + objc_property_t property = properties[idx]; + [propertyNames addObject:[NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding]]; + } + free(properties); + + [methodNames minusSet:propertyNames]; + NSString *factoryMethod = [methodNames anyObject]; + + return NSSelectorFromString(factoryMethod); +} + +static Class GenerateFactoryClassWithDefinition(Protocol *protocol, id factoryBlock) +{ + NSString *className = GetFactoryClassName(protocol); + const char *cClassName = [className cStringUsingEncoding:NSASCIIStringEncoding]; + + TyphoonAssistedFactoryDefinition *factoryDefinition = [[TyphoonAssistedFactoryDefinition alloc] init]; + [factoryDefinition configure:factoryBlock]; + + AssertValidProtocolForFactory(protocol, factoryDefinition); + + Class factoryClass = objc_allocateClassPair([TyphoonAssistedFactoryBase class], cClassName, 0); + // Add the factory method first, that way, the setters from the properties + // will not exist yet. + AddFactoryMethodsToFactory(factoryClass, protocol, factoryDefinition); + AddPropertiesToFactory(factoryClass, protocol); + class_addProtocol(factoryClass, protocol); + objc_registerClassPair(factoryClass); + + return factoryClass; +} + +static Class GetExistingFactoryClass(Protocol *protocol) +{ + NSString *className = GetFactoryClassName(protocol); + const char *cClassName = [className cStringUsingEncoding:NSASCIIStringEncoding]; + return objc_getClass(cClassName); +} + +static Class EnsureFactoryClassWithOneFactory(Protocol *protocol, id factoryBlock) +{ + Class factoryClass = GetExistingFactoryClass(protocol); + if (!factoryClass) + { + SEL factoryMethod = GuessFactoryMethodForProtocol(protocol); + + TyphoonAssistedFactoryDefinitionBlock definition = ^(TyphoonAssistedFactoryDefinition *definition) { + [definition factoryMethod:factoryMethod body:factoryBlock]; + }; + + factoryClass = GenerateFactoryClassWithDefinition(protocol, definition); + } + + return factoryClass; +} + +static Class EnsureFactoryClassWithDefinition(Protocol *protocol, TyphoonAssistedFactoryDefinitionBlock factoryBlock) +{ + Class factoryClass = GetExistingFactoryClass(protocol); + if (!factoryClass) + { + factoryClass = GenerateFactoryClassWithDefinition(protocol, factoryBlock); + } + + return factoryClass; +} + + ++ (void)initialize +{ + if (self == [TyphoonFactoryProvider class]) + { + sQueue = dispatch_queue_create("org.typhoonframework.TyphoonFactoryProvider", DISPATCH_QUEUE_SERIAL); + } +} + ++ (TyphoonDefinition *)withProtocol:(Protocol *)protocol dependencies:(TyphoonDefinitionBlock)dependenciesBlock factory:(id)factoryBlock +{ + __block Class factoryClass = nil; + dispatch_sync(sQueue, ^{ + factoryClass = EnsureFactoryClassWithOneFactory(protocol, factoryBlock); + }); + + return [TyphoonDefinition withClass:factoryClass properties:dependenciesBlock]; +} + ++ (TyphoonDefinition *)withProtocol:(Protocol *)protocol dependencies:(TyphoonDefinitionBlock)dependenciesBlock factories:(TyphoonAssistedFactoryDefinitionBlock)definitionBlock +{ + __block Class factoryClass = nil; + dispatch_sync(sQueue, ^{ + factoryClass = EnsureFactoryClassWithDefinition(protocol, definitionBlock); + }); + + return [TyphoonDefinition withClass:factoryClass properties:dependenciesBlock]; +} + +@end diff --git a/Source/Typhoon.h b/Source/Typhoon.h index 0909be85d..1a5b45fbf 100644 --- a/Source/Typhoon.h +++ b/Source/Typhoon.h @@ -13,6 +13,7 @@ #import "TyphoonDefinition+Infrastructure.h" #import "TyphoonInitializer.h" #import "TyphoonPropertyPlaceholderConfigurer.h" +#import "TyphoonFactoryProvider.h" #import "TyphoonResource.h" #import "TyphoonBundleResource.h" #import "TyphoonComponentFactory.h" diff --git a/Tests/Component/FactoryProvider/AuthService.h b/Tests/Component/FactoryProvider/AuthService.h new file mode 100644 index 000000000..4f2aad4a8 --- /dev/null +++ b/Tests/Component/FactoryProvider/AuthService.h @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import + +@protocol AuthService + +@end diff --git a/Tests/Component/FactoryProvider/AuthServiceImpl.h b/Tests/Component/FactoryProvider/AuthServiceImpl.h new file mode 100644 index 000000000..1700737ca --- /dev/null +++ b/Tests/Component/FactoryProvider/AuthServiceImpl.h @@ -0,0 +1,17 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "AuthService.h" + +@interface AuthServiceImpl : NSObject + +@end diff --git a/Tests/Component/FactoryProvider/AuthServiceImpl.m b/Tests/Component/FactoryProvider/AuthServiceImpl.m new file mode 100644 index 000000000..79be5a528 --- /dev/null +++ b/Tests/Component/FactoryProvider/AuthServiceImpl.m @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import "AuthServiceImpl.h" + +@implementation AuthServiceImpl + +@end diff --git a/Tests/Component/FactoryProvider/CreditService.h b/Tests/Component/FactoryProvider/CreditService.h new file mode 100644 index 000000000..972b160a6 --- /dev/null +++ b/Tests/Component/FactoryProvider/CreditService.h @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import + +@protocol CreditService + +@end diff --git a/Tests/Component/FactoryProvider/CreditServiceImpl.h b/Tests/Component/FactoryProvider/CreditServiceImpl.h new file mode 100644 index 000000000..9a3e1b07f --- /dev/null +++ b/Tests/Component/FactoryProvider/CreditServiceImpl.h @@ -0,0 +1,17 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "CreditService.h" + +@interface CreditServiceImpl : NSObject + +@end diff --git a/Tests/Component/FactoryProvider/CreditServiceImpl.m b/Tests/Component/FactoryProvider/CreditServiceImpl.m new file mode 100644 index 000000000..ce207e5de --- /dev/null +++ b/Tests/Component/FactoryProvider/CreditServiceImpl.m @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import "CreditServiceImpl.h" + +@implementation CreditServiceImpl + +@end diff --git a/Tests/Component/FactoryProvider/Payment.h b/Tests/Component/FactoryProvider/Payment.h new file mode 100644 index 000000000..0a4588af0 --- /dev/null +++ b/Tests/Component/FactoryProvider/Payment.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import + +@protocol Payment + +@property (nonatomic, strong, readonly) id creditService; +@property (nonatomic, strong, readonly) id authService; +@property (nonatomic, strong, readonly) NSDate *startDate; +@property (nonatomic, assign, readonly) NSUInteger amount; + +@end diff --git a/Tests/Component/FactoryProvider/PaymentFactory.h b/Tests/Component/FactoryProvider/PaymentFactory.h new file mode 100644 index 000000000..a9dc84bf3 --- /dev/null +++ b/Tests/Component/FactoryProvider/PaymentFactory.h @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "AuthService.h" +#import "CreditService.h" +#import "Payment.h" + +@protocol PaymentFactory + +@property (nonatomic, strong, readonly) id creditService; +@property (nonatomic, strong, readonly) id authService; + +- (id)paymentWithStartDate:(NSDate *)startDate amount:(NSUInteger)amount; + +@end diff --git a/Tests/Component/FactoryProvider/PaymentImpl.h b/Tests/Component/FactoryProvider/PaymentImpl.h new file mode 100644 index 000000000..526a45372 --- /dev/null +++ b/Tests/Component/FactoryProvider/PaymentImpl.h @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "AuthService.h" +#import "CreditService.h" +#import "Payment.h" + +@interface PaymentImpl : NSObject + +- (instancetype)initWithCreditService:(id)creditService + authService:(id)authService + startDate:(NSDate *)startDate + amount:(NSUInteger)amount; + +@end diff --git a/Tests/Component/FactoryProvider/PaymentImpl.m b/Tests/Component/FactoryProvider/PaymentImpl.m new file mode 100644 index 000000000..1ba47f66c --- /dev/null +++ b/Tests/Component/FactoryProvider/PaymentImpl.m @@ -0,0 +1,41 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import "PaymentImpl.h" + +@implementation PaymentImpl + +@synthesize creditService = _creditService; +@synthesize authService = _authService; +@synthesize startDate = _startDate; +@synthesize amount = _amount; + +- (instancetype)initWithCreditService:(id)creditService authService:(id)authService startDate:(NSDate *)startDate amount:(NSUInteger)amount +{ + self = [super init]; + if (self) + { + _creditService = creditService; + _authService = authService; + _startDate = startDate; + _amount = amount; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@:%p startDate:%@ amount:%lu>", + [self class], self, + _startDate, (unsigned long)_amount]; +} + +@end diff --git a/Tests/Component/FactoryProvider/Pizza.h b/Tests/Component/FactoryProvider/Pizza.h new file mode 100644 index 000000000..6f2031f8a --- /dev/null +++ b/Tests/Component/FactoryProvider/Pizza.h @@ -0,0 +1,20 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import + +@protocol Pizza + +@property (nonatomic, strong, readonly) id creditService; +@property (nonatomic, assign, readonly) double radius; +@property (nonatomic, copy, readonly) NSArray *ingredients; + +@end diff --git a/Tests/Component/FactoryProvider/PizzaFactory.h b/Tests/Component/FactoryProvider/PizzaFactory.h new file mode 100644 index 000000000..b8edf70a7 --- /dev/null +++ b/Tests/Component/FactoryProvider/PizzaFactory.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "CreditService.h" +#import "Pizza.h" + +@protocol PizzaFactory + +@property (nonatomic, strong, readonly) id creditService; + +- (id)pizzaWithRadius:(double)radius ingredients:(NSArray *)ingrendients; +- (id)smallPizzaWithIngredients:(NSArray *)ingredients; +- (id)mediumPizzaWithIngredients:(NSArray *)ingredients; +- (id)largePizzaWithIngredients:(NSArray *)ingredients; + +@end diff --git a/Tests/Component/FactoryProvider/PizzaImpl.h b/Tests/Component/FactoryProvider/PizzaImpl.h new file mode 100644 index 000000000..dd6790b02 --- /dev/null +++ b/Tests/Component/FactoryProvider/PizzaImpl.h @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "CreditService.h" +#import "Pizza.h" + +@interface PizzaImpl : NSObject + +- (instancetype)initWithCreditService:(id)creditService + radius:(double)radius + ingredients:(NSArray *)ingredients; + +@end diff --git a/Tests/Component/FactoryProvider/PizzaImpl.m b/Tests/Component/FactoryProvider/PizzaImpl.m new file mode 100644 index 000000000..544f111db --- /dev/null +++ b/Tests/Component/FactoryProvider/PizzaImpl.m @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import "PizzaImpl.h" + +@implementation PizzaImpl + +@synthesize creditService = _creditService; +@synthesize radius = _radius; +@synthesize ingredients = _ingredients; + +- (instancetype)initWithCreditService:(id)creditService radius:(double)radius ingredients:(NSArray *)ingredients +{ + self = [super init]; + if (self) + { + _creditService = creditService; + _radius = radius; + _ingredients = [ingredients copy]; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@:%p radius:%f ingredients:%lu>", + [self class], self, + _radius, (unsigned long)[_ingredients count]]; +} + +@end diff --git a/Tests/Component/FactoryProvider/TyphoonAssistedFactoryBaseTest.m b/Tests/Component/FactoryProvider/TyphoonAssistedFactoryBaseTest.m new file mode 100644 index 000000000..559ca39ac --- /dev/null +++ b/Tests/Component/FactoryProvider/TyphoonAssistedFactoryBaseTest.m @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "TyphoonAssistedFactoryBase.h" + +@interface TyphoonAssistedFactoryBaseTest : SenTestCase +@end + +@implementation TyphoonAssistedFactoryBaseTest +{ + TyphoonAssistedFactoryBase *assistedFactory; +} + +- (void)setUp +{ + assistedFactory = [[TyphoonAssistedFactoryBase alloc] init]; +} + +- (void)test_injection_value_should_return_nil_for_unexisting_keys +{ + assertThat([assistedFactory injectionValueForProperty:@"does-not-exist"], is(nilValue())); +} + +- (void)test_injection_value_should_return_injected_value +{ + id value = [[NSObject alloc] init]; + [assistedFactory setInjectionValue:value forProperty:@"property"]; + + assertThat([assistedFactory injectionValueForProperty:@"property"], is(equalTo(value))); +} + +- (void)test_should_respond_to_dummyGetter +{ + assertThatBool([assistedFactory respondsToSelector:@selector(_dummyGetter)], is(equalToBool(YES))); +} + +- (void)test_should_respond_to_setDummySetter +{ + assertThatBool([assistedFactory respondsToSelector:@selector(_setDummySetter:)], is(equalToBool(YES))); +} + +@end diff --git a/Tests/Component/FactoryProvider/TyphoonAssistedFactoryDefinitionTest.m b/Tests/Component/FactoryProvider/TyphoonAssistedFactoryDefinitionTest.m new file mode 100644 index 000000000..48423e493 --- /dev/null +++ b/Tests/Component/FactoryProvider/TyphoonAssistedFactoryDefinitionTest.m @@ -0,0 +1,104 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import "TyphoonAssistedFactoryDefinition.h" + +@interface TyphoonAssistedFactoryDefinitionTest : SenTestCase +@end + +@implementation TyphoonAssistedFactoryDefinitionTest +{ + TyphoonAssistedFactoryDefinition *factoryDefinition; +} + +- (void)setUp +{ + factoryDefinition = [[TyphoonAssistedFactoryDefinition alloc] init]; +} + +- (void)test_configure_should_inject_factory_itself_as_argument +{ + [factoryDefinition configure:^(TyphoonAssistedFactoryDefinition *definition) { + assertThat(definition, is(equalTo(factoryDefinition))); + }]; +} + +- (void)test_countOfFactoryMethods_should_return_zero_for_no_methods +{ + assertThatInteger(factoryDefinition.countOfFactoryMethods, is(equalToInteger(0))); +} + +- (void)test_countOfFactoryMethod_should_return_one_for_just_one_method +{ + [factoryDefinition factoryMethod:@selector(wadus) body:^{}]; + + assertThatInteger(factoryDefinition.countOfFactoryMethods, is(equalToInteger(1))); +} + +- (void)test_countOfFactoryMethod_should_return_the_number_of_factory_methods +{ + [factoryDefinition factoryMethod:@selector(wadus) body:^{}]; + [factoryDefinition factoryMethod:@selector(wadusWithWadus:) body:^{}]; + [factoryDefinition factoryMethod:@selector(wadusWithWadus:andWadus:) body:^{}]; + + assertThatInteger(factoryDefinition.countOfFactoryMethods, is(equalToInteger(3))); +} + +- (void)test_enumerateFactoryMethods_should_invoke_block_for_no_methods +{ + __block int count = 0; + + [factoryDefinition enumerateFactoryMethods:^(SEL name, id body) { + count += 1; + }]; + + assertThatInt(count, is(equalToInt(0))); +} + +- (void)test_enumerateFactoryMethods_should_invoke_block_for_number_of_factory_methods +{ + [factoryDefinition factoryMethod:@selector(wadus) body:^{}]; + [factoryDefinition factoryMethod:@selector(wadusWithWadus:) body:^{}]; + [factoryDefinition factoryMethod:@selector(wadusWithWadus:andWadus:) body:^{}]; + __block int count = 0; + + [factoryDefinition enumerateFactoryMethods:^(SEL name, id body) { + count += 1; + }]; + + assertThatInt(count, is(equalToInt(3))); +} + +- (void)test_enumerateFactoryMethods_should_invoke_block_with_configured_selector +{ + SEL selector = @selector(wadusWithWadus:andWadus:); + [factoryDefinition factoryMethod:selector body:^{}]; + + [factoryDefinition enumerateFactoryMethods:^(SEL name, id body) { + assertThat(NSStringFromSelector(name), is(@"wadusWithWadus:andWadus:")); + }]; +} + +- (void)test_enumerateFactoryMethods_should_invoke_block_with_configured_body +{ + id (^bodyBlock)(id) = ^id (id factory) { + return nil; + }; + + [factoryDefinition factoryMethod:@selector(wadus) body:bodyBlock]; + + [factoryDefinition enumerateFactoryMethods:^(SEL name, id body) { + assertThat(body, is(bodyBlock)); + }]; +} + +@end diff --git a/Tests/Component/FactoryProvider/TyphoonFactoryProviderTest.m b/Tests/Component/FactoryProvider/TyphoonFactoryProviderTest.m new file mode 100644 index 000000000..7fb7a1476 --- /dev/null +++ b/Tests/Component/FactoryProvider/TyphoonFactoryProviderTest.m @@ -0,0 +1,168 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// TYPHOON FRAMEWORK +// Copyright 2013, Jasper Blues & Contributors +// All Rights Reserved. +// +// NOTICE: The authors permit you to use, modify, and distribute this file +// in accordance with the terms of the license agreement accompanying it. +// +//////////////////////////////////////////////////////////////////////////////// + +#import +#import +#import "TyphoonAssistedFactoryBase.h" +#import "TyphoonFactoryProvider.h" +#import "TyphoonDefinition.h" +#import "PaymentFactory.h" +#import "PaymentImpl.h" +#import "PizzaFactory.h" +#import "PizzaImpl.h" + +@interface TyphoonFactoryProviderTest : SenTestCase +@end + +@implementation TyphoonFactoryProviderTest +{ + TyphoonDefinition *_pizzaFactoryDefinition; + TyphoonDefinition *_paymentFactoryDefinition; + + id _creditService; + id _authService; +} + +- (void)setUp +{ + _creditService = (id)[[NSObject alloc] init]; + _authService = (id)[[NSObject alloc] init]; +} + +- (TyphoonDefinition *)pizzaFactoryDefinition +{ + if (!_pizzaFactoryDefinition) + { + _pizzaFactoryDefinition = [TyphoonFactoryProvider withProtocol:@protocol(PizzaFactory) dependencies:^(TyphoonDefinition *definition) { + [definition injectProperty:@selector(creditService) withObjectInstance:_creditService]; + } factories:^(TyphoonAssistedFactoryDefinition *definition) { + [definition factoryMethod:@selector(pizzaWithRadius:ingredients:) body:^id (id factory, double radius, NSArray *ingredients) { + return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:radius ingredients:ingredients]; + }]; + [definition factoryMethod:@selector(smallPizzaWithIngredients:) body:^id (id factory, NSArray *ingredients) { + return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:5.0 ingredients:ingredients]; + }]; + [definition factoryMethod:@selector(mediumPizzaWithIngredients:) body:^id (id factory, NSArray *ingredients) { + return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:10.0 ingredients:ingredients]; + }]; + [definition factoryMethod:@selector(largePizzaWithIngredients:) body:^id (id factory, NSArray *ingredients) { + return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:20.0 ingredients:ingredients]; + }]; + }]; + } + + return _pizzaFactoryDefinition; +} + +- (TyphoonDefinition *)paymentFactoryDefinition +{ + if (!_paymentFactoryDefinition) + { + _paymentFactoryDefinition = [TyphoonFactoryProvider withProtocol:@protocol(PaymentFactory) dependencies:^(TyphoonDefinition *definition) { + [definition injectProperty:@selector(creditService) withObjectInstance:_creditService]; + [definition injectProperty:@selector(authService) withObjectInstance:_authService]; + } factory:^id (id factory, NSDate *startDate, NSUInteger amount) { + return [[PaymentImpl alloc] initWithCreditService:factory.creditService authService:factory.authService startDate:startDate amount:amount]; + }]; + } + + return _paymentFactoryDefinition; +} + +- (void)test_factory_definition_should_be_right_class +{ + Class klass = [self pizzaFactoryDefinition].type; + + assertThatBool(class_conformsToProtocol(klass, @protocol(PizzaFactory)), is(equalToBool(YES))); + + Class superklass = class_getSuperclass(klass); + assertThat(superklass, is([TyphoonAssistedFactoryBase class])); +} + +- (void)test_factory_definition_should_have_injected_properties +{ + NSSet *injectedProperties = [self paymentFactoryDefinition].injectedProperties; + + NSMutableArray *injectedPropertyNames = [NSMutableArray array]; + [injectedProperties enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { + [injectedPropertyNames addObject:[obj name]]; + }]; + + assertThat(injectedPropertyNames, hasCountOf(2)); + assertThat(injectedPropertyNames, hasItems(equalTo(@"creditService"), equalTo(@"authService"), nil)); +} + +- (void)test_factory_should_respond_to_properties +{ + Class klass = [self paymentFactoryDefinition].type; + id factory = [[klass alloc] init]; + + assertThatBool([factory respondsToSelector:@selector(creditService)], is(equalToBool(YES))); + assertThatBool([factory respondsToSelector:@selector(setCreditService:)], is(equalToBool(YES))); + assertThatBool([factory respondsToSelector:@selector(authService)], is(equalToBool(YES))); + assertThatBool([factory respondsToSelector:@selector(setAuthService:)], is(equalToBool(YES))); +} + +- (void)test_factory_should_implement_properties +{ + Class klass = [self pizzaFactoryDefinition].type; + id factory = [[klass alloc] init]; + + [(NSObject *)factory setValue:_creditService forKey:@"creditService"]; + assertThat(factory.creditService, is(_creditService)); +} + +- (void)test_factory_should_invoke_correct_method_blocks_1 +{ + Class klass = [self pizzaFactoryDefinition].type; + id factory = [[klass alloc] init]; + + [(NSObject *)factory setValue:_creditService forKey:@"creditService"]; + + id pizza = [factory pizzaWithRadius:123.0 ingredients:@[@"1", @"2"]]; + + assertThat(pizza.creditService, is(_creditService)); + assertThatDouble(pizza.radius, is(equalToDouble(123.0))); + assertThat(pizza.ingredients, hasItems(equalTo(@"1"), equalTo(@"2"), nil)); +} + +- (void)test_factory_should_invoke_correct_method_blocks_2 +{ + Class klass = [self pizzaFactoryDefinition].type; + id factory = [[klass alloc] init]; + + [(NSObject *)factory setValue:_creditService forKey:@"creditService"]; + + id pizza = [factory smallPizzaWithIngredients:@[@"3", @"4"]]; + + assertThat(pizza.creditService, is(_creditService)); + assertThatDouble(pizza.radius, is(equalToDouble(5.0))); + assertThat(pizza.ingredients, hasItems(equalTo(@"3"), equalTo(@"4"), nil)); +} + +- (void)test_factory_should_invoke_correct_method_blocks_3 +{ + Class klass = [self paymentFactoryDefinition].type; + id factory = [[klass alloc] init]; + + [(NSObject *)factory setValue:_creditService forKey:@"creditService"]; + [(NSObject *)factory setValue:_authService forKey:@"authService"]; + + NSDate *now = [NSDate date]; + id payment = [factory paymentWithStartDate:now amount:456]; + + assertThat(payment.creditService, is(_creditService)); + assertThat(payment.authService, is(_authService)); + assertThat(payment.startDate, is(now)); + assertThatInteger(payment.amount, is(equalToInteger(456))); +} + +@end diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 82d3fd85c..35ed95361 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -216,6 +216,27 @@ 4EFAF61417E0D54700E93ACF /* CROPrototypeB.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EFAF61217E0D54700E93ACF /* CROPrototypeB.m */; }; 4EFAF61517E0D54700E93ACF /* CROPrototypeB.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EFAF61217E0D54700E93ACF /* CROPrototypeB.m */; }; 52D870D7DE6E4FB09D059A5C /* libPods-ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F8B50DFBF255467DA9F7C30F /* libPods-ios.a */; }; + 6570968918313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570968818313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m */; }; + 6570968A18313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570968818313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m */; }; + 6570968B18313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570968818313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m */; }; + 6570968D18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570968C18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m */; }; + 6570968E18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570968C18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m */; }; + 6570968F18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570968C18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m */; }; + 657096911831449000C10DA4 /* TyphoonFactoryProviderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096901831449000C10DA4 /* TyphoonFactoryProviderTest.m */; }; + 657096921831449000C10DA4 /* TyphoonFactoryProviderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096901831449000C10DA4 /* TyphoonFactoryProviderTest.m */; }; + 657096931831449000C10DA4 /* TyphoonFactoryProviderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096901831449000C10DA4 /* TyphoonFactoryProviderTest.m */; }; + 65709698183146A800C10DA4 /* CreditServiceImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 65709697183146A800C10DA4 /* CreditServiceImpl.m */; }; + 65709699183146A800C10DA4 /* CreditServiceImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 65709697183146A800C10DA4 /* CreditServiceImpl.m */; }; + 6570969A183146A800C10DA4 /* CreditServiceImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 65709697183146A800C10DA4 /* CreditServiceImpl.m */; }; + 6570969E183146E800C10DA4 /* AuthServiceImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570969D183146E800C10DA4 /* AuthServiceImpl.m */; }; + 6570969F183146E800C10DA4 /* AuthServiceImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570969D183146E800C10DA4 /* AuthServiceImpl.m */; }; + 657096A0183146E800C10DA4 /* AuthServiceImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570969D183146E800C10DA4 /* AuthServiceImpl.m */; }; + 657096A41831472300C10DA4 /* PaymentImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096A31831472300C10DA4 /* PaymentImpl.m */; }; + 657096A51831472300C10DA4 /* PaymentImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096A31831472300C10DA4 /* PaymentImpl.m */; }; + 657096A61831472300C10DA4 /* PaymentImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096A31831472300C10DA4 /* PaymentImpl.m */; }; + 657096AB183147D600C10DA4 /* PizzaImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096AA183147D600C10DA4 /* PizzaImpl.m */; }; + 657096AC183147D600C10DA4 /* PizzaImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096AA183147D600C10DA4 /* PizzaImpl.m */; }; + 657096AD183147D600C10DA4 /* PizzaImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 657096AA183147D600C10DA4 /* PizzaImpl.m */; }; BA79824D0006B0F9E50A41D3 /* TyphoonStringUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA79854F29FE8A327A82E8BC /* TyphoonStringUtilsTests.m */; }; BA798380AD1E1F4160A4719C /* CollaboratingMiddleAgesAssembly.m in Sources */ = {isa = PBXBuildFile; fileRef = BA79870B653F1DB13DCE8543 /* CollaboratingMiddleAgesAssembly.m */; }; BA7983B770D5EB663A720186 /* ExtendedMiddleAgesAssembly.m in Sources */ = {isa = PBXBuildFile; fileRef = BA798863929DB6786A73114C /* ExtendedMiddleAgesAssembly.m */; }; @@ -373,6 +394,23 @@ 4EFAF60D17E0D50B00E93ACF /* CROPrototypeA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CROPrototypeA.m; sourceTree = ""; }; 4EFAF61117E0D54700E93ACF /* CROPrototypeB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CROPrototypeB.h; sourceTree = ""; }; 4EFAF61217E0D54700E93ACF /* CROPrototypeB.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CROPrototypeB.m; sourceTree = ""; }; + 6570968818313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonAssistedFactoryBaseTest.m; sourceTree = ""; }; + 6570968C18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonAssistedFactoryDefinitionTest.m; sourceTree = ""; }; + 657096901831449000C10DA4 /* TyphoonFactoryProviderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonFactoryProviderTest.m; sourceTree = ""; }; + 657096951831468900C10DA4 /* CreditService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CreditService.h; sourceTree = ""; }; + 65709696183146A800C10DA4 /* CreditServiceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CreditServiceImpl.h; sourceTree = ""; }; + 65709697183146A800C10DA4 /* CreditServiceImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CreditServiceImpl.m; sourceTree = ""; }; + 6570969B183146D700C10DA4 /* AuthService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthService.h; sourceTree = ""; }; + 6570969C183146E800C10DA4 /* AuthServiceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthServiceImpl.h; sourceTree = ""; }; + 6570969D183146E800C10DA4 /* AuthServiceImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AuthServiceImpl.m; sourceTree = ""; }; + 657096A11831470F00C10DA4 /* Payment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Payment.h; sourceTree = ""; }; + 657096A21831472300C10DA4 /* PaymentImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PaymentImpl.h; sourceTree = ""; }; + 657096A31831472300C10DA4 /* PaymentImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaymentImpl.m; sourceTree = ""; }; + 657096A71831476800C10DA4 /* PaymentFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PaymentFactory.h; sourceTree = ""; }; + 657096A8183147BB00C10DA4 /* Pizza.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Pizza.h; sourceTree = ""; }; + 657096A9183147D600C10DA4 /* PizzaImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PizzaImpl.h; sourceTree = ""; }; + 657096AA183147D600C10DA4 /* PizzaImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PizzaImpl.m; sourceTree = ""; }; + 657096AE1831480700C10DA4 /* PizzaFactory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PizzaFactory.h; sourceTree = ""; }; B1C044F0F19E4B448A36BEFB /* Pods-ios.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios.xcconfig"; path = "Pods/Pods-ios.xcconfig"; sourceTree = SOURCE_ROOT; }; B90D396911F743468FF96BB5 /* libPods-iOS Tests (Cocoapods).a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-iOS Tests (Cocoapods).a"; sourceTree = BUILT_PRODUCTS_DIR; }; BA79833897B7E0F78B3AF30D /* CollaboratingMiddleAgesAssembly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollaboratingMiddleAgesAssembly.h; sourceTree = ""; }; @@ -551,6 +589,7 @@ 46877CF717D70B5900910D6A /* Initializer */, 4B06339E17D29402009CDBD6 /* Injection Aware */, 4B84149D179B0EA600916BF5 /* Properties */, + 6570968718313B2C00C10DA4 /* FactoryProvider */, 4B8414A2179B0EA600916BF5 /* TyphoonDefinitionTests.m */, ); path = Component; @@ -772,6 +811,38 @@ name = CurrentlyResolvingOverwrite; sourceTree = ""; }; + 6570968718313B2C00C10DA4 /* FactoryProvider */ = { + isa = PBXGroup; + children = ( + 657096941831464A00C10DA4 /* Fixtures */, + 657096901831449000C10DA4 /* TyphoonFactoryProviderTest.m */, + 6570968C18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m */, + 6570968818313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m */, + ); + path = FactoryProvider; + sourceTree = ""; + }; + 657096941831464A00C10DA4 /* Fixtures */ = { + isa = PBXGroup; + children = ( + 6570969B183146D700C10DA4 /* AuthService.h */, + 657096951831468900C10DA4 /* CreditService.h */, + 657096A11831470F00C10DA4 /* Payment.h */, + 657096A71831476800C10DA4 /* PaymentFactory.h */, + 657096A8183147BB00C10DA4 /* Pizza.h */, + 657096AE1831480700C10DA4 /* PizzaFactory.h */, + 6570969C183146E800C10DA4 /* AuthServiceImpl.h */, + 6570969D183146E800C10DA4 /* AuthServiceImpl.m */, + 65709696183146A800C10DA4 /* CreditServiceImpl.h */, + 65709697183146A800C10DA4 /* CreditServiceImpl.m */, + 657096A21831472300C10DA4 /* PaymentImpl.h */, + 657096A31831472300C10DA4 /* PaymentImpl.m */, + 657096A9183147D600C10DA4 /* PizzaImpl.h */, + 657096AA183147D600C10DA4 /* PizzaImpl.m */, + ); + name = Fixtures; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -847,7 +918,7 @@ LastUpgradeCheck = 0500; TargetAttributes = { 4B9685A117D26E8200E69723 = { - TestTargetID = 4B84146B179B0E3900916BF5 /* iOS Tests (Cocoapods) */; + TestTargetID = 4B84146B179B0E3900916BF5; }; }; }; @@ -1081,6 +1152,7 @@ 4B8414F3179B0EA600916BF5 /* MiddleAgesAssembly.m in Sources */, 4B8414F5179B0EA600916BF5 /* TyphoonBlockComponentFactoryTests.m in Sources */, 4B8414F9179B0EA600916BF5 /* TyphoonSharedComponentFactoryTests.m in Sources */, + 657096AB183147D600C10DA4 /* PizzaImpl.m in Sources */, 4E7F059517F6F947005F5DCD /* TyphoonGenericStackTest.m in Sources */, 4B8414FB179B0EA600916BF5 /* TyphoonComponentFactory+InstanceBuilderTests.m in Sources */, 4B8414FD179B0EA600916BF5 /* TyphoonComponentFactoryTests.m in Sources */, @@ -1097,6 +1169,7 @@ 4B841513179B0EA600916BF5 /* ClassDDependsOnC.m in Sources */, 4B06338B17D29240009CDBD6 /* TyphoonAssemblySelectorAdviserTests.m in Sources */, 4B841515179B0EA600916BF5 /* ClassEDependsOnC.m in Sources */, + 657096A41831472300C10DA4 /* PaymentImpl.m in Sources */, 4B0633A417D29402009CDBD6 /* InjectionAwareAssembly.m in Sources */, 4B841517179B0EA600916BF5 /* Sword.m in Sources */, 4B841519179B0EA600916BF5 /* SwordFactory.m in Sources */, @@ -1115,14 +1188,18 @@ 4EB2191017B4F30D00B901C9 /* SingletonsChainAssembly.m in Sources */, 4E08794717B6762E004B86C9 /* NotSingletonA.m in Sources */, 46877CFA17D70B5900910D6A /* TyphoonParameterInjectedAsCollectionTests.m in Sources */, + 6570969E183146E800C10DA4 /* AuthServiceImpl.m in Sources */, 4E47A45817D8E1C700F967CA /* PrototypeInitInjected.m in Sources */, 4E47A45D17D8E1DC00F967CA /* PrototypePropertyInjected.m in Sources */, + 657096911831449000C10DA4 /* TyphoonFactoryProviderTest.m in Sources */, 4656F7AB17DB635700EDDC23 /* InfrastructureComponentsAssembly.m in Sources */, 4656F7D617DC244B00EDDC23 /* TyphoonPatcherTests.m in Sources */, 4656F7D917DC244B00EDDC23 /* TyphoonPropertyPlaceholderConfigurerTests.m in Sources */, + 6570968D18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m in Sources */, 4656F7DE17DC253A00EDDC23 /* TyphoonComponentFactoryPostProcessorMock.m in Sources */, 4EFAF60917E0D4EF00E93ACF /* CROSingletonA.m in Sources */, 4EFAF60E17E0D50B00E93ACF /* CROPrototypeA.m in Sources */, + 65709698183146A800C10DA4 /* CreditServiceImpl.m in Sources */, 4EFAF61317E0D54700E93ACF /* CROPrototypeB.m in Sources */, 4EA74DDC17E0FF440004EFC7 /* CROSingletonB.m in Sources */, 46A14E0C17E42F4E009455D4 /* TyphoonViewControllerNibResolverTests.m in Sources */, @@ -1130,6 +1207,7 @@ BA798D9AF84869F576347FCC /* TyphoonStringUtilsTests.m in Sources */, BA79872D527DFBB10780B4BE /* TyphoonTestUtilsTests.m in Sources */, BA798C0AFB9D472511FB7B43 /* CollaboratingMiddleAgesAssembly.m in Sources */, + 6570968918313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m in Sources */, BA7983B770D5EB663A720186 /* ExtendedMiddleAgesAssembly.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1149,6 +1227,7 @@ 4B8414FC179B0EA600916BF5 /* TyphoonComponentFactory+InstanceBuilderTests.m in Sources */, 4B8414FE179B0EA600916BF5 /* TyphoonComponentFactoryTests.m in Sources */, 4B841500179B0EA600916BF5 /* RXMLElement+XmlComponentFactoryTests.m in Sources */, + 6570968E18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m in Sources */, 4B841502179B0EA600916BF5 /* TyphoonXmlComponentFactoryTests.m in Sources */, 4B841504179B0EA600916BF5 /* AutoWiringKnight.m in Sources */, 4B841506179B0EA600916BF5 /* AutoWiringSubClassedKnight.m in Sources */, @@ -1168,6 +1247,7 @@ 4B84152E179B0EA600916BF5 /* TyphoonPassthroughTypeConverterTests.m in Sources */, 4B841530179B0EA600916BF5 /* TyphoonPrimitiveTypeConverterTests.m in Sources */, 4B841532179B0EA600916BF5 /* TyphoonTypeConverterRegistryTests.m in Sources */, + 6570969F183146E800C10DA4 /* AuthServiceImpl.m in Sources */, 4B841534179B0EA600916BF5 /* TyphoonTypeDescriptorTests.m in Sources */, 4B0633AB17D29402009CDBD6 /* TyphoonInjectionAwareTests.m in Sources */, 4B841536179B0EA600916BF5 /* TyphoonBundleResourceTests.m in Sources */, @@ -1177,7 +1257,10 @@ 4EB2190617B4F2C500B901C9 /* SingletonA.m in Sources */, 4EB2190917B4F2C500B901C9 /* SingletonB.m in Sources */, 4EB2191117B4F30D00B901C9 /* SingletonsChainAssembly.m in Sources */, + 657096AC183147D600C10DA4 /* PizzaImpl.m in Sources */, 4E08794817B6762E004B86C9 /* NotSingletonA.m in Sources */, + 657096A51831472300C10DA4 /* PaymentImpl.m in Sources */, + 657096921831449000C10DA4 /* TyphoonFactoryProviderTest.m in Sources */, 46877CFB17D70B5900910D6A /* TyphoonParameterInjectedAsCollectionTests.m in Sources */, 4E47A45917D8E1C700F967CA /* PrototypeInitInjected.m in Sources */, 4E47A45E17D8E1DC00F967CA /* PrototypePropertyInjected.m in Sources */, @@ -1188,6 +1271,8 @@ 4EFAF60A17E0D4EF00E93ACF /* CROSingletonA.m in Sources */, 4EFAF60F17E0D50B00E93ACF /* CROPrototypeA.m in Sources */, 4EFAF61417E0D54700E93ACF /* CROPrototypeB.m in Sources */, + 65709699183146A800C10DA4 /* CreditServiceImpl.m in Sources */, + 6570968A18313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m in Sources */, 4E7F059617F6F947005F5DCD /* TyphoonGenericStackTest.m in Sources */, 4EA74DDD17E0FF440004EFC7 /* CROSingletonB.m in Sources */, BA79824D0006B0F9E50A41D3 /* TyphoonStringUtilsTests.m in Sources */, @@ -1209,6 +1294,7 @@ 4BC648C717A20AD2007F0463 /* MiddleAgesAssembly.m in Sources */, 4BC648C817A20AD2007F0463 /* TyphoonBlockComponentFactoryTests.m in Sources */, 4BC648CA17A20AD2007F0463 /* TyphoonSharedComponentFactoryTests.m in Sources */, + 657096AD183147D600C10DA4 /* PizzaImpl.m in Sources */, 4E7F059717F6F947005F5DCD /* TyphoonGenericStackTest.m in Sources */, 4BC648CB17A20AD2007F0463 /* TyphoonComponentFactory+InstanceBuilderTests.m in Sources */, 4BC648CC17A20AD2007F0463 /* TyphoonComponentFactoryTests.m in Sources */, @@ -1225,6 +1311,7 @@ 4BC648D717A20AD2007F0463 /* ClassDDependsOnC.m in Sources */, 4B06338D17D29240009CDBD6 /* TyphoonAssemblySelectorAdviserTests.m in Sources */, 4BC648D817A20AD2007F0463 /* ClassEDependsOnC.m in Sources */, + 657096A61831472300C10DA4 /* PaymentImpl.m in Sources */, 4B0633A617D29402009CDBD6 /* InjectionAwareAssembly.m in Sources */, 4BC648D917A20AD2007F0463 /* Sword.m in Sources */, 4BC648DA17A20AD2007F0463 /* SwordFactory.m in Sources */, @@ -1243,14 +1330,18 @@ 4EB2191217B4F30D00B901C9 /* SingletonsChainAssembly.m in Sources */, 4E08794917B6762E004B86C9 /* NotSingletonA.m in Sources */, 46877CFC17D70B5900910D6A /* TyphoonParameterInjectedAsCollectionTests.m in Sources */, + 657096A0183146E800C10DA4 /* AuthServiceImpl.m in Sources */, 4E47A45A17D8E1C700F967CA /* PrototypeInitInjected.m in Sources */, 4E47A45F17D8E1DC00F967CA /* PrototypePropertyInjected.m in Sources */, + 657096931831449000C10DA4 /* TyphoonFactoryProviderTest.m in Sources */, 4656F7AD17DB635700EDDC23 /* InfrastructureComponentsAssembly.m in Sources */, 4656F7D817DC244B00EDDC23 /* TyphoonPatcherTests.m in Sources */, 4656F7DB17DC244B00EDDC23 /* TyphoonPropertyPlaceholderConfigurerTests.m in Sources */, + 6570968F18313F2A00C10DA4 /* TyphoonAssistedFactoryDefinitionTest.m in Sources */, 4656F7E017DC253A00EDDC23 /* TyphoonComponentFactoryPostProcessorMock.m in Sources */, 4EFAF60B17E0D4EF00E93ACF /* CROSingletonA.m in Sources */, 4EFAF61017E0D50B00E93ACF /* CROPrototypeA.m in Sources */, + 6570969A183146A800C10DA4 /* CreditServiceImpl.m in Sources */, 4EFAF61517E0D54700E93ACF /* CROPrototypeB.m in Sources */, 4EA74DDE17E0FF440004EFC7 /* CROSingletonB.m in Sources */, 46A14E0D17E42F4E009455D4 /* TyphoonViewControllerNibResolverTests.m in Sources */, @@ -1258,6 +1349,7 @@ BA798EC89A23D4FC0EFB465D /* TyphoonStringUtilsTests.m in Sources */, BA7989B2482C57289EE056FE /* TyphoonTestUtilsTests.m in Sources */, BA7989FEB2CB1A63235D9AD2 /* CollaboratingMiddleAgesAssembly.m in Sources */, + 6570968B18313C3500C10DA4 /* TyphoonAssistedFactoryBaseTest.m in Sources */, BA7983E82AAFE3FE67FC4E6D /* ExtendedMiddleAgesAssembly.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Typhoon.xcodeproj/project.pbxproj b/Typhoon.xcodeproj/project.pbxproj index f9f1fa1c9..ef8767e75 100644 --- a/Typhoon.xcodeproj/project.pbxproj +++ b/Typhoon.xcodeproj/project.pbxproj @@ -119,6 +119,18 @@ 4EF180F217E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EF180EF17E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.h */; }; 4EF180F317E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EF180F017E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.m */; }; 4EF180F417E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EF180F017E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.m */; }; + 6570967718312DC000C10DA4 /* TyphoonFactoryProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 6570967518312DC000C10DA4 /* TyphoonFactoryProvider.h */; }; + 6570967818312DC000C10DA4 /* TyphoonFactoryProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 6570967518312DC000C10DA4 /* TyphoonFactoryProvider.h */; }; + 6570967918312DC000C10DA4 /* TyphoonFactoryProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570967618312DC000C10DA4 /* TyphoonFactoryProvider.m */; }; + 6570967A18312DC000C10DA4 /* TyphoonFactoryProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570967618312DC000C10DA4 /* TyphoonFactoryProvider.m */; }; + 6570967D18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h in Headers */ = {isa = PBXBuildFile; fileRef = 6570967B18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h */; }; + 6570967E18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h in Headers */ = {isa = PBXBuildFile; fileRef = 6570967B18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h */; }; + 6570967F18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570967C18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m */; }; + 6570968018312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m in Sources */ = {isa = PBXBuildFile; fileRef = 6570967C18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m */; }; + 65709683183131F900C10DA4 /* TyphoonAssistedFactoryBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 65709681183131F900C10DA4 /* TyphoonAssistedFactoryBase.h */; }; + 65709684183131F900C10DA4 /* TyphoonAssistedFactoryBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 65709681183131F900C10DA4 /* TyphoonAssistedFactoryBase.h */; }; + 65709685183131F900C10DA4 /* TyphoonAssistedFactoryBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 65709682183131F900C10DA4 /* TyphoonAssistedFactoryBase.m */; }; + 65709686183131F900C10DA4 /* TyphoonAssistedFactoryBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 65709682183131F900C10DA4 /* TyphoonAssistedFactoryBase.m */; }; 6B3F39C216ABFAC3001A601C /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B3F399816ABF8E5001A601C /* Cocoa.framework */; }; 75AB535896063F5C883565AD /* TyphoonComponentFactory+TyphoonDefinitionRegisterer.h in Headers */ = {isa = PBXBuildFile; fileRef = 75AB534C877C46FCE88630DD /* TyphoonComponentFactory+TyphoonDefinitionRegisterer.h */; }; 75AB536480B518052CAC385B /* TyphoonDefinitionRegisterer.m in Sources */ = {isa = PBXBuildFile; fileRef = 75AB596E7ACCCA9D39B9168B /* TyphoonDefinitionRegisterer.m */; }; @@ -247,6 +259,12 @@ 4EF180EC17E1F6100011F4D4 /* TyphoonInstanceRegister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TyphoonInstanceRegister.h; path = InstanceRegister/TyphoonInstanceRegister.h; sourceTree = ""; }; 4EF180EF17E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TyphoonKeyedStackInstanceRegister.h; path = InstanceRegister/TyphoonKeyedStackInstanceRegister.h; sourceTree = ""; }; 4EF180F017E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TyphoonKeyedStackInstanceRegister.m; path = InstanceRegister/TyphoonKeyedStackInstanceRegister.m; sourceTree = ""; }; + 6570967518312DC000C10DA4 /* TyphoonFactoryProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TyphoonFactoryProvider.h; sourceTree = ""; }; + 6570967618312DC000C10DA4 /* TyphoonFactoryProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonFactoryProvider.m; sourceTree = ""; }; + 6570967B18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TyphoonAssistedFactoryDefinition.h; sourceTree = ""; }; + 6570967C18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonAssistedFactoryDefinition.m; sourceTree = ""; }; + 65709681183131F900C10DA4 /* TyphoonAssistedFactoryBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TyphoonAssistedFactoryBase.h; sourceTree = ""; }; + 65709682183131F900C10DA4 /* TyphoonAssistedFactoryBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TyphoonAssistedFactoryBase.m; sourceTree = ""; }; 6B3F397416ABF854001A601C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 6B3F399616ABF8E5001A601C /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 6B3F399816ABF8E5001A601C /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; @@ -454,6 +472,19 @@ name = ConcreteImplementations; sourceTree = ""; }; + 6570967418312D1700C10DA4 /* FactoryProvider */ = { + isa = PBXGroup; + children = ( + 6570967518312DC000C10DA4 /* TyphoonFactoryProvider.h */, + 6570967618312DC000C10DA4 /* TyphoonFactoryProvider.m */, + 6570967B18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h */, + 6570967C18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m */, + 65709681183131F900C10DA4 /* TyphoonAssistedFactoryBase.h */, + 65709682183131F900C10DA4 /* TyphoonAssistedFactoryBase.m */, + ); + path = FactoryProvider; + sourceTree = ""; + }; 6B3F396516ABF854001A601C = { isa = PBXGroup; children = ( @@ -681,6 +712,7 @@ 46877CDA17D70B2200910D6A /* Collection */, BA79837576EEAF3CCA49888A /* Properties */, BA798E985943E046737DF9F9 /* Initializer */, + 6570967418312D1700C10DA4 /* FactoryProvider */, BA798CA3A88193EC0242E6EC /* TyphoonDefinition.h */, BA798F905F4B216CEA7A4D8A /* TyphoonDefinition.m */, 4BED3D6317A853E6006D262C /* TyphoonComponentInjectedByValue.h */, @@ -756,6 +788,7 @@ 4BC6492B17A20BD7007F0463 /* TyphoonDefinition.h in Headers */, 4BC6492C17A20BD7007F0463 /* TyphoonTypeConverter.h in Headers */, 4BC6493017A20BD7007F0463 /* TyphoonTypeConverterRegistry.h in Headers */, + 6570967818312DC000C10DA4 /* TyphoonFactoryProvider.h in Headers */, 4BC6493117A20BD7007F0463 /* TyphoonTypeDescriptor.h in Headers */, 4BC6493217A20BD7007F0463 /* TyphoonBundleResource.h in Headers */, 4BC6493317A20BD7007F0463 /* TyphoonResource.h in Headers */, @@ -768,6 +801,7 @@ 4BC6493A17A20BD7007F0463 /* TyphoonAssembly.h in Headers */, 4BC6493B17A20BD7007F0463 /* TyphoonBlockComponentFactory.h in Headers */, 4BC6493C17A20BD7007F0463 /* TyphoonAutowire.h in Headers */, + 65709684183131F900C10DA4 /* TyphoonAssistedFactoryBase.h in Headers */, 4BC6493D17A20BD7007F0463 /* TyphoonShorthand.h in Headers */, 4BC6493E17A20BD7007F0463 /* TyphoonDefinition+InstanceBuilder.h in Headers */, 4BC6493F17A20BD7007F0463 /* TyphoonInitializer+InstanceBuilder.h in Headers */, @@ -783,6 +817,7 @@ 4656F7A117DB5B7C00EDDC23 /* TyphoonDefinition+Infrastructure.h in Headers */, 4656F7BC17DC19E500EDDC23 /* TyphoonComponentFactoryPostProcessor.h in Headers */, 4656F7C617DC1A2F00EDDC23 /* TyphoonPatcher.h in Headers */, + 6570967E18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h in Headers */, 4656F7CA17DC1A2F00EDDC23 /* TyphoonPatchObjectFactory.h in Headers */, 4656F7CE17DC1A2F00EDDC23 /* TyphoonPropertyPlaceholderConfigurer.h in Headers */, 4EF180E917E1F5C90011F4D4 /* TyphoonGenericStack.h in Headers */, @@ -813,12 +848,14 @@ BA798925FDAC1BC6E2937A2C /* TyphoonPropertyInjectedByReference.h in Headers */, BA798C2625716A576AD32B0D /* TyphoonPropertyInjectedByType.h in Headers */, BA79827EEB8CA87219EE5DC1 /* TyphoonPropertyInjectedWithStringRepresentation.h in Headers */, + 65709683183131F900C10DA4 /* TyphoonAssistedFactoryBase.h in Headers */, BA798CEF2FA97482AE3742F6 /* TyphoonPropertyInjectionDelegate.h in Headers */, BA79899502CBAEE235B62757 /* TyphoonInitializer.h in Headers */, BA7986F5C5D07F9A92B7B1CE /* TyphoonInjectedParameter.h in Headers */, BA79800736608E888611A7B3 /* TyphoonParameterInjectedByReference.h in Headers */, BA798CC420E311724097700A /* TyphoonParameterInjectedWithStringRepresentation.h in Headers */, BA798B291EDCA2E25701D626 /* TyphoonDefinition.h in Headers */, + 6570967718312DC000C10DA4 /* TyphoonFactoryProvider.h in Headers */, BA798BB4D9038A4C5E6B8FFF /* TyphoonTypeConverter.h in Headers */, BA798520440B17DB1AA9D6A6 /* TyphoonTypeConverterRegistry.h in Headers */, BA79869E6884D3F82DFD7F95 /* TyphoonTypeDescriptor.h in Headers */, @@ -850,6 +887,7 @@ 4656F7C917DC1A2F00EDDC23 /* TyphoonPatchObjectFactory.h in Headers */, 4656F7CD17DC1A2F00EDDC23 /* TyphoonPropertyPlaceholderConfigurer.h in Headers */, BA7986D5F77CF8E22DEA24CF /* TyphoonStringUtils.h in Headers */, + 6570967D18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.h in Headers */, 4EF180E817E1F5C90011F4D4 /* TyphoonGenericStack.h in Headers */, 4EF180ED17E1F6100011F4D4 /* TyphoonInstanceRegister.h in Headers */, 4EF180F117E1F6180011F4D4 /* TyphoonKeyedStackInstanceRegister.h in Headers */, @@ -932,7 +970,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6570967A18312DC000C10DA4 /* TyphoonFactoryProvider.m in Sources */, 4BC648F917A20BD7007F0463 /* TyphoonRXMLElement+XmlComponentFactory.m in Sources */, + 65709686183131F900C10DA4 /* TyphoonAssistedFactoryBase.m in Sources */, 4BC648FA17A20BD7007F0463 /* TyphoonRXMLElement.m in Sources */, 4BC648FB17A20BD7007F0463 /* TyphoonXmlComponentFactory.m in Sources */, 4BC648FD17A20BD7007F0463 /* TyphoonComponentFactory+InstanceBuilder.m in Sources */, @@ -942,6 +982,7 @@ 4B9BD34317A6DCBA008921C7 /* TyphoonAssemblySelectorAdviser.m in Sources */, 4BC6490017A20BD7007F0463 /* TyphoonPropertyInjectedByType.m in Sources */, 4BC6490117A20BD7007F0463 /* TyphoonPropertyInjectedWithStringRepresentation.m in Sources */, + 6570968018312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m in Sources */, 4BC6490217A20BD7007F0463 /* TyphoonInitializer.m in Sources */, 4BC6490317A20BD7007F0463 /* TyphoonParameterInjectedByReference.m in Sources */, 4BC6490417A20BD7007F0463 /* TyphoonParameterInjectedWithStringRepresentation.m in Sources */, @@ -989,15 +1030,18 @@ BA7985CC3C95239BC358DD3F /* TyphoonRXMLElement.m in Sources */, BA79838EC0B25B8F61282068 /* TyphoonXmlComponentFactory.m in Sources */, BA7989A7C8AE7E636C08962A /* TyphoonComponentFactory+InstanceBuilder.m in Sources */, + 6570967918312DC000C10DA4 /* TyphoonFactoryProvider.m in Sources */, BA798EDDD02EF1FBC226119A /* TyphoonComponentFactory.m in Sources */, BA798532B43FB3D827EFA950 /* TyphoonPropertyInjectedByReference.m in Sources */, 4B9BD34217A6DCBA008921C7 /* TyphoonAssemblySelectorAdviser.m in Sources */, BA798410D85DE95CC8DF4DDC /* TyphoonPropertyInjectedByType.m in Sources */, BA798ADC644922BAE4940888 /* TyphoonPropertyInjectedWithStringRepresentation.m in Sources */, BA798EACB35B10ABCFF95396 /* TyphoonInitializer.m in Sources */, + 6570967F18312EE500C10DA4 /* TyphoonAssistedFactoryDefinition.m in Sources */, BA798395EF6F246A6CE90B33 /* TyphoonParameterInjectedByReference.m in Sources */, BA798029705C5C0E65B08592 /* TyphoonParameterInjectedWithStringRepresentation.m in Sources */, BA798FD2FBC0CA91B17AEEFC /* TyphoonDefinition.m in Sources */, + 65709685183131F900C10DA4 /* TyphoonAssistedFactoryBase.m in Sources */, BA7988B88E27909DDF8502EF /* TyphoonTypeConverterRegistry.m in Sources */, BA798EAF78A81CE7687424A6 /* TyphoonTypeDescriptor.m in Sources */, BA79835B7CF6ED2D07D2C1C8 /* TyphoonBundleResource.m in Sources */,