refactor(injector): change toFactory to use reflector to construct dependencies
This commit is contained in:
		
							parent
							
								
									06a221671c
								
							
						
					
					
						commit
						d313cac42f
					
				| @ -1,4 +1,4 @@ | ||||
| import {FIELD, Type, bool} from 'facade/lang'; | ||||
| import {FIELD, Type, bool, isBlank} from 'facade/lang'; | ||||
| import {List, MapWrapper, ListWrapper} from 'facade/collection'; | ||||
| import {reflector} from './reflector'; | ||||
| import {Key} from './key'; | ||||
| @ -13,6 +13,7 @@ export class Dependency { | ||||
|     this.lazy = lazy; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export class Binding { | ||||
|   constructor(key:Key, factory:Function, dependencies:List, providedAsFuture:bool) { | ||||
|     this.key = key; | ||||
| @ -49,25 +50,27 @@ export class BindingBuilder { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   toFactory(dependencies:List, factoryFunction:Function):Binding { | ||||
|   toFactory(factoryFunction:Function, {dependencies=null}={}):Binding { | ||||
|     return new Binding( | ||||
|       Key.get(this.token), | ||||
|       reflector.convertToFactory(factoryFunction), | ||||
|       this._constructDependencies(dependencies), | ||||
|       this._constructDependencies(factoryFunction, dependencies), | ||||
|       false | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   toAsyncFactory(dependencies:List, factoryFunction:Function):Binding { | ||||
|   toAsyncFactory(factoryFunction:Function, {dependencies=null}={}):Binding { | ||||
|     return new Binding( | ||||
|       Key.get(this.token), | ||||
|       reflector.convertToFactory(factoryFunction), | ||||
|       this._constructDependencies(dependencies), | ||||
|       this._constructDependencies(factoryFunction, dependencies), | ||||
|       true | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   _constructDependencies(deps:List) { | ||||
|     return ListWrapper.map(deps, (t) => new Dependency(Key.get(t), false, false)); | ||||
|   _constructDependencies(factoryFunction:Function, dependencies:List) { | ||||
|     return isBlank(dependencies) ? | ||||
|       reflector.dependencies(factoryFunction) : | ||||
|       ListWrapper.map(dependencies, (t) => new Dependency(Key.get(t), false, false)); | ||||
|   } | ||||
| } | ||||
| @ -77,8 +77,8 @@ export class InvalidBindingError extends Error { | ||||
| } | ||||
| 
 | ||||
| export class NoAnnotationError extends Error { | ||||
|   constructor(type) { | ||||
|     this.message = `Cannot resolve all parameters for ${stringify(type)}`; | ||||
|   constructor(typeOrFunc) { | ||||
|     this.message = `Cannot resolve all parameters for ${stringify(typeOrFunc)}`; | ||||
|   } | ||||
| 
 | ||||
|   toString() { | ||||
|  | ||||
| @ -23,12 +23,13 @@ class Reflector { | ||||
|     return (args) => create(name, args).reflectee; | ||||
|   } | ||||
| 
 | ||||
|   List<Dependency> dependencies(Type type) { | ||||
|     ClassMirror classMirror = reflectType(type); | ||||
|     MethodMirror ctor = classMirror.declarations[classMirror.simpleName]; | ||||
|   List<Dependency> dependencies(typeOrFunc) { | ||||
|     final parameters = typeOrFunc is Type ? | ||||
|         _constructorParameters(typeOrFunc) : | ||||
|         _functionParameters(typeOrFunc); | ||||
| 
 | ||||
|     return new List.generate(ctor.parameters.length, (int pos) { | ||||
|       ParameterMirror p = ctor.parameters[pos]; | ||||
|     return new List.generate(parameters.length, (int pos) { | ||||
|       ParameterMirror p = parameters[pos]; | ||||
| 
 | ||||
|       final metadata = p.metadata.map((m) => m.reflectee); | ||||
| 
 | ||||
| @ -49,10 +50,20 @@ class Reflector { | ||||
|         return new Dependency(Key.get(p.type.reflectedType), false, false); | ||||
| 
 | ||||
|       } else { | ||||
|         throw new NoAnnotationError(type); | ||||
|         throw new NoAnnotationError(typeOrFunc); | ||||
|       } | ||||
|     }, growable:false); | ||||
|   } | ||||
| 
 | ||||
|   List<ParameterMirror> _functionParameters(Function func) { | ||||
|     return reflect(func).function.parameters; | ||||
|   } | ||||
| 
 | ||||
|   List<ParameterMirror> _constructorParameters(Type type) { | ||||
|     ClassMirror classMirror = reflectType(type); | ||||
|     MethodMirror ctor = classMirror.declarations[classMirror.simpleName]; | ||||
|     return ctor.parameters; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| final Reflector reflector = new Reflector(); | ||||
|  | ||||
| @ -14,14 +14,14 @@ class Reflector { | ||||
|     return (args) => factoryFunction(...args); | ||||
|   } | ||||
| 
 | ||||
|   dependencies(type:Type):List { | ||||
|     var p = type.parameters; | ||||
|     if (p == undefined && type.length == 0) return []; | ||||
|     if (p == undefined) throw new NoAnnotationError(type); | ||||
|     return type.parameters.map((p) => this._extractToken(type, p)); | ||||
|   dependencies(typeOrFunc):List { | ||||
|     var p = typeOrFunc.parameters; | ||||
|     if (p == undefined && typeOrFunc.length == 0) return []; | ||||
|     if (p == undefined) throw new NoAnnotationError(typeOrFunc); | ||||
|     return typeOrFunc.parameters.map((p) => this._extractToken(typeOrFunc, p)); | ||||
|   } | ||||
| 
 | ||||
|   _extractToken(constructedType:Type, annotations) { | ||||
|   _extractToken(typeOrFunc, annotations) { | ||||
|     var type; | ||||
| 
 | ||||
|     for (var paramAnnotation of annotations) { | ||||
| @ -42,7 +42,7 @@ class Reflector { | ||||
|     if (isPresent(type)) { | ||||
|       return this._createDependency(type, false, false); | ||||
|     } else { | ||||
|       throw new NoAnnotationError(constructedType); | ||||
|       throw new NoAnnotationError(typeOrFunc); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -30,7 +30,7 @@ export function main() { | ||||
|     describe("asyncGet", function () { | ||||
|       it('should return a future', function () { | ||||
|         var injector = new Injector([ | ||||
|           bind(UserList).toAsyncFactory([], fetchUsers) | ||||
|           bind(UserList).toAsyncFactory(fetchUsers) | ||||
|         ]); | ||||
|         var p = injector.asyncGet(UserList); | ||||
|         expect(p).toBeFuture(); | ||||
| @ -64,7 +64,7 @@ export function main() { | ||||
|       it('should return a future when instantiating a sync binding ' + | ||||
|       'with an async dependency', function (done) { | ||||
|         var injector = new Injector([ | ||||
|           bind(UserList).toAsyncFactory([], fetchUsers), | ||||
|           bind(UserList).toAsyncFactory(fetchUsers), | ||||
|           UserController | ||||
|         ]); | ||||
| 
 | ||||
| @ -77,7 +77,7 @@ export function main() { | ||||
| 
 | ||||
|       it("should create only one instance (async + async)", function (done) { | ||||
|         var injector = new Injector([ | ||||
|           bind(UserList).toAsyncFactory([], fetchUsers) | ||||
|           bind(UserList).toAsyncFactory(fetchUsers) | ||||
|         ]); | ||||
| 
 | ||||
|         var ul1 = injector.asyncGet(UserList); | ||||
| @ -109,7 +109,7 @@ export function main() { | ||||
|       it('should show the full path when error happens in a constructor', function (done) { | ||||
|         var injector = new Injector([ | ||||
|           UserController, | ||||
|           bind(UserList).toAsyncFactory([], function () { | ||||
|           bind(UserList).toAsyncFactory(function () { | ||||
|             throw "Broken UserList"; | ||||
|           }) | ||||
|         ]); | ||||
| @ -125,7 +125,7 @@ export function main() { | ||||
|     describe("get", function () { | ||||
|       it('should throw when instantiating an async binding', function () { | ||||
|         var injector = new Injector([ | ||||
|           bind(UserList).toAsyncFactory([], fetchUsers) | ||||
|           bind(UserList).toAsyncFactory(fetchUsers) | ||||
|         ]); | ||||
| 
 | ||||
|         expect(() => injector.get(UserList)) | ||||
| @ -134,7 +134,7 @@ export function main() { | ||||
| 
 | ||||
|       it('should throw when instantiating a sync binding with an dependency', function () { | ||||
|         var injector = new Injector([ | ||||
|           bind(UserList).toAsyncFactory([], fetchUsers), | ||||
|           bind(UserList).toAsyncFactory(fetchUsers), | ||||
|           UserController | ||||
|         ]); | ||||
| 
 | ||||
| @ -144,7 +144,7 @@ export function main() { | ||||
| 
 | ||||
|       it('should resolve synchronously when an async dependency requested as a future', function () { | ||||
|         var injector = new Injector([ | ||||
|           bind(UserList).toAsyncFactory([], fetchUsers), | ||||
|           bind(UserList).toAsyncFactory(fetchUsers), | ||||
|           AsyncUserController | ||||
|         ]); | ||||
|         var controller = injector.get(AsyncUserController); | ||||
| @ -155,7 +155,7 @@ export function main() { | ||||
| 
 | ||||
|       it('should wrap sync dependencies into futures if required', function () { | ||||
|         var injector = new Injector([ | ||||
|           bind(UserList).toFactory([], () => new UserList()), | ||||
|           bind(UserList).toFactory(() => new UserList()), | ||||
|           AsyncUserController | ||||
|         ]); | ||||
|         var controller = injector.get(AsyncUserController); | ||||
|  | ||||
| @ -108,9 +108,24 @@ export function main() { | ||||
|     }); | ||||
| 
 | ||||
|     it('should bind to a factory', function () { | ||||
|       function sportsCarFactory(e:Engine) { | ||||
|         return new SportsCar(e); | ||||
|       } | ||||
| 
 | ||||
|       var injector = new Injector([ | ||||
|         Engine, | ||||
|         bind(Car).toFactory([Engine], (e) => new SportsCar(e)) | ||||
|         bind(Car).toFactory(sportsCarFactory) | ||||
|       ]); | ||||
| 
 | ||||
|       var car = injector.get(Car); | ||||
|       expect(car).toBeAnInstanceOf(SportsCar); | ||||
|       expect(car.engine).toBeAnInstanceOf(Engine); | ||||
|     }); | ||||
| 
 | ||||
|     it('should support overriding factory dependencies', function () { | ||||
|       var injector = new Injector([ | ||||
|         Engine, | ||||
|         bind(Car).toFactory((e) => new SportsCar(e), {dependencies: [Engine]}) | ||||
|       ]); | ||||
| 
 | ||||
|       var car = injector.get(Car); | ||||
| @ -181,7 +196,7 @@ export function main() { | ||||
| 
 | ||||
|       var injector = new Injector([ | ||||
|         Car, | ||||
|         bind(Engine).toFactory([], () => isBroken ? new BrokenEngine() : new Engine()) | ||||
|         bind(Engine).toFactory(() => isBroken ? new BrokenEngine() : new Engine()) | ||||
|       ]); | ||||
| 
 | ||||
|       expect(() => injector.get(Car)).toThrow(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user