Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jeremy Saenz
committed
Mar 15, 2011
1 parent
e74ef65
commit a881935
Showing
8 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package | ||
{ | ||
public class AnotherExampleClass | ||
{ | ||
public var name:String = "AnotherClassInstance"; | ||
|
||
public function AnotherExampleClass() | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package | ||
{ | ||
public class ExampleClass | ||
{ | ||
public var name:String = "ExampleClassInstance"; | ||
|
||
public function ExampleClass() | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package | ||
{ | ||
import com.zaalabs.imbue.IInjector; | ||
|
||
import com.zaalabs.imbue.Injector; | ||
|
||
import flash.display.Sprite; | ||
|
||
public class ImbueExample extends Sprite | ||
{ | ||
|
||
[Inject] // this will inject an instance of ExampleClass into this property | ||
public var myFirstInjectedInstance:ExampleClass; | ||
|
||
[Inject] // this will inject an instance of AnotherExampleClass into this property | ||
public var mySecondInjectedInstance:AnotherExampleClass; | ||
|
||
public function ImbueExample() | ||
{ | ||
// create an instance of the classes we want to inject | ||
var myLocalMember:ExampleClass = new ExampleClass(); | ||
var anotherLocalMember:AnotherExampleClass = new AnotherExampleClass(); | ||
|
||
// This is actually all it takes to inject something | ||
var injector:IInjector = new Injector(); | ||
injector.mapValue(myLocalMember, ExampleClass); | ||
injector.mapValue(anotherLocalMember, AnotherExampleClass); | ||
// apply the injections! | ||
injector.apply(this); | ||
} | ||
|
||
[PostInject] | ||
// This gets called after an injection has occurred on the target | ||
public function onPostInject():void | ||
{ | ||
trace("We are all done injecting, now we can use our instance!"); | ||
trace("Name of first injected instance:",myFirstInjectedInstance.name); | ||
trace("Name of second injected instance:",mySecondInjectedInstance.name) | ||
} | ||
|
||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.zaalabs.imbue | ||
{ | ||
/** | ||
* IInjector is an interface that specifies a simple Dependency | ||
* Injection API. | ||
* | ||
* Features in this simple DI framework include metadata based | ||
* injections. | ||
* | ||
*/ | ||
public interface IInjector | ||
{ | ||
/** | ||
* Maps a value to a certain class, so when <code>apply()</code> | ||
* is called, the value will be injected into any property with the | ||
* [Inject] tag over it and that matches the class definition specified | ||
* in the <code>toClass</code> parameter. | ||
* | ||
* @param value The dependency that will be injected | ||
* @param toClass The class that the dependency will be injected into | ||
*/ | ||
function mapValue(value:Object, toClass:Class):void; | ||
|
||
/** | ||
* Applies the injection to the specified object. If you wish to have a | ||
* property injected into the specified object, make sure that an [Inject] | ||
* metadata tag is added to the desired target property and the value is | ||
* mapped in this injector using the <code>mapValue()</code> method. | ||
* | ||
* @param value the object that will be injected | ||
*/ | ||
function apply(value:Object):void; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.zaalabs.imbue | ||
{ | ||
public class InjectionTarget | ||
{ | ||
public var property:String; | ||
public var definition:Class; | ||
public var uri:String; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
package com.zaalabs.imbue | ||
{ | ||
import flash.utils.Dictionary; | ||
import flash.utils.describeType; | ||
import flash.utils.getDefinitionByName; | ||
|
||
/** | ||
* The Default implementation for the IInjector interface. It provides | ||
* some basic functionality for getting an dependency injection system | ||
* going. Nothing fancy or magical going on here, Pretty straightforward | ||
*/ | ||
public class Injector implements IInjector | ||
{ | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function mapValue(value:Object, toClass:Class):void | ||
{ | ||
mappedValues[toClass] = value; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public function apply(value:Object):void | ||
{ | ||
var definition:Class = getConstructor(value); | ||
var targets:Vector.<InjectionTarget> = getInjectionTargets(definition) as Vector.<InjectionTarget>; | ||
var postConstruct:Vector.<PostInjectionTarget> = getPostConstructTargets(definition) as Vector.<PostInjectionTarget>; | ||
|
||
// apply injections to each target | ||
for each(var target:InjectionTarget in targets) { | ||
applyToTarget(target,value); | ||
} | ||
|
||
if(targets.length) | ||
{ | ||
// apply post construct | ||
for each(var pcTarget:PostInjectionTarget in postConstruct) | ||
{ | ||
var f:Function = value[pcTarget.method]; | ||
if(f != null) f(); | ||
} | ||
} | ||
} | ||
|
||
//_____________________________________________________________________ | ||
// Constructor | ||
//_____________________________________________________________________ | ||
public function Injector() | ||
{ | ||
// Initialize the member properties | ||
mappedValues = new Dictionary(); | ||
} | ||
|
||
//_____________________________________________________________________ | ||
// Protected Properties | ||
//_____________________________________________________________________ | ||
/** | ||
* @private | ||
*/ | ||
protected var mappedValues:Dictionary; | ||
|
||
/** | ||
* @private | ||
*/ | ||
protected static var definitionCache:Dictionary = new Dictionary(); | ||
|
||
/** | ||
* @private | ||
*/ | ||
protected static var injectionTargetCache:Dictionary = new Dictionary(); | ||
|
||
/** | ||
* @private | ||
*/ | ||
protected static var postConstructTargetCache:Dictionary = new Dictionary(); | ||
|
||
//_____________________________________________________________________ | ||
// Protected methods | ||
//_____________________________________________________________________ | ||
/** | ||
* Returns InjectionTargets for the specified class, from either a cached | ||
* instance or calculated on demand. This function also stores caches for | ||
* DescribeType calls. | ||
* | ||
* Structuring calls this way makes the Injectors api more extendable by | ||
* exposing our caching mechanisms and abstracting it. | ||
* | ||
* @param value | ||
* @return | ||
*/ | ||
public static function getInjectionTargets(value:Class):Vector.<InjectionTarget> | ||
{ | ||
// Return it is we have it in the cache | ||
if (injectionTargetCache[value]) | ||
{ | ||
return injectionTargetCache[value] as Vector.<InjectionTarget>; | ||
} | ||
|
||
// Look over the xml type definition and create injector targets | ||
var result:Vector.<InjectionTarget> = new Vector.<InjectionTarget>(); | ||
|
||
// do a describe type if it is not in the cache | ||
if (!definitionCache[value]) | ||
definitionCache[value] = describeType(value); | ||
|
||
var definition:XML = definitionCache[value]; | ||
var target:InjectionTarget; | ||
|
||
for each (var node:XML in definition.factory.*.(name() == 'variable' || name() == 'accessor').metadata.(@name == 'Inject')) | ||
{ | ||
target = new InjectionTarget(); | ||
target.property = node.parent().@name; | ||
target.definition = getDefinitionByName(node.parent().@type) as Class; | ||
target.uri = node.parent().@uri; | ||
result.push(target); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
public static function getPostConstructTargets(value:Class):Vector.<PostInjectionTarget> | ||
{ | ||
// try to find it in the cache | ||
if(postConstructTargetCache[value]) | ||
{ | ||
return postConstructTargetCache[value] as Vector.<PostInjectionTarget>; | ||
} | ||
|
||
// Look over the xml type definition and create injector targets | ||
var result:Vector.<PostInjectionTarget> = new Vector.<PostInjectionTarget>(); | ||
|
||
// do a describe type if it is not in the cache | ||
if (!definitionCache[value]) | ||
definitionCache[value] = describeType(value); | ||
|
||
var definition:XML = definitionCache[value]; | ||
var target:PostInjectionTarget; | ||
|
||
for each (var node:XML in definition.factory.*.(name() == 'method').metadata.(@name == 'PostInject')) { | ||
target = new PostInjectionTarget(); | ||
target.method = node.parent().@name; | ||
result.push(target); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
protected function applyToTarget(target:InjectionTarget,value:Object):void | ||
{ | ||
var injectedValue:Object = mappedValues[target.definition]; | ||
|
||
if(!injectedValue) return; | ||
|
||
if(target.uri.length == 0) | ||
{ | ||
// Inject the regular way | ||
value[target.property] = injectedValue; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.zaalabs.imbue | ||
{ | ||
public class PostInjectionTarget | ||
{ | ||
public var method:String; | ||
|
||
public function PostInjectionTarget() | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.zaalabs.imbue | ||
{ | ||
import flash.utils.Proxy; | ||
import flash.utils.getDefinitionByName; | ||
import flash.utils.getQualifiedClassName; | ||
|
||
/** | ||
* This is a clever discovery in the SwiftSuspenders library, so I copied it :P | ||
* | ||
* Basically, some classes throw exeptions and such when we try to get the | ||
* constructor, so canstructor getting is then abstracted, and a simple is | ||
* check is used to see if we can get the costructor or use the more costly | ||
* <code>getQualifiedClassName()</code> method to get the class definition | ||
* | ||
* @param value | ||
* @return | ||
*/ | ||
public function getConstructor(value:Object):Class | ||
{ | ||
if (value is Proxy || value is Number || value is XML || value is XMLList) | ||
{ | ||
var qname:String = getQualifiedClassName(value); | ||
return Class(getDefinitionByName(qname)); | ||
} | ||
|
||
return value.constructor; | ||
} | ||
} |