Skip to content

Commit

Permalink
Implements container.rebind support #484 (#490)
Browse files Browse the repository at this point in the history
* Implemented container.rebind support #484

* Prevent one test from running in browsers
  • Loading branch information
remojansen authored Feb 7, 2017
1 parent afe64db commit 3ab1225
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 59 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": "3.0.0",
"version": "3.1.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 @@ -72,7 +72,7 @@
"run-sequence": "^1.2.0",
"sinon": "^1.17.3",
"tslint": "^4.4.2",
"typescript": "^2.1.5",
"typescript": "^2.2.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
}
Expand Down
25 changes: 24 additions & 1 deletion src/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,29 @@ class Container implements interfaces.Container {
};
};

let getRebindFunction = (moduleId: string) => {
return (serviceIdentifier: interfaces.ServiceIdentifier<any>) => {
let _rebind = this.rebind.bind(this);
let bindingToSyntax = _rebind(serviceIdentifier);
setModuleId(bindingToSyntax, moduleId);
return bindingToSyntax;
};
};

modules.forEach((module) => {

let bindFunction = getBindFunction(module.guid);
let unbindFunction = getUnbindFunction(module.guid);
let isboundFunction = getIsboundFunction(module.guid);
module.registry(bindFunction, unbindFunction, isboundFunction);
let rebindFunction = getRebindFunction(module.guid);

module.registry(
bindFunction,
unbindFunction,
isboundFunction,
rebindFunction
);

});

}
Expand All @@ -139,6 +157,11 @@ class Container implements interfaces.Container {
return new BindingToSyntax<T>(binding);
}

public rebind<T>(serviceIdentifier: interfaces.ServiceIdentifier<T>): interfaces.BindingToSyntax<T> {
this.unbind(serviceIdentifier);
return this.bind(serviceIdentifier);
}

// Removes a type binding from the registry by its key
public unbind(serviceIdentifier: interfaces.ServiceIdentifier<any>): void {
try {
Expand Down
4 changes: 2 additions & 2 deletions src/container/container_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { guid } from "../utils/guid";
class ContainerModule implements interfaces.ContainerModule {

public guid: string;
public registry: (bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound) => void;
public registry: interfaces.ContainerModuleCallBack;

public constructor(registry: (bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound) => void) {
public constructor(registry: interfaces.ContainerModuleCallBack) {
this.guid = guid();
this.registry = registry;
}
Expand Down
16 changes: 15 additions & 1 deletion src/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ namespace interfaces {
parent: Container | null;
options: ContainerOptions;
bind<T>(serviceIdentifier: ServiceIdentifier<T>): BindingToSyntax<T>;
rebind<T>(serviceIdentifier: interfaces.ServiceIdentifier<T>): interfaces.BindingToSyntax<T>;
unbind(serviceIdentifier: ServiceIdentifier<any>): void;
unbindAll(): void;
isBound(serviceIdentifier: ServiceIdentifier<any>): boolean;
Expand All @@ -185,6 +186,10 @@ namespace interfaces {
<T>(serviceIdentifier: ServiceIdentifier<T>): BindingToSyntax<T>;
}

export interface Rebind extends Function {
<T>(serviceIdentifier: ServiceIdentifier<T>): BindingToSyntax<T>;
}

export interface Unbind extends Function {
<T>(serviceIdentifier: ServiceIdentifier<T>): void;
}
Expand All @@ -195,7 +200,16 @@ namespace interfaces {

export interface ContainerModule {
guid: string;
registry: (bind: Bind, unbind: Unbind, isBound: IsBound) => void;
registry: ContainerModuleCallBack;
}

export interface ContainerModuleCallBack extends Function {
(
bind: interfaces.Bind,
unbind: interfaces.Unbind,
isBound: interfaces.IsBound,
rebind: interfaces.Rebind
): void;
}

export interface ContainerSnapshot {
Expand Down
21 changes: 21 additions & 0 deletions test/container/container.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,4 +606,25 @@ describe("Container", () => {

});

it("Should be able to override a binding using rebind", () => {

let TYPES = {
someType: "someType"
};

let container = new Container();
container.bind<number>(TYPES.someType).toConstantValue(1);
container.bind<number>(TYPES.someType).toConstantValue(2);

let values1 = container.getAll(TYPES.someType);
expect(values1[0]).to.eq(1);
expect(values1[1]).to.eq(2);

container.rebind<number>(TYPES.someType).toConstantValue(3);
let values2 = container.getAll(TYPES.someType);
expect(values2[0]).to.eq(3);
expect(values2[1]).to.eq(undefined);

});

});
42 changes: 42 additions & 0 deletions test/container/container_module.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,46 @@ describe("ContainerModule", () => {

});

it("Should be able to override a binding using rebind within a container module", () => {

let TYPES = {
someType: "someType"
};

let container = new Container();

let module1 = new ContainerModule(
(
bind: interfaces.Bind,
unbind: interfaces.Unbind,
isBound: interfaces.IsBound
) => {
bind<number>(TYPES.someType).toConstantValue(1);
bind<number>(TYPES.someType).toConstantValue(2);
}
);

let module2 = new ContainerModule(
(
bind: interfaces.Bind,
unbind: interfaces.Unbind,
isBound: interfaces.IsBound,
rebind: interfaces.Rebind
) => {
rebind<number>(TYPES.someType).toConstantValue(3);
}
);

container.load(module1);
let values1 = container.getAll(TYPES.someType);
expect(values1[0]).to.eq(1);
expect(values1[1]).to.eq(2);

container.load(module2);
let values2 = container.getAll(TYPES.someType);
expect(values2[0]).to.eq(3);
expect(values2[1]).to.eq(undefined);

});

});
44 changes: 0 additions & 44 deletions test/inversify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { interfaces } from "../src/interfaces/interfaces";
import { expect } from "chai";
import "es6-symbol/implement";
import * as ERROR_MSGS from "../src/constants/error_msgs";
import * as Stubs from "./utils/stubs";
import {
Container, injectable, inject, multiInject,
tagged, named, targetName, decorate, typeConstraint,
Expand Down Expand Up @@ -2658,49 +2657,6 @@ describe("InversifyJS", () => {

});

it("Should display a error when injecting into an abstract class", () => {

@injectable()
class Soldier extends Stubs.BaseSoldier { }

@injectable()
class Archer extends Stubs.BaseSoldier { }

@injectable()
class Knight extends Stubs.BaseSoldier { }

@injectable()
class Sword implements Stubs.Weapon { }

@injectable()
class Bow implements Stubs.Weapon { }

@injectable()
class DefaultWeapon implements Stubs.Weapon { }

let container = new Container();

container.bind<Stubs.Weapon>("Weapon").to(DefaultWeapon).whenInjectedInto(Soldier);
container.bind<Stubs.Weapon>("Weapon").to(Sword).whenInjectedInto(Knight);
container.bind<Stubs.Weapon>("Weapon").to(Bow).whenInjectedInto(Archer);
container.bind<Stubs.BaseSoldier>("BaseSoldier").to(Soldier).whenTargetNamed("default");
container.bind<Stubs.BaseSoldier>("BaseSoldier").to(Knight).whenTargetNamed("knight");
container.bind<Stubs.BaseSoldier>("BaseSoldier").to(Archer).whenTargetNamed("archer");

let throw1 = () => { container.getNamed<Stubs.BaseSoldier>("BaseSoldier", "default"); };
let throw2 = () => { container.getNamed<Stubs.BaseSoldier>("BaseSoldier", "knight"); };
let throw3 = () => { container.getNamed<Stubs.BaseSoldier>("BaseSoldier", "archer"); };

function getError(className: string) {
return ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH_1 + className + ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH_2;
}

expect(throw1).to.throw(getError("Soldier"));
expect(throw2).to.throw(getError("Knight"));
expect(throw3).to.throw(getError("Archer"));

});

it("Should be able to inject a regular derived class", () => {

const SYMBOLS = {
Expand Down
44 changes: 44 additions & 0 deletions test/node/error_messages.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect } from "chai";
import * as ERROR_MSGS from "../../src/constants/error_msgs";
import * as Stubs from "../utils/stubs";
import {
Container, injectable
} from "../../src/inversify";
Expand Down Expand Up @@ -83,4 +84,47 @@ describe("Error message when resolving fails", () => {

});

it("Should display a error when injecting into an abstract class", () => {

@injectable()
class Soldier extends Stubs.BaseSoldier { }

@injectable()
class Archer extends Stubs.BaseSoldier { }

@injectable()
class Knight extends Stubs.BaseSoldier { }

@injectable()
class Sword implements Stubs.Weapon { }

@injectable()
class Bow implements Stubs.Weapon { }

@injectable()
class DefaultWeapon implements Stubs.Weapon { }

let container = new Container();

container.bind<Stubs.Weapon>("Weapon").to(DefaultWeapon).whenInjectedInto(Soldier);
container.bind<Stubs.Weapon>("Weapon").to(Sword).whenInjectedInto(Knight);
container.bind<Stubs.Weapon>("Weapon").to(Bow).whenInjectedInto(Archer);
container.bind<Stubs.BaseSoldier>("BaseSoldier").to(Soldier).whenTargetNamed("default");
container.bind<Stubs.BaseSoldier>("BaseSoldier").to(Knight).whenTargetNamed("knight");
container.bind<Stubs.BaseSoldier>("BaseSoldier").to(Archer).whenTargetNamed("archer");

let throw1 = () => { container.getNamed<Stubs.BaseSoldier>("BaseSoldier", "default"); };
let throw2 = () => { container.getNamed<Stubs.BaseSoldier>("BaseSoldier", "knight"); };
let throw3 = () => { container.getNamed<Stubs.BaseSoldier>("BaseSoldier", "archer"); };

function getError(className: string) {
return ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH_1 + className + ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH_2;
}

expect(throw1).to.throw(getError("Soldier"));
expect(throw2).to.throw(getError("Knight"));
expect(throw3).to.throw(getError("Archer"));

});

});
Loading

0 comments on commit 3ab1225

Please sign in to comment.