Skip to content

Commit

Permalink
Fix OnlyInstantiableByContainer
Browse files Browse the repository at this point in the history
  • Loading branch information
Thiago Bustamante committed Feb 28, 2020
1 parent e7df1d7 commit 980b9f8
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typescript-ioc",
"version": "3.0.1",
"version": "3.0.2",
"description": "A Lightweight annotation-based dependency injection container for typescript.",
"author": "Thiago da Rosa de Bustamante <[email protected]>",
"scripts": {
Expand Down
12 changes: 2 additions & 10 deletions src/container/container-binding-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,9 @@ export class IoCBindConfig implements Config {
if (!this.iocScope) {
this.scope(Scope.Local);
}
if (this.decoratedConstructor) {
return this.getContainerManagedInstance(context);
} else {
return this.iocScope.resolve(this.iocFactory, this.source, context);
}
}

private getContainerManagedInstance(context: BuildContext) {
InjectorHandler.unblockInstantiation(this.source);
InjectorHandler.unblockInstantiation();
const instance = this.iocScope.resolve(this.iocFactory, this.source, context);
InjectorHandler.blockInstantiation(this.source);
InjectorHandler.blockInstantiation();
return instance;
}

Expand Down
18 changes: 9 additions & 9 deletions src/container/injection-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,35 @@ import { InstanceFactory } from './container-types';
import { BuildContext } from '../model';

const BUILD_CONTEXT_KEY = '__BuildContext';
const BLOCK_INSTANTIATION_KEY = '__block_Instantiation';
const IOC_WRAPPER_CLASS = 'ioc_wrapper';

/**
* Utility class to handle injection behavior on class decorations.
*/
export class InjectorHandler {
public static constructorNameRegEx = /function (\w*)/;
private static instantiationsBlocked = true;


public static instrumentConstructor(source: Function) {
let newConstructor: any;
// tslint:disable-next-line:class-name
newConstructor = class ioc_wrapper extends (source as FunctionConstructor) {
constructor(...args: Array<any>) {
super(...args);
InjectorHandler.assertInstantiable(source);
InjectorHandler.assertInstantiable();
}
};
newConstructor['__parent'] = source;
InjectorHandler.blockInstantiation(source);
return newConstructor;
}

public static blockInstantiation(source: Function) {
source[BLOCK_INSTANTIATION_KEY] = true;
public static blockInstantiation() {
InjectorHandler.instantiationsBlocked = true;
}

public static unblockInstantiation(source: Function) {
source[BLOCK_INSTANTIATION_KEY] = false;
public static unblockInstantiation() {
InjectorHandler.instantiationsBlocked = false;
}

public static getConstructorFromType(target: Function): FunctionConstructor {
Expand Down Expand Up @@ -92,8 +92,8 @@ export class InjectorHandler {
}
}

private static assertInstantiable(target: any) {
if (target[BLOCK_INSTANTIATION_KEY]) {
private static assertInstantiable() {
if (InjectorHandler.instantiationsBlocked) {
throw new TypeError('Can not instantiate it. The instantiation is blocked for this class. ' +
'Ask Container for it, using Container.get');
}
Expand Down
11 changes: 11 additions & 0 deletions test/integration/ioc-container-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,17 @@ describe('@OnlyInstantiableByContainer decorator', () => {
expect(instance).toBeDefined();
});

it('should allow Container instantiation of Singleton classes with instrumented parent.', () => {
@OnlyInstantiableByContainer
class First { }

@OnlyInstantiableByContainer
class Second extends First { }

const instance: First = Container.get(Second);
expect(instance).toBeDefined();
});

it('should allow scope change to Local from Singleton.', () => {
Container.bind(SingletonInstantiation).scope(Scope.Local);
const instance: SingletonInstantiation = Container.get(SingletonInstantiation);
Expand Down
2 changes: 1 addition & 1 deletion test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"removeComments": true,
"sourceMap": true,
"strictNullChecks": false,
"target": "es5"
"target": "es6"
},
"include": [
"**/*.spec.ts"
Expand Down
4 changes: 2 additions & 2 deletions test/unit/container/container-binding-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ describe('IoCBindConfig', () => {
bindConfig.instrumentConstructor();

expect(bindConfig.getInstance(buildContext)).toEqual(instance);
expect(mockInjectorBlockInstantiation).toBeCalledWith(MyBaseType);
expect(mockInjectorBlockInstantiation).toBeCalled();
expect(bindConfig.iocScope.resolve).toBeCalledWith(bindConfig.iocFactory, MyBaseType, buildContext);
expect(mockInjectorUnBlockInstantiation).toBeCalledWith(MyBaseType);
expect(mockInjectorUnBlockInstantiation).toBeCalledWith();
});
});

Expand Down
21 changes: 3 additions & 18 deletions test/unit/container/injection-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,27 @@ describe('InjectorHandler', () => {
const newConstructor = InjectorHandler.instrumentConstructor(MyBaseType);
expect(newConstructor.name).toEqual('ioc_wrapper');
expect(newConstructor['__parent']).toEqual(MyBaseType);
expect((MyBaseType as any)['__block_Instantiation']).toBeTruthy();
});

it('should keep creating valid instances for the baseType', () => {
class MyBaseType { }
const newConstructor = InjectorHandler.instrumentConstructor(MyBaseType);
InjectorHandler.unblockInstantiation(MyBaseType);
InjectorHandler.unblockInstantiation();
expect(new newConstructor()).toBeInstanceOf(MyBaseType);
InjectorHandler.blockInstantiation();
});
});

describe('blockInstantiation()', () => {
it('should configure the constructor as non instantiable', () => {
class MyBaseType { }
InjectorHandler.blockInstantiation(MyBaseType);
expect((MyBaseType as any)['__block_Instantiation']).toBeTruthy();
});

it('should avoid that instrumented constructor create instances', () => {
class MyBaseType { }
const newConstructor = InjectorHandler.instrumentConstructor(MyBaseType);
InjectorHandler.blockInstantiation(MyBaseType);
InjectorHandler.blockInstantiation();
expect(() => new newConstructor())
.toThrow(new TypeError('Can not instantiate it. The instantiation is blocked for this class. Ask Container for it, using Container.get'));
});
});

describe('unblockInstantiation()', () => {
it('should configure the constructor as instantiable', () => {
class MyBaseType { }
InjectorHandler.blockInstantiation(MyBaseType);
InjectorHandler.unblockInstantiation(MyBaseType);
expect((MyBaseType as any)['__block_Instantiation']).toBeFalsy();
});
});

describe('injectContext()', () => {
it('should inject the context as a hidden property into the target', () => {
class MyBaseType { }
Expand Down

0 comments on commit 980b9f8

Please sign in to comment.