fix(di): refactor bindings to support Dart annotations

This commit is contained in:
Yegor Jbanov
2015-04-09 23:17:05 -07:00
parent ff6e7754ae
commit 6c8398df9b
7 changed files with 207 additions and 78 deletions

View File

@ -1,9 +1,9 @@
import {Type, isBlank, isPresent} from 'angular2/src/facade/lang';
import {Type, isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {reflector} from 'angular2/src/reflection/reflection';
import {Key} from './key';
import {Inject, InjectLazy, InjectPromise, Optional, DependencyAnnotation} from './annotations';
import {NoAnnotationError} from './exceptions';
import {NoAnnotationError, InvalidBindingError} from './exceptions';
export class Dependency {
key:Key;
@ -11,6 +11,7 @@ export class Dependency {
lazy:boolean;
optional:boolean;
properties:List;
constructor(key:Key, asPromise:boolean, lazy:boolean, optional:boolean, properties:List) {
this.key = key;
this.asPromise = asPromise;
@ -24,13 +25,100 @@ export class Dependency {
}
}
var _EMPTY_LIST = []; // TODO: make const when supported
/**
* Declaration of a dependency binding.
*/
export class Binding {
token;
toClass:Type;
toValue;
toAlias;
toFactory:Function;
toAsyncFactory:Function;
dependencies:List;
@CONST()
constructor(
token,
{
toClass,
toValue,
toAlias,
toFactory,
toAsyncFactory,
deps
}) {
this.token = token;
this.toClass = toClass;
this.toValue = toValue;
this.toAlias = toAlias;
this.toFactory = toFactory;
this.toAsyncFactory = toAsyncFactory;
this.dependencies = deps;
}
resolve(): ResolvedBinding {
var factoryFn:Function;
var resolvedDeps;
var isAsync = false;
if (isPresent(this.toClass)) {
factoryFn = reflector.factory(this.toClass);
resolvedDeps = _dependenciesFor(this.toClass);
} else if (isPresent(this.toAlias)) {
factoryFn = (aliasInstance) => aliasInstance;
resolvedDeps = [Dependency.fromKey(Key.get(this.toAlias))];
} else if (isPresent(this.toFactory)) {
factoryFn = this.toFactory;
resolvedDeps = _constructDependencies(this.toFactory, this.dependencies);
} else if (isPresent(this.toAsyncFactory)) {
factoryFn = this.toAsyncFactory;
resolvedDeps = _constructDependencies(this.toAsyncFactory, this.dependencies);
isAsync = true;
} else {
factoryFn = () => this.toValue;
resolvedDeps = _EMPTY_LIST;
}
return new ResolvedBinding(
Key.get(this.token),
factoryFn,
resolvedDeps,
isAsync
);
}
static resolveAll(bindings:List): List {
var resolvedList = ListWrapper.createFixedSize(bindings.length);
for (var i = 0; i < bindings.length; i++) {
var unresolved = bindings[i];
var resolved;
if (unresolved instanceof Type) {
resolved = bind(unresolved).toClass(unresolved).resolve();
} else if (unresolved instanceof Binding) {
resolved = unresolved.resolve();
} else if (unresolved instanceof List) {
resolved = Binding.resolveAll(unresolved);
} else if (unresolved instanceof BindingBuilder) {
throw new InvalidBindingError(unresolved.token);
} else {
throw new InvalidBindingError(unresolved);
}
resolvedList[i] = resolved;
}
return resolvedList;
}
}
/// Dependency binding with resolved keys and dependencies.
export class ResolvedBinding {
key:Key;
factory:Function;
dependencies:List;
dependencies:List<Dependency>;
providedAsPromise:boolean;
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean) {
constructor(key:Key, factory:Function, dependencies:List<Dependency>, providedAsPromise:boolean) {
this.key = key;
this.factory = factory;
this.dependencies = dependencies;
@ -38,66 +126,54 @@ export class Binding {
}
}
/**
* Provides fluent API for imperative construction of [Binding] objects.
*/
export function bind(token):BindingBuilder {
return new BindingBuilder(token);
}
/**
* Helper class for [bind] function.
*/
export class BindingBuilder {
token;
constructor(token) {
this.token = token;
}
toClass(type:Type):Binding {
return new Binding(
Key.get(this.token),
reflector.factory(type),
_dependenciesFor(type),
false
);
return new Binding(this.token, {toClass: type});
}
toValue(value):Binding {
return new Binding(
Key.get(this.token),
() => value,
[],
false
);
return new Binding(this.token, {toValue: value});
}
toAlias(aliasToken):Binding {
return new Binding(
Key.get(this.token),
(aliasInstance) => aliasInstance,
[Dependency.fromKey(Key.get(aliasToken))],
false
);
return new Binding(this.token, {toAlias: aliasToken});
}
toFactory(factoryFunction:Function, dependencies:List = null):Binding {
return new Binding(
Key.get(this.token),
factoryFunction,
this._constructDependencies(factoryFunction, dependencies),
false
);
return new Binding(this.token, {
toFactory: factoryFunction,
deps: dependencies
});
}
toAsyncFactory(factoryFunction:Function, dependencies:List = null):Binding {
return new Binding(
Key.get(this.token),
factoryFunction,
this._constructDependencies(factoryFunction, dependencies),
true
);
return new Binding(this.token, {
toAsyncFactory: factoryFunction,
deps: dependencies
});
}
}
_constructDependencies(factoryFunction:Function, dependencies:List) {
return isBlank(dependencies) ?
_dependenciesFor(factoryFunction) :
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
}
function _constructDependencies(factoryFunction:Function, dependencies:List) {
return isBlank(dependencies) ?
_dependenciesFor(factoryFunction) :
ListWrapper.map(dependencies, (t) => Dependency.fromKey(Key.get(t)));
}
function _dependenciesFor(typeOrFunc):List {