Skip to content

Commit

Permalink
Merge pull request #35 from Spy-Seth/feature/destroy
Browse files Browse the repository at this point in the history
Add the abilities to destroy the container
  • Loading branch information
shouze authored Oct 19, 2016
2 parents f5b4450 + 63f2562 commit 617e160
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var fooServiceInstance = container.getService('foo');

var barParameterValue = container.getParameter('bar');

constainer.destroy();
```

## Configuration
Expand Down
40 changes: 37 additions & 3 deletions src/Container.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var Container = function Container(serviceDefinitionCollection, parameterCollect
this.parameterCollection = parameterCollection;

this.serviceStorage = new ServiceStorage();
this.isDestroyed = false;

if (validateContainer) {
this.serviceDefinitionCollection.checkCyclicDependencies();
Expand All @@ -31,7 +32,9 @@ var Container = function Container(serviceDefinitionCollection, parameterCollect
* @return {*|null}
*/
Container.prototype.getParameter = function (name) {
if (!this.parameterCollection.hasParameter(name)) {
if (this.isDestroyed) {
throw new Error('This container instance has been destroyed.');
} else if (!this.parameterCollection.hasParameter(name)) {
throw new Error('Unknown parameter "' + name + '".');
}

Expand All @@ -47,7 +50,9 @@ Container.prototype.getParameter = function (name) {
* @return {*}
*/
Container.prototype.getService = function (name) {
if (!this.serviceDefinitionCollection.hasServiceDefinition(name)) {
if (this.isDestroyed) {
throw new Error('This container instance has been destroyed.');
} else if (!this.serviceDefinitionCollection.hasServiceDefinition(name)) {
throw new Error('Unknown service "' + name + '".');
}

Expand Down Expand Up @@ -77,7 +82,9 @@ Container.prototype.getService = function (name) {
* @param {Function} mock
*/
Container.prototype.mockService = function (name, mock) {
if (!this.serviceDefinitionCollection.hasServiceDefinition(name)) {
if (this.isDestroyed) {
throw new Error('This container instance has been destroyed.');
} else if (!this.serviceDefinitionCollection.hasServiceDefinition(name)) {
throw new Error('Unknown service "' + name + '".');
}

Expand All @@ -89,4 +96,31 @@ Container.prototype.mockService = function (name, mock) {
this.serviceStorage.replaceInstance(name, mock);
};

/**
* Will try to destroy all services by calling the method `destructor` on it (if
* it exist) to ask them nicely to destroy themself and by removing the internal
* reference to the service to simplify the life of the garbage colletor.
* The container will also be inusable.
*/
Container.prototype.destroy = function () {
if (this.isDestroyed) {
throw new Error('This container instance has already been destroyed.');
}

this.isDestroyed = true;

var self = this;
this.serviceDefinitionCollection.forEach(function (serviceDefinition) {
if (self.serviceStorage.hasInstance(serviceDefinition.getName())) {
var serviceInstance = self.serviceStorage.getInstance(serviceDefinition.getName());

if (typeof serviceInstance.destructor === 'function') {
serviceInstance.destructor();
}
}

delete self.serviceStorage;
});
};

module.exports = Container;
14 changes: 14 additions & 0 deletions tests/fixture/valid/ServiceF.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

/**
* A destroyable service.
*
* @constructor
*/
var ServiceF = function ServiceF() {
};

ServiceF.prototype.destructor = function () {
};

module.exports = ServiceF;
43 changes: 43 additions & 0 deletions tests/functionals/ContainerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var ServiceDefinitionCollection = require('./../../src/ServiceDefinitionCollecti
var servicesConfigurationValid = require('./../fixture/valid/services');
var ServiceA = require('./../fixture/valid/ServiceA');
var ServiceC = require('./../fixture/valid/ServiceC');
var ServiceF = require('./../fixture/valid/ServiceF');

describe('Container', function () {
it('should return the parameter value', function () {
Expand Down Expand Up @@ -162,4 +163,46 @@ describe('Container', function () {
it.skip('should replace a cached service by a mocker one', function () {
// TODO
});

it('should be inusable after it\'s destruction', function () {
var serviceDefinitionCollection = new ServiceDefinitionCollection();
var parameterCollection = new ParameterCollection();

var container = new Container(serviceDefinitionCollection, parameterCollection);
container.destroy();

expect(() => {
container.getParameter()
}).to.throw('This container instance has been destroyed.');

expect(() => {
container.getService()
}).to.throw('This container instance has been destroyed.');

expect(() => {
container.mockService()
}).to.throw('This container instance has been destroyed.');
});

it('should invoke the `destructor` method on a service when the container is destroyed', function () {
var serviceDefinitionF = new ServiceDefinition(
'foo.serviceF',
ServiceF,
new FunctionArgumentCollection(),
true,
new CallCollection()
);

var serviceDefinitionCollection = new ServiceDefinitionCollection([serviceDefinitionF]);
var parameterCollection = new ParameterCollection();

var container = new Container(serviceDefinitionCollection, parameterCollection);

var serviceFInstance = container.getService('foo.serviceF');
var destructorSpy = sinon.spy(serviceFInstance, 'destructor');

container.destroy();

expect(destructorSpy).to.have.been.calledOnce;
});
});

0 comments on commit 617e160

Please sign in to comment.