Skip to content

Commit

Permalink
Implements support for "inRequestScope" + Upgrades TypeScript@2.6.1 (#…
Browse files Browse the repository at this point in the history
…661)

* Upgraded TS @ 2.6.1

* WIP inRequestScope() support

* Implemented inRequestScope() support

* Preparing release 4.5.0

* Added docs for inRequestScope

* Removed temp comment

* Improved docs
  • Loading branch information
remojansen authored Nov 2, 2017
1 parent 026f566 commit e4be45c
Show file tree
Hide file tree
Showing 24 changed files with 359 additions and 87 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "inversify",
"version": "4.4.0",
"version": "4.5.0",
"description": "A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.",
"main": "lib/inversify.js",
"jsnext:main": "es/inversify.js",
Expand Down Expand Up @@ -73,7 +73,7 @@
"run-sequence": "2.2.0",
"sinon": "4.0.2",
"tslint": "5.8.0",
"typescript": "2.5.3",
"typescript": "2.6.1",
"vinyl-buffer": "1.0.0",
"vinyl-source-stream": "1.1.0"
}
Expand Down
6 changes: 1 addition & 5 deletions src/constants/error_msgs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { template } from "../utils/template";

export const DUPLICATED_INJECTABLE_DECORATOR = "Cannot apply @injectable decorator multiple times.";
export const DUPLICATED_METADATA = "Metadata key was used more than once in a parameter:";
export const NULL_ARGUMENT = "NULL argument";
Expand Down Expand Up @@ -35,6 +33,4 @@ export const CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE = "Invalid Container
"be a boolean";

export const MULTIPLE_POST_CONSTRUCT_METHODS = "Cannot apply @postConstruct decorator multiple times in the same class";
export const POST_CONSTRUCT_ERROR = template`@postConstruct error in class ${0}: ${1}`;


export const POST_CONSTRUCT_ERROR = (...values: any[]) => `@postConstruct error in class ${values[0]}: ${values[1]}`;
1 change: 1 addition & 0 deletions src/constants/literal_types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { interfaces } from "../interfaces/interfaces";

let BindingScopeEnum: interfaces.BindingScopeEnum = {
Request: "Request",
Singleton: "Singleton",
Transient: "Transient"
};
Expand Down
11 changes: 10 additions & 1 deletion src/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
namespace interfaces {

export type BindingScope = "Singleton" | "Transient";
export type BindingScope = "Singleton" | "Transient" | "Request";

export type BindingType = "ConstantValue" | "Constructor" | "DynamicValue" | "Factory" |
"Function" | "Instance" | "Invalid" | "Provider";

export type TargetType = "ConstructorArgument" | "ClassProperty" | "Variable";

export interface BindingScopeEnum {
Request: interfaces.BindingScope;
Singleton: interfaces.BindingScope;
Transient: interfaces.BindingScope;
}
Expand Down Expand Up @@ -126,6 +127,12 @@ namespace interfaces {
value(): string;
}

export type ResolveRequestHandler = (
request: interfaces.Request
) => any;

export type RequestScope = Map<any, any> | null;

export interface Request {
guid: string;
serviceIdentifier: ServiceIdentifier<any>;
Expand All @@ -134,6 +141,7 @@ namespace interfaces {
childRequests: Request[];
target: Target;
bindings: Binding<any>[];
requestScope: RequestScope;
addChildRequest(
serviceIdentifier: ServiceIdentifier<any>,
bindings: (Binding<any> | Binding<any>[]),
Expand Down Expand Up @@ -262,6 +270,7 @@ namespace interfaces {
export interface BindingInSyntax<T> {
inSingletonScope(): BindingWhenOnSyntax<T>;
inTransientScope(): BindingWhenOnSyntax<T>;
inRequestScope(): BindingWhenOnSyntax<T>;
}

export interface BindingInWhenOnSyntax<T> extends BindingInSyntax<T>, BindingWhenOnSyntax<T> { }
Expand Down
9 changes: 9 additions & 0 deletions src/planning/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Request implements interfaces.Request {
public bindings: interfaces.Binding<any>[];
public childRequests: interfaces.Request[];
public target: interfaces.Target;
public requestScope: interfaces.RequestScope;

public constructor(
serviceIdentifier: interfaces.ServiceIdentifier<any>,
Expand All @@ -25,6 +26,14 @@ class Request implements interfaces.Request {
this.target = target;
this.childRequests = [];
this.bindings = (Array.isArray(bindings) ? bindings : [bindings]);

// Set requestScope if Request is the root request
if (parentRequest === null) {
this.requestScope = new Map<any, any>();
} else {
this.requestScope = null;
}

}

public addChildRequest(
Expand Down
5 changes: 3 additions & 2 deletions src/resolution/instantiation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { POST_CONSTRUCT_ERROR } from "../constants/error_msgs";
function _injectProperties(
instance: any,
childRequests: interfaces.Request[],
resolveRequest: (request: interfaces.Request) => any
resolveRequest: interfaces.ResolveRequestHandler
): any {

let propertyInjectionsRequests = childRequests.filter((childRequest: interfaces.Request) => {
Expand Down Expand Up @@ -47,7 +47,8 @@ function _postConstruct(constr: interfaces.Newable<any>, result: any): void {
function resolveInstance(
constr: interfaces.Newable<any>,
childRequests: interfaces.Request[],
resolveRequest: (request: interfaces.Request) => any): any {
resolveRequest: interfaces.ResolveRequestHandler
): any {

let result: any = null;

Expand Down
34 changes: 29 additions & 5 deletions src/resolution/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { getServiceIdentifierAsString } from "../utils/serialization";
import { resolveInstance } from "./instantiation";
import * as ERROR_MSGS from "../constants/error_msgs";

function _resolveRequest(request: interfaces.Request): any {
const _resolveRequest = (requestScope: interfaces.RequestScope) =>
(request: interfaces.Request): any => {

let bindings = request.bindings;
let childRequests = request.childRequests;
Expand All @@ -20,7 +21,8 @@ function _resolveRequest(request: interfaces.Request): any {

// Create an array instead of creating an instance
return childRequests.map((childRequest: interfaces.Request) => {
return _resolveRequest(childRequest);
const _f = _resolveRequest(requestScope);
return _f(childRequest);
});

} else {
Expand All @@ -33,11 +35,20 @@ function _resolveRequest(request: interfaces.Request): any {

let binding = bindings[0];
let isSingleton = binding.scope === BindingScopeEnum.Singleton;
let isRequestSingleton = binding.scope === BindingScopeEnum.Request;

if (isSingleton && binding.activated === true) {
return binding.cache;
}

if (
isRequestSingleton &&
requestScope !== null &&
requestScope.has(binding.guid)
) {
return requestScope.get(binding.guid);
}

if (binding.type === BindingTypeEnum.ConstantValue) {
result = binding.cache;
} else if (binding.type === BindingTypeEnum.Function) {
Expand All @@ -51,7 +62,11 @@ function _resolveRequest(request: interfaces.Request): any {
} else if (binding.type === BindingTypeEnum.Provider && binding.provider !== null) {
result = binding.provider(request.parentContext);
} else if (binding.type === BindingTypeEnum.Instance && binding.implementationType !== null) {
result = resolveInstance(binding.implementationType, childRequests, _resolveRequest);
result = resolveInstance(
binding.implementationType,
childRequests,
_resolveRequest(requestScope)
);
} else {
// The user probably created a binding but didn't finish it
// e.g. container.bind<T>("Something"); missing BindingToSyntax
Expand All @@ -70,13 +85,22 @@ function _resolveRequest(request: interfaces.Request): any {
binding.activated = true;
}

if (
isRequestSingleton &&
requestScope !== null &&
!requestScope.has(binding.guid)
) {
requestScope.set(binding.guid, result);
}

return result;
}

}
};

function resolve<T>(context: interfaces.Context): T {
return _resolveRequest(context.plan.rootRequest);
const _f = _resolveRequest(context.plan.rootRequest.requestScope);
return _f(context.plan.rootRequest);
}

export { resolve };
5 changes: 5 additions & 0 deletions src/syntax/binding_in_syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ class BindingInSyntax<T> implements interfaces.BindingInSyntax<T> {
this._binding = binding;
}

public inRequestScope(): interfaces.BindingWhenOnSyntax<T> {
this._binding.scope = BindingScopeEnum.Request;
return new BindingWhenOnSyntax<T>(this._binding);
}

public inSingletonScope(): interfaces.BindingWhenOnSyntax<T> {
this._binding.scope = BindingScopeEnum.Singleton;
return new BindingWhenOnSyntax<T>(this._binding);
Expand Down
4 changes: 4 additions & 0 deletions src/syntax/binding_in_when_on_syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class BindingInWhenOnSyntax<T> implements interfaces.BindingInSyntax<T>, interfa
this._bindingInSyntax = new BindingInSyntax<T>(binding);
}

public inRequestScope(): interfaces.BindingWhenOnSyntax<T> {
return this._bindingInSyntax.inRequestScope();
}

public inSingletonScope(): interfaces.BindingWhenOnSyntax<T> {
return this._bindingInSyntax.inSingletonScope();
}
Expand Down
10 changes: 0 additions & 10 deletions src/utils/template.ts

This file was deleted.

17 changes: 17 additions & 0 deletions test/annotation/inject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ class DecoratedWarrior {
this._primaryWeapon = primary;
this._secondaryWeapon = secondary;
}

public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}

}

class InvalidDecoratorUsageWarrior {
Expand All @@ -43,11 +51,20 @@ class InvalidDecoratorUsageWarrior {
}

public test(a: string) { /*...*/ }

public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}

}

describe("@inject", () => {

it("Should generate metadata for named parameters", () => {

let metadataKey = METADATA_KEY.TAGGED;
let paramsMetadata = Reflect.getMetadata(metadataKey, DecoratedWarrior);
expect(paramsMetadata).to.be.an("object");
Expand Down
7 changes: 7 additions & 0 deletions test/annotation/injectable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ describe("@injectable", () => {
this._secondaryWeapon = secondaryWeapon;
}

public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}

}

let metadata = Reflect.getMetadata(METADATA_KEY.PARAM_TYPES, Warrior);
Expand Down
13 changes: 13 additions & 0 deletions test/annotation/multi_inject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class DecoratedWarrior {
this._primaryWeapon = weapons[0];
this._secondaryWeapon = weapons[1];
}

public mock() {
console.log(this._primaryWeapon, this._secondaryWeapon);
}

}

class InvalidDecoratorUsageWarrior {
Expand All @@ -37,6 +42,14 @@ class InvalidDecoratorUsageWarrior {
}

public test(a: string) { /*...*/ }

public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}

}

describe("@multiInject", () => {
Expand Down
15 changes: 15 additions & 0 deletions test/annotation/named.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,17 @@ class NamedWarrior {
this._primaryWeapon = primary;
this._secondaryWeapon = secondary;
}

public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}
}

class InvalidDecoratorUsageWarrior {

private _primaryWeapon: Weapon;
private _secondaryWeapon: Weapon;

Expand All @@ -38,6 +46,13 @@ class InvalidDecoratorUsageWarrior {
}

public test(a: string) { /*...*/ }

public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}
}

describe("@named", () => {
Expand Down
3 changes: 3 additions & 0 deletions test/annotation/post_construct.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ describe("@postConstruct", () => {
public testMethod() {
this.useMessage = "Used Katana!";
}
public debug() {
return this.useMessage;
}
}
let metadata: Metadata = Reflect.getMetadata(METADATA_KEY.POST_CONSTRUCT, Katana);
expect(metadata.value).to.be.equal("testMethod");
Expand Down
19 changes: 19 additions & 0 deletions test/annotation/tagged.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ class TaggedWarrior {
this._primaryWeapon = primary;
this._secondaryWeapon = secondary;
}
public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}
}

class DoubleTaggedWarrior {
Expand All @@ -36,6 +42,12 @@ class DoubleTaggedWarrior {
this._primaryWeapon = primary;
this._secondaryWeapon = secondary;
}
public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}
}

class InvalidDecoratorUsageWarrior {
Expand All @@ -52,6 +64,13 @@ class InvalidDecoratorUsageWarrior {
}

public test(a: string) { /*...*/ }

public debug() {
return {
primaryWeapon: this._primaryWeapon,
secondaryWeapon: this._secondaryWeapon
};
}
}

describe("@Tagged", () => {
Expand Down
Loading

0 comments on commit e4be45c

Please sign in to comment.