Created
April 8, 2019 18:07
-
-
Save tizzo/bcbe56f6ff0e599a835ef6bde08b8cb6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const should = require('should'); | |
const DiC = require('../../lib/DependencyInjectionContainer'); | |
/** | |
* @class Service class test fixture. | |
*/ | |
class Foo { | |
constructor() { | |
this.custructorArgs = arguments; | |
} | |
setValue(value) { | |
this.value = value; | |
} | |
getValue() { | |
return this.value; | |
} | |
bar() { | |
return 'baz'; | |
} | |
} | |
describe('DependencyInjectionContainer', () => { | |
describe('service registration', () => { | |
it('should throw if you try to access a raw unregistered service', () => { | |
const c = new DiC(); | |
should.throws(() => c.getRaw('foo'), Error); | |
}); | |
it('should throw if you try to instantiate an unregistered service', () => { | |
const c = new DiC(); | |
should.throws(() => c.get('foo'), Error); | |
}); | |
it('should be able to register and instantiate a simple service', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo); | |
c.get('foo').bar().should.equal('baz'); | |
}); | |
it('should allow a service to specify static arguments', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.registerArgument('foo') | |
.registerArgument('bar'); | |
c.get('foo').custructorArgs[0].should.equal('foo'); | |
c.get('foo').custructorArgs[1].should.equal('bar'); | |
}); | |
it('should allow a service to specify dynamic arguments', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.registerArgument((container) => 'foo') | |
.registerArgument('bar'); | |
c.get('foo').custructorArgs[0].should.equal('foo'); | |
c.get('foo').custructorArgs[1].should.equal('bar'); | |
}); | |
it('should allow services to register properties that must be set', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.addSetter('setValue', 'blah'); | |
c.get('foo').getValue().should.equal('blah'); | |
}); | |
it('should support setting simple properties', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.addProperty('someProperty', 'darth vader'); | |
c.get('foo').someProperty.should.equal('darth vader'); | |
}); | |
it('should support setting dynamic properties', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.addProperty('someProperty', () => 'darth vader'); | |
c.get('foo').someProperty.should.equal('darth vader'); | |
}); | |
it('should allow one service to inherit from the definition of another', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.addProperty('name', 'Darth'); | |
c.registerService('foo_child', Foo) | |
.inherits('foo') | |
.addProperty('surname', 'Vader'); | |
c.get('foo_child').name.should.equal('Darth'); | |
c.get('foo_child').surname.should.equal('Vader'); | |
}); | |
it('should support chained inheritance', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.addProperty('name', 'Darth'); | |
c.registerService('foo_child', Foo) | |
.inherits('foo') | |
.addProperty('surname', 'Vader'); | |
c.registerService('foo_grandchild', Foo) | |
.inherits('foo_child') | |
.addProperty('son', 'Luke'); | |
c.get('foo_grandchild').name.should.equal('Darth'); | |
c.get('foo_grandchild').surname.should.equal('Vader'); | |
c.get('foo_grandchild').son.should.equal('Luke'); | |
}); | |
it('should allow convenience property syntax for fetching serices', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.addProperty('name', 'Darth'); | |
c.foo.name.should.equal('Darth'); | |
}); | |
}); | |
describe('value registration', () => { | |
it('should allow a value to be registered', () => { | |
const c = new DiC(); | |
c.registerValue('foo', {bar: 'baz'}); | |
c.foo.bar.should.equal('baz'); | |
}); | |
}); | |
describe('value protection', () => { | |
it('should prevent the execution of a registered function', () => { | |
const c = new DiC(); | |
c.registerService('foo', Foo) | |
.addProperty('someProperty', c.protect(() => 'darth vader')); | |
c.foo.someProperty().should.equal('darth vader'); | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const Service = require('./Service'); | |
const _services = Symbol('_services'); | |
const _values = Symbol('_values'); | |
/** | |
* @class A simple dependency injection container for lazy loading and instantiating services. | |
*/ | |
class DependencyInjectionContainer { | |
constructor() { | |
this[_services] = {}; | |
this[_values] = {}; | |
} | |
/** | |
* Registers a value in the container. | |
* | |
* @param {string} name - The name of this value | |
* @param {mixed} value - The value to be registered within the container. | |
*/ | |
registerValue(name, value) { | |
this[_values][name] = value | |
Object.defineProperty(this, name, { | |
get: this.get.bind(this, name), | |
}); | |
return this[_values][name]; | |
} | |
/** | |
* Registers a service in the container. | |
* | |
* @param {string} name - The name of this service | |
* @param {Class} ServiceClass - The class that will be instantiated when loading this class. | |
* @param {Object} options - An options object passed through to the Service constructor. | |
*/ | |
registerService(name, ServiceClass, options = {}) { | |
this[_services][name] = new Service(name, ServiceClass, options); | |
Object.defineProperty(this, name, { | |
get: this.get.bind(this, name), | |
}); | |
return this[_services][name]; | |
} | |
/** | |
* Get an instantiated service or value. | |
* | |
* @param {String} name - The name of the service or value to instantiate. | |
* @return {mixed} - The fully instantiated service. | |
*/ | |
get(name) { | |
if (this[_services][name]) { | |
return this.getRaw(name).get(this); | |
} | |
else if(this[_values][name]) { | |
return this[_values][name]; | |
} | |
throw new Error(`No service or value registered for ${name}`); | |
} | |
/** | |
* Wraps a callable function to ensure that it is not executed when fetched. | |
* | |
* This is useful for wrapping functions that need to be injected into services. | |
* | |
* @param {mixed} value - A parameter to wrap in a function | |
* @return {Function} - An anonymous function that returns the provided value. | |
*/ | |
protect(value) { | |
return function() { | |
return value; | |
}; | |
} | |
/** | |
* Retrieves the service object rather than the instantiated service. | |
* | |
* @param {string} name - The name of the service object to return. | |
* @return {Service} - The Service object. | |
*/ | |
getRaw(name) { | |
if (this[_services][name] === undefined && this[_values][name] === undefined) { | |
throw new Error(`The service ${name} has not been defined`); | |
} | |
return this[_services][name]; | |
} | |
} | |
module.exports = DependencyInjectionContainer; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment