Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: appsquickly/typhoon
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1.1.7
Choose a base ref
...
head repository: appsquickly/typhoon
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1.1.8
Choose a head ref

Commits on Apr 17, 2013

  1. Update README.md

    jasperblues committed Apr 17, 2013
    Copy the full SHA
    9fff9f3 View commit details

Commits on Apr 23, 2013

  1. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    a7a6dc0 View commit details
  2. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    94f7ec9 View commit details
  3. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    87f2094 View commit details
  4. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    c24742a View commit details
  5. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    e73de56 View commit details
  6. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    1b3877a View commit details
  7. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    81f1418 View commit details
  8. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    d72b26f View commit details
  9. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    c514fa8 View commit details
  10. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    9d0e7b7 View commit details
  11. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    ad02e33 View commit details
  12. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    7e1af9a View commit details
  13. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    47692d4 View commit details
  14. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    53a58b1 View commit details
  15. Update README.md

    jasperblues committed Apr 23, 2013
    Copy the full SHA
    2e7c39e View commit details

Commits on May 8, 2013

  1. Added support for injecting an instance of an object that has been cr…

    …eated externally.
    bryn committed May 8, 2013
    Copy the full SHA
    58248ec View commit details
  2. Changed injectInstance to injectProperties

    bryn committed May 8, 2013
    Copy the full SHA
    fa3a323 View commit details
  3. Merge pull request #15 from BrynCooke/master

    Added 'injectProperties' to allow injection of externally created objects.
    jasperblues committed May 8, 2013
    Copy the full SHA
    f07c821 View commit details

Commits on May 9, 2013

  1. Copy the full SHA
    8b27b5f View commit details
  2. Merge pull request #16 from BrynCooke/master

    Fix bug in injectProperties when dealing with subclassing
    jasperblues committed May 9, 2013
    Copy the full SHA
    63297c4 View commit details

Commits on May 13, 2013

  1. Injecting properties now only injects using the definition for an exa…

    …ct match to the class.
    bryn committed May 13, 2013
    Copy the full SHA
    4303262 View commit details

Commits on May 17, 2013

  1. Copy the full SHA
    599bf33 View commit details
  2. update podspec.

    Jasper Blues committed May 17, 2013
    Copy the full SHA
    2cdb1e5 View commit details
  3. Copy the full SHA
    ea15f6c View commit details
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# Typhoon! (www.typhoonframework.org)

A new dependency injection container for Objective-C. Light-weight, yet full-featured.
A new dependency injection container for Objective-C. Light-weight, yet full-featured and super-easy to use.

## Status? It's ready to use!
## Familiar with Dependency Injection?

* <a href="https://github.com/jasperblues/Typhoon-example">Try the sample application</a>.
* <a href="https://github.com/jasperblues/Typhoon/wiki/Assembling-Components-with-Blocks">Read the User Guide</a>.
* <a href="https://github.com/jasperblues/Typhoon-example">Try the sample application</a>.
* <a href="https://github.com/jasperblues/Typhoon#design-goals--features">Check the feature list</a>.

otherwise. . .

### What is Dependency Injection?

@@ -114,22 +117,20 @@ following:
* . . . ability to configure components for use in eg ___Test___ vs ___Production___ scenarios. This faciliates a
good compromise between integration testing and pure unit testing. (Biggest testing bang-for-your-buck).

* ***Encourages polymorphism and makes it easy to have multiple configurations of the same base-class or protocol.
For example a MasterCardPaymentEngine and a VisaPaymentEngine, both implementing a PaymentEngine protocol.
(Some other DI containers for Objective-C have problems with this).***
* ***Doesn't get all "meta" on you. Supports auto-wiring "annotations" (macros), for simple cases. When convention-over-configuration doesn't provide a simple answer, it
gets straight-to-the-point by defining what depends on what.***

* Supports both auto-wiring by type and wiring-by-reference.
* No fragmented qualifier macros, modules, etc. Makes it easy to have multiple configurations of the same base-class or protocol.

* Application assembly - the wiring of dependencies and configuration management - is all encapsulated in a
convenient document. Now you know where to look if you need to change something.
convenient document. At the same time this document can be grouped into chapters. Now you know where to look if you need to change something.

* Non-invasive.
* Non-invasive. Its not necessary to change ***any*** of your classes to use the framework. It does not swizzle your classes, so you can use the same class with or without dependency injection.

* Supports both initializer and property injection. In the case of the latter, it has customizable call-backs to
ensure that the class is in the required state before and after properties are set.

* Flexibility. Supports different approaches of dependency injection for different scenarios, including "annotations"
and GUI tool-support.
* Flexibility. Supports different approaches of dependency injection for different scenarios - native block-style, auto-wiring macros or Spring-style XML.

* Lean. It has a very low footprint, so is appropriate for CPU and memory constrained devices.

@@ -171,6 +172,7 @@ each commit. (If you'd like the script I will share it).

##Current Work?

* ***New!!*** Just added support for collections - NSArray and NSSet properties injected by type-converted value or references. . . . docs and dictionaries coming soon.
* More <a href="http://www.jetbrains.com/objc/">AppCode IDE</a> integration. (Thanks to Jetbrains for the assistance).
* API docs.

85 changes: 51 additions & 34 deletions Source/Factory/Block/TyphoonBlockComponentFactory.m
Original file line number Diff line number Diff line change
@@ -87,27 +87,54 @@ - (NSArray*)populateCache:(TyphoonAssembly*)assembly
{
@synchronized (self)
{
unsigned int methodCount;
Method* methodList = class_copyMethodList([assembly class], &methodCount);
for (int i = 0; i < methodCount; i++)
{
Method method = methodList[i];
// NSLog(@"Selector: %@", NSStringFromSelector(method_getName(method)));
NSSet *definitionSelectors = [self obtainDefinitionSelectors:assembly];

[definitionSelectors enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
objc_msgSend(assembly, (SEL)[obj pointerValue]);
}];

NSMutableDictionary* dictionary = [assembly cachedSelectors];
return [dictionary allValues];
}
}

- (NSSet *)obtainDefinitionSelectors:(TyphoonAssembly*)assembly
{
NSMutableSet *definitionSelectors = [[NSMutableSet alloc] init];

Class currentClass = [assembly class];
while (strcmp(class_getName(currentClass), "TyphoonAssembly") != 0) {
[definitionSelectors unionSet:[self obtainDefinitionSelectorsInAssemblyClass:currentClass]];
currentClass = class_getSuperclass(currentClass);
}

return definitionSelectors;
}

int argumentCount = method_getNumberOfArguments(method);
if (argumentCount == 2)
- (NSSet *)obtainDefinitionSelectorsInAssemblyClass:(Class)class
{
NSMutableSet *definitionSelectors = [[NSMutableSet alloc] init];

unsigned int methodCount;
Method* methodList = class_copyMethodList(class, &methodCount);
for (int i = 0; i < methodCount; i++)
{
Method method = methodList[i];
// NSLog(@"Selector: %@", NSStringFromSelector(method_getName(method)));

int argumentCount = method_getNumberOfArguments(method);
if (argumentCount == 2)
{
SEL methodSelector = method_getName(method);
if (![class selectorReserved:methodSelector])
{
SEL methodSelector = method_getName(method);
if (![[assembly class] selectorReserved:methodSelector])
{
objc_msgSend(assembly, methodSelector);
}
[definitionSelectors addObject:[NSValue valueWithPointer:methodSelector]];
}
}
NSMutableDictionary* dictionary = [assembly cachedSelectors];
free(methodList);
return [dictionary allValues];
}
free(methodList);

return definitionSelectors;
}

- (void)applyBeforeAdviceToAssemblyMethods:(TyphoonAssembly*)assembly
@@ -117,24 +144,14 @@ - (void)applyBeforeAdviceToAssemblyMethods:(TyphoonAssembly*)assembly
if (![swizzleRegistry containsObject:[assembly class]])
{
[swizzleRegistry addObject:[assembly class]];
unsigned int methodCount;
Method* methodList = class_copyMethodList([assembly class], &methodCount);
for (int i = 0; i < methodCount; i++)
{
Method method = methodList[i];
int argumentCount = method_getNumberOfArguments(method);
if (argumentCount == 2)
{
SEL methodSelector = method_getName(method);
if ([TyphoonAssembly selectorReserved:methodSelector] == NO)
{
SEL swizzled = NSSelectorFromString(
[NSStringFromSelector(methodSelector) stringByAppendingString:TYPHOON_BEFORE_ADVICE_SUFFIX]);
[[assembly class] typhoon_swizzleMethod:methodSelector withMethod:swizzled error:nil];
}
}
}
free(methodList);

NSSet *definitionSelectors = [self obtainDefinitionSelectors:assembly];
[definitionSelectors enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
SEL methodSelector = (SEL)[obj pointerValue];
SEL swizzled = NSSelectorFromString(
[NSStringFromSelector(methodSelector) stringByAppendingString:TYPHOON_BEFORE_ADVICE_SUFFIX]);
[[assembly class] typhoon_swizzleMethod:methodSelector withMethod:swizzled error:nil];
}];
}
}
}
Original file line number Diff line number Diff line change
@@ -13,9 +13,9 @@

#import <Foundation/Foundation.h>
#import "TyphoonComponentFactory.h"

#import "TyphoonIntrospectiveNSObject.h"
@interface TyphoonComponentFactory (InstanceBuilder)

- (id)buildInstanceWithDefinition:(TyphoonDefinition*)definition;

- (void)injectPropertyDependenciesOn:(id <TyphoonIntrospectiveNSObject>)instance withDefinition:(TyphoonDefinition*)definition;
@end
Original file line number Diff line number Diff line change
@@ -63,6 +63,25 @@ - (id)buildInstanceWithDefinition:(TyphoonDefinition*)definition
return instance;
}

- (void)injectPropertyDependenciesOn:(id <TyphoonIntrospectiveNSObject>)instance withDefinition:(TyphoonDefinition*)definition
{
[self doBeforePropertyInjectionOn:instance withDefinition:definition];

for (id <TyphoonInjectedProperty> property in [definition injectedProperties])
{
TyphoonTypeDescriptor* typeDescriptor = [instance typeForPropertyWithName:property.name];
if (typeDescriptor == nil)
{
[NSException raise:NSInvalidArgumentException
format:@"Tried to inject property '%@' on object of type '%@', but the instance has no setter for this property.",
property.name, [instance class]];
}
[self doPropertyInjection:instance property:property typeDescriptor:typeDescriptor];
}

[self doAfterPropertyInjectionOn:instance withDefinition:definition];
}


/* ============================================================ Private Methods ========================================================= */
- (id)invokeInitializerOn:(id)instanceOrClass withDefinition:(TyphoonDefinition*)definition
@@ -100,25 +119,6 @@ - (id)invokeInitializerOn:(id)instanceOrClass withDefinition:(TyphoonDefinition*
/* ====================================================================================================================================== */
#pragma mark - Property Injection

- (void)injectPropertyDependenciesOn:(id <TyphoonIntrospectiveNSObject>)instance withDefinition:(TyphoonDefinition*)definition
{
[self doBeforePropertyInjectionOn:instance withDefinition:definition];

for (id <TyphoonInjectedProperty> property in [definition injectedProperties])
{
TyphoonTypeDescriptor* typeDescriptor = [instance typeForPropertyWithName:property.name];
if (typeDescriptor == nil)
{
[NSException raise:NSInvalidArgumentException
format:@"Tried to inject property '%@' on object of type '%@', but the instance has no setter for this property.",
property.name, [instance class]];
}
[self doPropertyInjection:instance property:property typeDescriptor:typeDescriptor];
}

[self doAfterPropertyInjectionOn:instance withDefinition:definition];
}

- (void)doBeforePropertyInjectionOn:(id <TyphoonIntrospectiveNSObject>)instance withDefinition:(TyphoonDefinition*)definition
{
if ([instance conformsToProtocol:@protocol(TyphoonPropertyInjectionDelegate)])
5 changes: 5 additions & 0 deletions Source/Factory/TyphoonComponentFactory.h
Original file line number Diff line number Diff line change
@@ -70,4 +70,9 @@

- (void)attachMutator:(id<TyphoonComponentFactoryMutator>)mutator;

/**
* Injects the properties of an object
*/
- (void)injectProperties:(id)instance;

@end
12 changes: 12 additions & 0 deletions Source/Factory/TyphoonComponentFactory.m
Original file line number Diff line number Diff line change
@@ -164,6 +164,16 @@ - (void)attachMutator:(id)mutator
[_mutators addObject:mutator];
}

- (void)injectProperties:(id)instance {
Class class = [instance class];
for (TyphoonDefinition* definition in _registry)
{
if(definition.type == class)
{
[self injectPropertyDependenciesOn:instance withDefinition:definition];
}
}
}

/* ============================================================ Utility Methods ========================================================= */
- (NSString*)description
@@ -239,4 +249,6 @@ - (void)performMutationsIfRequired
}
}



@end
13 changes: 13 additions & 0 deletions Tests/Factory/Block/MiddleAgesAssemblyOverride.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// MiddleAgesAssemblyOverride.h
// Typhoon
//
// Created by Jose Gonzalez Gomez on 17/05/13.
// Copyright (c) 2013 Jasper Blues. All rights reserved.
//

#import "MiddleAgesAssembly.h"


@interface MiddleAgesAssemblyOverride : MiddleAgesAssembly
@end
26 changes: 26 additions & 0 deletions Tests/Factory/Block/MiddleAgesAssemblyOverride.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// MiddleAgesAssemblyOverride.m
// Typhoon
//
// Created by Jose Gonzalez Gomez on 17/05/13.
// Copyright (c) 2013 Jasper Blues. All rights reserved.
//

#import "MiddleAgesAssemblyOverride.h"
#import "TyphoonDefinition.h"
#import "Knight.h"


@implementation MiddleAgesAssemblyOverride

- (id)knight
{
return [TyphoonDefinition withClass:[Knight class] properties:^(TyphoonDefinition* definition)
{
[definition injectProperty:@selector(quest) withDefinition:[self defaultQuest]];
[definition injectProperty:@selector(damselsRescued) withValueAsText:@"50"]; // different from parent assembly definition
[definition setScope:TyphoonScopeDefault];
}];
}

@end
47 changes: 47 additions & 0 deletions Tests/Factory/Block/TyphoonBlockComponentFactoryOverrideTests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// TyphoonBlockComponentFactoryOverrideTests.m
// Typhoon
//
// Created by Jose Gonzalez Gomez on 17/05/13.
// Copyright (c) 2013 Jasper Blues. All rights reserved.
//

#import <SenTestingKit/SenTestingKit.h>
#import "TyphoonSharedComponentFactoryTests.h"
#import "TyphoonBlockComponentFactory.h"
#import "TyphoonPropertyPlaceholderConfigurer.h"
#import "TyphoonBundleResource.h"
#import "ExceptionTestAssembly.h"
#import "MiddleAgesAssemblyOverride.h"
#import "Knight.h"


@interface TyphoonBlockComponentFactoryOverrideTests : TyphoonSharedComponentFactoryTests
@end


@implementation TyphoonBlockComponentFactoryOverrideTests

- (void)setUp
{
_componentFactory = [[TyphoonBlockComponentFactory alloc] initWithAssembly:[MiddleAgesAssemblyOverride assembly]];
TyphoonPropertyPlaceholderConfigurer* configurer = [[TyphoonPropertyPlaceholderConfigurer alloc] init];
[configurer usePropertyStyleResource:[TyphoonBundleResource withName:@"SomeProperties.properties"]];
[_componentFactory attachMutator:configurer];
[_componentFactory makeDefault];

_exceptionTestFactory = [[TyphoonBlockComponentFactory alloc] initWithAssembly:[ExceptionTestAssembly assembly]];
}

// This test overrides the same test in the base class, testing for the damselsRescued overrided value in MiddleAgesAssemblyOverride
- (void)test_injects_properties_by_reference_and_by_value
{
Knight* knight = [_componentFactory componentForKey:@"knight"];

assertThat(knight, notNilValue());
assertThat(knight.quest, notNilValue());
assertThat([knight.quest questName], equalTo(@"Campaign Quest"));
assertThatUnsignedLongLong(knight.damselsRescued, equalToUnsignedLongLong(50));
}

@end
45 changes: 45 additions & 0 deletions Tests/Factory/TyphoonComponentFactoryTests.m
Original file line number Diff line number Diff line change
@@ -203,4 +203,49 @@ - (void)test_able_to_describe_itself
assertThat(description, equalTo(@"<TyphoonComponentFactory: _registry=(\n)>"));
}

/* ====================================================================================================================================== */
#pragma mark - Inject properties

- (void)test_injectProperties
{
[_componentFactory register:[TyphoonDefinition withClass:[Knight class] properties:^(TyphoonDefinition* definition)
{
[definition injectProperty:@selector(quest)];
}]];
[_componentFactory register:[TyphoonDefinition withClass:[CampaignQuest class] key:@"quest"]];

Knight* knight = [[Knight alloc] init];
[_componentFactory injectProperties:knight];

assertThat(knight.quest, notNilValue());

}

- (void)test_injectProperties_subclassing
{
[_componentFactory register:[TyphoonDefinition withClass:[Knight class] properties:^(TyphoonDefinition* definition)
{
[definition injectProperty:@selector(quest)];
}]];
[_componentFactory register:[TyphoonDefinition withClass:[CavalryMan class] properties:^(TyphoonDefinition* definition)
{
[definition injectProperty:@selector(hitRatio) withValueAsText:@"3.0"];
}]];
[_componentFactory register:[TyphoonDefinition withClass:[CampaignQuest class] key:@"quest"]];

CavalryMan* cavalryMan = [[CavalryMan alloc] init];
[_componentFactory injectProperties:cavalryMan];

assertThat(cavalryMan.quest, nilValue());
assertThatFloat(cavalryMan.hitRatio, equalToFloat(3.0f));

Knight* knight = [[Knight alloc] init];
[_componentFactory injectProperties:knight];

assertThat(knight.quest, notNilValue());


}


@end
Loading