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