Skip to content

Commit a01e640

Browse files
#88 Default Life time for container.
1 parent 9708347 commit a01e640

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

README.md

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,43 +104,63 @@ class Service {
104104
```
105105

106106
### Life Time control.
107-
> By default, containers resolve singletons when using **useClass** and **useFactory**. Change it by setting **lifeTime** attribute to **LifeTime.PerRequest**.
107+
> By default, containers resolve singletons when using **useClass** and **useFactory**. You can specify life time individually for each item in a container by setting **lifeTime** attribute to **LifeTime.PerRequest**.
108108
109109
```typescript
110110
import { LifeTime } from 'container-ioc';
111111

112112
container.register([
113-
{ token: TService, useClass: Service, lifeTime: LifeTime.PerRequest }
113+
{
114+
token: TService,
115+
useClass: Service,
116+
lifeTime: LifeTime.PerRequest
117+
}
114118
]);
115119
```
116120
```typescript
117121
container.register([
118122
{
119123
token: TService,
120-
useFactory: () => { serve(): void {} },
124+
useFactory: () => {
125+
return {
126+
serve(): void {}
127+
}
128+
},
121129
lifeTime: LifeTime.PerRequest }
122130
]);
123131
```
124-
125-
### Hierarchical containers.
126-
> If container can't find value, it will look it up in ascendant containers.
132+
> Or you can set default life time for all items in a container by passing an option object.
127133
```typescript
134+
const container = new Container({
135+
defaultLifeTime: LifeTime.PerRequest
136+
});
137+
```
128138

129-
let parentContainer = new Container();
130-
let childContainer = parentContainer.createChild();
131-
132-
parentContainer.register({ token: TApplication, useClass: Application });
139+
### Hierarchical containers.
140+
> If container can't find a value within itself, it will look it up in ascendant containers. There a 3 ways to set a parent for a container.
133141
134-
childContainer.resolve(TApplication);
142+
###### 1. Container.createChild() method.
143+
```typescript
144+
const parentContainer = new Container();
145+
const childContainer = parentContainer.createChild();
135146
```
136-
> You can also assign parent container to any other container
147+
148+
###### 2. Container.setParent() method.
137149
```typescript
138-
let parent = new Container();
139-
let child = new Container();
150+
const parent = new Container();
151+
const child = new Container();
140152

141153
child.setParent(parent);
142154
```
143155

156+
###### 3. Via Container's constructor with options.
157+
```typescript
158+
const parent = new Container();
159+
const child = new Container({
160+
parent: parent
161+
});
162+
```
163+
144164
### Using Factories
145165
```typescript
146166
/* Without injections */

src/lib/container.interface.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { IInjectionInstance, ProviderToken, RegistrationProvider } from './interfaces';
1+
import { IInjectionInstance, ProviderToken, RegistrationProvider, LifeTime } from './interfaces';
2+
3+
export interface IContainerOptions {
4+
parent?: IContainer;
5+
defaultLifeTime?: LifeTime;
6+
}
27

38
export interface IContainer {
49
register(provider: RegistrationProvider|RegistrationProvider[]): void;

src/lib/container.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IConstructor, IInjectionInstance, IInjectionMd, IProvider, LifeTime, ProviderToken, RegistrationProvider } from './interfaces';
22
import { FactoryFunction, IFactory, IRegistryData, RegistryData } from './registry-data';
3-
import { IContainer } from './container.interface';
3+
import { IContainer, IContainerOptions } from './container.interface';
44
import { InvalidProviderProvidedError, NoProviderError } from './exceptions';
55
import { INJECTABLE_MD_KEY, INJECTIONS_MD_KEY } from './metadata/keys';
66
import { IMetadataAnnotator } from './metadata/metadata-annotator.interface';
@@ -9,9 +9,17 @@ import { AnnotatorProvider } from './metadata/index';
99
const MetadataAnnotator: IMetadataAnnotator = AnnotatorProvider.get();
1010

1111
export class Container implements IContainer {
12+
private static DEFAULT_LIFE_TIME = LifeTime.Persistent;
1213
private registry: Map<ProviderToken, IRegistryData> = new Map();
14+
private parent: IContainer;
15+
private defaultLifeTime: LifeTime = Container.DEFAULT_LIFE_TIME;
1316

14-
constructor(private parent?: IContainer) {}
17+
constructor(options?: IContainerOptions) {
18+
if (options) {
19+
this.parent = <IContainer> options.parent;
20+
this.defaultLifeTime = options.defaultLifeTime || this.defaultLifeTime;
21+
}
22+
}
1523

1624
public register(provider: RegistrationProvider|RegistrationProvider[]): void {
1725
provider = this.nornalizeProvider(provider);
@@ -29,11 +37,11 @@ export class Container implements IContainer {
2937
}
3038

3139
public createScope(): IContainer {
32-
return new Container(this);
40+
return new Container({ parent: this });
3341
}
3442

3543
public createChild(): IContainer {
36-
return new Container(this);
44+
return this.createScope();
3745
}
3846

3947
public setParent(parent: IContainer): void {
@@ -90,7 +98,7 @@ export class Container implements IContainer {
9098
registryData.factory.inject = this.convertTokensToInjectionMd(<ProviderToken> provider.inject);
9199
}
92100

93-
registryData.lifeTime = provider.lifeTime || LifeTime.Persistent; // TODO issue #88
101+
registryData.lifeTime = provider.lifeTime || this.defaultLifeTime;
94102
}
95103

96104
this.registry.set(provider.token, registryData);

src/tests/container.spec.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,27 @@ describe('Container', () => {
221221
expect(instance1).not.to.be.equal(instance2);
222222
expect(instance1.b).to.be.equal(instance2.b);
223223
});
224+
225+
it('should set default lifeTime via options', () => {
226+
const cont = new Container({
227+
defaultLifeTime: LifeTime.PerRequest
228+
});
229+
230+
@Injectable()
231+
class A {}
232+
233+
cont.register({ token: A, useClass: A });
234+
let instance1 = cont.resolve(A);
235+
let instance2 = cont.resolve(A);
236+
237+
expect(instance1).not.to.be.equal(instance2);
238+
239+
cont.register({ token: 'IB', useFactory: () => ({}) });
240+
instance1 = cont.resolve('IB');
241+
instance2 = cont.resolve('IB');
242+
243+
expect(instance1).not.to.be.equal(instance2);
244+
});
224245
});
225246

226247
describe('Errors', () => {
@@ -339,11 +360,38 @@ describe('Container', () => {
339360

340361
});
341362

342-
describe('createScope()', () => {
343-
it('should create child scope', () => {
363+
describe('Constructor', () => {
364+
it('should set defaultLife through option object', () => {
365+
const cont = new Container({ defaultLifeTime: LifeTime.PerRequest });
366+
367+
cont.register({ token: 'A', useFactory: () => ({})});
368+
369+
const instance1 = cont.resolve('A');
370+
const instance2 = cont.resolve('A');
371+
372+
expect(instance1).not.to.be.equal(instance2);
373+
});
374+
});
375+
376+
describe('createChild()', () => {
377+
it('should create child container', () => {
344378
const childContainer: any = container.createChild();
345379
expect(childContainer).to.be.ok;
346380
expect(childContainer.parent).to.equal(container);
347381
});
348382
});
383+
384+
describe('setParent()', () => {
385+
it('should set parent for a container', () => {
386+
const parentContainer = new Container();
387+
const childContainer = new Container();
388+
childContainer.setParent(parentContainer);
389+
390+
parentContainer.register({ token: 'A', useValue: 'string' });
391+
392+
const value = childContainer.resolve('A');
393+
394+
expect(value).to.be.equal('string');
395+
});
396+
});
349397
});

0 commit comments

Comments
 (0)