Skip to content

Commit

Permalink
Merge pull request #1309 from inversify/fix/issue-1297-fix-on-activat…
Browse files Browse the repository at this point in the history
…ion-on-singleton-scope

fix: update binding_to_syntax to use singleton scope according to docs
  • Loading branch information
notaphplover authored Apr 16, 2021
2 parents 28d20cc + 88ff0b5 commit 784fec7
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed
- Fix `Target.isTagged()` to exclude `optional` from tag injections #1190.
- Update `toConstructor`, `toFactory`, `toFunction`, `toAutoFactory`, `toProvider` and `toConstantValue` to have singleton scope #1297.

## [5.0.1] - 2018-10-17
### Added
Expand Down
8 changes: 7 additions & 1 deletion src/syntax/binding_to_syntax.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as ERROR_MSGS from "../constants/error_msgs";
import { BindingTypeEnum } from "../constants/literal_types";
import { BindingScopeEnum, BindingTypeEnum } from "../constants/literal_types";
import { interfaces } from "../interfaces/interfaces";
import { BindingInWhenOnSyntax } from "./binding_in_when_on_syntax";
import { BindingWhenOnSyntax } from "./binding_when_on_syntax";
Expand Down Expand Up @@ -31,6 +31,7 @@ class BindingToSyntax<T> implements interfaces.BindingToSyntax<T> {
this._binding.cache = value;
this._binding.dynamicValue = null;
this._binding.implementationType = null;
this._binding.scope = BindingScopeEnum.Singleton;
return new BindingWhenOnSyntax<T>(this._binding);
}

Expand All @@ -45,12 +46,14 @@ class BindingToSyntax<T> implements interfaces.BindingToSyntax<T> {
public toConstructor<T2>(constructor: interfaces.Newable<T2>): interfaces.BindingWhenOnSyntax<T> {
this._binding.type = BindingTypeEnum.Constructor;
this._binding.implementationType = constructor as any;
this._binding.scope = BindingScopeEnum.Singleton;
return new BindingWhenOnSyntax<T>(this._binding);
}

public toFactory<T2>(factory: interfaces.FactoryCreator<T2>): interfaces.BindingWhenOnSyntax<T> {
this._binding.type = BindingTypeEnum.Factory;
this._binding.factory = factory;
this._binding.scope = BindingScopeEnum.Singleton;
return new BindingWhenOnSyntax<T>(this._binding);
}

Expand All @@ -59,6 +62,7 @@ class BindingToSyntax<T> implements interfaces.BindingToSyntax<T> {
if (typeof func !== "function") { throw new Error(ERROR_MSGS.INVALID_FUNCTION_BINDING); }
const bindingWhenOnSyntax = this.toConstantValue(func);
this._binding.type = BindingTypeEnum.Function;
this._binding.scope = BindingScopeEnum.Singleton;
return bindingWhenOnSyntax;
}

Expand All @@ -68,12 +72,14 @@ class BindingToSyntax<T> implements interfaces.BindingToSyntax<T> {
const autofactory = () => context.container.get<T2>(serviceIdentifier);
return autofactory;
};
this._binding.scope = BindingScopeEnum.Singleton;
return new BindingWhenOnSyntax<T>(this._binding);
}

public toProvider<T2>(provider: interfaces.ProviderCreator<T2>): interfaces.BindingWhenOnSyntax<T> {
this._binding.type = BindingTypeEnum.Provider;
this._binding.provider = provider;
this._binding.scope = BindingScopeEnum.Singleton;
return new BindingWhenOnSyntax<T>(this._binding);
}

Expand Down
144 changes: 144 additions & 0 deletions test/bugs/issue_1297.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { expect } from "chai";
import * as sinon from "sinon";
import { Container, injectable, interfaces } from "../../src/inversify";

describe("Issue 1297", () => {
it('should call onActivation once if the service is a constant value binding', () => {
const container = new Container();

const onActivationHandlerSpy = sinon.spy<
(ctx: interfaces.Context, message: string) => string
>((_ctx: interfaces.Context, message: string) => message);

container.bind("message")
.toConstantValue("Hello world")
.onActivation(onActivationHandlerSpy);

container.get("message");
container.get("message");

expect(onActivationHandlerSpy.callCount).to.eq(1);
});

it('should call onActivation once if the service is a factory binding', () => {

@injectable()
class Katana {
public hit() {
return "cut!";
}
}

const container = new Container();

const onActivationHandlerSpy = sinon.spy<
(ctx: interfaces.Context, instance: interfaces.Factory<Katana>) => interfaces.Factory<Katana>
>((_ctx: interfaces.Context, instance: interfaces.Factory<Katana>) => instance);

container.bind<Katana>("Katana").to(Katana);

container.bind<interfaces.Factory<Katana>>("Factory<Katana>").toFactory<Katana>((context) =>
() =>
context.container.get<Katana>("Katana")).onActivation(onActivationHandlerSpy);

container.get("Factory<Katana>");
container.get("Factory<Katana>");

expect(onActivationHandlerSpy.callCount).to.eq(1);
});

it('should call onActivation once if the service is an auto factory binding', () => {

@injectable()
class Katana {
public hit() {
return "cut!";
}
}

const container = new Container();

const onActivationHandlerSpy = sinon.spy<
(ctx: interfaces.Context, instance: interfaces.Factory<Katana>) => interfaces.Factory<Katana>
>((_ctx: interfaces.Context, instance: interfaces.Factory<Katana>) => instance);

container.bind<Katana>("Katana").to(Katana);

container.bind<interfaces.Factory<Katana>>("Factory<Katana>")
.toAutoFactory<Katana>("Katana").onActivation(onActivationHandlerSpy);

container.get("Factory<Katana>");
container.get("Factory<Katana>");

expect(onActivationHandlerSpy.callCount).to.eq(1);
});

it('should call onActivation once if the service is a function binding', () => {

const container = new Container();

const onActivationHandlerSpy = sinon.spy<
(ctx: interfaces.Context, messageGenerator: () => string) => () => string
>((_ctx: interfaces.Context, messageGenerator: () => string) => messageGenerator);

container.bind<() => string>("message")
.toFunction(() => "Hello world")
.onActivation(onActivationHandlerSpy);

container.get("message");
container.get("message");

expect(onActivationHandlerSpy.callCount).to.eq(1);
});

it('should call onActivation once if the service is a constructor binding', () => {

@injectable()
class Katana {
public hit() {
return "cut!";
}
}

const container = new Container();

const onActivationHandlerSpy = sinon.spy<
(ctx: interfaces.Context, injectableObj: unknown) => unknown
>((_ctx: interfaces.Context, injectableObj: unknown) => injectableObj);

container.bind("Katana")
.toConstructor<Katana>(Katana)
.onActivation(onActivationHandlerSpy);

container.get("Katana");
container.get("Katana");

expect(onActivationHandlerSpy.callCount).to.eq(1);
});

it('should call onActivation once if the service is a provider binding', () => {

@injectable()
class Katana {
public hit() {
return "cut!";
}
}

const container = new Container();

const onActivationHandlerSpy = sinon.spy<
(ctx: interfaces.Context, injectableObj: unknown) => unknown
>((_ctx: interfaces.Context, injectableObj: unknown) => injectableObj);

container.bind("Provider<Katana>")
.toProvider<Katana>((context: interfaces.Context) =>
() =>
Promise.resolve(new Katana())).onActivation(onActivationHandlerSpy);

container.get("Provider<Katana>");
container.get("Provider<Katana>");

expect(onActivationHandlerSpy.callCount).to.eq(1);
});
});

0 comments on commit 784fec7

Please sign in to comment.