Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
drekka committed Nov 16, 2016
2 parents b1b7a75 + 0c66d87 commit 2b8c286
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 26 deletions.
42 changes: 32 additions & 10 deletions Alchemic.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion README.md
Expand Up @@ -20,7 +20,12 @@ ___Main features___
* Support for NSUserDefaults and Apple's cloud based key-value stores.
* and much, much more.

# V2.2.0 #
# v2.2.1

* Fixed bug where tranient watches were triggering the re-injection non-transient injections.
* Changed LogInitialModel to just logModel to dump the model into the logs on startup.

# v2.2.0

* Merged Swift support back into main Alchemic framework.
* Fixed bug where it was possible to inject a nil from a nillable factory into a non-nullable variable or method argument.
Expand Down
28 changes: 17 additions & 11 deletions alchemic/ALCClassObjectFactory.m
Expand Up @@ -32,7 +32,7 @@ @implementation ALCClassObjectFactory {
NSHashTable *_injectedObjectHistory;

// The notification observer listening to dependency updates. Currently only used by transient dependencies.
id _dependencyChangedObserver;
id _transientChangedObserver;

}

Expand Down Expand Up @@ -85,7 +85,7 @@ -(void)resolveWithStack:(NSMutableArray<id<ALCResolvable>> *)resolvingStack mode
for (ALCVariableDependency *dependency in _dependencies) {
if (dependency.referencesTransients) {
STLog(self, @"%@ is references a transient, configuring factory to watch for changes", [ALCRuntime forClass:self.type.objcClass propertyDescription:dependency.name]);
// Any dependency which refers to transients must be nillable.
// Any dependency which refers to transients must be nillable by default.
[dependency configureWithOptions:@[AcNillable]];
[self setUpTransientWatch];
}
Expand All @@ -94,23 +94,29 @@ -(void)resolveWithStack:(NSMutableArray<id<ALCResolvable>> *)resolvingStack mode

-(void) setUpTransientWatch {

if (_injectedObjectHistory) {
// Already watching for transient changes.
return;
}

// We need to track all objects we have injected which are still active so we can re-inject them on a transient change.
_injectedObjectHistory = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:0];

AcWeakSelf;
void (^dependenciesChanged)(NSNotification *notificaton) = ^(NSNotification *notification) {
void (^objectStored) (NSNotification *notificaton) = ^(NSNotification *notification) {

AcStrongSelf;
id sourceObjectFactory = notification.object;
id<ALCObjectFactory> sourceObjectFactory = notification.object;

if (strongSelf->_injectedObjectHistory.count > 0) {
// Only check dependencies if the object factory that generated the notification is a transient factory and we have at least one object that may need a new value injected.
if (strongSelf->_transientChangedObserver && sourceObjectFactory.isTransient) {

STLog(self, @"%@ checking transient changed notification", NSStringFromClass(self.type.objcClass));
STLog(self, @"Transient factory stored new object. Checking dependencies of factory %@ ...", self);

// Loop through all dependencies and check to see if they are watching the factory that has changed it's value.
for (ALCVariableDependency *variableDependency in strongSelf->_dependencies) {
if ([variableDependency referencesObjectFactory:sourceObjectFactory]) {
STLog(self, @"Found dependency which needs updating, injecting %lu objects", (unsigned long) strongSelf->_injectedObjectHistory.count);
STLog(self, @"Dependency %@ is watching factory, injecting %lu objects", [ALCRuntime forClass:self.type.objcClass propertyDescription:variableDependency.name], (unsigned long) strongSelf->_injectedObjectHistory.count);
for (id object in strongSelf->_injectedObjectHistory) {
[strongSelf injectObject:object dependency:variableDependency];
}
Expand All @@ -119,10 +125,10 @@ -(void) setUpTransientWatch {
}
};

self->_dependencyChangedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AlchemicDidStoreObject
self->_transientChangedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AlchemicDidStoreObject
object:nil
queue:nil
usingBlock:dependenciesChanged];
usingBlock:objectStored];
}

-(BOOL) isReady {
Expand Down Expand Up @@ -150,8 +156,8 @@ -(ALCBlockWithObject) objectCompletion {
}

-(void) unload {
if (_dependencyChangedObserver) {
[[NSNotificationCenter defaultCenter] removeObserver:_dependencyChangedObserver];
if (_transientChangedObserver) {
[[NSNotificationCenter defaultCenter] removeObserver:_transientChangedObserver];
}
}

Expand Down
2 changes: 1 addition & 1 deletion alchemic/ALCContextImpl.m
Expand Up @@ -63,7 +63,7 @@ -(void) start {
self->_finishedStartingOp = nil;
self->_status = ALCStatusStarted;
[[NSNotificationCenter defaultCenter] postNotificationName:AlchemicDidFinishStarting object:self];
STLog(@"LogInitialModel", @"Alchemic started.%@", self);
STLog(@"LogModel", @"Alchemic started.%@", self);
}];

// Setup dependencies.
Expand Down
5 changes: 2 additions & 3 deletions alchemic/ALCModelImpl.m
Expand Up @@ -113,9 +113,8 @@ -(void) resolveDependencies {
-(void) modelWillResolve {
STLog(self, @"Model will resolve");
for (NSObject<ALCResolveAspect> *aspect in _resolveAspects) {
STLog(self, @"Checking %@", aspect);
if ([[aspect class] enabled] && [aspect respondsToSelector:@selector(modelWillResolve:)]) {
STLog(self, @"Calling aspect will resolve");
STLog(self, @"Calling modelWillResolve in Aspect %@", aspect);
[aspect modelWillResolve:self];
}
}
Expand All @@ -124,7 +123,7 @@ -(void) modelWillResolve {
-(void) modelDidResolve {
for (NSObject<ALCResolveAspect> *aspect in _resolveAspects) {
if ([[aspect class] enabled] && [aspect respondsToSelector:@selector(modelDidResolve:)]) {
STLog(self, @"Calling aspect did resolve");
STLog(self, @"Calling modelDidResolve in Aspect %@", aspect);
[aspect modelDidResolve:self];
}
}
Expand Down
99 changes: 99 additions & 0 deletions alchemicTests/ALCTypeExtensionTests.swift
@@ -0,0 +1,99 @@
//
// ALCTypeExtensionTests.swift
// alchemic
//
// Created by Derek Clarkson on 11/11/16.
// Copyright © 2016 Derek Clarkson. All rights reserved.
//

import XCTest
@testable import Alchemic

class ALCTypeExtensionTests: XCTestCase {

func testBool() {
let type:ALCType = ALCType.bool;
XCTAssertEqual(.bool, type.type)
}

func testChar() {
let type:ALCType = ALCType.char;
XCTAssertEqual(.char, type.type)
}

func testCharPointer() {
let type:ALCType = ALCType.charPointer;
XCTAssertEqual(.charPointer, type.type)
}

func testInt() {
let type:ALCType = ALCType.int;
XCTAssertEqual(.int, type.type)
}

func testFloat() {
let type:ALCType = ALCType.float;
XCTAssertEqual(.float, type.type)
}

func testDouble() {
let type:ALCType = ALCType.double;
XCTAssertEqual(.double, type.type)
}

func testLong() {
let type:ALCType = ALCType.long;
XCTAssertEqual(.long, type.type)
}

func testLongLong() {
let type:ALCType = ALCType.longLong;
XCTAssertEqual(.longLong, type.type)
}

func testShort() {
let type:ALCType = ALCType.short;
XCTAssertEqual(.short, type.type)
}

func testUnsignedChar() {
let type:ALCType = ALCType.unsignedChar;
XCTAssertEqual(.unsignedChar, type.type)
}

func testUnsignedShort() {
let type:ALCType = ALCType.unsignedShort;
XCTAssertEqual(.unsignedShort, type.type)
}

func testUnsignedInt() {
let type:ALCType = ALCType.unsignedInt;
XCTAssertEqual(.unsignedInt, type.type)
}

func testUnsignedLong() {
let type:ALCType = ALCType.unsignedLong;
XCTAssertEqual(.unsignedLong, type.type)
}

func testUnsignedLongLong() {
let type:ALCType = ALCType.unsignedLongLong;
XCTAssertEqual(.unsignedLongLong, type.type)
}

func testCGPoint() {
let type:ALCType = ALCType.cgPoint;
XCTAssertEqual(.cgPoint, type.type)
}

func testCGSize() {
let type:ALCType = ALCType.cgSize;
XCTAssertEqual(.cgSize, type.type)
}

func testCGRect() {
let type:ALCType = ALCType.cgRect;
XCTAssertEqual(.cgRect, type.type)
}

}
23 changes: 23 additions & 0 deletions alchemicTests/AlchemicMacrosTests.swift
@@ -0,0 +1,23 @@
//
// AlchemicMacrosTests.swift
// alchemic
//
// Created by Derek Clarkson on 13/11/16.
// Copyright © 2016 Derek Clarkson. All rights reserved.
//

import XCTest
@testable import Alchemic

class AlchemicMacrosTests: XCTestCase {

override func setUp() {
let model = ALCModelImpl()
(Alchemic.mainContext() as! ALCContextImpl).setValue(model, forKey:"_model")
}

func testAcRegister() {
}


}
4 changes: 4 additions & 0 deletions alchemicTests/Unit tests-Bridging-Header.h
@@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

1 change: 1 addition & 0 deletions module.modulemap
@@ -1,6 +1,7 @@
module Alchemic {
umbrella header "Headers/Alchemic.h"
requires objc_arc
export Alchemic.Private
link framework "StoryTeller"
}

Expand Down

0 comments on commit 2b8c286

Please sign in to comment.