Skip to content

Commit

Permalink
Returned instances are injected the component factory (also for block…
Browse files Browse the repository at this point in the history
…s!).

This commit completes the previous one, making factories based on blocks also
inject the factory when appropriate.

The idea is using the already working implementation from the initializer
closures: the block turns into the implementation of a hidden method, the
real method gets intercepted by forwardInvocation, which gets redirected to
the hidden method, and the return value gets injected with the component
factory if needed.

Tests added or updated for the new feature.

See #144
  • Loading branch information
drodriguez committed Feb 2, 2014
1 parent 20d8032 commit 074fcd1
Show file tree
Hide file tree
Showing 18 changed files with 358 additions and 77 deletions.
46 changes: 32 additions & 14 deletions A-Typhoon.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#import "TyphoonAssistedFactoryBase.h"

@class TyphoonAssistedFactoryMethodClosure;
@protocol TyphoonAssistedFactoryMethodClosure;

/**
* Complements TyphoonAssistedFactoryBase to allow methods defined by closures.
Expand All @@ -28,11 +28,11 @@
/**
* Sets the given closure for the given selector.
*/
+ (void)_fmc_setClosure:(TyphoonAssistedFactoryMethodClosure *)closure forSelector:(SEL)selector;
+ (void)_fmc_setClosure:(id<TyphoonAssistedFactoryMethodClosure>)closure forSelector:(SEL)selector;

/**
* Returns the closure associated with the given selector.
*/
+ (TyphoonAssistedFactoryMethodClosure *)_fmc_closureForSelector:(SEL)selector;
+ (id<TyphoonAssistedFactoryMethodClosure>)_fmc_closureForSelector:(SEL)selector;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

@implementation TyphoonAssistedFactoryBase (TyphoonFactoryMethodClosure)

+ (void)_fmc_setClosure:(TyphoonAssistedFactoryMethodClosure *)closure forSelector:(SEL)selector
+ (void)_fmc_setClosure:(id<TyphoonAssistedFactoryMethodClosure>)closure forSelector:(SEL)selector
{
NSMutableDictionary *closures = [self _fmc_closures];
@synchronized(closures)
Expand All @@ -29,7 +29,7 @@ + (void)_fmc_setClosure:(TyphoonAssistedFactoryMethodClosure *)closure forSelect
}
}

+ (TyphoonAssistedFactoryMethodClosure *)_fmc_closureForSelector:(SEL)selector
+ (id<TyphoonAssistedFactoryMethodClosure>)_fmc_closureForSelector:(SEL)selector
{
NSMutableDictionary *closures = [self _fmc_closures];
@synchronized(closures)
Expand Down Expand Up @@ -65,7 +65,7 @@ - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature)
{
TyphoonAssistedFactoryMethodClosure *closure = [[self class] _fmc_closureForSelector:aSelector];
id<TyphoonAssistedFactoryMethodClosure> closure = [[self class] _fmc_closureForSelector:aSelector];
signature = closure.methodSignature;
}

Expand All @@ -76,7 +76,7 @@ - (void)forwardInvocation:(NSInvocation *)anInvocation
{
// Find the factory method closure related to this invocation selector, and
// create a new invocation from it, using the arguments of this invocation.
TyphoonAssistedFactoryMethodClosure *closure = [[self class] _fmc_closureForSelector:anInvocation.selector];
id<TyphoonAssistedFactoryMethodClosure> closure = [[self class] _fmc_closureForSelector:anInvocation.selector];
NSInvocation *closureInvocation = [closure invocationWithFactory:self forwardedInvocation:anInvocation];
[closureInvocation invoke];

Expand Down
32 changes: 32 additions & 0 deletions Source/Factory/Provider/TyphoonAssistedFactoryMethodBlockClosure.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2014, 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 <Foundation/Foundation.h>

#import "TyphoonAssistedFactoryMethodClosure.h"

/**
* A closure of a factory method. Internally this object stores a description of
* both the factory method and the init method selector. Each time you invoke the
* factory method (through forwardInvocation) a simple 1-to-1 mapping of the
* arguments will be performed, and the call will be forwarded.
*
* Users should not use this class directly.
*/
@interface TyphoonAssistedFactoryMethodBlockClosure : NSObject <TyphoonAssistedFactoryMethodClosure>

/**
* Creates a new closure pointing to the given selector, for the factory method
* described by methodSignature.
*/
- (instancetype)initWithSelector:(SEL)selector methodSignature:(NSMethodSignature *)methodSignature;

@end
53 changes: 53 additions & 0 deletions Source/Factory/Provider/TyphoonAssistedFactoryMethodBlockClosure.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// TyphoonAssistedFactoryMethodBlockClosure.m
// A-Typhoon
//
// Created by Daniel Rodríguez Troitiño on 01/02/14.
// Copyright (c) 2014 Jasper Blues. All rights reserved.
//

#import "TyphoonAssistedFactoryMethodBlockClosure.h"

@implementation TyphoonAssistedFactoryMethodBlockClosure
{
SEL _selector;
}

@synthesize methodSignature = _methodSignature;

- (instancetype)initWithSelector:(SEL)selector methodSignature:(NSMethodSignature *)methodSignature
{
self = [super init];
if (self)
{
NSParameterAssert(selector);
NSParameterAssert(methodSignature);

_selector = selector;
_methodSignature = methodSignature;
}

return self;
}

- (NSInvocation *)invocationWithFactory:(id)factory forwardedInvocation:(NSInvocation *)anInvocation
{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_methodSignature];
invocation.target = factory;
invocation.selector = _selector;

NSUInteger numberOfArguments = [_methodSignature numberOfArguments];
for (NSUInteger idx = 2; idx < numberOfArguments; idx++) {
NSUInteger argumentSize = 0;
NSGetSizeAndAlignment([_methodSignature getArgumentTypeAtIndex:idx], &argumentSize, NULL);

void *argument = malloc(argumentSize);
[anInvocation getArgument:argument atIndex:idx];
[invocation setArgument:argument atIndex:idx];
free(argument);
}

return invocation;
}

@end
14 changes: 12 additions & 2 deletions Source/Factory/Provider/TyphoonAssistedFactoryMethodBlockCreator.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

#include <objc/runtime.h>

#import "TyphoonAssistedFactoryBase+TyphoonFactoryMethodClosure.h"
#import "TyphoonAssistedFactoryMethodBlock.h"

#import "TyphoonAssistedFactoryMethodBlockClosure.h"

@interface TyphoonAssistedFactoryMethodBlockCreator ()

Expand All @@ -29,8 +30,17 @@ @implementation TyphoonAssistedFactoryMethodBlockCreator
- (void)createFromProtocol:(Protocol *)protocol inClass:(Class)factoryClass
{
struct objc_method_description methodDescription = [self methodDescriptionFor:self.factoryMethod.factoryMethod inProtocol:protocol];
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:methodDescription.types];

// To be able to intercept the result, we need to create the method with
// other name.
NSString *name = [NSString stringWithFormat:@"typhoon_interceptable_%@", NSStringFromSelector(methodDescription.name)];
SEL nameSEL = sel_registerName([name UTF8String]);
IMP methodIMP = imp_implementationWithBlock(self.factoryMethod.bodyBlock);
class_addMethod(factoryClass, methodDescription.name, methodIMP, methodDescription.types);
class_addMethod(factoryClass, nameSEL, methodIMP, methodDescription.types);

TyphoonAssistedFactoryMethodBlockClosure *closure = [[TyphoonAssistedFactoryMethodBlockClosure alloc] initWithSelector:nameSEL methodSignature:methodSignature];
[factoryClass _fmc_setClosure:closure forSelector:self.factoryMethod.factoryMethod];
}

@end
35 changes: 9 additions & 26 deletions Source/Factory/Provider/TyphoonAssistedFactoryMethodClosure.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////////////////////////
//
// TYPHOON FRAMEWORK
// Copyright 2013, Jasper Blues & Contributors
// Copyright 2014, Jasper Blues & Contributors
// All Rights Reserved.
//
// NOTICE: The authors permit you to use, modify, and distribute this file
Expand All @@ -11,34 +11,17 @@

#import <Foundation/Foundation.h>

#include <objc/runtime.h>
@protocol TyphoonAssistedFactoryMethodClosure <NSObject>


@class TyphoonAssistedFactoryMethodInitializer;

/**
* A closure of a factory method. Internally this object stores a description of
* both the factory method and the init method. Each time you invoke the
* factory method (through forwardInvocation) the mapping described in the method
* initializer will be performed, and the call will be forwarded.
*
* Users should not use this class directly.
*/
@interface TyphoonAssistedFactoryMethodClosure : NSObject

@property (nonatomic, retain, readonly) NSMethodSignature *methodSignature;

/**
* Creates a new closure from the description of the initializer, for the
* factory method described by methodSignature.
*/
- (instancetype)initWithInitializer:(TyphoonAssistedFactoryMethodInitializer *)initializer methodSignature:(NSMethodSignature *)methodSignature;
@property (nonatomic, strong, readonly) NSMethodSignature *methodSignature;

/**
* Returns an invocation filled with the right target instance, the right init
* selector and the arguments according to the initializer parameters, using
* factory to find the property values, and forwardedInvocation to find the
* arguments to the factory method.
* Returns an invocation filled with the right target instance, the right
* selector and the arguments according to the specifics of one of the types
* following this protocol. For blocks, this method will simply copy the
* forwardedInvocation arguments into the new invocation, for initializers, the
* type will take the initializer parameters, using factory to find the property
* values, and forwardedInvocation to find the arguments to the factory method.
*/
- (NSInvocation *)invocationWithFactory:(id)factory forwardedInvocation:(NSInvocation *)anInvocation;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
////////////////////////////////////////////////////////////////////////////////
//
// 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 <Foundation/Foundation.h>

#include "TyphoonAssistedFactoryMethodClosure.h"


@class TyphoonAssistedFactoryMethodInitializer;

/**
* A closure of a factory method. Internally this object stores a description of
* both the factory method and the init method. Each time you invoke the
* factory method (through forwardInvocation) the mapping described in the method
* initializer will be performed, and the call will be forwarded.
*
* Users should not use this class directly.
*/
@interface TyphoonAssistedFactoryMethodInitializerClosure : NSObject <TyphoonAssistedFactoryMethodClosure>

/**
* Creates a new closure from the description of the initializer, for the
* factory method described by methodSignature.
*/
- (instancetype)initWithInitializer:(TyphoonAssistedFactoryMethodInitializer *)initializer methodSignature:(NSMethodSignature *)methodSignature;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
////////////////////////////////////////////////////////////////////////////////

#import "TyphoonAssistedFactoryMethodClosure.h"
#import "TyphoonAssistedFactoryMethodInitializerClosure.h"

#include <objc/message.h>

Expand All @@ -22,14 +22,16 @@
#import "TyphoonAssistedFactoryParameterInjectedWithProperty.h"


@implementation TyphoonAssistedFactoryMethodClosure
@implementation TyphoonAssistedFactoryMethodInitializerClosure
{
Class _returnType;
SEL _initSelector;
NSArray *_parameters;
NSMethodSignature *_closedMethodSignature;
}

@synthesize methodSignature = _methodSignature;

- (instancetype)initWithInitializer:(TyphoonAssistedFactoryMethodInitializer *)initializer methodSignature:(NSMethodSignature *)methodSignature
{
self = [super init];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#import "TyphoonAssistedFactoryMethodCreator+Private.h"

#import "TyphoonAssistedFactoryBase+TyphoonFactoryMethodClosure.h"
#import "TyphoonAssistedFactoryMethodClosure.h"
#import "TyphoonAssistedFactoryMethodInitializerClosure.h"
#import "TyphoonAssistedFactoryMethodInitializer.h"


Expand All @@ -31,7 +31,7 @@ - (void)createFromProtocol:(Protocol *)protocol inClass:(Class)factoryClass
struct objc_method_description methodDescription = [self methodDescriptionFor:self.factoryMethod.factoryMethod inProtocol:protocol];
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:methodDescription.types];

TyphoonAssistedFactoryMethodClosure *closure = [[TyphoonAssistedFactoryMethodClosure alloc] initWithInitializer:self.factoryMethod methodSignature:methodSignature];
TyphoonAssistedFactoryMethodInitializerClosure *closure = [[TyphoonAssistedFactoryMethodInitializerClosure alloc] initWithInitializer:self.factoryMethod methodSignature:methodSignature];
[factoryClass _fmc_setClosure:closure forSelector:self.factoryMethod.factoryMethod];
}

Expand Down
2 changes: 2 additions & 0 deletions Tests/Factory/Provider/PaymentFactoryAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@

- (id)paymentFactory;

- (id)pizzaFactory;

@end
22 changes: 22 additions & 0 deletions Tests/Factory/Provider/PaymentFactoryAssembly.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#import "CreditServiceImpl.h"
#import "PaymentFactory.h"
#import "PaymentImpl.h"
#import "PizzaFactory.h"
#import "PizzaImpl.h"

@implementation PaymentFactoryAssembly

Expand All @@ -37,4 +39,24 @@ - (id)paymentFactory
} returns:[PaymentImpl class]];
}

- (id)pizzaFactory
{
return [TyphoonFactoryProvider withProtocol:@protocol(PizzaFactory) dependencies:^(TyphoonDefinition *definition) {
[definition injectProperty:@selector(creditService)];
} factories:^(TyphoonAssistedFactoryDefinition *definition) {
[definition factoryMethod:@selector(pizzaWithRadius:ingredients:) body:^id (id<PizzaFactory> factory, double radius, NSArray *ingredients) {
return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:radius ingredients:ingredients];
}];
[definition factoryMethod:@selector(smallPizzaWithIngredients:) body:^id (id<PizzaFactory> factory, NSArray *ingredients) {
return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:1 ingredients:ingredients];
}];
[definition factoryMethod:@selector(mediumPizzaWithIngredients:) body:^id (id<PizzaFactory> factory, NSArray *ingredients) {
return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:3 ingredients:ingredients];
}];
[definition factoryMethod:@selector(largePizzaWithIngredients:) body:^id (id<PizzaFactory> factory, NSArray *ingredients) {
return [[PizzaImpl alloc] initWithCreditService:factory.creditService radius:5 ingredients:ingredients];
}];
}];
}

@end
4 changes: 3 additions & 1 deletion Tests/Factory/Provider/Pizza.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

#import <Foundation/Foundation.h>

@protocol Pizza <NSObject>
#import "Typhoon.h"

@protocol Pizza <TyphoonComponentFactoryAware, NSObject>

@property (nonatomic, strong, readonly) id<CreditService> creditService;
@property (nonatomic, assign, readonly) double radius;
Expand Down
3 changes: 3 additions & 0 deletions Tests/Factory/Provider/PizzaImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
////////////////////////////////////////////////////////////////////////////////

#import <Foundation/Foundation.h>

#import "CreditService.h"
#import "Pizza.h"

@interface PizzaImpl : NSObject <Pizza>

@property (nonatomic, strong) id factory;

- (instancetype)initWithCreditService:(id<CreditService>)creditService
radius:(double)radius
ingredients:(NSArray *)ingredients;
Expand Down
Loading

0 comments on commit 074fcd1

Please sign in to comment.