Skip to content

Commit 35cfdc7

Browse files
authored
Merge pull request #65 from inversify/fix
Implements inversify/InversifyJS/issues/674#issuecomment-349302658
2 parents cafc953 + 41e3817 commit 35cfdc7

12 files changed

+146
-28
lines changed

README.md

+49
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,55 @@ class Shuriken implements Weapon {
296296
}
297297
```
298298

299+
### Using @provideFluent multiple times
300+
301+
If you try to apply `@provideFluent` multiple times:
302+
303+
```ts
304+
let container = new Container();
305+
let provideFluent = fluentProvide(container);
306+
307+
const provideSingleton = (identifier: any) => {
308+
return provideFluent(identifier)
309+
.inSingletonScope()
310+
.done();
311+
};
312+
313+
function shouldThrow() {
314+
@provideSingleton("Ninja")
315+
@provideSingleton("SilentNinja")
316+
class Ninja {}
317+
return Ninja;
318+
}
319+
```
320+
321+
The library will throw an exception:
322+
323+
> Cannot apply @provideFluent decorator multiple times but is has been used multiple times in Ninja Please use @done(true) if you are trying to declare multiple bindings!
324+
325+
We throw an exception to ensure that you are are not trying to apply `@fluentProvide` multiple times by mistake.
326+
327+
You can overcome this by passing the `force` argument to `done()`:
328+
329+
```ts
330+
let container = new Container();
331+
let provideFluent = fluentProvide(container);
332+
333+
const provideSingleton = (identifier: any) => {
334+
return provideFluent(identifier)
335+
.inSingletonScope()
336+
.done(true); // IMPORTANT!
337+
};
338+
339+
function shouldThrow() {
340+
@provideSingleton("Ninja")
341+
@provideSingleton("SilentNinja")
342+
class Ninja {}
343+
return Ninja;
344+
}
345+
```
346+
347+
299348
## The auto provide utility
300349
This library includes a small utility apply to add the default `@provide` decorator to all
301350
the public properties of a module:

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "inversify-binding-decorators",
3-
"version": "3.1.0",
3+
"version": "3.2.0",
44
"description": "An utility that allows developers to declare InversifyJS bindings using ES2016 decorators",
55
"main": "lib/index.js",
66
"jsnext:main": "es/index.js",

src/decorator/provide.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ function provide(container: interfaces.Container) {
2020
try {
2121
decorate(injectable(), target);
2222
} catch (e) {
23-
throw new Error(`${e.message} ` +
24-
"Please use @provide(ID, true) if you are trying to declare multiple bindings!");
23+
throw new Error(
24+
"Cannot apply @provide decorator multiple times but is has been used " +
25+
`multiple times in ${target.name} ` +
26+
"Please use @provide(ID, true) if you are trying to declare multiple bindings!"
27+
);
2528
}
2629
}
2730

src/interfaces/interfaces.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { interfaces as inversifyInterfaces } from "inversify";
33
namespace interfaces {
44

55
export interface ProvideDoneSyntax {
6-
done(): (target: any) => any;
6+
done(force?: boolean): (target: any) => any;
77
}
88

99
export interface ProvideInSyntax<T> extends ProvideDoneSyntax {

src/syntax/provide_done_syntax.ts

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import interfaces from "../interfaces/interfaces";
22
import { decorate, injectable } from "inversify";
3-
import { interfaces as inversifyInterfaces } from "inversify";
3+
import { interfaces as inversifyInterfaces, METADATA_KEY } from "inversify";
44

55
class ProvideDoneSyntax<T> implements interfaces.ProvideDoneSyntax {
66

@@ -10,10 +10,30 @@ class ProvideDoneSyntax<T> implements interfaces.ProvideDoneSyntax {
1010
this._binding = binding;
1111
}
1212

13-
public done() {
14-
return (target: any) => {
15-
decorate(injectable(), target);
16-
this._binding.implementationType = target;
13+
public done(force?: boolean) {
14+
const that = this;
15+
return function (target: any) {
16+
17+
const isAlreadyDecorated = Reflect.hasOwnMetadata(METADATA_KEY.PARAM_TYPES, target);
18+
const redecorateWithInject = force === true;
19+
20+
if (redecorateWithInject === true && isAlreadyDecorated === false) {
21+
decorate(injectable(), target);
22+
} else if (redecorateWithInject === true && isAlreadyDecorated === true) {
23+
// Do nothing
24+
} else {
25+
try {
26+
decorate(injectable(), target);
27+
} catch (e) {
28+
throw new Error(
29+
"Cannot apply @provideFluent decorator multiple times but is has been used " +
30+
`multiple times in ${target.name} ` +
31+
"Please use @done(true) if you are trying to declare multiple bindings!"
32+
);
33+
}
34+
}
35+
36+
that._binding.implementationType = target;
1737
return target;
1838
};
1939
}

src/syntax/provide_in_syntax.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ class ProvideInSyntax<T> implements interfaces.ProvideInSyntax<T> {
3232
return new ProvideWhenOnSyntax(provideWhenSyntax, provideOnSyntax);
3333
}
3434

35-
public done() {
35+
public done(force?: boolean) {
3636
let binding: inversifyInterfaces.Binding<T> = (<any>this._bindingInSyntax)._binding;
3737
let provideDoneSyntax = new ProvideDoneSyntax<T>(binding);
38-
return provideDoneSyntax.done();
38+
return provideDoneSyntax.done(force);
3939
}
4040

4141
}

src/syntax/provide_in_when_on_syntax.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ class ProvideInWhenOnSyntax<T> implements interfaces.ProvideInWhenOnSyntax<T> {
8585
return this._provideInSyntax.inTransientScope();
8686
}
8787

88-
public done() {
89-
return this._provideInSyntax.done();
88+
public done(force?: boolean) {
89+
return this._provideInSyntax.done(force);
9090
}
9191

9292
}

src/syntax/provide_on_syntax.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ class ProvideOnSyntax<T> implements interfaces.ProvideOnSyntax<T> {
2020
return new ProvideWhenSyntax(bindingWhenSyntax, this._provideDoneSyntax);
2121
}
2222

23-
public done() {
24-
return this._provideDoneSyntax.done();
23+
public done(force?: boolean) {
24+
return this._provideDoneSyntax.done(force);
2525
}
2626

2727
}

src/syntax/provide_when_on_syntax.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ class ProvideWhenOnSyntax<T> implements interfaces.ProvideWhenOnSyntax<T> {
7474
return this._provideOnSyntax.onActivation(fn);
7575
}
7676

77-
public done() {
78-
return this._provideWhenSyntax.done();
77+
public done(force?: boolean) {
78+
return this._provideWhenSyntax.done(force);
7979
}
8080

8181
}

src/syntax/provide_when_syntax.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ class ProvideWhenSyntax<T> implements interfaces.ProvideWhenSyntax<T> {
8585
return new ProvideOnSyntax<T>(bindingOnSyntax, this._provideDoneSyntax);
8686
}
8787

88-
public done() {
89-
return this._provideDoneSyntax.done();
88+
public done(force?: boolean) {
89+
return this._provideDoneSyntax.done(force);
9090
}
9191

9292
}

test/decorator/fluent_provide.test.ts

+49-5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,14 @@ import "reflect-metadata";
55

66
describe("fluentProvide", () => {
77

8-
let container = new Container();
9-
108
it("Should return a configurable decorator", () => {
11-
9+
let container = new Container();
1210
let provide = fluentProvide(container);
1311
expect(typeof provide).eqls("function");
14-
1512
});
1613

1714
it("Should return an instance of ProvideInWhenOnSyntax once it is configured", () => {
18-
15+
let container = new Container();
1916
let provide = fluentProvide(container);
2017
let provideInWhenOnSyntax = provide("SomeTypeID");
2118
expect((<any>provideInWhenOnSyntax)._provideInSyntax).not.to.be.eqls(null);
@@ -24,6 +21,53 @@ describe("fluentProvide", () => {
2421
expect((<any>provideInWhenOnSyntax)._provideInSyntax).not.to.be.eqls(undefined);
2522
expect((<any>provideInWhenOnSyntax)._provideWhenSyntax).not.to.be.eqls(undefined);
2623
expect((<any>provideInWhenOnSyntax)._provideOnSyntax).not.to.be.eqls(undefined);
24+
});
25+
26+
it("Should throw if @fluentProvide is applied more than once without force flag", () => {
27+
28+
let container = new Container();
29+
let provideFluent = fluentProvide(container);
30+
31+
const provideSingleton = (identifier: any) => {
32+
return provideFluent(identifier)
33+
.inSingletonScope()
34+
.done();
35+
};
36+
37+
function shouldThrow() {
38+
@provideSingleton("Ninja")
39+
@provideSingleton("SilentNinja")
40+
class Ninja {}
41+
return Ninja;
42+
}
43+
44+
expect(shouldThrow).to.throw(
45+
"Cannot apply @provideFluent decorator multiple times but is has been used " +
46+
"multiple times in Ninja " +
47+
"Please use @done(true) if you are trying to declare multiple bindings!"
48+
);
49+
50+
});
51+
52+
it("Should work if @provide is applied more than once with force flag", () => {
53+
54+
let container = new Container();
55+
let provideFluent = fluentProvide(container);
56+
57+
const provideSingleton = (identifier: any) => {
58+
return provideFluent(identifier)
59+
.inSingletonScope()
60+
.done(true); // IMPORTANT!
61+
};
62+
63+
function shouldThrow() {
64+
@provideSingleton("Ninja")
65+
@provideSingleton("SilentNinja")
66+
class Ninja {}
67+
return Ninja;
68+
}
69+
70+
expect(shouldThrow).not.to.throw();
2771

2872
});
2973

test/decorator/provide.test.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe("provide", () => {
2929

3030
});
3131

32-
it("Should throw if applied more than once without force flag", () => {
32+
it("Should throw if @provide is applied more than once without force flag", () => {
3333

3434
const myContainer = new Container();
3535
const provide = _provide(myContainer);
@@ -42,12 +42,14 @@ describe("provide", () => {
4242
}
4343

4444
expect(shouldThrow).to.throw(
45-
"Cannot apply @injectable decorator multiple times. " +
46-
"Please use @provide(ID, true) if you are trying to declare multiple bindings!");
45+
"Cannot apply @provide decorator multiple times but is has been used " +
46+
"multiple times in Ninja " +
47+
"Please use @provide(ID, true) if you are trying to declare multiple bindings!"
48+
);
4749

4850
});
4951

50-
it("Should work if applied more than once with force flag", () => {
52+
it("Should work if @provide is applied more than once with force flag", () => {
5153

5254
const myContainer = new Container();
5355
const provide = _provide(myContainer);

0 commit comments

Comments
 (0)