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…
Reference in New Issue