refactor(injector): change reflector to collect the resolving path only when an error occurs
This commit is contained in:
parent
15305b6cd7
commit
f63a5dd158
|
@ -1,23 +1,27 @@
|
||||||
import {ListWrapper, List} from 'facade/collection';
|
import {ListWrapper, List} from 'facade/collection';
|
||||||
import {stringify} from 'facade/lang';
|
import {stringify} from 'facade/lang';
|
||||||
|
import {Key} from './key';
|
||||||
|
|
||||||
function constructResolvingPath(keys: List) {
|
function constructResolvingPath(keys: List) {
|
||||||
if (keys.length > 1) {
|
if (keys.length > 1) {
|
||||||
var tokenStrs = ListWrapper.map(keys, (k) => stringify(k.token));
|
var reversed = ListWrapper.reversed(keys);
|
||||||
|
var tokenStrs = ListWrapper.map(reversed, (k) => stringify(k.token));
|
||||||
return " (" + tokenStrs.join(' -> ') + ")";
|
return " (" + tokenStrs.join(' -> ') + ")";
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NoProviderError extends Error {
|
export class ProviderError extends Error {
|
||||||
constructor(keys:List){
|
constructor(key:Key, constructResolvingMessage:Function){
|
||||||
this.message = this._constructResolvingMessage(keys);
|
this.keys = [key];
|
||||||
|
this.constructResolvingMessage = constructResolvingMessage;
|
||||||
|
this.message = this.constructResolvingMessage(this.keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
_constructResolvingMessage(keys:List) {
|
addKey(key: Key) {
|
||||||
var last = stringify(ListWrapper.last(keys).token);
|
ListWrapper.push(this.keys, key);
|
||||||
return `No provider for ${last}!${constructResolvingPath(keys)}`;
|
this.message = this.constructResolvingMessage(this.keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
|
@ -25,19 +29,22 @@ export class NoProviderError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AsyncProviderError extends Error {
|
export class NoProviderError extends ProviderError {
|
||||||
constructor(keys:List){
|
constructor(key:Key){
|
||||||
this.message = this._constructResolvingMessage(keys);
|
super(key, function(keys:List) {
|
||||||
|
var first = stringify(ListWrapper.first(keys).token);
|
||||||
|
return `No provider for ${first}!${constructResolvingPath(keys)}`;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_constructResolvingMessage(keys:List) {
|
export class AsyncProviderError extends ProviderError {
|
||||||
var last = stringify(ListWrapper.last(keys).token);
|
constructor(key:Key){
|
||||||
return `Cannot instantiate ${last} synchronously. ` +
|
super(key, function(keys:List) {
|
||||||
`It is provided as a future!${constructResolvingPath(keys)}`;
|
var first = stringify(ListWrapper.first(keys).token);
|
||||||
}
|
return `Cannot instantiate ${first} synchronously. ` +
|
||||||
|
`It is provided as a future!${constructResolvingPath(keys)}`;
|
||||||
toString() {
|
});
|
||||||
return this.message;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {Map, List, MapWrapper, ListWrapper} from 'facade/collection';
|
import {Map, List, MapWrapper, ListWrapper} from 'facade/collection';
|
||||||
import {Binding, BindingBuilder, bind} from './binding';
|
import {Binding, BindingBuilder, bind} from './binding';
|
||||||
import {NoProviderError, InvalidBindingError, AsyncProviderError} from './exceptions';
|
import {ProviderError, NoProviderError, InvalidBindingError, AsyncProviderError} from './exceptions';
|
||||||
import {Type, isPresent, isBlank} from 'facade/lang';
|
import {Type, isPresent, isBlank} from 'facade/lang';
|
||||||
import {Future, FutureWrapper} from 'facade/async';
|
import {Future, FutureWrapper} from 'facade/async';
|
||||||
import {Key} from './key';
|
import {Key} from './key';
|
||||||
|
@ -32,19 +32,15 @@ export class Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
getByKey(key:Key) {
|
getByKey(key:Key) {
|
||||||
return this._getByKey(key, [], false);
|
return this._getByKey(key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncGetByKey(key:Key) {
|
asyncGetByKey(key:Key) {
|
||||||
return this._getByKey(key, [], true);
|
return this._getByKey(key, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getByKey(key:Key, resolving:List, async) {
|
_getByKey(key:Key, async) {
|
||||||
var keyId = key.id;
|
var keyId = key.id;
|
||||||
//TODO: vsavkin: use LinkedList to remove clone
|
|
||||||
resolving = ListWrapper.clone(resolving)
|
|
||||||
ListWrapper.push(resolving, key);
|
|
||||||
|
|
||||||
if (key.token === Injector) return this._injector(async);
|
if (key.token === Injector) return this._injector(async);
|
||||||
|
|
||||||
var instance = this._get(this._instances, keyId);
|
var instance = this._get(this._instances, keyId);
|
||||||
|
@ -53,14 +49,14 @@ export class Injector {
|
||||||
var binding = this._get(this._bindings, keyId);
|
var binding = this._get(this._bindings, keyId);
|
||||||
|
|
||||||
if (isPresent(binding)) {
|
if (isPresent(binding)) {
|
||||||
return this._instantiate(key, binding, resolving, async);
|
return this._instantiate(key, binding, async);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresent(this._parent)) {
|
if (isPresent(this._parent)) {
|
||||||
return this._parent._getByKey(key, resolving, async);
|
return this._parent._getByKey(key, async);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NoProviderError(resolving);
|
throw new NoProviderError(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
createChild(bindings:List):Injector {
|
createChild(bindings:List):Injector {
|
||||||
|
@ -78,31 +74,37 @@ export class Injector {
|
||||||
return ListWrapper.get(list, index);
|
return ListWrapper.get(list, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
_instantiate(key:Key, binding:Binding, resolving:List, async) {
|
_instantiate(key:Key, binding:Binding, async) {
|
||||||
if (binding.async && !async) {
|
if (binding.async && !async) {
|
||||||
throw new AsyncProviderError(resolving);
|
throw new AsyncProviderError(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (async) {
|
if (async) {
|
||||||
return this._instantiateAsync(key, binding, resolving, async);
|
return this._instantiateAsync(key, binding, async);
|
||||||
} else {
|
} else {
|
||||||
return this._instantiateSync(key, binding, resolving, async);
|
return this._instantiateSync(key, binding, async);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_instantiateSync(key:Key, binding:Binding, resolving:List, async) {
|
_instantiateSync(key:Key, binding:Binding, async) {
|
||||||
var deps = ListWrapper.map(binding.dependencies, d => this._getByKey(d, resolving, false));
|
try {
|
||||||
var instance = binding.factory(deps);
|
var deps = ListWrapper.map(binding.dependencies, d => this._getByKey(d, false));
|
||||||
ListWrapper.set(this._instances, key.id, instance);
|
var instance = binding.factory(deps);
|
||||||
if (!binding.async && async) {
|
ListWrapper.set(this._instances, key.id, instance);
|
||||||
return FutureWrapper.value(instance);
|
if (!binding.async && async) {
|
||||||
|
return FutureWrapper.value(instance);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ProviderError) e.addKey(key);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
return instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_instantiateAsync(key:Key, binding:Binding, resolving:List, async):Future {
|
_instantiateAsync(key:Key, binding:Binding, async):Future {
|
||||||
var instances = this._createInstances();
|
var instances = this._createInstances();
|
||||||
var futures = ListWrapper.map(binding.dependencies, d => this._getByKey(d, resolving, true));
|
var futures = ListWrapper.map(binding.dependencies, d => this._getByKey(d, true));
|
||||||
return FutureWrapper.wait(futures).
|
return FutureWrapper.wait(futures).
|
||||||
then(binding.factory).
|
then(binding.factory).
|
||||||
then(function(instance) {
|
then(function(instance) {
|
||||||
|
|
|
@ -72,5 +72,7 @@ export function main () {
|
||||||
expect(() => injector.get(UserController))
|
expect(() => injector.get(UserController))
|
||||||
.toThrowError('Cannot instantiate UserList synchronously. It is provided as a future! (UserController -> UserList)');
|
.toThrowError('Cannot instantiate UserList synchronously. It is provided as a future! (UserController -> UserList)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// resolve exceptions and async
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -2,7 +2,10 @@ import {describe, it, expect, beforeEach} from 'test_lib/test_lib';
|
||||||
import {Injector, Inject, bind} from 'di/di';
|
import {Injector, Inject, bind} from 'di/di';
|
||||||
|
|
||||||
class Engine {}
|
class Engine {}
|
||||||
class Dashboard {}
|
class DashboardSoftware {}
|
||||||
|
class Dashboard {
|
||||||
|
constructor(software: DashboardSoftware){}
|
||||||
|
}
|
||||||
class TurboEngine extends Engine{}
|
class TurboEngine extends Engine{}
|
||||||
|
|
||||||
class Car {
|
class Car {
|
||||||
|
@ -135,10 +138,9 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show the full path when no provider', function() {
|
it('should show the full path when no provider', function() {
|
||||||
var injector = new Injector([CarWithDashboard, Engine]);
|
var injector = new Injector([CarWithDashboard, Engine, Dashboard]);
|
||||||
|
|
||||||
expect(() => injector.get(CarWithDashboard)).
|
expect(() => injector.get(CarWithDashboard)).
|
||||||
toThrowError('No provider for Dashboard! (CarWithDashboard -> Dashboard)');
|
toThrowError('No provider for DashboardSoftware! (CarWithDashboard -> Dashboard -> DashboardSoftware)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,15 @@ class ListWrapper {
|
||||||
static forEach(list, fn) {
|
static forEach(list, fn) {
|
||||||
list.forEach(fn);
|
list.forEach(fn);
|
||||||
}
|
}
|
||||||
static last(list) {
|
static first(List list) {
|
||||||
|
return list.first;
|
||||||
|
}
|
||||||
|
static last(List list) {
|
||||||
return list.last;
|
return list.last;
|
||||||
}
|
}
|
||||||
|
static reversed(List list) {
|
||||||
|
return list.reversed;
|
||||||
|
}
|
||||||
static void push(List l, e) { l.add(e); }
|
static void push(List l, e) { l.add(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,18 @@ export class ListWrapper {
|
||||||
static push(array, el) {
|
static push(array, el) {
|
||||||
array.push(el);
|
array.push(el);
|
||||||
}
|
}
|
||||||
|
static first(array) {
|
||||||
|
if (!array) return null;
|
||||||
|
return array[0];
|
||||||
|
}
|
||||||
static last(array) {
|
static last(array) {
|
||||||
if (!array || array.length == 0) return null;
|
if (!array || array.length == 0) return null;
|
||||||
return array[array.length - 1];
|
return array[array.length - 1];
|
||||||
}
|
}
|
||||||
|
static reversed(array) {
|
||||||
|
var a = ListWrapper.clone(array);
|
||||||
|
return a.reverse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SetWrapper {
|
export class SetWrapper {
|
||||||
|
|
Loading…
Reference in New Issue