refactor(di): unified di injector and core injector
BREAKING CHANGES: * InjectAsync and InjectLazy have been removed * toAsyncFactory has been removed
This commit is contained in:
parent
b688dee4c8
commit
22d3943831
|
@ -22,9 +22,8 @@ export {Form} from './src/forms/directives/form_interface';
|
|||
export {TypeDecorator, ClassDefinition} from './src/util/decorators';
|
||||
export {Query} from './src/core/annotations_impl/di';
|
||||
export {ControlContainer} from './src/forms/directives/control_container';
|
||||
export {Injectable} from './src/di/annotations_impl';
|
||||
export {Injectable, Visibility} from './src/di/annotations_impl';
|
||||
export {BaseQueryList} from './src/core/compiler/base_query_list';
|
||||
export {AppProtoView, AppView, AppViewContainer} from './src/core/compiler/view';
|
||||
export * from './src/change_detection/parser/ast';
|
||||
export {Visibility} from './src/core/annotations_impl/visibility';
|
||||
export {AppViewManager} from './src/core/compiler/view_manager';
|
||||
|
|
|
@ -31,14 +31,13 @@ export {
|
|||
|
||||
export {
|
||||
Inject,
|
||||
InjectPromise,
|
||||
InjectLazy,
|
||||
Optional,
|
||||
Injectable,
|
||||
forwardRef,
|
||||
resolveForwardRef,
|
||||
ForwardRefFn,
|
||||
Injector,
|
||||
ProtoInjector,
|
||||
Binding,
|
||||
bind,
|
||||
Key,
|
||||
|
@ -52,7 +51,12 @@ export {
|
|||
OpaqueToken,
|
||||
ResolvedBinding,
|
||||
BindingBuilder,
|
||||
Dependency
|
||||
Dependency,
|
||||
Visibility,
|
||||
Self,
|
||||
Parent,
|
||||
Ancestor,
|
||||
Unbounded
|
||||
} from './di';
|
||||
|
||||
export * from './core';
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* @description
|
||||
* Define angular core API here.
|
||||
*/
|
||||
export * from './src/core/annotations/visibility';
|
||||
export * from './src/core/annotations/view';
|
||||
export * from './src/core/application';
|
||||
export * from './src/core/application_tokens';
|
||||
|
@ -19,4 +18,4 @@ export {ViewRef, ProtoViewRef} from './src/core/compiler/view_ref';
|
|||
export {ViewContainerRef} from './src/core/compiler/view_container_ref';
|
||||
export {ElementRef} from './src/core/compiler/element_ref';
|
||||
|
||||
export {NgZone} from './src/core/zone/ng_zone';
|
||||
export {NgZone} from './src/core/zone/ng_zone';
|
|
@ -7,16 +7,39 @@
|
|||
|
||||
export {
|
||||
InjectAnnotation,
|
||||
InjectPromiseAnnotation,
|
||||
InjectLazyAnnotation,
|
||||
OptionalAnnotation,
|
||||
InjectableAnnotation,
|
||||
DependencyAnnotation
|
||||
DependencyAnnotation,
|
||||
VisibilityAnnotation,
|
||||
SelfAnnotation,
|
||||
ParentAnnotation,
|
||||
AncestorAnnotation,
|
||||
UnboundedAnnotation
|
||||
} from './src/di/annotations';
|
||||
|
||||
export {Inject, InjectPromise, InjectLazy, Optional, Injectable} from './src/di/decorators';
|
||||
export {
|
||||
Inject,
|
||||
Optional,
|
||||
Injectable,
|
||||
Visibility,
|
||||
Self,
|
||||
Parent,
|
||||
Ancestor,
|
||||
Unbounded
|
||||
} from './src/di/decorators';
|
||||
export {self} from './src/di/annotations_impl';
|
||||
export {forwardRef, resolveForwardRef, ForwardRefFn} from './src/di/forward_ref';
|
||||
export {resolveBindings, Injector} from './src/di/injector';
|
||||
export {
|
||||
resolveBindings,
|
||||
Injector,
|
||||
ProtoInjector,
|
||||
PUBLIC_AND_PRIVATE,
|
||||
PUBLIC,
|
||||
PRIVATE,
|
||||
undefinedValue,
|
||||
InjectorInlineStrategy,
|
||||
InjectorDynamicStrategy
|
||||
} from './src/di/injector';
|
||||
export {Binding, BindingBuilder, ResolvedBinding, Dependency, bind} from './src/di/binding';
|
||||
export {Key, KeyRegistry, TypeLiteral} from './src/di/key';
|
||||
export {
|
||||
|
@ -26,6 +49,7 @@ export {
|
|||
CyclicDependencyError,
|
||||
InstantiationError,
|
||||
InvalidBindingError,
|
||||
NoAnnotationError
|
||||
NoAnnotationError,
|
||||
OutOfBoundsError
|
||||
} from './src/di/exceptions';
|
||||
export {OpaqueToken} from './src/di/opaque_token';
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
library angular2.core.decorators;
|
||||
|
||||
export '../annotations_impl/annotations.dart';
|
||||
export '../annotations_impl/visibility.dart';
|
||||
export '../annotations_impl/view.dart';
|
||||
export '../annotations_impl/di.dart';
|
||||
|
|
|
@ -5,12 +5,6 @@ import {
|
|||
DirectiveArgs
|
||||
} from './annotations';
|
||||
import {ViewAnnotation, ViewArgs} from './view';
|
||||
import {
|
||||
SelfAnnotation,
|
||||
ParentAnnotation,
|
||||
AncestorAnnotation,
|
||||
UnboundedAnnotation
|
||||
} from './visibility';
|
||||
import {AttributeAnnotation, QueryAnnotation} from './di';
|
||||
import {makeDecorator, makeParamDecorator, TypeDecorator, Class} from '../../util/decorators';
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
|
@ -46,12 +40,6 @@ export var Directive = <Directive>makeDecorator(DirectiveAnnotation);
|
|||
/* from view */
|
||||
export var View = <View>makeDecorator(ViewAnnotation, (fn: any) => fn.View = View);
|
||||
|
||||
/* from visibility */
|
||||
export var Self = makeParamDecorator(SelfAnnotation);
|
||||
export var Parent = makeParamDecorator(ParentAnnotation);
|
||||
export var Ancestor = makeParamDecorator(AncestorAnnotation);
|
||||
export var Unbounded = makeParamDecorator(UnboundedAnnotation);
|
||||
|
||||
/* from di */
|
||||
export var Attribute = makeParamDecorator(AttributeAnnotation);
|
||||
export var Query = makeParamDecorator(QueryAnnotation);
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
library angular2.core.annotations.visibility;
|
||||
|
||||
export "../annotations_impl/visibility.dart";
|
|
@ -1,6 +0,0 @@
|
|||
export {
|
||||
Self as SelfAnnotation,
|
||||
Ancestor as AncestorAnnotation,
|
||||
Parent as ParentAnnotation,
|
||||
Unbounded as UnboundedAnnotation
|
||||
} from '../annotations_impl/visibility';
|
|
@ -1,6 +1,6 @@
|
|||
import {CONST, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {List} from 'angular2/src/facade/collection';
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {Injectable, self} from 'angular2/src/di/annotations_impl';
|
||||
import {DEFAULT} from 'angular2/change_detection';
|
||||
|
||||
// type StringMap = {[idx: string]: string};
|
||||
|
@ -788,7 +788,7 @@ export class Directive extends Injectable {
|
|||
selector, properties, events, host, lifecycle, hostInjector, exportAs,
|
||||
compileChildren = true,
|
||||
}: DirectiveArgs = {}) {
|
||||
super();
|
||||
super(self);
|
||||
this.selector = selector;
|
||||
this.properties = properties;
|
||||
this.events = events;
|
||||
|
|
|
@ -1,213 +0,0 @@
|
|||
import {CONST, CONST_EXPR, isBlank} from 'angular2/src/facade/lang';
|
||||
import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
|
||||
|
||||
@CONST()
|
||||
export class Visibility extends DependencyAnnotation {
|
||||
constructor(public depth: number, public crossComponentBoundaries: boolean,
|
||||
public _includeSelf: boolean) {
|
||||
super();
|
||||
}
|
||||
|
||||
get includeSelf(): boolean { return isBlank(this._includeSelf) ? false : this._includeSelf; }
|
||||
toString() {
|
||||
return `@Visibility(depth: ${this.depth}, crossComponentBoundaries: ${this.crossComponentBoundaries}, includeSelf: ${this.includeSelf}})`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from its element.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Here is a simple directive that retrieves a dependency from its element.
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: '[dependency]',
|
||||
* properties: [
|
||||
* 'id: dependency'
|
||||
* ]
|
||||
* })
|
||||
* class Dependency {
|
||||
* id:string;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @Directive({
|
||||
* selector: '[my-directive]'
|
||||
* })
|
||||
* class Dependency {
|
||||
* constructor(@Self() dependency:Dependency) {
|
||||
* expect(dependency.id).toEqual(1);
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We use this with the following HTML template:
|
||||
*
|
||||
* ```
|
||||
*<div dependency="1" my-directive></div>
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
@CONST()
|
||||
export class Self extends Visibility {
|
||||
constructor() { super(0, false, true); }
|
||||
toString() { return `@Self()`; }
|
||||
}
|
||||
|
||||
// make constants after switching to ts2dart
|
||||
export var self = new Self();
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from the direct parent.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Here is a simple directive that retrieves a dependency from its parent element.
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: '[dependency]',
|
||||
* properties: [
|
||||
* 'id: dependency'
|
||||
* ]
|
||||
* })
|
||||
* class Dependency {
|
||||
* id:string;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @Directive({
|
||||
* selector: '[my-directive]'
|
||||
* })
|
||||
* class Dependency {
|
||||
* constructor(@Parent() dependency:Dependency) {
|
||||
* expect(dependency.id).toEqual(1);
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We use this with the following HTML template:
|
||||
*
|
||||
* ```
|
||||
* <div dependency="1">
|
||||
* <div dependency="2" my-directive></div>
|
||||
* </div>
|
||||
* ```
|
||||
* The `@Parent()` annotation in our constructor forces the injector to retrieve the dependency from
|
||||
* the
|
||||
* parent element (even thought the current element could resolve it): Angular injects
|
||||
* `dependency=1`.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
@CONST()
|
||||
export class Parent extends Visibility {
|
||||
constructor({self}: {self?: boolean} = {}) { super(1, false, self); }
|
||||
toString() { return `@Parent(self: ${this.includeSelf}})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any ancestor element within the same
|
||||
* shadow boundary.
|
||||
*
|
||||
* An ancestor is any element between the parent element and the shadow root.
|
||||
*
|
||||
* Use {@link Unbounded} if you need to cross upper shadow boundaries.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Here is a simple directive that retrieves a dependency from an ancestor element.
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: '[dependency]',
|
||||
* properties: [
|
||||
* 'id: dependency'
|
||||
* ]
|
||||
* })
|
||||
* class Dependency {
|
||||
* id:string;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @Directive({
|
||||
* selector: '[my-directive]'
|
||||
* })
|
||||
* class Dependency {
|
||||
* constructor(@Ancestor() dependency:Dependency) {
|
||||
* expect(dependency.id).toEqual(2);
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We use this with the following HTML template:
|
||||
*
|
||||
* ```
|
||||
* <div dependency="1">
|
||||
* <div dependency="2">
|
||||
* <div>
|
||||
* <div dependency="3" my-directive></div>
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* The `@Ancestor()` annotation in our constructor forces the injector to retrieve the dependency
|
||||
* from the
|
||||
* nearest ancestor element:
|
||||
* - The current element `dependency="3"` is skipped because it is not an ancestor.
|
||||
* - Next parent has no directives `<div>`
|
||||
* - Next parent has the `Dependency` directive and so the dependency is satisfied.
|
||||
*
|
||||
* Angular injects `dependency=2`.
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
@CONST()
|
||||
export class Ancestor extends Visibility {
|
||||
constructor({self}: {self?: boolean} = {}) { super(999999, false, self); }
|
||||
toString() { return `@Ancestor(self: ${this.includeSelf}})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any ancestor element, crossing
|
||||
* component boundaries.
|
||||
*
|
||||
* Use {@link Ancestor} to look for ancestors within the current shadow boundary only.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Here is a simple directive that retrieves a dependency from an ancestor element.
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: '[dependency]',
|
||||
* properties: [
|
||||
* 'id: dependency'
|
||||
* ]
|
||||
* })
|
||||
* class Dependency {
|
||||
* id:string;
|
||||
* }
|
||||
*
|
||||
*
|
||||
* @Directive({
|
||||
* selector: '[my-directive]'
|
||||
* })
|
||||
* class Dependency {
|
||||
* constructor(@Unbounded() dependency:Dependency) {
|
||||
* expect(dependency.id).toEqual(2);
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
@CONST()
|
||||
export class Unbounded extends Visibility {
|
||||
constructor({self}: {self?: boolean} = {}) { super(999999, true, self); }
|
||||
toString() { return `@Unbounded(self: ${this.includeSelf}})`; }
|
||||
}
|
|
@ -79,20 +79,19 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
|||
.toValue(DOM.defaultDoc()),
|
||||
bind(appComponentTypeToken).toValue(appComponentType),
|
||||
bind(appComponentRefToken)
|
||||
.toAsyncFactory(
|
||||
.toFactory(
|
||||
(dynamicComponentLoader, injector, testability, registry) => {
|
||||
|
||||
// TODO(rado): investigate whether to support bindings on root component.
|
||||
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector)
|
||||
.then((componentRef) => {
|
||||
registry.registerApplication(componentRef.location.nativeElement, testability);
|
||||
|
||||
return componentRef;
|
||||
});
|
||||
},
|
||||
[DynamicComponentLoader, Injector, Testability, TestabilityRegistry]),
|
||||
|
||||
bind(appComponentType).toFactory((ref) => ref.instance, [appComponentRefToken]),
|
||||
bind(appComponentType)
|
||||
.toFactory((p: Promise<any>) => p.then(ref => ref.instance), [appComponentRefToken]),
|
||||
bind(LifeCycle)
|
||||
.toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),
|
||||
[ExceptionHandler]),
|
||||
|
@ -293,20 +292,19 @@ export function bootstrap(appComponentType: Type,
|
|||
// index.html and main.js are possible.
|
||||
|
||||
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone);
|
||||
var compRefToken: Promise<any> =
|
||||
PromiseWrapper.wrap(() => appInjector.get(appComponentRefToken));
|
||||
var tick = (componentRef) => {
|
||||
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
|
||||
// retrieve life cycle: may have already been created if injected in root component
|
||||
var lc = appInjector.get(LifeCycle);
|
||||
lc.registerWith(zone, appChangeDetector);
|
||||
lc.tick(); // the first tick that will bootstrap the app
|
||||
|
||||
PromiseWrapper.then(
|
||||
appInjector.asyncGet(appComponentRefToken),
|
||||
(componentRef) => {
|
||||
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
|
||||
// retrieve life cycle: may have already been created if injected in root component
|
||||
var lc = appInjector.get(LifeCycle);
|
||||
lc.registerWith(zone, appChangeDetector);
|
||||
lc.tick(); // the first tick that will bootstrap the app
|
||||
|
||||
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
||||
},
|
||||
|
||||
(err, stackTrace) => {bootstrapProcess.reject(err, stackTrace)});
|
||||
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
||||
};
|
||||
PromiseWrapper.then(compRefToken, tick,
|
||||
(err, stackTrace) => {bootstrapProcess.reject(err, stackTrace)});
|
||||
});
|
||||
|
||||
return bootstrapProcess.promise;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,9 +5,12 @@
|
|||
|
||||
export {
|
||||
Inject as InjectAnnotation,
|
||||
InjectPromise as InjectPromiseAnnotation,
|
||||
InjectLazy as InjectLazyAnnotation,
|
||||
Optional as OptionalAnnotation,
|
||||
Injectable as InjectableAnnotation,
|
||||
Visibility as VisibilityAnnotation,
|
||||
Self as SelfAnnotation,
|
||||
Parent as ParentAnnotation,
|
||||
Ancestor as AncestorAnnotation,
|
||||
Unbounded as UnboundedAnnotation,
|
||||
DependencyAnnotation, // abstract base class, does not need a decorator
|
||||
} from './annotations_impl';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {CONST, stringify} from "angular2/src/facade/lang";
|
||||
import {CONST, CONST_EXPR, stringify, isBlank, isPresent} from "angular2/src/facade/lang";
|
||||
|
||||
/**
|
||||
* A parameter annotation that specifies a dependency.
|
||||
|
@ -18,44 +18,6 @@ export class Inject {
|
|||
toString(): string { return `@Inject(${stringify(this.token)})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter annotation that specifies a `Promise` of a dependency.
|
||||
*
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@InjectPromise(MyService) aServicePromise:Promise<MyService>) {
|
||||
* aServicePromise.then(aService:MyService => ...);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
@CONST()
|
||||
export class InjectPromise {
|
||||
constructor(public token) {}
|
||||
toString(): string { return `@InjectPromise(${stringify(this.token)})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter annotation that creates a synchronous lazy dependency.
|
||||
*
|
||||
* ```
|
||||
* class AComponent {
|
||||
* constructor(@InjectLazy(MyService) aServiceFn:Function) {
|
||||
* var aService:MyService = aServiceFn();
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
@CONST()
|
||||
export class InjectLazy {
|
||||
constructor(public token) {}
|
||||
toString(): string { return `@InjectLazy(${stringify(this.token)})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter annotation that marks a dependency as optional. {@link Injector} provides `null` if
|
||||
* the dependency is not found.
|
||||
|
@ -124,4 +86,172 @@ export class DependencyAnnotation {
|
|||
*/
|
||||
@CONST()
|
||||
export class Injectable {
|
||||
constructor(public visibility: Visibility = unbounded) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies how injector should resolve a dependency.
|
||||
*
|
||||
* See {@link Self}, {@link Parent}, {@link Ancestor}, {@link Unbounded}.
|
||||
*
|
||||
* @exportedAs angular2/di_annotations
|
||||
*/
|
||||
@CONST()
|
||||
export class Visibility extends DependencyAnnotation {
|
||||
constructor(public depth: number, public crossComponentBoundaries: boolean,
|
||||
public _includeSelf: boolean) {
|
||||
super();
|
||||
}
|
||||
|
||||
get includeSelf(): boolean { return isBlank(this._includeSelf) ? false : this._includeSelf; }
|
||||
|
||||
toString(): string {
|
||||
return `@Visibility(depth: ${this.depth}, crossComponentBoundaries: ${this.crossComponentBoundaries}, includeSelf: ${this.includeSelf}})`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from itself.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Self() dependency:Dependency) {}
|
||||
* }
|
||||
*
|
||||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
|
||||
* var nd = inj.get(NeedsDependency);
|
||||
* expect(nd.dependency).toBeAnInstanceOf(Dependency);
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
@CONST()
|
||||
export class Self extends Visibility {
|
||||
constructor() { super(0, false, true); }
|
||||
toString(): string { return `@Self()`; }
|
||||
}
|
||||
|
||||
export const self = CONST_EXPR(new Self());
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from the direct parent.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Parent() dependency:Dependency) {}
|
||||
* }
|
||||
*
|
||||
* var parent = Injector.resolveAndCreate([
|
||||
* bind(Dependency).toClass(ParentDependency)
|
||||
* ]);
|
||||
* var child = parent.resolveAndCreateChild([NeedsDependency, Depedency]);
|
||||
* var nd = child.get(NeedsDependency);
|
||||
* expect(nd.dependency).toBeAnInstanceOf(ParentDependency);
|
||||
* ```
|
||||
*
|
||||
* You can make an injector to retrive a dependency either from itself or its direct parent by
|
||||
* setting self to true.
|
||||
*
|
||||
* ```
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Parent({self:true}) dependency:Dependency) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
@CONST()
|
||||
export class Parent extends Visibility {
|
||||
constructor({self}: {self?: boolean} = {}) { super(1, false, self); }
|
||||
toString(): string { return `@Parent(self: ${this.includeSelf}})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any ancestor from the same boundary.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Ancestor() dependency:Dependency) {}
|
||||
* }
|
||||
*
|
||||
* var parent = Injector.resolveAndCreate([
|
||||
* bind(Dependency).toClass(AncestorDependency)
|
||||
* ]);
|
||||
* var child = parent.resolveAndCreateChild([]);
|
||||
* var grandChild = child.resolveAndCreateChild([NeedsDependency, Depedency]);
|
||||
* var nd = grandChild.get(NeedsDependency);
|
||||
* expect(nd.dependency).toBeAnInstanceOf(AncestorDependency);
|
||||
* ```
|
||||
*
|
||||
* You can make an injector to retrive a dependency either from itself or its ancestor by setting
|
||||
* self to true.
|
||||
*
|
||||
* ```
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Ancestor({self:true}) dependency:Dependency) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
@CONST()
|
||||
export class Ancestor extends Visibility {
|
||||
constructor({self}: {self?: boolean} = {}) { super(999999, false, self); }
|
||||
toString(): string { return `@Ancestor(self: ${this.includeSelf}})`; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that an injector should retrieve a dependency from any ancestor, crossing boundaries.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```
|
||||
* class Dependency {
|
||||
* }
|
||||
*
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Ancestor() dependency:Dependency) {}
|
||||
* }
|
||||
*
|
||||
* var parent = Injector.resolveAndCreate([
|
||||
* bind(Dependency).toClass(AncestorDependency)
|
||||
* ]);
|
||||
* var child = parent.resolveAndCreateChild([]);
|
||||
* var grandChild = child.resolveAndCreateChild([NeedsDependency, Depedency]);
|
||||
* var nd = grandChild.get(NeedsDependency);
|
||||
* expect(nd.dependency).toBeAnInstanceOf(AncestorDependency);
|
||||
* ```
|
||||
*
|
||||
* You can make an injector to retrive a dependency either from itself or its ancestor by setting
|
||||
* self to true.
|
||||
*
|
||||
* ```
|
||||
* class NeedsDependency {
|
||||
* constructor(public @Ancestor({self:true}) dependency:Dependency) {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @exportedAs angular2/di
|
||||
*/
|
||||
@CONST()
|
||||
export class Unbounded extends Visibility {
|
||||
constructor({self}: {self?: boolean} = {}) { super(999999, true, self); }
|
||||
toString(): string { return `@Unbounded(self: ${this.includeSelf}})`; }
|
||||
}
|
||||
|
||||
export const unbounded = CONST_EXPR(new Unbounded({self: true}));
|
|
@ -13,9 +13,10 @@ import {reflector} from 'angular2/src/reflection/reflection';
|
|||
import {Key} from './key';
|
||||
import {
|
||||
Inject,
|
||||
InjectLazy,
|
||||
InjectPromise,
|
||||
Injectable,
|
||||
Visibility,
|
||||
Optional,
|
||||
unbounded,
|
||||
DependencyAnnotation
|
||||
} from './annotations_impl';
|
||||
import {NoAnnotationError} from './exceptions';
|
||||
|
@ -25,10 +26,10 @@ import {resolveForwardRef} from './forward_ref';
|
|||
* @private
|
||||
*/
|
||||
export class Dependency {
|
||||
constructor(public key: Key, public asPromise: boolean, public lazy: boolean,
|
||||
public optional: boolean, public properties: List<any>) {}
|
||||
constructor(public key: Key, public optional: boolean, public visibility: Visibility,
|
||||
public properties: List<any>) {}
|
||||
|
||||
static fromKey(key: Key) { return new Dependency(key, false, false, false, []); }
|
||||
static fromKey(key: Key) { return new Dependency(key, false, _defaulVisiblity(key.token), []); }
|
||||
}
|
||||
|
||||
const _EMPTY_LIST = CONST_EXPR([]);
|
||||
|
@ -158,35 +159,7 @@ export class Binding {
|
|||
toFactory: Function;
|
||||
|
||||
/**
|
||||
* Binds a key to a function which computes the value asynchronously.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* new Binding(Number, { toAsyncFactory: () => {
|
||||
* return new Promise((resolve) => resolve(1 + 2));
|
||||
* }}),
|
||||
* new Binding(String, { toFactory: (value) => { return "Value: " + value; },
|
||||
* dependencies: [Number]})
|
||||
* ]);
|
||||
*
|
||||
* injector.asyncGet(Number).then((v) => expect(v).toBe(3));
|
||||
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
|
||||
* ```
|
||||
*
|
||||
* The interesting thing to note is that event though `Number` has an async factory, the `String`
|
||||
* factory function takes the resolved value. This shows that the {@link Injector} delays
|
||||
*executing the
|
||||
*`String` factory
|
||||
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using
|
||||
* the `asyncGet` API in the {@link Injector}.
|
||||
*
|
||||
*/
|
||||
toAsyncFactory: Function;
|
||||
|
||||
/**
|
||||
* Used in conjunction with `toFactory` or `toAsyncFactory` and specifies a set of dependencies
|
||||
* Used in conjunction with `toFactory` and specifies a set of dependencies
|
||||
* (as `token`s) which should be injected into the factory function.
|
||||
*
|
||||
* ## Example
|
||||
|
@ -204,12 +177,11 @@ export class Binding {
|
|||
*/
|
||||
dependencies: List<any>;
|
||||
|
||||
constructor(token, {toClass, toValue, toAlias, toFactory, toAsyncFactory, deps}: {
|
||||
constructor(token, {toClass, toValue, toAlias, toFactory, deps}: {
|
||||
toClass?: Type,
|
||||
toValue?: any,
|
||||
toAlias?: any,
|
||||
toFactory?: Function,
|
||||
toAsyncFactory?: Function,
|
||||
deps?: List<any>
|
||||
}) {
|
||||
this.token = token;
|
||||
|
@ -217,7 +189,6 @@ export class Binding {
|
|||
this.toValue = toValue;
|
||||
this.toAlias = toAlias;
|
||||
this.toFactory = toFactory;
|
||||
this.toAsyncFactory = toAsyncFactory;
|
||||
this.dependencies = deps;
|
||||
}
|
||||
|
||||
|
@ -230,7 +201,6 @@ export class Binding {
|
|||
resolve(): ResolvedBinding {
|
||||
var factoryFn: Function;
|
||||
var resolvedDeps;
|
||||
var isAsync = false;
|
||||
if (isPresent(this.toClass)) {
|
||||
var toClass = resolveForwardRef(this.toClass);
|
||||
factoryFn = reflector.factory(toClass);
|
||||
|
@ -241,16 +211,12 @@ export class Binding {
|
|||
} 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);
|
||||
return new ResolvedBinding(Key.get(this.token), factoryFn, resolvedDeps);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,12 +244,7 @@ export class ResolvedBinding {
|
|||
/**
|
||||
* Arguments (dependencies) to the `factory` function.
|
||||
*/
|
||||
public dependencies: List<Dependency>,
|
||||
|
||||
/**
|
||||
* Specifies whether the `factory` function returns a `Promise`.
|
||||
*/
|
||||
public providedAsPromise: boolean) {}
|
||||
public dependencies: List<Dependency>) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -417,33 +378,6 @@ export class BindingBuilder {
|
|||
toFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
|
||||
return new Binding(this.token, {toFactory: factoryFunction, deps: dependencies});
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a key to a function which computes the value asynchronously.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ```javascript
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* bind(Number).toAsyncFactory(() => {
|
||||
* return new Promise((resolve) => resolve(1 + 2));
|
||||
* }),
|
||||
* bind(String).toFactory((v) => { return "Value: " + v; }, [Number])
|
||||
* ]);
|
||||
*
|
||||
* injector.asyncGet(Number).then((v) => expect(v).toBe(3));
|
||||
* injector.asyncGet(String).then((v) => expect(v).toBe('Value: 3'));
|
||||
* ```
|
||||
*
|
||||
* The interesting thing to note is that event though `Number` has an async factory, the `String`
|
||||
* factory function takes the resolved value. This shows that the {@link Injector} delays
|
||||
* executing of the `String` factory
|
||||
* until after the `Number` is resolved. This can only be done if the `token` is retrieved using
|
||||
* the `asyncGet` API in the {@link Injector}.
|
||||
*/
|
||||
toAsyncFactory(factoryFunction: Function, dependencies?: List<any>): Binding {
|
||||
return new Binding(this.token, {toAsyncFactory: factoryFunction, deps: dependencies});
|
||||
}
|
||||
}
|
||||
|
||||
function _constructDependencies(factoryFunction: Function,
|
||||
|
@ -470,33 +404,30 @@ function _extractToken(typeOrFunc, annotations /*List<any> | any*/,
|
|||
var depProps = [];
|
||||
var token = null;
|
||||
var optional = false;
|
||||
var lazy = false;
|
||||
var asPromise = false;
|
||||
|
||||
if (!isArray(annotations)) {
|
||||
return _createDependency(annotations, asPromise, lazy, optional, depProps);
|
||||
return _createDependency(annotations, optional, _defaulVisiblity(annotations), depProps);
|
||||
}
|
||||
|
||||
var visibility = null;
|
||||
var defaultVisibility = unbounded;
|
||||
|
||||
for (var i = 0; i < annotations.length; ++i) {
|
||||
var paramAnnotation = annotations[i];
|
||||
|
||||
if (paramAnnotation instanceof Type) {
|
||||
token = paramAnnotation;
|
||||
defaultVisibility = _defaulVisiblity(token);
|
||||
|
||||
} else if (paramAnnotation instanceof Inject) {
|
||||
token = paramAnnotation.token;
|
||||
|
||||
} else if (paramAnnotation instanceof InjectPromise) {
|
||||
token = paramAnnotation.token;
|
||||
asPromise = true;
|
||||
|
||||
} else if (paramAnnotation instanceof InjectLazy) {
|
||||
token = paramAnnotation.token;
|
||||
lazy = true;
|
||||
|
||||
} else if (paramAnnotation instanceof Optional) {
|
||||
optional = true;
|
||||
|
||||
} else if (paramAnnotation instanceof Visibility) {
|
||||
visibility = paramAnnotation;
|
||||
|
||||
} else if (paramAnnotation instanceof DependencyAnnotation) {
|
||||
if (isPresent(paramAnnotation.token)) {
|
||||
token = paramAnnotation.token;
|
||||
|
@ -505,15 +436,29 @@ function _extractToken(typeOrFunc, annotations /*List<any> | any*/,
|
|||
}
|
||||
}
|
||||
|
||||
if (isBlank(visibility)) {
|
||||
visibility = defaultVisibility;
|
||||
}
|
||||
|
||||
token = resolveForwardRef(token);
|
||||
|
||||
if (isPresent(token)) {
|
||||
return _createDependency(token, asPromise, lazy, optional, depProps);
|
||||
return _createDependency(token, optional, visibility, depProps);
|
||||
} else {
|
||||
throw new NoAnnotationError(typeOrFunc, params);
|
||||
}
|
||||
}
|
||||
|
||||
function _createDependency(token, asPromise, lazy, optional, depProps): Dependency {
|
||||
return new Dependency(Key.get(token), asPromise, lazy, optional, depProps);
|
||||
function _defaulVisiblity(typeOrFunc) {
|
||||
try {
|
||||
if (!(typeOrFunc instanceof Type)) return unbounded;
|
||||
var f = ListWrapper.filter(reflector.annotations(typeOrFunc), s => s instanceof Injectable);
|
||||
return f.length === 0 ? unbounded : f[0].visibility;
|
||||
} catch (e) {
|
||||
return unbounded;
|
||||
}
|
||||
}
|
||||
|
||||
function _createDependency(token, optional, visibility, depProps): Dependency {
|
||||
return new Dependency(Key.get(token), optional, visibility, depProps);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
import {
|
||||
InjectAnnotation,
|
||||
InjectPromiseAnnotation,
|
||||
InjectLazyAnnotation,
|
||||
OptionalAnnotation,
|
||||
InjectableAnnotation
|
||||
InjectableAnnotation,
|
||||
VisibilityAnnotation,
|
||||
SelfAnnotation,
|
||||
ParentAnnotation,
|
||||
AncestorAnnotation,
|
||||
UnboundedAnnotation
|
||||
} from './annotations';
|
||||
import {makeDecorator, makeParamDecorator} from '../util/decorators';
|
||||
|
||||
export var Inject = makeParamDecorator(InjectAnnotation);
|
||||
export var InjectPromise = makeParamDecorator(InjectPromiseAnnotation);
|
||||
export var InjectLazy = makeParamDecorator(InjectLazyAnnotation);
|
||||
export var Optional = makeParamDecorator(OptionalAnnotation);
|
||||
export var Injectable = makeDecorator(InjectableAnnotation);
|
||||
export var Visibility = makeParamDecorator(VisibilityAnnotation);
|
||||
export var Self = makeParamDecorator(SelfAnnotation);
|
||||
export var Parent = makeParamDecorator(ParentAnnotation);
|
||||
export var Ancestor = makeParamDecorator(AncestorAnnotation);
|
||||
export var Unbounded = makeParamDecorator(UnboundedAnnotation);
|
|
@ -140,15 +140,17 @@ export class CyclicDependencyError extends AbstractBindingError {
|
|||
export class InstantiationError extends AbstractBindingError {
|
||||
cause;
|
||||
causeKey;
|
||||
stack;
|
||||
|
||||
// TODO(tbosch): Can't do key:Key as this results in a circular dependency!
|
||||
constructor(cause, key) {
|
||||
constructor(cause, stack, key) {
|
||||
super(key, function(keys: List<any>) {
|
||||
var first = stringify(ListWrapper.first(keys).token);
|
||||
return `Error during instantiation of ${first}!${constructResolvingPath(keys)}. ORIGINAL ERROR: ${cause}`;
|
||||
});
|
||||
this.cause = cause;
|
||||
this.causeKey = key;
|
||||
this.stack = stack;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,3 +200,18 @@ export class NoAnnotationError extends BaseException {
|
|||
|
||||
toString(): string { return this.message; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when getting an object by index.
|
||||
*
|
||||
* @exportedAs angular2/di_errors
|
||||
*/
|
||||
export class OutOfBoundsError extends BaseException {
|
||||
message: string;
|
||||
constructor(index) {
|
||||
super();
|
||||
this.message = `Index ${index} is out-of-bounds.`;
|
||||
}
|
||||
|
||||
toString(): string { return this.message; }
|
||||
}
|
||||
|
|
|
@ -8,22 +8,371 @@ import {
|
|||
AsyncBindingError,
|
||||
CyclicDependencyError,
|
||||
InstantiationError,
|
||||
InvalidBindingError
|
||||
InvalidBindingError,
|
||||
OutOfBoundsError
|
||||
} from './exceptions';
|
||||
import {FunctionWrapper, Type, isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {Key} from './key';
|
||||
import {resolveForwardRef} from './forward_ref';
|
||||
import {self, unbounded} from './annotations_impl';
|
||||
|
||||
const _constructing = CONST_EXPR(new Object());
|
||||
const _notFound = CONST_EXPR(new Object());
|
||||
|
||||
class _Waiting {
|
||||
constructor(public promise: Promise<any>) {}
|
||||
// Threshold for the dynamic version
|
||||
const _MAX_CONSTRUCTION_COUNTER = 10;
|
||||
|
||||
export const undefinedValue = CONST_EXPR(new Object());
|
||||
|
||||
export const PUBLIC = 1;
|
||||
export const PRIVATE = 2;
|
||||
export const PUBLIC_AND_PRIVATE = 3;
|
||||
|
||||
export interface ProtoInjectorStrategy {
|
||||
getBindingAtIndex(index: number): ResolvedBinding;
|
||||
createInjectorStrategy(inj: Injector): InjectorStrategy;
|
||||
}
|
||||
|
||||
function _isWaiting(obj): boolean {
|
||||
return obj instanceof _Waiting;
|
||||
export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
|
||||
binding0: ResolvedBinding = null;
|
||||
binding1: ResolvedBinding = null;
|
||||
binding2: ResolvedBinding = null;
|
||||
binding3: ResolvedBinding = null;
|
||||
binding4: ResolvedBinding = null;
|
||||
binding5: ResolvedBinding = null;
|
||||
binding6: ResolvedBinding = null;
|
||||
binding7: ResolvedBinding = null;
|
||||
binding8: ResolvedBinding = null;
|
||||
binding9: ResolvedBinding = null;
|
||||
|
||||
keyId0: number = null;
|
||||
keyId1: number = null;
|
||||
keyId2: number = null;
|
||||
keyId3: number = null;
|
||||
keyId4: number = null;
|
||||
keyId5: number = null;
|
||||
keyId6: number = null;
|
||||
keyId7: number = null;
|
||||
keyId8: number = null;
|
||||
keyId9: number = null;
|
||||
|
||||
visibility0: number = null;
|
||||
visibility1: number = null;
|
||||
visibility2: number = null;
|
||||
visibility3: number = null;
|
||||
visibility4: number = null;
|
||||
visibility5: number = null;
|
||||
visibility6: number = null;
|
||||
visibility7: number = null;
|
||||
visibility8: number = null;
|
||||
visibility9: number = null;
|
||||
|
||||
constructor(protoEI: ProtoInjector, bd: any[]) {
|
||||
var length = bd.length;
|
||||
|
||||
if (length > 0) {
|
||||
this.binding0 = bd[0].binding;
|
||||
this.keyId0 = bd[0].getKeyId();
|
||||
this.visibility0 = bd[0].visibility;
|
||||
}
|
||||
if (length > 1) {
|
||||
this.binding1 = bd[1].binding;
|
||||
this.keyId1 = bd[1].getKeyId();
|
||||
this.visibility1 = bd[1].visibility;
|
||||
}
|
||||
if (length > 2) {
|
||||
this.binding2 = bd[2].binding;
|
||||
this.keyId2 = bd[2].getKeyId();
|
||||
this.visibility2 = bd[2].visibility;
|
||||
}
|
||||
if (length > 3) {
|
||||
this.binding3 = bd[3].binding;
|
||||
this.keyId3 = bd[3].getKeyId();
|
||||
this.visibility3 = bd[3].visibility;
|
||||
}
|
||||
if (length > 4) {
|
||||
this.binding4 = bd[4].binding;
|
||||
this.keyId4 = bd[4].getKeyId();
|
||||
this.visibility4 = bd[4].visibility;
|
||||
}
|
||||
if (length > 5) {
|
||||
this.binding5 = bd[5].binding;
|
||||
this.keyId5 = bd[5].getKeyId();
|
||||
this.visibility5 = bd[5].visibility;
|
||||
}
|
||||
if (length > 6) {
|
||||
this.binding6 = bd[6].binding;
|
||||
this.keyId6 = bd[6].getKeyId();
|
||||
this.visibility6 = bd[6].visibility;
|
||||
}
|
||||
if (length > 7) {
|
||||
this.binding7 = bd[7].binding;
|
||||
this.keyId7 = bd[7].getKeyId();
|
||||
this.visibility7 = bd[7].visibility;
|
||||
}
|
||||
if (length > 8) {
|
||||
this.binding8 = bd[8].binding;
|
||||
this.keyId8 = bd[8].getKeyId();
|
||||
this.visibility8 = bd[8].visibility;
|
||||
}
|
||||
if (length > 9) {
|
||||
this.binding9 = bd[9].binding;
|
||||
this.keyId9 = bd[9].getKeyId();
|
||||
this.visibility9 = bd[9].visibility;
|
||||
}
|
||||
}
|
||||
|
||||
getBindingAtIndex(index: number): any {
|
||||
if (index == 0) return this.binding0;
|
||||
if (index == 1) return this.binding1;
|
||||
if (index == 2) return this.binding2;
|
||||
if (index == 3) return this.binding3;
|
||||
if (index == 4) return this.binding4;
|
||||
if (index == 5) return this.binding5;
|
||||
if (index == 6) return this.binding6;
|
||||
if (index == 7) return this.binding7;
|
||||
if (index == 8) return this.binding8;
|
||||
if (index == 9) return this.binding9;
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
createInjectorStrategy(injector: Injector): InjectorStrategy {
|
||||
return new InjectorInlineStrategy(injector, this);
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
||||
bindings: ResolvedBinding[];
|
||||
keyIds: number[];
|
||||
visibilities: number[];
|
||||
|
||||
constructor(protoInj: ProtoInjector, bd: any[]) {
|
||||
var len = bd.length;
|
||||
|
||||
this.bindings = ListWrapper.createFixedSize(len);
|
||||
this.keyIds = ListWrapper.createFixedSize(len);
|
||||
this.visibilities = ListWrapper.createFixedSize(len);
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
this.bindings[i] = bd[i].binding;
|
||||
this.keyIds[i] = bd[i].getKeyId();
|
||||
this.visibilities[i] = bd[i].visibility;
|
||||
}
|
||||
}
|
||||
|
||||
getBindingAtIndex(index: number): any {
|
||||
if (index < 0 || index >= this.bindings.length) {
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
return this.bindings[index];
|
||||
}
|
||||
|
||||
createInjectorStrategy(ei: Injector): InjectorStrategy {
|
||||
return new InjectorDynamicStrategy(this, ei);
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtoInjector {
|
||||
_strategy: ProtoInjectorStrategy;
|
||||
|
||||
constructor(public parent: ProtoInjector, rb: any[], public distanceToParent: number) {
|
||||
this._strategy = rb.length > _MAX_CONSTRUCTION_COUNTER ?
|
||||
new ProtoInjectorDynamicStrategy(this, rb) :
|
||||
new ProtoInjectorInlineStrategy(this, rb);
|
||||
}
|
||||
|
||||
getBindingAtIndex(index: number): any { return this._strategy.getBindingAtIndex(index); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface InjectorStrategy {
|
||||
getObjByKeyId(keyId: number, visibility: number): any;
|
||||
getObjAtIndex(index: number): any;
|
||||
getMaxNumberOfObjects(): number;
|
||||
|
||||
hydrate(): void;
|
||||
dehydrate(): void;
|
||||
}
|
||||
|
||||
export class InjectorInlineStrategy implements InjectorStrategy {
|
||||
obj0: any = null;
|
||||
obj1: any = null;
|
||||
obj2: any = null;
|
||||
obj3: any = null;
|
||||
obj4: any = null;
|
||||
obj5: any = null;
|
||||
obj6: any = null;
|
||||
obj7: any = null;
|
||||
obj8: any = null;
|
||||
obj9: any = null;
|
||||
|
||||
constructor(public injector: Injector, public protoStrategy: ProtoInjectorInlineStrategy) {}
|
||||
|
||||
hydrate(): void {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
|
||||
if (isPresent(p.keyId0) && isBlank(this.obj0)) this.obj0 = inj._new(p.binding0);
|
||||
if (isPresent(p.keyId1) && isBlank(this.obj1)) this.obj1 = inj._new(p.binding1);
|
||||
if (isPresent(p.keyId2) && isBlank(this.obj2)) this.obj2 = inj._new(p.binding2);
|
||||
if (isPresent(p.keyId3) && isBlank(this.obj3)) this.obj3 = inj._new(p.binding3);
|
||||
if (isPresent(p.keyId4) && isBlank(this.obj4)) this.obj4 = inj._new(p.binding4);
|
||||
if (isPresent(p.keyId5) && isBlank(this.obj5)) this.obj5 = inj._new(p.binding5);
|
||||
if (isPresent(p.keyId6) && isBlank(this.obj6)) this.obj6 = inj._new(p.binding6);
|
||||
if (isPresent(p.keyId7) && isBlank(this.obj7)) this.obj7 = inj._new(p.binding7);
|
||||
if (isPresent(p.keyId8) && isBlank(this.obj8)) this.obj8 = inj._new(p.binding8);
|
||||
if (isPresent(p.keyId9) && isBlank(this.obj9)) this.obj9 = inj._new(p.binding9);
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
this.obj0 = null;
|
||||
this.obj1 = null;
|
||||
this.obj2 = null;
|
||||
this.obj3 = null;
|
||||
this.obj4 = null;
|
||||
this.obj5 = null;
|
||||
this.obj6 = null;
|
||||
this.obj7 = null;
|
||||
this.obj8 = null;
|
||||
this.obj9 = null;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: number): any {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
|
||||
if (p.keyId0 === keyId && (p.visibility0 & visibility) > 0) {
|
||||
if (isBlank(this.obj0)) {
|
||||
this.obj0 = inj._new(p.binding0);
|
||||
}
|
||||
return this.obj0;
|
||||
}
|
||||
if (p.keyId1 === keyId && (p.visibility1 & visibility) > 0) {
|
||||
if (isBlank(this.obj1)) {
|
||||
this.obj1 = inj._new(p.binding1);
|
||||
}
|
||||
return this.obj1;
|
||||
}
|
||||
if (p.keyId2 === keyId && (p.visibility2 & visibility) > 0) {
|
||||
if (isBlank(this.obj2)) {
|
||||
this.obj2 = inj._new(p.binding2);
|
||||
}
|
||||
return this.obj2;
|
||||
}
|
||||
if (p.keyId3 === keyId && (p.visibility3 & visibility) > 0) {
|
||||
if (isBlank(this.obj3)) {
|
||||
this.obj3 = inj._new(p.binding3);
|
||||
}
|
||||
return this.obj3;
|
||||
}
|
||||
if (p.keyId4 === keyId && (p.visibility4 & visibility) > 0) {
|
||||
if (isBlank(this.obj4)) {
|
||||
this.obj4 = inj._new(p.binding4);
|
||||
}
|
||||
return this.obj4;
|
||||
}
|
||||
if (p.keyId5 === keyId && (p.visibility5 & visibility) > 0) {
|
||||
if (isBlank(this.obj5)) {
|
||||
this.obj5 = inj._new(p.binding5);
|
||||
}
|
||||
return this.obj5;
|
||||
}
|
||||
if (p.keyId6 === keyId && (p.visibility6 & visibility) > 0) {
|
||||
if (isBlank(this.obj6)) {
|
||||
this.obj6 = inj._new(p.binding6);
|
||||
}
|
||||
return this.obj6;
|
||||
}
|
||||
if (p.keyId7 === keyId && (p.visibility7 & visibility) > 0) {
|
||||
if (isBlank(this.obj7)) {
|
||||
this.obj7 = inj._new(p.binding7);
|
||||
}
|
||||
return this.obj7;
|
||||
}
|
||||
if (p.keyId8 === keyId && (p.visibility8 & visibility) > 0) {
|
||||
if (isBlank(this.obj8)) {
|
||||
this.obj8 = inj._new(p.binding8);
|
||||
}
|
||||
return this.obj8;
|
||||
}
|
||||
if (p.keyId9 === keyId && (p.visibility9 & visibility) > 0) {
|
||||
if (isBlank(this.obj9)) {
|
||||
this.obj9 = inj._new(p.binding9);
|
||||
}
|
||||
return this.obj9;
|
||||
}
|
||||
|
||||
return undefinedValue;
|
||||
}
|
||||
|
||||
getObjAtIndex(index: number): any {
|
||||
if (index == 0) return this.obj0;
|
||||
if (index == 1) return this.obj1;
|
||||
if (index == 2) return this.obj2;
|
||||
if (index == 3) return this.obj3;
|
||||
if (index == 4) return this.obj4;
|
||||
if (index == 5) return this.obj5;
|
||||
if (index == 6) return this.obj6;
|
||||
if (index == 7) return this.obj7;
|
||||
if (index == 8) return this.obj8;
|
||||
if (index == 9) return this.obj9;
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
getMaxNumberOfObjects(): number { return _MAX_CONSTRUCTION_COUNTER; }
|
||||
}
|
||||
|
||||
|
||||
export class InjectorDynamicStrategy implements InjectorStrategy {
|
||||
objs: any[];
|
||||
|
||||
constructor(public protoStrategy: ProtoInjectorDynamicStrategy, public injector: Injector) {
|
||||
this.objs = ListWrapper.createFixedSize(protoStrategy.bindings.length);
|
||||
}
|
||||
|
||||
hydrate(): void {
|
||||
var p = this.protoStrategy;
|
||||
for (var i = 0; i < p.keyIds.length; i++) {
|
||||
if (isPresent(p.keyIds[i]) && isBlank(this.objs[i])) {
|
||||
this.objs[i] = this.injector._new(p.bindings[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dehydrate(): void { ListWrapper.fill(this.objs, null); }
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: number): any {
|
||||
var p = this.protoStrategy;
|
||||
|
||||
for (var i = 0; i < p.keyIds.length; i++) {
|
||||
if (p.keyIds[i] === keyId && (p.visibilities[i] & visibility) > 0) {
|
||||
if (isBlank(this.objs[i])) {
|
||||
this.objs[i] = this.injector._new(p.bindings[i]);
|
||||
}
|
||||
|
||||
return this.objs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return undefinedValue;
|
||||
}
|
||||
|
||||
getObjAtIndex(index: number): any {
|
||||
if (index < 0 || index >= this.objs.length) {
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
|
||||
return this.objs[index];
|
||||
}
|
||||
|
||||
getMaxNumberOfObjects(): number { return this.objs.length; }
|
||||
}
|
||||
|
||||
export class BindingData {
|
||||
constructor(public binding: ResolvedBinding, public visibility: number){};
|
||||
|
||||
getKeyId(): number { return this.binding.key.id; }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,10 +416,6 @@ function _isWaiting(obj): boolean {
|
|||
* @exportedAs angular2/di
|
||||
*/
|
||||
export class Injector {
|
||||
private _instances: List<any>;
|
||||
private _asyncStrategy: _AsyncInjectorStrategy;
|
||||
private _syncStrategy: _SyncInjectorStrategy;
|
||||
|
||||
/**
|
||||
* Turns a list of binding definitions into an internal resolved list of resolved bindings.
|
||||
*
|
||||
|
@ -108,7 +453,11 @@ export class Injector {
|
|||
*/
|
||||
static resolveAndCreate(bindings: List<Type | Binding | List<any>>,
|
||||
{defaultBindings = false}: any = {}): Injector {
|
||||
return new Injector(Injector.resolve(bindings), null, defaultBindings);
|
||||
var resolvedBindings = Injector.resolve(bindings);
|
||||
var bd = resolvedBindings.map(b => new BindingData(b, PUBLIC));
|
||||
var proto = new ProtoInjector(null, bd, 0);
|
||||
var inj = new Injector(proto);
|
||||
return inj;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,67 +470,63 @@ export class Injector {
|
|||
*/
|
||||
static fromResolvedBindings(bindings: List<ResolvedBinding>,
|
||||
{defaultBindings = false}: any = {}): Injector {
|
||||
return new Injector(bindings, null, defaultBindings);
|
||||
var bd = bindings.map(b => new BindingData(b, PUBLIC));
|
||||
var proto = new ProtoInjector(null, bd, 0);
|
||||
var inj = new Injector(proto);
|
||||
return inj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param `bindings` A sparse list of {@link ResolvedBinding}s. See `resolve` for the
|
||||
* {@link Injector}.
|
||||
* @param `parent` Parent Injector or `null` if root Injector.
|
||||
* @param `defaultBindings` Setting to true will auto-create bindings. (Only use with root
|
||||
* injector.)
|
||||
*/
|
||||
constructor(private _bindings: List<ResolvedBinding>, private _parent: Injector,
|
||||
private _defaultBindings: boolean) {
|
||||
this._instances = this._createInstances();
|
||||
this._asyncStrategy = new _AsyncInjectorStrategy(this);
|
||||
this._syncStrategy = new _SyncInjectorStrategy(this);
|
||||
_strategy: InjectorStrategy;
|
||||
_parent: Injector;
|
||||
_host: Injector;
|
||||
_constructionCounter: number = 0;
|
||||
|
||||
// TODO vsavkin remove it after DI and EI are merged
|
||||
private _ei: any;
|
||||
|
||||
constructor(public _proto: ProtoInjector) {
|
||||
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct parent of this injector.
|
||||
*/
|
||||
get(token): any { return this._getByKey(Key.get(token), unbounded, false, null); }
|
||||
|
||||
getOptional(token): any { return this._getByKey(Key.get(token), unbounded, true, null); }
|
||||
|
||||
getObjAtIndex(index: number): any { return this._strategy.getObjAtIndex(index); }
|
||||
|
||||
get parent(): Injector { return this._parent; }
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector.
|
||||
*
|
||||
* @param `token`: usually the `Type` of an object. (Same as the token used while setting up a
|
||||
*binding).
|
||||
* @returns an instance represented by the token. Throws if not found.
|
||||
*/
|
||||
get(token) { return this._getByKey(Key.get(token), false, false, false); }
|
||||
get strategy() { return this._strategy; }
|
||||
|
||||
hydrate(parent: Injector, host: Injector, ei: any) {
|
||||
this._constructionCounter = 0;
|
||||
this._parent = parent;
|
||||
this._host = host;
|
||||
this._ei = ei;
|
||||
|
||||
this._strategy.hydrate();
|
||||
}
|
||||
|
||||
dehydrate(): void { this._strategy.dehydrate(); }
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector.
|
||||
*
|
||||
* @param `token`: usually a `Type`. (Same as the token used while setting up a binding).
|
||||
* @returns an instance represented by the token. Returns `null` if not found.
|
||||
*/
|
||||
getOptional(token) { return this._getByKey(Key.get(token), false, false, true); }
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the injector asynchronously. Used with asynchronous bindings.
|
||||
*
|
||||
* @param `token`: usually a `Type`. (Same as token used while setting up a binding).
|
||||
* @returns a `Promise` which resolves to the instance represented by the token.
|
||||
*/
|
||||
asyncGet(token): Promise<any> { return this._getByKey(Key.get(token), true, false, false); }
|
||||
|
||||
/**
|
||||
* Creates a child injector and loads a new set of bindings into it.
|
||||
*
|
||||
* A resolution is a process of flattening multiple nested lists and converting individual
|
||||
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
|
||||
* for the {@link Injector} for performance-sensitive code.
|
||||
*
|
||||
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
|
||||
* recursive list of more bindings.
|
||||
*
|
||||
*/
|
||||
* Creates a child injector and loads a new set of bindings into it.
|
||||
*
|
||||
* A resolution is a process of flattening multiple nested lists and converting individual
|
||||
* bindings into a list of {@link ResolvedBinding}s. The resolution can be cached by `resolve`
|
||||
* for the {@link Injector} for performance-sensitive code.
|
||||
*
|
||||
* @param `bindings` can be a list of `Type`, {@link Binding}, {@link ResolvedBinding}, or a
|
||||
* recursive list of more bindings.
|
||||
*
|
||||
*/
|
||||
resolveAndCreateChild(bindings: List<Type | Binding | List<any>>): Injector {
|
||||
return new Injector(Injector.resolve(bindings), this, false);
|
||||
var resovledBindings = Injector.resolve(bindings);
|
||||
var bd = resovledBindings.map(b => new BindingData(b, PUBLIC));
|
||||
var proto = new ProtoInjector(this._proto, bd, 1);
|
||||
var inj = new Injector(proto);
|
||||
inj._parent = this;
|
||||
return inj;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,26 +537,184 @@ export class Injector {
|
|||
* @returns a new child {@link Injector}.
|
||||
*/
|
||||
createChildFromResolved(bindings: List<ResolvedBinding>): Injector {
|
||||
return new Injector(bindings, this, false);
|
||||
var bd = bindings.map(b => new BindingData(b, PUBLIC));
|
||||
var proto = new ProtoInjector(this._proto, bd, 1);
|
||||
var inj = new Injector(proto);
|
||||
inj._parent = this;
|
||||
return inj;
|
||||
}
|
||||
|
||||
_createInstances(): List<any> { return ListWrapper.createFixedSize(Key.numberOfKeys + 1); }
|
||||
|
||||
_getByKey(key: Key, returnPromise: boolean, returnLazy: boolean, optional: boolean) {
|
||||
if (returnLazy) {
|
||||
return () => this._getByKey(key, returnPromise, false, optional);
|
||||
_new(binding: ResolvedBinding): any {
|
||||
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
|
||||
throw new CyclicDependencyError(binding.key);
|
||||
}
|
||||
|
||||
var strategy = returnPromise ? this._asyncStrategy : this._syncStrategy;
|
||||
var factory = binding.factory;
|
||||
var deps = binding.dependencies;
|
||||
var length = deps.length;
|
||||
|
||||
var instance = strategy.readFromCache(key);
|
||||
if (instance !== _notFound) return instance;
|
||||
var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19;
|
||||
try {
|
||||
d0 = length > 0 ? this._getByDependency(deps[0], binding.key) : null;
|
||||
d1 = length > 1 ? this._getByDependency(deps[1], binding.key) : null;
|
||||
d2 = length > 2 ? this._getByDependency(deps[2], binding.key) : null;
|
||||
d3 = length > 3 ? this._getByDependency(deps[3], binding.key) : null;
|
||||
d4 = length > 4 ? this._getByDependency(deps[4], binding.key) : null;
|
||||
d5 = length > 5 ? this._getByDependency(deps[5], binding.key) : null;
|
||||
d6 = length > 6 ? this._getByDependency(deps[6], binding.key) : null;
|
||||
d7 = length > 7 ? this._getByDependency(deps[7], binding.key) : null;
|
||||
d8 = length > 8 ? this._getByDependency(deps[8], binding.key) : null;
|
||||
d9 = length > 9 ? this._getByDependency(deps[9], binding.key) : null;
|
||||
d10 = length > 10 ? this._getByDependency(deps[10], binding.key) : null;
|
||||
d11 = length > 11 ? this._getByDependency(deps[11], binding.key) : null;
|
||||
d12 = length > 12 ? this._getByDependency(deps[12], binding.key) : null;
|
||||
d13 = length > 13 ? this._getByDependency(deps[13], binding.key) : null;
|
||||
d14 = length > 14 ? this._getByDependency(deps[14], binding.key) : null;
|
||||
d15 = length > 15 ? this._getByDependency(deps[15], binding.key) : null;
|
||||
d16 = length > 16 ? this._getByDependency(deps[16], binding.key) : null;
|
||||
d17 = length > 17 ? this._getByDependency(deps[17], binding.key) : null;
|
||||
d18 = length > 18 ? this._getByDependency(deps[18], binding.key) : null;
|
||||
d19 = length > 19 ? this._getByDependency(deps[19], binding.key) : null;
|
||||
} catch (e) {
|
||||
if (e instanceof AbstractBindingError) e.addKey(binding.key);
|
||||
throw e;
|
||||
}
|
||||
|
||||
instance = strategy.instantiate(key);
|
||||
if (instance !== _notFound) return instance;
|
||||
var obj;
|
||||
try {
|
||||
switch (length) {
|
||||
case 0:
|
||||
obj = factory();
|
||||
break;
|
||||
case 1:
|
||||
obj = factory(d0);
|
||||
break;
|
||||
case 2:
|
||||
obj = factory(d0, d1);
|
||||
break;
|
||||
case 3:
|
||||
obj = factory(d0, d1, d2);
|
||||
break;
|
||||
case 4:
|
||||
obj = factory(d0, d1, d2, d3);
|
||||
break;
|
||||
case 5:
|
||||
obj = factory(d0, d1, d2, d3, d4);
|
||||
break;
|
||||
case 6:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5);
|
||||
break;
|
||||
case 7:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6);
|
||||
break;
|
||||
case 8:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7);
|
||||
break;
|
||||
case 9:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8);
|
||||
break;
|
||||
case 10:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
|
||||
break;
|
||||
case 11:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
|
||||
break;
|
||||
case 12:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
|
||||
break;
|
||||
case 13:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
|
||||
break;
|
||||
case 14:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
|
||||
break;
|
||||
case 15:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14);
|
||||
break;
|
||||
case 16:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15);
|
||||
break;
|
||||
case 17:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16);
|
||||
break;
|
||||
case 18:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17);
|
||||
break;
|
||||
case 19:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17, d18);
|
||||
break;
|
||||
case 20:
|
||||
obj = factory(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16,
|
||||
d17, d18, d19);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
throw new InstantiationError(e, e.stack, binding.key);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (isPresent(this._parent)) {
|
||||
return this._parent._getByKey(key, returnPromise, returnLazy, optional);
|
||||
private _getByDependency(dep: any, requestor: Key): any {
|
||||
var special = isPresent(this._ei) ? this._ei.getDependency(dep) : undefinedValue;
|
||||
if (special !== undefinedValue) {
|
||||
return special;
|
||||
} else {
|
||||
return this._getByKey(dep.key, dep.visibility, dep.optional, requestor);
|
||||
}
|
||||
}
|
||||
|
||||
private _getByKey(key: Key, depVisibility: any, optional: boolean, requestor: Key): any {
|
||||
if (key.token === Injector) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var inj = this;
|
||||
var ei = this._ei;
|
||||
|
||||
// TODO vsavkin remove after DI and EI are merged
|
||||
var bindingVisibility =
|
||||
isPresent(ei) && ei.isComponentKey(requestor) ? PUBLIC_AND_PRIVATE : PUBLIC;
|
||||
|
||||
var depth = depVisibility.depth;
|
||||
|
||||
if (!depVisibility.includeSelf) {
|
||||
depth -= inj._proto.distanceToParent;
|
||||
|
||||
if (isPresent(inj._parent)) {
|
||||
inj = inj._parent;
|
||||
} else {
|
||||
inj = inj._host;
|
||||
bindingVisibility = depVisibility.crossComponentBoundaries ? PUBLIC : PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
while (inj != null && depth >= 0) {
|
||||
var obj = inj._strategy.getObjByKeyId(key.id, bindingVisibility);
|
||||
if (obj !== undefinedValue) return obj;
|
||||
|
||||
depth -= inj._proto.distanceToParent;
|
||||
|
||||
// we check only one mode with the PRIVATE visibility
|
||||
if (bindingVisibility === PRIVATE) break;
|
||||
|
||||
if (isPresent(inj._parent)) {
|
||||
inj = inj._parent;
|
||||
} else {
|
||||
inj = inj._host;
|
||||
bindingVisibility = depVisibility.crossComponentBoundaries ? PUBLIC : PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO vsavkin remove after DI and EI are merged
|
||||
if (isPresent(ei)) {
|
||||
var appInj = <Injector>this._ei.appInjector(requestor);
|
||||
if (optional) {
|
||||
return appInj.getOptional(key);
|
||||
} else {
|
||||
return appInj.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (optional) {
|
||||
|
@ -221,149 +724,13 @@ export class Injector {
|
|||
}
|
||||
}
|
||||
|
||||
_resolveDependencies(key: Key, binding: ResolvedBinding, forceAsync: boolean): List<any> {
|
||||
try {
|
||||
var getDependency = d => this._getByKey(d.key, forceAsync || d.asPromise, d.lazy, d.optional);
|
||||
return ListWrapper.map(binding.dependencies, getDependency);
|
||||
} catch (e) {
|
||||
this._clear(key);
|
||||
if (e instanceof AbstractBindingError) e.addKey(key);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
_getInstance(key: Key) {
|
||||
if (this._instances.length <= key.id) return null;
|
||||
return ListWrapper.get(this._instances, key.id);
|
||||
}
|
||||
|
||||
_setInstance(key: Key, obj): void { ListWrapper.set(this._instances, key.id, obj); }
|
||||
|
||||
_getBinding(key: Key) {
|
||||
var binding = this._bindings.length <= key.id ? null : ListWrapper.get(this._bindings, key.id);
|
||||
|
||||
if (isBlank(binding) && this._defaultBindings) {
|
||||
var token: any = key.token;
|
||||
return bind(key.token).toClass(token).resolve();
|
||||
} else {
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
|
||||
_markAsConstructing(key: Key): void { this._setInstance(key, _constructing); }
|
||||
|
||||
_clear(key: Key): void { this._setInstance(key, null); }
|
||||
}
|
||||
|
||||
interface _InjectorStrategy {
|
||||
readFromCache(key: Key);
|
||||
instantiate(key: Key);
|
||||
}
|
||||
|
||||
class _SyncInjectorStrategy implements _InjectorStrategy {
|
||||
constructor(private _injector: Injector) {}
|
||||
|
||||
readFromCache(key: Key) {
|
||||
if (key.token === Injector) {
|
||||
return this._injector;
|
||||
}
|
||||
|
||||
var instance = this._injector._getInstance(key);
|
||||
|
||||
if (instance === _constructing) {
|
||||
throw new CyclicDependencyError(key);
|
||||
} else if (isPresent(instance) && !_isWaiting(instance)) {
|
||||
return instance;
|
||||
} else {
|
||||
return _notFound;
|
||||
}
|
||||
}
|
||||
|
||||
instantiate(key: Key) {
|
||||
var binding = this._injector._getBinding(key);
|
||||
if (isBlank(binding)) return _notFound;
|
||||
|
||||
if (binding.providedAsPromise) throw new AsyncBindingError(key);
|
||||
|
||||
// add a marker so we can detect cyclic dependencies
|
||||
this._injector._markAsConstructing(key);
|
||||
|
||||
var deps = this._injector._resolveDependencies(key, binding, false);
|
||||
return this._createInstance(key, binding, deps);
|
||||
}
|
||||
|
||||
_createInstance(key: Key, binding: ResolvedBinding, deps: List<any>) {
|
||||
try {
|
||||
var instance = FunctionWrapper.apply(binding.factory, deps);
|
||||
this._injector._setInstance(key, instance);
|
||||
return instance;
|
||||
} catch (e) {
|
||||
this._injector._clear(key);
|
||||
throw new InstantiationError(e, key);
|
||||
}
|
||||
// TODO vsavkin remove after DI and EI are merged
|
||||
getAppInjector(): Injector {
|
||||
if (isBlank(this._ei)) return this;
|
||||
return <Injector>this._ei.appInjector(null);
|
||||
}
|
||||
}
|
||||
|
||||
class _AsyncInjectorStrategy implements _InjectorStrategy {
|
||||
constructor(private _injector: Injector) {}
|
||||
|
||||
readFromCache(key: Key) {
|
||||
if (key.token === Injector) {
|
||||
return PromiseWrapper.resolve(this._injector);
|
||||
}
|
||||
|
||||
var instance = this._injector._getInstance(key);
|
||||
|
||||
if (instance === _constructing) {
|
||||
throw new CyclicDependencyError(key);
|
||||
} else if (_isWaiting(instance)) {
|
||||
return instance.promise;
|
||||
} else if (isPresent(instance)) {
|
||||
return PromiseWrapper.resolve(instance);
|
||||
} else {
|
||||
return _notFound;
|
||||
}
|
||||
}
|
||||
|
||||
instantiate(key: Key) /* Promise?? */ {
|
||||
var binding = this._injector._getBinding(key);
|
||||
if (isBlank(binding)) return _notFound;
|
||||
|
||||
// add a marker so we can detect cyclic dependencies
|
||||
this._injector._markAsConstructing(key);
|
||||
|
||||
var deps = this._injector._resolveDependencies(key, binding, true);
|
||||
var depsPromise = PromiseWrapper.all(deps);
|
||||
|
||||
var promise = PromiseWrapper.then(depsPromise, null, (e, s) => this._errorHandler(key, e, s))
|
||||
.then(deps => this._findOrCreate(key, binding, deps))
|
||||
.then(instance => this._cacheInstance(key, instance));
|
||||
|
||||
this._injector._setInstance(key, new _Waiting(promise));
|
||||
return promise;
|
||||
}
|
||||
|
||||
_errorHandler(key: Key, e, stack): Promise<any> {
|
||||
if (e instanceof AbstractBindingError) e.addKey(key);
|
||||
return PromiseWrapper.reject(e, stack);
|
||||
}
|
||||
|
||||
_findOrCreate(key: Key, binding: ResolvedBinding, deps: List<any>) {
|
||||
try {
|
||||
var instance = this._injector._getInstance(key);
|
||||
if (!_isWaiting(instance)) return instance;
|
||||
return FunctionWrapper.apply(binding.factory, deps);
|
||||
} catch (e) {
|
||||
this._injector._clear(key);
|
||||
throw new InstantiationError(e, key);
|
||||
}
|
||||
}
|
||||
|
||||
_cacheInstance(key, instance) {
|
||||
this._injector._setInstance(key, instance);
|
||||
return instance
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveBindings(bindings: List<Type | Binding | List<any>>): List<ResolvedBinding> {
|
||||
var resolvedList = ListWrapper.createFixedSize(bindings.length);
|
||||
|
@ -397,9 +764,7 @@ function flattenBindings(bindings: List<ResolvedBinding>): List<ResolvedBinding>
|
|||
|
||||
function _createListOfBindings(
|
||||
flattenedBindings: Map<number, ResolvedBinding>): List<ResolvedBinding> {
|
||||
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
|
||||
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
|
||||
return bindings;
|
||||
return MapWrapper.values(flattenedBindings);
|
||||
}
|
||||
|
||||
function _flattenBindings(bindings: List<ResolvedBinding | List<any>>,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {Directive, Parent} from 'angular2/annotations';
|
||||
import {Directive} from 'angular2/annotations';
|
||||
import {Parent} from 'angular2/di';
|
||||
import {ViewContainerRef, ProtoViewRef} from 'angular2/core';
|
||||
import {isPresent, isBlank, normalizeBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, List, MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
|
|
@ -49,8 +49,8 @@ class MapWrapper {
|
|||
}
|
||||
}
|
||||
static Iterable iterable(Map m) => new IterableMap(m);
|
||||
static Iterable keys(Map m) => m.keys;
|
||||
static Iterable values(Map m) => m.values;
|
||||
static List keys(Map m) => m.keys.toList();
|
||||
static List values(Map m) => m.values.toList();
|
||||
}
|
||||
|
||||
class StringMapWrapper {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {Directive, Ancestor, onDestroy, onInit} from 'angular2/angular2';
|
||||
import {Inject, forwardRef, Binding} from 'angular2/di';
|
||||
import {Directive, onDestroy, onInit} from 'angular2/angular2';
|
||||
import {Inject, Ancestor, forwardRef, Binding} from 'angular2/di';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
|||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {List, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
|
||||
import {Directive, Ancestor, onDestroy, onChange, Query, QueryList} from 'angular2/angular2';
|
||||
import {forwardRef, Binding, Inject} from 'angular2/di';
|
||||
import {Directive, onDestroy, onChange, Query, QueryList} from 'angular2/angular2';
|
||||
import {forwardRef, Ancestor, Binding, Inject} from 'angular2/di';
|
||||
|
||||
import {ControlContainer} from './control_container';
|
||||
import {NgControl} from './ng_control';
|
||||
|
|
|
@ -2,8 +2,8 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
|||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {Directive, Ancestor, onChange, Query, QueryList} from 'angular2/angular2';
|
||||
import {forwardRef, Binding} from 'angular2/di';
|
||||
import {Directive, onChange, Query, QueryList} from 'angular2/angular2';
|
||||
import {forwardRef, Ancestor, Binding} from 'angular2/di';
|
||||
|
||||
import {NgControl} from './ng_control';
|
||||
import {Control} from '../model';
|
||||
|
|
|
@ -2,8 +2,8 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
|||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {Directive, Ancestor, onChange, QueryList, Query} from 'angular2/angular2';
|
||||
import {forwardRef, Binding} from 'angular2/di';
|
||||
import {Directive, onChange, QueryList, Query} from 'angular2/angular2';
|
||||
import {forwardRef, Ancestor, Binding} from 'angular2/di';
|
||||
|
||||
import {NgControl} from './ng_control';
|
||||
import {Control} from '../model';
|
||||
|
|
|
@ -26,15 +26,17 @@ export class RouterOutlet {
|
|||
private _componentRef: ComponentRef;
|
||||
private _elementRef: ElementRef;
|
||||
private _currentInstruction: Instruction;
|
||||
private _injector: Injector;
|
||||
|
||||
constructor(elementRef: ElementRef, private _loader: DynamicComponentLoader,
|
||||
private _parentRouter: routerMod.Router, private _injector: Injector,
|
||||
private _parentRouter: routerMod.Router, _injector: Injector,
|
||||
@Attribute('name') nameAttr: string) {
|
||||
// TODO: reintroduce with new // sibling routes
|
||||
// if (isBlank(nameAttr)) {
|
||||
// nameAttr = 'default';
|
||||
//}
|
||||
|
||||
this._injector = _injector.getAppInjector();
|
||||
this._elementRef = elementRef;
|
||||
|
||||
this._childRouter = null;
|
||||
|
|
|
@ -144,6 +144,8 @@ export class TestComponentBuilder {
|
|||
|
||||
// TODO(juliemr): can/should this be optional?
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
|
||||
|
||||
return this._injector.get(DynamicComponentLoader)
|
||||
.loadAsRoot(rootComponentType, `#${rootElId}`, this._injector)
|
||||
.then((componentRef) => { return new RootTestComponent(componentRef); });
|
||||
|
|
|
@ -85,6 +85,7 @@ export function main() {
|
|||
inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise =
|
||||
bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e, t) => { throw e; });
|
||||
|
||||
PromiseWrapper.then(refPromise, null, (reason) => {
|
||||
expect(reason.message)
|
||||
.toContain(
|
||||
|
@ -108,24 +109,6 @@ export function main() {
|
|||
expect(refPromise).not.toBe(null);
|
||||
});
|
||||
|
||||
it('should resolve an injector promise and contain bindings',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||
refPromise.then((ref) => {
|
||||
expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should provide the application component in the injector',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||
refPromise.then((ref) => {
|
||||
expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display hello world', inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||
refPromise.then((ref) => {
|
||||
|
@ -151,7 +134,7 @@ export function main() {
|
|||
bootstrap(HelloRootCmp3, [testBindings, bind("appBinding").toValue("BoundValue")]);
|
||||
|
||||
refPromise.then((ref) => {
|
||||
expect(ref.injector.get(HelloRootCmp3).appBinding).toEqual("BoundValue");
|
||||
expect(ref.hostComponent.appBinding).toEqual("BoundValue");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
@ -161,7 +144,7 @@ export function main() {
|
|||
var refPromise = bootstrap(HelloRootCmp4, testBindings);
|
||||
|
||||
refPromise.then((ref) => {
|
||||
expect(ref.injector.get(HelloRootCmp4).lc).toBe(ref.injector.get(LifeCycle));
|
||||
expect(ref.hostComponent.lc).toBe(ref.injector.get(LifeCycle));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
@ -183,7 +166,7 @@ export function main() {
|
|||
.then((refs: ApplicationRef[]) => {
|
||||
var registry = refs[0].injector.get(TestabilityRegistry);
|
||||
var testabilities =
|
||||
[refs[0].injector.asyncGet(Testability), refs[1].injector.asyncGet(Testability)];
|
||||
[refs[0].injector.get(Testability), refs[1].injector.get(Testability)];
|
||||
PromiseWrapper.all(testabilities)
|
||||
.then((testabilities: Testability[]) => {
|
||||
expect(registry.findTestabilityInTree(el)).toEqual(testabilities[0]);
|
||||
|
|
|
@ -34,17 +34,13 @@ import {
|
|||
} from 'angular2/src/core/compiler/element_injector';
|
||||
import * as dirAnn from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {
|
||||
Parent,
|
||||
Ancestor,
|
||||
Unbounded,
|
||||
Attribute,
|
||||
Query,
|
||||
Component,
|
||||
Directive,
|
||||
onDestroy
|
||||
} from 'angular2/annotations';
|
||||
import * as ngDiAnn from 'angular2/src/core/annotations_impl/visibility';
|
||||
import {bind, Injector, Binding, resolveBindings, Optional, Inject, Injectable} from 'angular2/di';
|
||||
import {bind, Injector, Binding, resolveBindings, Optional, Inject, Injectable, Self, Parent, Ancestor, Unbounded, self} from 'angular2/di';
|
||||
import * as diAnn from 'angular2/src/di/annotations_impl';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
|
||||
|
@ -78,13 +74,16 @@ class DummyElementRef extends SpyObject {
|
|||
noSuchMethod(m) { return super.noSuchMethod(m); }
|
||||
}
|
||||
|
||||
@Injectable(self)
|
||||
class SimpleDirective {}
|
||||
|
||||
class SimpleService {}
|
||||
|
||||
@Injectable(self)
|
||||
class SomeOtherDirective {}
|
||||
|
||||
var _constructionCount = 0;
|
||||
@Injectable(self)
|
||||
class CountingDirective {
|
||||
count;
|
||||
constructor() {
|
||||
|
@ -93,6 +92,7 @@ class CountingDirective {
|
|||
}
|
||||
}
|
||||
|
||||
@Injectable(self)
|
||||
class FancyCountingDirective extends CountingDirective {
|
||||
constructor() { super(); }
|
||||
}
|
||||
|
@ -139,6 +139,12 @@ class NeedsService {
|
|||
constructor(@Inject("service") service) { this.service = service; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class NeedsAncestorService {
|
||||
service: any;
|
||||
constructor(@Ancestor() @Inject("service") service) { this.service = service; }
|
||||
}
|
||||
|
||||
class HasEventEmitter {
|
||||
emitter;
|
||||
constructor() { this.emitter = "emitter"; }
|
||||
|
@ -581,7 +587,6 @@ export function main() {
|
|||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
|
||||
it("should instantiate hostInjector injectables that have dependencies with set visibility",
|
||||
function() {
|
||||
var childInj = parentChildInjectors(
|
||||
|
@ -597,7 +602,7 @@ export function main() {
|
|||
bind('injectable2')
|
||||
.toFactory(
|
||||
(val) => `${val}-injectable2`,
|
||||
[[new diAnn.Inject('injectable1'), new ngDiAnn.Parent()]])
|
||||
[[new diAnn.Inject('injectable1'), new diAnn.Parent()]])
|
||||
]
|
||||
}))]);
|
||||
expect(childInj.get('injectable2')).toEqual('injectable1-injectable2');
|
||||
|
@ -648,7 +653,8 @@ export function main() {
|
|||
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
|
||||
});
|
||||
|
||||
it("should not instantiate a directive in a view that depends on hostInjector bindings of a decorator directive", () => {
|
||||
it("should not instantiate a directive in a view that has an ancestor dependency on hostInjector"+
|
||||
" bindings of a decorator directive", () => {
|
||||
expect(() => {
|
||||
hostShadowInjectors(
|
||||
ListWrapper.concat([
|
||||
|
@ -657,7 +663,7 @@ export function main() {
|
|||
hostInjector: [bind('service').toValue('hostService')]})
|
||||
)], extraBindings),
|
||||
|
||||
ListWrapper.concat([NeedsService], extraBindings)
|
||||
ListWrapper.concat([NeedsAncestorService], extraBindings)
|
||||
);
|
||||
}).toThrowError(new RegExp("No provider for service!"));
|
||||
});
|
||||
|
|
|
@ -55,7 +55,8 @@ main() {
|
|||
});
|
||||
|
||||
describe('Error handling', () {
|
||||
it('should preserve Error stack traces thrown from components', inject([
|
||||
//TODO: vsavkin reenable this test after merging DI and EI
|
||||
xit('should preserve Error stack traces thrown from components', inject([
|
||||
TestComponentBuilder,
|
||||
AsyncTestCompleter
|
||||
], (tb, async) {
|
||||
|
@ -69,7 +70,8 @@ main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should preserve non-Error stack traces thrown from components', inject([
|
||||
//TODO: vsavkin reenable this test after merging DI and EI
|
||||
xit('should preserve non-Error stack traces thrown from components', inject([
|
||||
TestComponentBuilder,
|
||||
AsyncTestCompleter
|
||||
], (tb, async) {
|
||||
|
|
|
@ -33,7 +33,18 @@ import {
|
|||
} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {Injector, bind, Injectable, Binding, forwardRef, OpaqueToken, Inject} from 'angular2/di';
|
||||
import {
|
||||
Injector,
|
||||
bind,
|
||||
Injectable,
|
||||
Binding,
|
||||
forwardRef,
|
||||
OpaqueToken,
|
||||
Inject,
|
||||
Parent,
|
||||
Ancestor,
|
||||
Unbounded
|
||||
} from 'angular2/di';
|
||||
import {
|
||||
PipeFactory,
|
||||
PipeRegistry,
|
||||
|
@ -45,18 +56,9 @@ import {
|
|||
ON_PUSH
|
||||
} from 'angular2/change_detection';
|
||||
|
||||
import {
|
||||
Directive,
|
||||
Component,
|
||||
View,
|
||||
Parent,
|
||||
Ancestor,
|
||||
Unbounded,
|
||||
Attribute,
|
||||
Query
|
||||
} from 'angular2/annotations';
|
||||
import {Directive, Component, View, Attribute, Query} from 'angular2/annotations';
|
||||
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||
import * as visAnn from 'angular2/src/core/annotations_impl/visibility';
|
||||
import * as visAnn from 'angular2/src/di/annotations_impl';
|
||||
|
||||
import {QueryList} from 'angular2/src/core/compiler/query_list';
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ export function main() {
|
|||
}
|
||||
|
||||
function directiveBinding({metadata}: {metadata?: any} = {}) {
|
||||
return new DirectiveBinding(Key.get("dummy"), null, [], false, [], [], [], metadata);
|
||||
return new DirectiveBinding(Key.get("dummy"), null, [], [], [], [], metadata);
|
||||
}
|
||||
|
||||
function createRenderProtoView(elementBinders = null, type: renderApi.ViewType = null) {
|
||||
|
|
|
@ -210,7 +210,7 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
var injector = Injector.resolveAndCreate([]);
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, injector);
|
||||
expect(utils.spy('hydrateRootHostView')).toHaveBeenCalledWith(createdViews[0], injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
|
@ -301,7 +301,7 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
var injector = Injector.resolveAndCreate([]);
|
||||
var contextView =
|
||||
createView(createProtoView([createEmptyElBinder(), createEmptyElBinder()]));
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, wrapPv(childProtoView),
|
||||
|
|
|
@ -42,7 +42,7 @@ export function main() {
|
|||
var directiveResolver;
|
||||
var utils;
|
||||
|
||||
function createInjector() { return new Injector([], null, false); }
|
||||
function createInjector() { return Injector.resolveAndCreate([]); }
|
||||
|
||||
function createDirectiveBinding(type) {
|
||||
var annotation = directiveResolver.resolve(type);
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
import {Injector, bind, Key} from 'angular2/di';
|
||||
import {Inject, InjectPromise, Injectable} from 'angular2/src/di/decorators';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
class UserList {}
|
||||
|
||||
function fetchUsers() {
|
||||
return PromiseWrapper.resolve(new UserList());
|
||||
}
|
||||
|
||||
class SynchronousUserList {}
|
||||
|
||||
@Injectable()
|
||||
class UserController {
|
||||
list: UserList;
|
||||
constructor(list: UserList) { this.list = list; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class AsyncUserController {
|
||||
userList;
|
||||
constructor(@InjectPromise(UserList) userList) { this.userList = userList; }
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe("async injection", function() {
|
||||
|
||||
describe("asyncGet", function() {
|
||||
it('should return a promise', function() {
|
||||
var injector = Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers)]);
|
||||
var p = injector.asyncGet(UserList);
|
||||
expect(p).toBePromise();
|
||||
});
|
||||
|
||||
it('should return a promise when the binding is sync', function() {
|
||||
var injector = Injector.resolveAndCreate([SynchronousUserList]);
|
||||
var p = injector.asyncGet(SynchronousUserList);
|
||||
expect(p).toBePromise();
|
||||
});
|
||||
|
||||
it("should return a promise when the binding is sync (from cache)", function() {
|
||||
var injector = Injector.resolveAndCreate([UserList]);
|
||||
expect(injector.get(UserList)).toBeAnInstanceOf(UserList);
|
||||
expect(injector.asyncGet(UserList)).toBePromise();
|
||||
});
|
||||
|
||||
it('should return the injector', inject([AsyncTestCompleter], (async) => {
|
||||
var injector = Injector.resolveAndCreate([]);
|
||||
var p = injector.asyncGet(Injector);
|
||||
p.then(function(injector) {
|
||||
expect(injector).toBe(injector);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return a promise when instantiating a sync binding ' +
|
||||
'with an async dependency',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var injector = Injector.resolveAndCreate(
|
||||
[bind(UserList).toAsyncFactory(fetchUsers), UserController]);
|
||||
|
||||
injector.asyncGet(UserController)
|
||||
.then(function(userController) {
|
||||
expect(userController).toBeAnInstanceOf(UserController);
|
||||
expect(userController.list).toBeAnInstanceOf(UserList);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should create only one instance (async + async)",
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var injector = Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers)]);
|
||||
|
||||
var ul1 = injector.asyncGet(UserList);
|
||||
var ul2 = injector.asyncGet(UserList);
|
||||
|
||||
PromiseWrapper.all([ul1, ul2])
|
||||
.then(function(uls) {
|
||||
expect(uls[0]).toBe(uls[1]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should create only one instance (sync + async)", inject([AsyncTestCompleter], (async) => {
|
||||
var injector = Injector.resolveAndCreate([UserList]);
|
||||
|
||||
var promise = injector.asyncGet(UserList);
|
||||
var ul = injector.get(UserList);
|
||||
|
||||
expect(promise).toBePromise();
|
||||
expect(ul).toBeAnInstanceOf(UserList);
|
||||
|
||||
promise.then(function(ful) {
|
||||
expect(ful).toBe(ul);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show the full path when error happens in a constructor',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var injector = Injector.resolveAndCreate([
|
||||
UserController,
|
||||
bind(UserList).toAsyncFactory(function() { throw "Broken UserList"; })
|
||||
]);
|
||||
|
||||
var promise = injector.asyncGet(UserController);
|
||||
PromiseWrapper.then(promise, null, function(e) {
|
||||
expect(e.message).toContain(
|
||||
`Error during instantiation of UserList! (${stringify(UserController)} -> UserList)`);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("get", function() {
|
||||
it('should throw when instantiating an async binding', function() {
|
||||
var injector = Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers)]);
|
||||
|
||||
expect(() => injector.get(UserList))
|
||||
.toThrowError(
|
||||
'Cannot instantiate UserList synchronously. It is provided as a promise!');
|
||||
});
|
||||
|
||||
it('should throw when instantiating a sync binding with an async dependency', function() {
|
||||
var injector =
|
||||
Injector.resolveAndCreate([bind(UserList).toAsyncFactory(fetchUsers), UserController]);
|
||||
|
||||
expect(() => injector.get(UserController))
|
||||
.toThrowError(new RegExp(
|
||||
'Cannot instantiate UserList synchronously. It is provided as a promise!'));
|
||||
});
|
||||
|
||||
it('should not throw when instantiating a sync binding with a resolved async dependency',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var injector = Injector.resolveAndCreate(
|
||||
[bind(UserList).toAsyncFactory(fetchUsers), UserController]);
|
||||
|
||||
injector.asyncGet(UserList).then((_) => {
|
||||
expect(() => { injector.get(UserController); }).not.toThrow();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should resolve synchronously when an async dependency requested as a promise',
|
||||
function() {
|
||||
var injector = Injector.resolveAndCreate(
|
||||
[bind(UserList).toAsyncFactory(fetchUsers), AsyncUserController]);
|
||||
var controller = injector.get(AsyncUserController);
|
||||
|
||||
expect(controller).toBeAnInstanceOf(AsyncUserController);
|
||||
expect(controller.userList).toBePromise();
|
||||
});
|
||||
|
||||
it('should wrap sync dependencies into promises if required', function() {
|
||||
var injector = Injector.resolveAndCreate(
|
||||
[bind(UserList).toFactory(() => new UserList()), AsyncUserController]);
|
||||
var controller = injector.get(AsyncUserController);
|
||||
|
||||
expect(controller).toBeAnInstanceOf(AsyncUserController);
|
||||
expect(controller.userList).toBePromise();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -28,14 +28,10 @@ main() {
|
|||
expect(const Binding(Foo, toFactory: fn).toFactory).toBe(fn);
|
||||
});
|
||||
|
||||
it('can create constant from async factory', () {
|
||||
expect(const Binding(Foo, toAsyncFactory: fn).toAsyncFactory).toBe(fn);
|
||||
});
|
||||
|
||||
it('can be used in annotation', () {
|
||||
ClassMirror mirror = reflectType(Annotated);
|
||||
var bindings = mirror.metadata[0].reflectee.bindings;
|
||||
expect(bindings.length).toBe(6);
|
||||
expect(bindings.length).toBe(5);
|
||||
bindings.forEach((b) {
|
||||
expect(b).toBeA(Binding);
|
||||
});
|
||||
|
@ -57,7 +53,6 @@ class Annotation {
|
|||
const Binding(Foo, toClass: Bar),
|
||||
const Binding(Foo, toValue: 5),
|
||||
const Binding(Foo, toAlias: Bar),
|
||||
const Binding(Foo, toFactory: fn),
|
||||
const Binding(Foo, toAsyncFactory: fn),
|
||||
const Binding(Foo, toFactory: fn)
|
||||
])
|
||||
class Annotated {}
|
||||
|
|
|
@ -9,13 +9,22 @@ import {
|
|||
DependencyAnnotation,
|
||||
Injectable
|
||||
} from 'angular2/di';
|
||||
import {Optional, Inject, InjectLazy} from 'angular2/src/di/decorators';
|
||||
import {Optional, Inject} from 'angular2/src/di/decorators';
|
||||
import * as ann from 'angular2/src/di/annotations_impl';
|
||||
|
||||
class CustomDependencyAnnotation extends DependencyAnnotation {}
|
||||
|
||||
class Engine {}
|
||||
|
||||
@Injectable(ann.self)
|
||||
class EngineWithSetVisibility {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class CarNeedsEngineWithSetVisibility {
|
||||
constructor(engine: EngineWithSetVisibility) {}
|
||||
}
|
||||
|
||||
class BrokenEngine {
|
||||
constructor() { throw new BaseException("Broken Engine"); }
|
||||
}
|
||||
|
@ -35,12 +44,6 @@ class Car {
|
|||
constructor(engine: Engine) { this.engine = engine; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class CarWithLazyEngine {
|
||||
engineFactory;
|
||||
constructor(@InjectLazy(Engine) engineFactory) { this.engineFactory = engineFactory; }
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class CarWithOptionalEngine {
|
||||
engine;
|
||||
|
@ -236,10 +239,6 @@ export function main() {
|
|||
expect(() => injector.get(Car))
|
||||
.toThrowError(
|
||||
`Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)})`);
|
||||
|
||||
expect(() => injector.asyncGet(Car))
|
||||
.toThrowError(
|
||||
`Cannot instantiate cyclic dependency! (${stringify(Car)} -> ${stringify(Engine)} -> ${stringify(Car)})`);
|
||||
});
|
||||
|
||||
it('should show the full path when error happens in a constructor', () => {
|
||||
|
@ -274,25 +273,6 @@ export function main() {
|
|||
expect(injector.get('null')).toBe(null);
|
||||
});
|
||||
|
||||
describe("default bindings", () => {
|
||||
it("should be used when no matching binding found", () => {
|
||||
var injector = Injector.resolveAndCreate([], {defaultBindings: true});
|
||||
|
||||
var car = injector.get(Car);
|
||||
|
||||
expect(car).toBeAnInstanceOf(Car);
|
||||
});
|
||||
|
||||
it("should use the matching binding when it is available", () => {
|
||||
var injector =
|
||||
Injector.resolveAndCreate([bind(Car).toClass(SportsCar)], {defaultBindings: true});
|
||||
|
||||
var car = injector.get(Car);
|
||||
|
||||
expect(car).toBeAnInstanceOf(SportsCar);
|
||||
});
|
||||
});
|
||||
|
||||
describe("child", () => {
|
||||
it('should load instances from parent injector', () => {
|
||||
var parent = Injector.resolveAndCreate([Engine]);
|
||||
|
@ -324,17 +304,6 @@ export function main() {
|
|||
expect(engineFromChild).toBeAnInstanceOf(TurboEngine);
|
||||
});
|
||||
|
||||
it("should create child injectors without default bindings", () => {
|
||||
var parent = Injector.resolveAndCreate([], {defaultBindings: true});
|
||||
var child = parent.resolveAndCreateChild([]);
|
||||
|
||||
// child delegates to parent the creation of Car
|
||||
var childCar = child.get(Car);
|
||||
var parentCar = parent.get(Car);
|
||||
|
||||
expect(childCar).toBe(parentCar);
|
||||
});
|
||||
|
||||
it("should give access to direct parent", () => {
|
||||
var parent = Injector.resolveAndCreate([]);
|
||||
var child = parent.resolveAndCreateChild([]);
|
||||
|
@ -342,25 +311,6 @@ export function main() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("lazy", () => {
|
||||
it("should create dependencies lazily", () => {
|
||||
var injector = Injector.resolveAndCreate([Engine, CarWithLazyEngine]);
|
||||
|
||||
var car = injector.get(CarWithLazyEngine);
|
||||
expect(car.engineFactory()).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
it("should cache instance created lazily", () => {
|
||||
var injector = Injector.resolveAndCreate([Engine, CarWithLazyEngine]);
|
||||
|
||||
var car = injector.get(CarWithLazyEngine);
|
||||
var e1 = car.engineFactory();
|
||||
var e2 = car.engineFactory();
|
||||
|
||||
expect(e1).toBe(e2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolve', () => {
|
||||
it('should resolve and flatten', () => {
|
||||
var bindings = Injector.resolve([Engine, [BrokenEngine]]);
|
||||
|
@ -374,20 +324,16 @@ export function main() {
|
|||
var bindings = Injector.resolve([
|
||||
forwardRef(() => Engine),
|
||||
[bind(forwardRef(() => BrokenEngine)).toClass(forwardRef(() => Engine))],
|
||||
bind(forwardRef(() => String)).toFactory(() => 'OK', [forwardRef(() => Engine)]),
|
||||
bind(forwardRef(() => DashboardSoftware))
|
||||
.toAsyncFactory(() => 123, [forwardRef(() => BrokenEngine)])
|
||||
bind(forwardRef(() => String)).toFactory(() => 'OK', [forwardRef(() => Engine)])
|
||||
]);
|
||||
|
||||
var engineBinding = bindings[Key.get(Engine).id];
|
||||
var brokenEngineBinding = bindings[Key.get(BrokenEngine).id];
|
||||
var stringBinding = bindings[Key.get(String).id];
|
||||
var dashboardSoftwareBinding = bindings[Key.get(DashboardSoftware).id];
|
||||
var engineBinding = bindings[0];
|
||||
var brokenEngineBinding = bindings[1];
|
||||
var stringBinding = bindings[2];
|
||||
|
||||
expect(engineBinding.factory() instanceof Engine).toBe(true);
|
||||
expect(brokenEngineBinding.factory() instanceof Engine).toBe(true);
|
||||
expect(stringBinding.dependencies[0].key).toEqual(Key.get(Engine));
|
||||
expect(dashboardSoftwareBinding.dependencies[0].key).toEqual(Key.get(BrokenEngine));
|
||||
});
|
||||
|
||||
it('should support overriding factory dependencies with dependency annotations', () => {
|
||||
|
@ -396,11 +342,25 @@ export function main() {
|
|||
.toFactory((e) => "result",
|
||||
[[new ann.Inject("dep"), new CustomDependencyAnnotation()]])
|
||||
]);
|
||||
var binding = bindings[Key.get("token").id];
|
||||
var binding = bindings[0];
|
||||
|
||||
expect(binding.dependencies[0].key.token).toEqual("dep");
|
||||
expect(binding.dependencies[0].properties).toEqual([new CustomDependencyAnnotation()]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("default visibility", () => {
|
||||
it("should use the provided visibility", () => {
|
||||
var bindings = Injector.resolve([CarNeedsEngineWithSetVisibility, EngineWithSetVisibility]);
|
||||
var carBinding = bindings[0];
|
||||
expect(carBinding.dependencies[0].visibility).toEqual(ann.self);
|
||||
});
|
||||
|
||||
it("should set the default visibility to unbounded", () => {
|
||||
var bindings = Injector.resolve([Car, Engine]);
|
||||
var carBinding = bindings[0];
|
||||
expect(carBinding.dependencies[0].visibility).toEqual(ann.unbounded);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ export function main() {
|
|||
var router = applicationRef.hostComponent.router;
|
||||
PromiseWrapper.catchError(router.navigate('/cause-error'), (error) => {
|
||||
expect(el).toHaveText('outer { oh no }');
|
||||
expect(error.message).toBe('oops!');
|
||||
expect(error.message).toContain('oops!');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
|
@ -89,7 +89,6 @@ export function main() {
|
|||
router.navigate('/parent/child');
|
||||
});
|
||||
}));
|
||||
|
||||
// TODO: add a test in which the child component has bindings
|
||||
});
|
||||
}
|
||||
|
@ -107,14 +106,12 @@ class AppCmp {
|
|||
constructor(public router: Router, public location: LocationStrategy) {}
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'parent-cmp'})
|
||||
@View({template: `parent { <router-outlet></router-outlet> }`, directives: routerDirectives})
|
||||
@RouteConfig([{path: '/child', component: HelloCmp}])
|
||||
class ParentCmp {
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'app-cmp'})
|
||||
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
|
||||
@RouteConfig([{path: '/parent/...', component: ParentCmp}])
|
||||
|
|
|
@ -23,7 +23,7 @@ export function main() {
|
|||
bootstrap(AppComponent)
|
||||
.then((ref) => {
|
||||
var injector = ref.injector;
|
||||
var app: AppComponent = injector.get(AppComponent);
|
||||
var app: AppComponent = ref.hostComponent;
|
||||
var lifeCycle = injector.get(LifeCycle);
|
||||
|
||||
bindAction('#reset', function() {
|
||||
|
|
|
@ -125,7 +125,7 @@ export function main() {
|
|||
bootstrap(AppComponent, _createBindings())
|
||||
.then((ref) => {
|
||||
var injector = ref.injector;
|
||||
app = injector.get(AppComponent);
|
||||
app = ref.hostComponent;
|
||||
lifecycle = injector.get(LifeCycle);
|
||||
bindAction('#ng2DestroyDom', ng2DestroyDom);
|
||||
bindAction('#ng2CreateDom', ng2CreateDom);
|
||||
|
|
|
@ -85,7 +85,6 @@ export function main() {
|
|||
function ng2CreateDom() {
|
||||
var values = count++ % 2 == 0 ? ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*'] :
|
||||
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', '-'];
|
||||
|
||||
app.initData = buildTree(maxDepth, values, 0);
|
||||
lifeCycle.tick();
|
||||
}
|
||||
|
@ -98,7 +97,7 @@ export function main() {
|
|||
var injector = ref.injector;
|
||||
lifeCycle = injector.get(LifeCycle);
|
||||
|
||||
app = injector.get(AppComponent);
|
||||
app = ref.hostComponent;
|
||||
bindAction('#ng2DestroyDom', ng2DestroyDom);
|
||||
bindAction('#ng2CreateDom', ng2CreateDom);
|
||||
bindAction('#ng2UpdateDomProfile', profile(ng2CreateDom, noop, 'ng2-update'));
|
||||
|
|
|
@ -8,10 +8,10 @@ export class MultiMetric extends Metric {
|
|||
static createBindings(childTokens): List<Binding> {
|
||||
return [
|
||||
bind(_CHILDREN)
|
||||
.toAsyncFactory((injector) => PromiseWrapper.all(
|
||||
ListWrapper.map(childTokens, (token) => injector.asyncGet(token))),
|
||||
[Injector]),
|
||||
bind(MultiMetric).toFactory((children) => new MultiMetric(children), [_CHILDREN])
|
||||
.toFactory(
|
||||
(injector: Injector) => ListWrapper.map(childTokens, (token) => injector.get(token)),
|
||||
[Injector]),
|
||||
bind(MultiMetric).toFactory(children => new MultiMetric(children), [_CHILDREN])
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -52,4 +52,4 @@ function mergeStringMaps(maps): Object {
|
|||
return result;
|
||||
}
|
||||
|
||||
var _CHILDREN = new OpaqueToken('MultiMetric.children');
|
||||
var _CHILDREN = new OpaqueToken('MultiMetric.children');
|
|
@ -9,10 +9,10 @@ export class MultiReporter extends Reporter {
|
|||
static createBindings(childTokens: List<any>): List<Binding> {
|
||||
return [
|
||||
bind(_CHILDREN)
|
||||
.toAsyncFactory((injector) => PromiseWrapper.all(
|
||||
ListWrapper.map(childTokens, (token) => injector.asyncGet(token))),
|
||||
[Injector]),
|
||||
bind(MultiReporter).toFactory((children) => new MultiReporter(children), [_CHILDREN])
|
||||
.toFactory(
|
||||
(injector: Injector) => ListWrapper.map(childTokens, (token) => injector.get(token)),
|
||||
[Injector]),
|
||||
bind(MultiReporter).toFactory(children => new MultiReporter(children), [_CHILDREN])
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Injector, bind, Binding} from 'angular2/di';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {Sampler, SampleState} from './sampler';
|
||||
import {ConsoleReporter} from './reporter/console_reporter';
|
||||
|
@ -50,9 +50,31 @@ export class Runner {
|
|||
if (isPresent(bindings)) {
|
||||
sampleBindings.push(bindings);
|
||||
}
|
||||
return Injector.resolveAndCreate(sampleBindings)
|
||||
.asyncGet(Sampler)
|
||||
.then((sampler) => sampler.sample());
|
||||
|
||||
var inj = Injector.resolveAndCreate(sampleBindings);
|
||||
var adapter = inj.get(WebDriverAdapter);
|
||||
|
||||
return PromiseWrapper
|
||||
.all([adapter.capabilities(), adapter.executeScript('return window.navigator.userAgent;')])
|
||||
.then((args) => {
|
||||
var capabilities = args[0];
|
||||
var userAgent = args[1];
|
||||
|
||||
// This might still create instances twice. We are creating a new injector with all the
|
||||
// bindings.
|
||||
// Only WebDriverAdapter is reused.
|
||||
// TODO vsavkin consider changing it when toAsyncFactory is added back or when child
|
||||
// injectors are handled better.
|
||||
var injector = Injector.resolveAndCreate([
|
||||
sampleBindings,
|
||||
bind(Options.CAPABILITIES).toValue(capabilities),
|
||||
bind(Options.USER_AGENT).toValue(userAgent),
|
||||
bind(WebDriverAdapter).toValue(adapter)
|
||||
]);
|
||||
|
||||
var sampler = injector.get(Sampler);
|
||||
return sampler.sample();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,10 +96,4 @@ var _DEFAULT_BINDINGS = [
|
|||
Validator.bindTo(RegressionSlopeValidator),
|
||||
WebDriverExtension.bindTo([ChromeDriverExtension, FirefoxDriverExtension, IOsDriverExtension]),
|
||||
Metric.bindTo(MultiMetric),
|
||||
|
||||
bind(Options.CAPABILITIES)
|
||||
.toAsyncFactory((adapter) => adapter.capabilities(), [WebDriverAdapter]),
|
||||
bind(Options.USER_AGENT)
|
||||
.toAsyncFactory((adapter) => adapter.executeScript('return window.navigator.userAgent;'),
|
||||
[WebDriverAdapter])
|
||||
];
|
||||
|
|
|
@ -14,11 +14,11 @@ import {Options} from './common_options';
|
|||
@ABSTRACT()
|
||||
export class WebDriverExtension {
|
||||
static bindTo(childTokens): List<Binding> {
|
||||
return [
|
||||
var res = [
|
||||
bind(_CHILDREN)
|
||||
.toAsyncFactory((injector) => PromiseWrapper.all(
|
||||
ListWrapper.map(childTokens, (token) => injector.asyncGet(token))),
|
||||
[Injector]),
|
||||
.toFactory(
|
||||
(injector: Injector) => ListWrapper.map(childTokens, (token) => injector.get(token)),
|
||||
[Injector]),
|
||||
bind(WebDriverExtension)
|
||||
.toFactory(
|
||||
(children, capabilities) => {
|
||||
|
@ -35,6 +35,7 @@ export class WebDriverExtension {
|
|||
},
|
||||
[_CHILDREN, Options.CAPABILITIES])
|
||||
];
|
||||
return res;
|
||||
}
|
||||
|
||||
gc(): Promise<any> { throw new BaseException('NYI'); }
|
||||
|
|
|
@ -18,15 +18,15 @@ import {Metric, MultiMetric, bind, Injector} from 'benchpress/common';
|
|||
|
||||
export function main() {
|
||||
function createMetric(ids) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockMetric(id))),
|
||||
MultiMetric.createBindings(ids)
|
||||
])
|
||||
.asyncGet(MultiMetric);
|
||||
var m = Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockMetric(id))),
|
||||
MultiMetric.createBindings(ids)
|
||||
])
|
||||
.get(MultiMetric);
|
||||
return PromiseWrapper.resolve(m);
|
||||
}
|
||||
|
||||
describe('multi metric', () => {
|
||||
|
||||
it('should merge descriptions', inject([AsyncTestCompleter], (async) => {
|
||||
createMetric(['m1', 'm2'])
|
||||
.then((m) => {
|
||||
|
|
|
@ -19,11 +19,12 @@ import {Reporter, MultiReporter, bind, Injector, MeasureValues} from 'benchpress
|
|||
|
||||
export function main() {
|
||||
function createReporters(ids) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockReporter(id))),
|
||||
MultiReporter.createBindings(ids)
|
||||
])
|
||||
.asyncGet(MultiReporter);
|
||||
var r = Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockReporter(id))),
|
||||
MultiReporter.createBindings(ids)
|
||||
])
|
||||
.get(MultiReporter);
|
||||
return PromiseWrapper.resolve(r);
|
||||
}
|
||||
|
||||
describe('multi reporter', () => {
|
||||
|
|
|
@ -52,7 +52,7 @@ export function main() {
|
|||
it('should set SampleDescription.id', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner()
|
||||
.sample({id: 'someId'})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((_) => injector.get(SampleDescription))
|
||||
.then((desc) => {
|
||||
expect(desc.id).toBe('someId');
|
||||
async.done();
|
||||
|
@ -62,9 +62,8 @@ export function main() {
|
|||
it('should merge SampleDescription.description', inject([AsyncTestCompleter], (async) => {
|
||||
createRunner([bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1})])
|
||||
.sample({id: 'someId', bindings: [bind(Options.SAMPLE_DESCRIPTION).toValue({'b': 2})]})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((_) => injector.get(SampleDescription))
|
||||
.then((desc) => {
|
||||
|
||||
expect(desc.description)
|
||||
.toEqual(
|
||||
{'forceGc': false, 'userAgent': 'someUserAgent', 'a': 1, 'b': 2, 'v': 11});
|
||||
|
@ -76,7 +75,7 @@ export function main() {
|
|||
inject([AsyncTestCompleter], (async) => {
|
||||
createRunner()
|
||||
.sample({id: 'someId'})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((_) => injector.get(SampleDescription))
|
||||
.then((desc) => {
|
||||
|
||||
expect(desc.metrics).toEqual({'m1': 'some metric'});
|
||||
|
@ -125,10 +124,10 @@ export function main() {
|
|||
.toValue({'a': 2}),
|
||||
]
|
||||
})
|
||||
.then((_) => injector.asyncGet(SampleDescription))
|
||||
.then((_) => injector.get(SampleDescription))
|
||||
.then((desc) => {
|
||||
|
||||
expect(injector.get(SampleDescription).description['a']).toBe(2);
|
||||
expect(desc.description['a']).toBe(2);
|
||||
async.done();
|
||||
});
|
||||
|
||||
|
@ -139,6 +138,7 @@ export function main() {
|
|||
|
||||
class MockWebDriverAdapter extends WebDriverAdapter {
|
||||
executeScript(script): Promise<string> { return PromiseWrapper.resolve('someUserAgent'); }
|
||||
capabilities() { return null; }
|
||||
}
|
||||
|
||||
class MockValidator extends Validator {
|
||||
|
|
|
@ -19,12 +19,14 @@ import {WebDriverExtension, bind, Injector, Options} from 'benchpress/common';
|
|||
|
||||
export function main() {
|
||||
function createExtension(ids, caps) {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockExtension(id))),
|
||||
bind(Options.CAPABILITIES).toValue(caps),
|
||||
WebDriverExtension.bindTo(ids)
|
||||
])
|
||||
.asyncGet(WebDriverExtension);
|
||||
return PromiseWrapper.wrap(() => {
|
||||
return Injector.resolveAndCreate([
|
||||
ListWrapper.map(ids, (id) => bind(id).toValue(new MockExtension(id))),
|
||||
bind(Options.CAPABILITIES).toValue(caps),
|
||||
WebDriverExtension.bindTo(ids)
|
||||
])
|
||||
.get(WebDriverExtension);
|
||||
});
|
||||
}
|
||||
|
||||
describe('WebDriverExtension.bindTo', () => {
|
||||
|
@ -44,7 +46,6 @@ export function main() {
|
|||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,4 +10,4 @@
|
|||
<demo-app>Loading...</demo-app>
|
||||
$SCRIPTS$
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
Loading…
Reference in New Issue