refactor(di): added support for custom dep providers

This commit is contained in:
vsavkin 2015-07-06 10:38:12 -07:00
parent e987ac4034
commit 569766fa8b
7 changed files with 94 additions and 45 deletions

View File

@ -56,7 +56,8 @@ export {
Self, Self,
Parent, Parent,
Ancestor, Ancestor,
Unbounded Unbounded,
DependencyProvider
} from './di'; } from './di';
export * from './core'; export * from './core';

View File

@ -33,6 +33,7 @@ export {
resolveBindings, resolveBindings,
Injector, Injector,
ProtoInjector, ProtoInjector,
DependencyProvider,
PUBLIC_AND_PRIVATE, PUBLIC_AND_PRIVATE,
PUBLIC, PUBLIC,
PRIVATE, PRIVATE,

View File

@ -28,6 +28,7 @@ import {
resolveBindings, resolveBindings,
Visibility, Visibility,
VisibilityAnnotation, VisibilityAnnotation,
DependencyProvider,
self self
} from 'angular2/di'; } from 'angular2/di';
import { import {
@ -429,7 +430,7 @@ export class ProtoElementInjector {
} }
export class ElementInjector extends TreeNode<ElementInjector> { export class ElementInjector extends TreeNode<ElementInjector> implements DependencyProvider {
private _host: ElementInjector; private _host: ElementInjector;
private _preBuiltObjects = null; private _preBuiltObjects = null;
@ -447,9 +448,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
constructor(public _proto: ProtoElementInjector, parent: ElementInjector) { constructor(public _proto: ProtoElementInjector, parent: ElementInjector) {
super(parent); super(parent);
this._injector = new Injector(this._proto.protoInjector); this._injector = new Injector(this._proto.protoInjector, null, this);
this._injector.ei = this; // TODO savkin remove after mergin DI and EI
// we couple ourselves to the injector strategy to avoid polymoprhic calls // we couple ourselves to the injector strategy to avoid polymoprhic calls
var injectorStrategy = <any>this._injector.internalStrategy; var injectorStrategy = <any>this._injector.internalStrategy;
this._strategy = injectorStrategy instanceof InjectorInlineStrategy ? this._strategy = injectorStrategy instanceof InjectorInlineStrategy ?
@ -588,7 +587,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); } isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); }
getDependency(dep: any): any { getDependency(injector: Injector, binding: ResolvedBinding, dep: Dependency): any {
var key: Key = dep.key; var key: Key = dep.key;
if (!(dep instanceof DirectiveDependency)) return undefinedValue; if (!(dep instanceof DirectiveDependency)) return undefinedValue;

View File

@ -402,6 +402,13 @@ export class BindingWithVisibility {
getKeyId(): number { return this.binding.key.id; } getKeyId(): number { return this.binding.key.id; }
} }
/**
* Used to provide dependencies that cannot be easily expressed as bindings.
*/
export interface DependencyProvider {
getDependency(injector: Injector, binding: ResolvedBinding, dependency: Dependency): any;
}
/** /**
* A dependency injection container used for resolving dependencies. * A dependency injection container used for resolving dependencies.
* *
@ -476,10 +483,12 @@ export class Injector {
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
*recursive list of more *recursive list of more
* bindings. * bindings.
* @param `depProvider`
*/ */
static resolveAndCreate(bindings: List<Type | Binding | List<any>>): Injector { static resolveAndCreate(bindings: List<Type | Binding | List<any>>,
depProvider: DependencyProvider = null): Injector {
var resolvedBindings = Injector.resolve(bindings); var resolvedBindings = Injector.resolve(bindings);
return Injector.fromResolvedBindings(resolvedBindings); return Injector.fromResolvedBindings(resolvedBindings, depProvider);
} }
/** /**
@ -488,11 +497,13 @@ export class Injector {
* *
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the * @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the
* {@link Injector}. * {@link Injector}.
* @param `depProvider`
*/ */
static fromResolvedBindings(bindings: List<ResolvedBinding>): Injector { static fromResolvedBindings(bindings: List<ResolvedBinding>,
depProvider: DependencyProvider = null): Injector {
var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC)); var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC));
var proto = new ProtoInjector(bd, 0); var proto = new ProtoInjector(bd, 0);
var inj = new Injector(proto); var inj = new Injector(proto, null, depProvider);
return inj; return inj;
} }
@ -500,10 +511,8 @@ export class Injector {
_isBoundary: boolean = false; _isBoundary: boolean = false;
_constructionCounter: number = 0; _constructionCounter: number = 0;
// TODO vsavkin remove it after DI and EI are merged constructor(public _proto: ProtoInjector, public _parent: Injector = null,
ei: any; private _depProvider: DependencyProvider = null) {
constructor(public _proto: ProtoInjector, public _parent: Injector = null) {
this._strategy = _proto._strategy.createInjectorStrategy(this); this._strategy = _proto._strategy.createInjectorStrategy(this);
} }
@ -555,11 +564,12 @@ export class Injector {
* *
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a * @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
* recursive list of more bindings. * recursive list of more bindings.
* * @param `depProvider`
*/ */
resolveAndCreateChild(bindings: List<Type | Binding | List<any>>): Injector { resolveAndCreateChild(bindings: List<Type | Binding | List<any>>,
depProvider: DependencyProvider = null): Injector {
var resovledBindings = Injector.resolve(bindings); var resovledBindings = Injector.resolve(bindings);
return this.createChildFromResolved(resovledBindings); return this.createChildFromResolved(resovledBindings, depProvider);
} }
/** /**
@ -567,12 +577,14 @@ export class Injector {
* *
* @param `bindings`: A sparse list of {@link ResolvedBinding}s. * @param `bindings`: A sparse list of {@link ResolvedBinding}s.
* See `resolve` for the {@link Injector}. * See `resolve` for the {@link Injector}.
* @param `depProvider`
* @returns a new child {@link Injector}. * @returns a new child {@link Injector}.
*/ */
createChildFromResolved(bindings: List<ResolvedBinding>): Injector { createChildFromResolved(bindings: List<ResolvedBinding>,
depProvider: DependencyProvider = null): Injector {
var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC)); var bd = bindings.map(b => new BindingWithVisibility(b, PUBLIC));
var proto = new ProtoInjector(bd, 1); var proto = new ProtoInjector(bd, 1);
var inj = new Injector(proto); var inj = new Injector(proto, null, depProvider);
inj._parent = this; inj._parent = this;
return inj; return inj;
} }
@ -588,26 +600,26 @@ export class Injector {
var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19; var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19;
try { try {
d0 = length > 0 ? this._getByDependency(deps[0], visibility) : null; d0 = length > 0 ? this._getByDependency(binding, deps[0], visibility) : null;
d1 = length > 1 ? this._getByDependency(deps[1], visibility) : null; d1 = length > 1 ? this._getByDependency(binding, deps[1], visibility) : null;
d2 = length > 2 ? this._getByDependency(deps[2], visibility) : null; d2 = length > 2 ? this._getByDependency(binding, deps[2], visibility) : null;
d3 = length > 3 ? this._getByDependency(deps[3], visibility) : null; d3 = length > 3 ? this._getByDependency(binding, deps[3], visibility) : null;
d4 = length > 4 ? this._getByDependency(deps[4], visibility) : null; d4 = length > 4 ? this._getByDependency(binding, deps[4], visibility) : null;
d5 = length > 5 ? this._getByDependency(deps[5], visibility) : null; d5 = length > 5 ? this._getByDependency(binding, deps[5], visibility) : null;
d6 = length > 6 ? this._getByDependency(deps[6], visibility) : null; d6 = length > 6 ? this._getByDependency(binding, deps[6], visibility) : null;
d7 = length > 7 ? this._getByDependency(deps[7], visibility) : null; d7 = length > 7 ? this._getByDependency(binding, deps[7], visibility) : null;
d8 = length > 8 ? this._getByDependency(deps[8], visibility) : null; d8 = length > 8 ? this._getByDependency(binding, deps[8], visibility) : null;
d9 = length > 9 ? this._getByDependency(deps[9], visibility) : null; d9 = length > 9 ? this._getByDependency(binding, deps[9], visibility) : null;
d10 = length > 10 ? this._getByDependency(deps[10], visibility) : null; d10 = length > 10 ? this._getByDependency(binding, deps[10], visibility) : null;
d11 = length > 11 ? this._getByDependency(deps[11], visibility) : null; d11 = length > 11 ? this._getByDependency(binding, deps[11], visibility) : null;
d12 = length > 12 ? this._getByDependency(deps[12], visibility) : null; d12 = length > 12 ? this._getByDependency(binding, deps[12], visibility) : null;
d13 = length > 13 ? this._getByDependency(deps[13], visibility) : null; d13 = length > 13 ? this._getByDependency(binding, deps[13], visibility) : null;
d14 = length > 14 ? this._getByDependency(deps[14], visibility) : null; d14 = length > 14 ? this._getByDependency(binding, deps[14], visibility) : null;
d15 = length > 15 ? this._getByDependency(deps[15], visibility) : null; d15 = length > 15 ? this._getByDependency(binding, deps[15], visibility) : null;
d16 = length > 16 ? this._getByDependency(deps[16], visibility) : null; d16 = length > 16 ? this._getByDependency(binding, deps[16], visibility) : null;
d17 = length > 17 ? this._getByDependency(deps[17], visibility) : null; d17 = length > 17 ? this._getByDependency(binding, deps[17], visibility) : null;
d18 = length > 18 ? this._getByDependency(deps[18], visibility) : null; d18 = length > 18 ? this._getByDependency(binding, deps[18], visibility) : null;
d19 = length > 19 ? this._getByDependency(deps[19], visibility) : null; d19 = length > 19 ? this._getByDependency(binding, deps[19], visibility) : null;
} catch (e) { } catch (e) {
if (e instanceof AbstractBindingError) e.addKey(binding.key); if (e instanceof AbstractBindingError) e.addKey(binding.key);
throw e; throw e;
@ -689,8 +701,11 @@ export class Injector {
return obj; return obj;
} }
private _getByDependency(dep: Dependency, bindingVisibility: number): any { private _getByDependency(binding: ResolvedBinding, dep: Dependency,
var special = isPresent(this.ei) ? this.ei.getDependency(dep) : undefinedValue; bindingVisibility: number): any {
var special = isPresent(this._depProvider) ?
this._depProvider.getDependency(this, binding, dep) :
undefinedValue;
if (special !== undefinedValue) { if (special !== undefinedValue) {
return special; return special;
} else { } else {

View File

@ -1,6 +1,7 @@
library test_lib.spies; library test_lib.spies;
import 'package:angular2/change_detection.dart'; import 'package:angular2/change_detection.dart';
import 'package:angular2/di.dart';
import './test_lib.dart'; import './test_lib.dart';
@proxy @proxy
@ -21,4 +22,9 @@ class SpyPipe extends SpyObject implements Pipe {
@proxy @proxy
class SpyPipeFactory extends SpyObject implements PipeFactory { class SpyPipeFactory extends SpyObject implements PipeFactory {
noSuchMethod(m) => super.noSuchMethod(m); noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyDependencyProvider extends SpyObject implements DependencyProvider {
noSuchMethod(m) => super.noSuchMethod(m);
} }

View File

@ -3,6 +3,9 @@ import {
ProtoChangeDetector, ProtoChangeDetector,
DynamicChangeDetector DynamicChangeDetector
} from 'angular2/change_detection'; } from 'angular2/change_detection';
import {DependencyProvider} from 'angular2/di';
import {BasePipe} from 'angular2/src/change_detection/pipes/pipe'; import {BasePipe} from 'angular2/src/change_detection/pipes/pipe';
import {SpyObject, proxy} from './test_lib'; import {SpyObject, proxy} from './test_lib';
@ -18,4 +21,6 @@ export class SpyPipe extends SpyObject {
constructor() { super(BasePipe); } constructor() { super(BasePipe); }
} }
export class SpyPipeFactory extends SpyObject {} export class SpyPipeFactory extends SpyObject {}
export class SpyDependencyProvider extends SpyObject {}

View File

@ -1,5 +1,13 @@
import {isBlank, BaseException, stringify} from 'angular2/src/facade/lang'; import {isBlank, BaseException, stringify} from 'angular2/src/facade/lang';
import {describe, ddescribe, it, iit, expect, beforeEach} from 'angular2/test_lib'; import {
describe,
ddescribe,
it,
iit,
expect,
beforeEach,
SpyDependencyProvider
} from 'angular2/test_lib';
import { import {
Injector, Injector,
bind, bind,
@ -107,8 +115,8 @@ export function main() {
strategyClass: InjectorDynamicStrategy strategyClass: InjectorDynamicStrategy
}].forEach((context) => { }].forEach((context) => {
function createInjector(bindings: any[]) { function createInjector(bindings: any[], dependencyProvider = null) {
return Injector.resolveAndCreate(bindings.concat(context['bindings'])); return Injector.resolveAndCreate(bindings.concat(context['bindings']), dependencyProvider);
} }
describe(`injector ${context['strategy']}`, () => { describe(`injector ${context['strategy']}`, () => {
@ -315,6 +323,20 @@ export function main() {
var injector = createInjector([bind('null').toValue(null)]); var injector = createInjector([bind('null').toValue(null)]);
expect(injector.get('null')).toBe(null); expect(injector.get('null')).toBe(null);
}); });
it('should use custom dependency provider', () => {
var e = new Engine();
var depProvider = <any>new SpyDependencyProvider();
depProvider.spy("getDependency").andReturn(e);
var bindings = Injector.resolve([Car]);
var injector = Injector.fromResolvedBindings(bindings, depProvider);
expect(injector.get(Car).engine).toEqual(e);
expect(depProvider.spy("getDependency"))
.toHaveBeenCalledWith(injector, bindings[0], bindings[0].dependencies[0]);
});
}); });