Skip to content

Instantly share code, notes, and snippets.

@lennerd
Last active August 29, 2015 14:07
Show Gist options
  • Save lennerd/30c35aef819bc0f25be0 to your computer and use it in GitHub Desktop.
Save lennerd/30c35aef819bc0f25be0 to your computer and use it in GitHub Desktop.
Simple library for dependency injection in Javascript. Heavily inspired by the Pimple library of Fabien Potencier.
/*!
* Copyright (c) 2014 Lennart Hildebrandt
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
(function() {
var Scope = this.Scope = function() {
this._definitions = {};
};
Scope.prototype = {
set: function(key, value) {
return this._definitions[key] = new Definition(value, this);
},
get: function(key) {
if (!this.has(key)) throw unknownKeyError(key);
return this._definitions[key].create(_.rest(arguments));
},
has: function(key) {
return this._definitions[key] != null;
},
raw: function(key) {
if (!this.has(key)) throw unknownKeyError(key);
return this._definitions[key];
},
extend: function(key, callable) {
var definition = this.raw(key),
value = definition.value;
function extended() {
var superValue = value.apply(this, arguments),
args = Array.prototype.unshift(arguments, superValue);
return callable.apply(this, args);
}
definition.value = extended;
return definition;
}
};
function Definition(value, context) {
this.value = value;
this.context = context;
this._factory = false;
this._protected = false;
}
Definition.prototype = {
bind: function(context) {
this.context = context;
return this;
},
factory: function() {
this._factory = true;
return this;
},
protect: function() {
this._protected = true;
return this;
},
create: function() {
var definition = this,
value = definition.value,
raw, args;
if ('raw' in definition || definition._protected || !_.isFunction(value)) {
return value;
}
args = _.flatten(arguments, true);
raw = value;
value = value.apply(definition.context, args);
if (definition._factory) {
return value;
}
definition.raw = raw;
return definition.value = value;
}
};
function unknownKeyError(key) {
return new Error('Unknown key: ' + key);
}
})();
@lennerd
Copy link
Author

lennerd commented Oct 11, 2014

Underscore

I used Underscore.js to simplify the library base. But all it's used helpers can be replaced by Vanilla JS or similar helpers from other libraries easily.

Usage

// Create dependency injection container
var scope = new Scope();

// Add service definition creating a singleton service
scope.set('serivce.one', function() {
    return new FirstService();
});

// Add factory service definition with a reference to another service,
// meaning getting the service will allways create a new instance:
// scope.get('service.two') !==  scope.get('service.two')
scope.set('service.two', function() {
    return new SecondService(scope.get('serivce.one'));
}).factory();

// You can also add arguments to a factory service definition ...
scope.set('service.three', function(options) {
    return new ThirdService(options);
}).factory();

scope.get('service.three', { bar: 'foo' });
// => New instance of the third service class containing the passed option hash

// Let's add a function as scope value itself, not as service ...
scope.set('service.function', function(foo) {
    return 'Hello ' + foo + '!';
}).protect();

scope.get('service.function')('World');
// => 'Hello World!'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment