Skip to content

Commit

Permalink
More graceful teardown
Browse files Browse the repository at this point in the history
  • Loading branch information
vaukalak authored and darscan committed May 25, 2014
1 parent 2c47c92 commit 16f2ab2
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 22 deletions.
31 changes: 25 additions & 6 deletions src/org/swiftsuspenders/Injector.as
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,19 @@ package org.swiftsuspenders
return mapping;
}

/**
* Checks if an instance is managed by this Injector.
*
* <p>An instance is "managed" if it was created or injected into by this Injector.</p>
*
* @param instance The instance to check
* @return True if the Injector is managing this instance
*/
public function hasManagedInstance(instance : Object) : Boolean
{
return _managedObjects[instance];
}

/**
* Inspects the given object and injects into all injection points configured for its class.
*
Expand Down Expand Up @@ -440,10 +453,7 @@ package org.swiftsuspenders
*/
public function destroyInstance(instance : Object) : void
{
if (!instance)
{
return;
}
delete _managedObjects[instance];
const type : Class = _reflector.getClass(instance);
const typeDescription : TypeDescription = getTypeDescription(type);
for (var preDestroyHook : PreDestroyInjectionPoint = typeDescription.preDestroyMethods;
Expand Down Expand Up @@ -471,9 +481,18 @@ package org.swiftsuspenders
{
mapping.getProvider().destroy();
}
var objectsToRemove:Vector.<Object> = new Vector.<Object>();
for each (var instance : Object in _managedObjects)
{
destroyInstance(instance);
instance && objectsToRemove.push(instance);
}
while(objectsToRemove.length)
{
destroyInstance(objectsToRemove.pop());
}
for (var mappingId:String in providerMappings)
{
delete providerMappings[mappingId];
}
_mappings = new Dictionary();
_mappingsInProcess = new Dictionary();
Expand Down Expand Up @@ -700,4 +719,4 @@ package org.swiftsuspenders
new InjectionEvent(InjectionEvent.POST_CONSTRUCT, target, targetType));
}
}
}
}
15 changes: 3 additions & 12 deletions src/org/swiftsuspenders/dependencyproviders/SingletonProvider.as
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ package org.swiftsuspenders.dependencyproviders

import org.swiftsuspenders.Injector;
import org.swiftsuspenders.errors.InjectorError;
import org.swiftsuspenders.typedescriptions.PreDestroyInjectionPoint;
import org.swiftsuspenders.typedescriptions.TypeDescription;

public class SingletonProvider implements DependencyProvider
{
Expand Down Expand Up @@ -64,18 +62,11 @@ package org.swiftsuspenders.dependencyproviders
public function destroy() : void
{
_destroyed = true;
if (!_response)
if (_response && _creatingInjector.hasManagedInstance(_response))
{
return;
}
const typeDescription : TypeDescription =
_creatingInjector.getTypeDescription(_responseType);
for (var preDestroyHook : PreDestroyInjectionPoint = typeDescription.preDestroyMethods;
preDestroyHook; preDestroyHook = PreDestroyInjectionPoint(preDestroyHook.next))
{
preDestroyHook.applyInjection(_response, _responseType, _creatingInjector);
_creatingInjector.destroyInstance(_response);
}
_response = null;
}
}
}
}
4 changes: 2 additions & 2 deletions src/org/swiftsuspenders/dependencyproviders/ValueProvider.as
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ package org.swiftsuspenders.dependencyproviders

public function destroy() : void
{
//TODO: figure out whether to invoke pre destroy methods here. And how.
_value = null;
}
}
}
}
65 changes: 63 additions & 2 deletions test/org/swiftsuspenders/InjectorTests.as
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ package org.swiftsuspenders

import mx.collections.ArrayCollection;

import org.flexunit.asserts.fail;

import org.hamcrest.assertThat;
import org.hamcrest.collection.array;
import org.hamcrest.core.isA;
Expand Down Expand Up @@ -69,6 +71,7 @@ package org.swiftsuspenders
import org.swiftsuspenders.support.types.ComplexClazz;
import org.swiftsuspenders.support.types.Interface;
import org.swiftsuspenders.support.types.Interface2;
import org.swiftsuspenders.support.types.ObjectToDestroy;
import org.swiftsuspenders.typedescriptions.TypeDescription;
import org.swiftsuspenders.utils.SsInternal;

Expand Down Expand Up @@ -898,7 +901,17 @@ package org.swiftsuspenders
}

[Test]
public function destroyInstance_invokes_PreDestroy_methods_on_instance() : void
public function destroyInstance_invokes_PreDestroy_methods_on_managed_instance() : void
{
const target : Clazz = new Clazz();
assertThat(target, hasPropertyWithValue("preDestroyCalled", false));
injector.injectInto(target);
injector.destroyInstance(target);
assertThat(target, hasPropertyWithValue("preDestroyCalled", true));
}

[Test]
public function destroyInstance_invokes_PreDestroy_methods_on_unmanaged_instance() : void
{
const target : Clazz = new Clazz();
assertThat(target, hasPropertyWithValue("preDestroyCalled", false));
Expand All @@ -920,6 +933,40 @@ package org.swiftsuspenders
assertThat(singleton2, hasPropertyWithValue("preDestroyCalled", true));
}

[Test]
public function teardown_does_not_destroy_what_is_already_destroyed() : void
{
injector.map(ObjectToDestroy).asSingleton();
const singleton : ObjectToDestroy = injector.getInstance(ObjectToDestroy);
injector.destroyInstance(singleton);
injector.teardown();
assertThat(singleton, hasPropertyWithValue("destroyCounter", 1));
}

[Test]
public function injectInto_removes_object_from_deleted_objects() : void
{
injector.map(ObjectToDestroy).asSingleton();
const singleton : ObjectToDestroy = injector.getInstance(ObjectToDestroy);
injector.destroyInstance(singleton);
injector.injectInto(singleton);
injector.teardown();
assertThat(singleton, hasPropertyWithValue("destroyCounter", 2));
}

[Test]
public function teardown_clears_provider_mappings():void
{
injector.map(Clazz);
injector.map(Interface);
injector.map(ObjectToDestroy).asSingleton();
injector.teardown();
for each(var o:Object in injector.SsInternal::providerMappings)
{
fail("providerMappings should be empty");
}
}

[Test]
public function teardown_destroys_all_instances_it_injected_into() : void
{
Expand All @@ -934,6 +981,20 @@ package org.swiftsuspenders
assertThat(target2, hasPropertyWithValue("preDestroyCalled", true));
}

[Test]
public function managed_instance_test() : void
{
const target1 : Clazz = new Clazz();
injector.injectInto(target1);
const target2 : Clazz = new Clazz();
injector.injectInto(target2);
Assert.assertTrue(injector.hasManagedInstance(target1));
Assert.assertTrue(injector.hasManagedInstance(target2));
injector.teardown();
Assert.assertFalse(injector.hasManagedInstance(target1));
Assert.assertFalse(injector.hasManagedInstance(target2));
}

[Test]
public function fallbackProvider_is_null_by_default() : void
{
Expand Down Expand Up @@ -1159,4 +1220,4 @@ package org.swiftsuspenders
// Assert.assertEquals("Instance field 'property1' should be identical to Instance field 'namedProperty2'", injectee.property1, injectee.namedProperty2);
// }
}
}
}
11 changes: 11 additions & 0 deletions test/org/swiftsuspenders/support/types/ObjectToDestroy.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.swiftsuspenders.support.types {
public class ObjectToDestroy {

public var destroyCounter:int;

[PreDestroy]
public function onDestroy() : void {
destroyCounter++;
}
}
}

0 comments on commit 16f2ab2

Please sign in to comment.