Skip to content

Commit

Permalink
Working on #193 (#429)
Browse files Browse the repository at this point in the history
* Remove default exports

* Upgraded tslint

* Implemented #408

* Implementes #419

* fixes #405

* release 3.0.0-beta.2

* Implements #421 & refactor enum -> literal types

* Added #421 docs

* Working on #193

* Working on #193

* Working on #193

* Working on #193

* Working on #193

* Working on #193

* Working on #193

* Working on #193
  • Loading branch information
remojansen committed Dec 2, 2016
1 parent a268e4c commit 9f5f1ac
Show file tree
Hide file tree
Showing 31 changed files with 312 additions and 221 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ InversifyJS requires a modern JavaScript engine with support for:

If your environment don't support one of these you will need to import a shim or polyfill.

> :warning: **The `reflect-metadata` polyfill should be imported only once in your entire application** because the Reflect object is mean to be a global singleton. More details about this can be found [here](https://github.com/inversify/InversifyJS/issues/262#issuecomment-227593844).
Check out the [Environment support and polyfills](https://github.com/inversify/InversifyJS/blob/master/wiki/environment.md)
page in the wiki and the [Basic example](https://github.com/inversify/inversify-basic-example) to learn more.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"run-sequence": "^1.2.0",
"sinon": "^1.17.3",
"tslint": "^4.0.1",
"typescript": "^2.0.2",
"typescript": "^2.1.1",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
}
Expand Down
14 changes: 8 additions & 6 deletions src/bindings/binding.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { interfaces } from "../interfaces/interfaces";
import { guid } from "../utils/guid";
import { BindingTypeEnum } from "../constants/literal_types";

class Binding<T> implements interfaces.Binding<T> {

public guid: string;
Expand All @@ -15,13 +16,13 @@ class Binding<T> implements interfaces.Binding<T> {
public serviceIdentifier: interfaces.ServiceIdentifier<T>;

// The constructor of a class which must implement T
public implementationType: interfaces.Newable<T>;
public implementationType: interfaces.Newable<T> | null;

// Cache used to allow singleton scope and BindingType.ConstantValue bindings
public cache: T;
public cache: T | null;

// Cache used to allow BindingType.DynamicValue bindings
public dynamicValue: (context: interfaces.Context) => T;
public dynamicValue: ((context: interfaces.Context) => T) | null;

// The scope mode to be used
public scope: interfaces.BindingScope;
Expand All @@ -30,16 +31,16 @@ class Binding<T> implements interfaces.Binding<T> {
public type: interfaces.BindingType;

// A factory method used in BindingType.Factory bindings
public factory: interfaces.FactoryCreator<T>;
public factory: interfaces.FactoryCreator<T> | null;

// An async factory method used in BindingType.Provider bindings
public provider: interfaces.ProviderCreator<T>;
public provider: interfaces.ProviderCreator<T> | null;

// A constraint used to limit the contexts in which this binding is applicable
public constraint: (request: interfaces.Request) => boolean;

// On activation handler (invoked just before an instance is added to cache and injected)
public onActivation: (context: interfaces.Context, injectable: T) => T;
public onActivation: ((context: interfaces.Context, injectable: T) => T) | null;

constructor(serviceIdentifier: interfaces.ServiceIdentifier<T>, defaultScope: interfaces.BindingScope) {
this.guid = guid();
Expand All @@ -53,6 +54,7 @@ class Binding<T> implements interfaces.Binding<T> {
this.factory = null;
this.provider = null;
this.onActivation = null;
this.dynamicValue = null;
}

public clone(): interfaces.Binding<T> {
Expand Down
6 changes: 4 additions & 2 deletions src/constants/error_msgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ export const INVALID_DECORATOR_OPERATION = "The @inject @multiInject @tagged and
export const ARGUMENTS_LENGTH_MISMATCH_1 = "The number of constructor arguments in the derived class ";
export const ARGUMENTS_LENGTH_MISMATCH_2 = " must be >= than the number of constructor arguments of its base class.";

export const KERNEL_OPTIONS_MUST_BE_AN_OBJECT = "Invalid Container constructor argument. Container options " +
export const CONTAINER_OPTIONS_MUST_BE_AN_OBJECT = "Invalid Container constructor argument. Container options " +
"must be an object.";

export const KERNEL_OPTIONS_INVALID_DEFAULT_SCOPE = "Invalid Container option. Default scope must " +
export const CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE = "Invalid Container option. Default scope must " +
"be a string ('singleton' or 'transient').";

export const INVALID_BINDING_PROPERTY = "TODO";
68 changes: 38 additions & 30 deletions src/container/container.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { interfaces } from "../interfaces/interfaces";
import { Binding } from "../bindings/binding";
import { Lookup } from "./lookup";
import { plan, createMockRequest } from "../planning/planner";
import { plan, createMockRequest, getBindingDictionary } from "../planning/planner";
import { resolve } from "../resolution/resolver";
import { BindingToSyntax } from "../syntax/binding_to_syntax";
import { getServiceIdentifierAsString } from "../utils/serialization";
Expand All @@ -14,18 +14,18 @@ import { BindingScopeEnum, TargetTypeEnum } from "../constants/literal_types";
class Container implements interfaces.Container {

public guid: string;
public parent: interfaces.Container | null;
public readonly options: interfaces.ContainerOptions;
private _middleware: interfaces.Next;
private _middleware: interfaces.Next | null;
private _bindingDictionary: interfaces.Lookup<interfaces.Binding<any>>;
private _snapshots: Array<interfaces.ContainerSnapshot>;
private _parentContainer: interfaces.Container;

public static merge(container1: interfaces.Container, container2: interfaces.Container): interfaces.Container {

let container = new Container();
let bindingDictionary: interfaces.Lookup<interfaces.Binding<any>> = (<any>container)._bindingDictionary;
let bindingDictionary1: interfaces.Lookup<interfaces.Binding<any>> = (<any>container1)._bindingDictionary;
let bindingDictionary2: interfaces.Lookup<interfaces.Binding<any>> = (<any>container2)._bindingDictionary;
let bindingDictionary: interfaces.Lookup<interfaces.Binding<any>> = getBindingDictionary(container);
let bindingDictionary1: interfaces.Lookup<interfaces.Binding<any>> = getBindingDictionary(container1);
let bindingDictionary2: interfaces.Lookup<interfaces.Binding<any>> = getBindingDictionary(container2);

function copyDictionary(
origing: interfaces.Lookup<interfaces.Binding<any>>,
Expand All @@ -52,14 +52,14 @@ class Container implements interfaces.Container {
if (containerOptions !== undefined) {

if (typeof containerOptions !== "object") {
throw new Error(`${ERROR_MSGS.KERNEL_OPTIONS_MUST_BE_AN_OBJECT}`);
throw new Error(`${ERROR_MSGS.CONTAINER_OPTIONS_MUST_BE_AN_OBJECT}`);
} else if (containerOptions.defaultScope === undefined) {
throw new Error(`${ERROR_MSGS.KERNEL_OPTIONS_INVALID_DEFAULT_SCOPE}`);
throw new Error(`${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE}`);
} else if (
containerOptions.defaultScope !== BindingScopeEnum.Singleton &&
containerOptions.defaultScope !== BindingScopeEnum.Transient
) {
throw new Error(`${ERROR_MSGS.KERNEL_OPTIONS_INVALID_DEFAULT_SCOPE}`);
throw new Error(`${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE}`);
}

this.options = {
Expand All @@ -76,22 +76,29 @@ class Container implements interfaces.Container {
this._bindingDictionary = new Lookup<interfaces.Binding<any>>();
this._snapshots = [];
this._middleware = null;
this._parentContainer = null;
this.parent = null;
}

public load(...modules: interfaces.ContainerModule[]): void {

let setModuleId = (bindingToSyntax: any, moduleId: string) => {
bindingToSyntax._binding.moduleId = moduleId;
};

let getBindFunction = (moduleId: string) => {
return (serviceIdentifier: interfaces.ServiceIdentifier<any>) => {
let _bind = this.bind.bind(this);
let bindingToSyntax = _bind(serviceIdentifier);
(<any>bindingToSyntax)._binding.moduleId = moduleId;
setModuleId(bindingToSyntax, moduleId);
return bindingToSyntax;
};
};

modules.forEach((module) => {
let bindFunction = getBindFunction(module.guid);
module.registry(bindFunction);
});

}

public unload(...modules: interfaces.ContainerModule[]): void {
Expand Down Expand Up @@ -139,12 +146,12 @@ class Container implements interfaces.Container {
return this.isBoundTagged(serviceIdentifier, METADATA_KEY.NAMED_TAG, named);
}

// Note: we can only identify basic tagged bindings not complex constraints (e.g ancerstors)
// Users can try-catch calls to container.get<T>("T") if they really need to do check if a
// binding with a complex constraint is available.
public isBoundTagged(serviceIdentifier: interfaces.ServiceIdentifier<any>, key: string|number|symbol, value: any): boolean {
let bindings = this._bindingDictionary.get(serviceIdentifier);
let request = createMockRequest(serviceIdentifier, key, value);
// Note: we can only identify basic tagged bindings not complex constraints (e.g ancerstors)
// Users can try-catch calls to container.get<T>("T") if they really need to do check if a
// binding with a complex constraint is available.
let request = createMockRequest(this, serviceIdentifier, key, value);
return bindings.some((b) => b.constraint(request));
}

Expand All @@ -153,10 +160,10 @@ class Container implements interfaces.Container {
}

public restore(): void {
if (this._snapshots.length === 0) {
let snapshot = this._snapshots.pop();
if (snapshot === undefined) {
throw new Error(ERROR_MSGS.NO_MORE_SNAPSHOTS_AVAILABLE);
}
let snapshot = this._snapshots.pop();
this._bindingDictionary = snapshot.bindings;
this._middleware = snapshot.middleware;
}
Expand All @@ -167,14 +174,6 @@ class Container implements interfaces.Container {
return child;
}

public set parent (container: interfaces.Container) {
this._parentContainer = container;
}

public get parent() {
return this._parentContainer;
}

public applyMiddleware(...middlewares: interfaces.Middleware[]): void {
let initial: interfaces.Next = (this._middleware) ? this._middleware : this._planAndResolve();
this._middleware = middlewares.reduce((prev, curr) => {
Expand Down Expand Up @@ -223,9 +222,9 @@ class Container implements interfaces.Container {
value?: any
): (T|T[]) {

let result: (T|T[]) = null;
let result: (T|T[]) | null = null;

let args: interfaces.NextArgs = {
let defaultArgs: interfaces.NextArgs = {
avoidConstraints: avoidConstraints,
contextInterceptor: (context: interfaces.Context) => { return context; },
isMultiInject: isMultiInject,
Expand All @@ -236,12 +235,12 @@ class Container implements interfaces.Container {
};

if (this._middleware) {
result = this._middleware(args);
result = this._middleware(defaultArgs);
if (result === undefined || result === null) {
throw new Error(ERROR_MSGS.INVALID_MIDDLEWARE_RETURN);
}
} else {
result = this._planAndResolve<T>()(args);
result = this._planAndResolve<T>()(defaultArgs);
}

return result;
Expand All @@ -252,6 +251,8 @@ class Container implements interfaces.Container {
// with the Resolver and that is what this function is about
private _planAndResolve<T>(): (args: interfaces.NextArgs) => (T|T[]) {
return (args: interfaces.NextArgs) => {

// create a plan
let context = plan(
this,
args.isMultiInject,
Expand All @@ -261,10 +262,17 @@ class Container implements interfaces.Container {
args.value,
args.avoidConstraints
);
let result = resolve<T>(args.contextInterceptor(context));

// apply context interceptor
context = args.contextInterceptor(context);

// resolve plan
let result = resolve<T>(context);
return result;

};
}

}

export { Container };
4 changes: 2 additions & 2 deletions src/container/container_snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { interfaces } from "../interfaces/interfaces";
class ContainerSnapshot implements interfaces.ContainerSnapshot {

public bindings: interfaces.Lookup<interfaces.Binding<any>>;
public middleware: interfaces.Next;
public middleware: interfaces.Next | null;

public static of(bindings: interfaces.Lookup<interfaces.Binding<any>>, middleware: interfaces.Next) {
public static of(bindings: interfaces.Lookup<interfaces.Binding<any>>, middleware: interfaces.Next | null) {
let snapshot = new ContainerSnapshot();
snapshot.bindings = bindings;
snapshot.middleware = middleware;
Expand Down
7 changes: 6 additions & 1 deletion src/container/lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Lookup<T extends interfaces.Clonable<T>> implements interfaces.Lookup<T> {
this._map = new Map<interfaces.ServiceIdentifier<any>, T[]>();
}

public getMap() {
return this._map;
}

// adds a new entry to _map
public add(serviceIdentifier: interfaces.ServiceIdentifier<any>, value: T): void {

Expand All @@ -31,8 +35,9 @@ class Lookup<T extends interfaces.Clonable<T>> implements interfaces.Lookup<T> {
if (serviceIdentifier === null || serviceIdentifier === undefined) { throw new Error(ERROR_MSGS.NULL_ARGUMENT); }

let entry = this._map.get(serviceIdentifier);

if (entry !== undefined) {
return this._map.get(serviceIdentifier);
return entry;
} else {
throw new Error(ERROR_MSGS.KEY_NOT_FOUND);
}
Expand Down
27 changes: 14 additions & 13 deletions src/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ namespace interfaces {
moduleId: string;
activated: boolean;
serviceIdentifier: ServiceIdentifier<T>;
implementationType: Newable<T>;
factory: FactoryCreator<any>;
provider: ProviderCreator<any>;
constraint: ConstraintFunction;
onActivation: (context: Context, injectable: T) => T;
cache: T;
dynamicValue: (context: Context) => T;
dynamicValue: ((context: interfaces.Context) => T) | null;
scope: BindingScope;
type: BindingType;
implementationType: Newable<T> | null;
factory: FactoryCreator<any> | null;
provider: ProviderCreator<any> | null;
onActivation: ((context: interfaces.Context, injectable: T) => T) | null;
cache: T | null;
}

export interface Factory<T> extends Function {
Expand All @@ -73,7 +73,7 @@ namespace interfaces {

export interface NextArgs {
avoidConstraints: boolean;
contextInterceptor?: (contexts: Context) => Context;
contextInterceptor: ((contexts: Context) => Context);
isMultiInject: boolean;
targetType: TargetType;
serviceIdentifier: interfaces.ServiceIdentifier<any>;
Expand Down Expand Up @@ -126,7 +126,7 @@ namespace interfaces {
guid: string;
serviceIdentifier: ServiceIdentifier<any>;
parentContext: Context;
parentRequest: Request;
parentRequest: Request | null;
childRequests: Request[];
target: Target;
bindings: Binding<any>[];
Expand All @@ -143,8 +143,8 @@ namespace interfaces {
type: TargetType;
name: QueryableString;
metadata: Array<Metadata>;
getNamedTag(): interfaces.Metadata;
getCustomTags(): interfaces.Metadata[];
getNamedTag(): interfaces.Metadata | null;
getCustomTags(): interfaces.Metadata[] | null;
hasTag(key: string|number|symbol): boolean;
isArray(): boolean;
matchesArray(name: interfaces.ServiceIdentifier<any>): boolean;
Expand All @@ -160,7 +160,7 @@ namespace interfaces {

export interface Container {
guid: string;
parent: Container;
parent: Container | null;
options: ContainerOptions;
bind<T>(serviceIdentifier: ServiceIdentifier<T>): BindingToSyntax<T>;
unbind(serviceIdentifier: ServiceIdentifier<any>): void;
Expand Down Expand Up @@ -191,7 +191,7 @@ namespace interfaces {

export interface ContainerSnapshot {
bindings: Lookup<Binding<any>>;
middleware: Next;
middleware: Next | null;
}

export interface Clonable<T> {
Expand All @@ -200,6 +200,7 @@ namespace interfaces {

export interface Lookup<T> extends Clonable<Lookup<T>> {
add(serviceIdentifier: ServiceIdentifier<any>, value: T): void;
getMap(): Map<interfaces.ServiceIdentifier<any>, T[]>;
get(serviceIdentifier: ServiceIdentifier<any>): T[];
remove(serviceIdentifier: interfaces.ServiceIdentifier<any>): void;
removeByCondition(condition: (item: T) => boolean): void;
Expand Down Expand Up @@ -252,8 +253,8 @@ namespace interfaces {
}

export interface ConstraintFunction extends Function {
(request: Request) : boolean;
metaData?: Metadata;
(request: Request | null): boolean;
}

}
Expand Down
Loading

0 comments on commit 9f5f1ac

Please sign in to comment.