New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

sack

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sack - npm Package Compare versions

Comparing version 1.3.0 to 2.0.0

lib/IoCBinding.js

7

HISTORY.md
# Change History
## 2.0.0 (2014-04-13)
* API change for registering (building pattern style). See README file.
* Dependencies can now be registered as weak (can be overridden) or strong.
* Container no longer registers itself as a dependency.
* Removal of `call()` and `registerInjects()` (for now).
## 1.3.0 (2014-02-22)

@@ -4,0 +11,0 @@

2

index.js
module.exports = {
Container: require('./lib/Container.js')
};
module.exports = Container;
var getArgs = require('typedef').getArguments;
var getName = require('typedef').getName;
var _ = require('underscore');
var getArguments = require('typedef').getArguments;
var _ = require('underscore');
var IoCBinding = require('./IoCBinding.js');
/**
* Basic dependency resolver / IoC container that allows for the registering of
* types and factories via callback functions, as well as auto-guessing
* constructor and function injecting via parameter names.
* types and instances, as well as auto-guessing constructor and function
* injecting via parameter names.
* @constructor

@@ -15,237 +15,60 @@ */

{
this._types = {};
// Injected post-instance
this._injects = [];
this.register('container', this);
/**
* @type {object.<string,IoCBinding>}
*/
this._bindings = {};
}
/**
* @param {Function=} T
* @param {Function=} closure
* @param {boolean=} shared
* @param {Object=} instance
* Add a new dependency
* @param {string} name
* @param {Function|object} source
* @return {IoCBinding}
*/
function Entry(T, closure, deps, shared, instance)
Container.prototype.register = function(name, source)
{
this.T = T || null;
this.closure = closure || null;
this.deps = deps || [];
this.shared = shared !== undefined ? shared : false;
this.instance = instance || null;
}
var existing = this._bindings[name];
if (existing && !existing.isWeak())
throw new Error('Cannot override: ' + name);
/**
* Register a class constructor or object instance into the container under a
* string tag.
* @param {String} name The name we want register something under.
* @param {Function} T The class constructor of the type we want to register.
* @param {boolean=} shared If true, this dep will only be instantiated once (a
* la singleton).
*/
Container.prototype.register = function(name, T, shared)
{
if (T instanceof Function)
return this.registerConstructor(name, T, shared);
else
return this.registerInstance(name, T, shared);
};
// Create the binding with a ref back to the building function
var _this = this;
var binding = new IoCBinding(name, source, function(b) {
return _this._build(b._source, b.getDependencyNames());
});
/**
* Register an existing object instance into the container under a string tag.
* @param {String} name The name we want to register this dependency under.
* @param {Object} instance The object we want returned every time we resolve
* this dependency.
*/
Container.prototype.registerInstance = function(name, instance)
{
this._types[name] = new Entry(
null, null, null, true, instance);
this._bindings[name] = binding;
return binding;
};
/**
* Register a callback function into the container under a string tag.
* @param {String} name The name we want to register this dependency under.
* @param {Function} closure A callback function we want to call when creating
* this dependency.
* @param {boolean=} shared If true, this dependency will only be instantiated
* once (a la singleton).
* @param {string} thing
* @return {object}
*/
Container.prototype.registerClosure = function(name, closure, shared)
Container.prototype.make = function(thing)
{
this._types[name] = new Entry(
null, closure, getArgs(closure), !!shared, null);
};
/**
* Register a factory callback function under an object constructor.
* @param {Function} T Constructor to register.
* @param {Function} factory Callback function that fires to create the class.
*/
Container.prototype.registerFactory = function(T, factory)
{
this._types['$$factory_function$$' + getName(T)] = new Entry(
T, factory, getArgs(factory), false, null);
};
/**
* Register a constructor function under a string tag. Dependencies are inject
* via constructor parameters.
* @param {String} name The name we want to register this dependency under.
* @param {boolean=} shared If true, this dependency will only be instantiated
* once (a la singleton).
*/
Container.prototype.registerConstructor = function(name, T, shared)
{
this._types[name] = new Entry(
T, null, getArgs(T), !!shared, null);
};
/**
* Register a singleton (instantiated-once) class constructor dependency into
* this container.
* @param {Function} T The class constructor of the type we want to register.
*/
Container.prototype.shared = function(name, T)
{
return this.register(name, T, true);
};
/**
* For a given type that comes out of a factory, assign instance properties to
* it everytime.
* @param {Function} T The class.
* @param {Object.<string,*>} injectMap
*/
Container.prototype.registerInjects = function(T, injectMap)
{
var map = this._getInjectMap(T);
_.extend(map, injectMap);
};
/**
* Request the object provided by the container registed under construct T.
* @param {Function} T Constructor function we want to find the factory for.
* @return {Object} The dependency.
*/
Container.prototype.factory = function(T)
{
// Attempt to find our registered class to know how to make it...
for (var n in this._types) {
var entry = this._types[n];
if (entry.T !== T || !entry.closure) continue;
var deps = this._resolveDeps(entry.deps);
return entry.closure.call(null, deps);
if (typeof thing === 'string') {
var binding = this._resolve(thing);
return binding.build();
}
// Otherwise fall back to build
return this.build(T);
};
/**
* Create a new object T with named dependencies in its constructor resolved.
* @param {Function} T
* @return {Object} The dependency.
*/
Container.prototype.build = function(T)
{
var deps = this._resolveDeps(getArgs(T));
return this._doInjects(T, callNew(T, deps));
};
/**
* Create a stringly-named dependency from the container.
* @param {String} name The string tag of the dependency we want to create.
* @return {Object} The dependency.
*/
Container.prototype.make = function(name)
{
// Fall back to call if provided 2 args
if (arguments.length === 2) {
return this.call.apply(this, arguments);
// Fall back to factory if provided a constructor
} else if (name instanceof Function) {
return this.factory(name);
// If not a string (like a rando object), just NOP
} else if (!_(name).isString()) {
return name;
if (typeof thing === 'function') {
var depNames = getArguments(thing);
return this._build(thing, depNames);
}
var entry = this._resolve(name);
// Short circuit if we've already got our object cached
if (entry.shared && entry.instance) {
return entry.instance;
}
// Resolve all of the expressed dependencies
var deps = this._resolveDeps(entry.deps);
// Create
var instance;
if (entry.T) {
instance = this._doInjects(entry.T, callNew(entry.T, deps));
} else {
instance = entry.closure.call(null, deps);
}
// Stash singleton
if (entry.shared)
entry.instance = instance;
return instance;
// Nop if it's not a string dep or a constructor/closure
return thing;
};
/**
* Execute a function in some context, resolving all its parameters as
* dependencies.
* @param {Object} context Function execution context.
* @param {Function} f Function to call.
* @return {*} Whatever the function returns.
* Where the magic happens
* @param {IoCBinding} binding
* @return {object}
*/
Container.prototype.call = function(context, f)
Container.prototype._build = function(T, depNames)
{
var deps = this._resolveDeps(getArgs(f));
return f.apply(context, deps);
};
Container.prototype._doInjects = function(T, instance)
{
var map = this._getInjectMap(T);
_.extend(instance, map);
return instance;
};
/**
* Get any inject maps for instances based on class T
* @param {Function} T
* @return {Object.<String,*>}
*/
Container.prototype._getInjectMap = function(T)
{
for (var n = 0; n < this._injects.length; n++) {
var entry = this._injects[n];
if (entry.T === T)
return entry.map;
}
// New one
var map = {};
this._injects.push({T: T, map: map });
return map;
};
/**
* Resolve a list of string dependencies into real dependencies
* @param {Array.<String>} deps
* @return {Array.<Object>}
* @private
*/
Container.prototype._resolveDeps = function(deps)
{
var _this = this;
return _(deps).map(function(d) { return _this.make(d); });
var deps = _.map(depNames, function(d) { return _this.make(d); });
return callNew(T, deps);
};

@@ -256,3 +79,3 @@

* @param {String} name The dependency name.
* @return {Entry} Information about this named dependency.
* @return {IoCBinding} Information about this named dependency.
* @private

@@ -262,10 +85,13 @@ */

{
var info = this._types[name];
if (!info)
var binding = this._bindings[name];
if (!binding)
throw new Error(
'Cannot resolve dependency "' + name + '"');
return info;
return binding;
};
// Give us a way to instantiate a new class with an array of args
/**
* Give us a way to instantiate a new class with an array of args
* @private
*/
function callNew(T, args)

@@ -277,2 +103,1 @@ {

}
{
"name": "sack",
"version": "1.3.0",
"version": "2.0.0",
"description": "An Inversion-of-Control container for all your dependency injection needs.",

@@ -48,5 +48,5 @@ "keywords": [

"dependencies": {
"underscore": "~1.5.2",
"typedef": "~1.0.2"
"typedef": "^1.0.2",
"underscore": "^1.6.0"
}
}

@@ -69,3 +69,3 @@ # Sack

In large applicaitons, things like model stores, application
In large applications, things like model stores, application
configs, service handles, etc. are all used across several different parts of

@@ -77,3 +77,10 @@ an application. Relying on every consumer object to manage its dependencies

The goal is to reduce how tightly coupled your various objects are by removing
the knowledge of *how to create* other dependency objects, and simply rely on
expressing *what kind* of objects we need.
This facilitates testing, as complex dependencies that are injected can be
substituted with [mocks](http://en.wikipedia.org/wiki/Mock_object), and the
consuming classes are not tied to a specific implementation.
## Usage

@@ -99,6 +106,7 @@

Register a constructor function that will get executed *one time* when the
first time a dependency is resolved, and then re-used after that (singleton):
first time the dependency is resolved, and then re-used after that (singleton
pattern):
```javascript
container.shared('service', MyService);
container.register('service', MyService).asSingleton();
```

@@ -109,3 +117,3 @@

```javascript
container.register('service', someService);
container.register('service', someService).asInstance();
```

@@ -126,38 +134,8 @@

```javascript
container.shared('service', function() {
container.register('service', function() {
return new MyService();
});
}).asSingleton();
```
Register a factory callback for a specific class constructor. This is useful
for allowing objects to create several of another object without explictly
knowing how:
```javascript
container.registerFactory(GameEntity, function(pool) {
return pool.aquire(GameEntity);
});
```
To be more explicit, you can use the more verbose methods of registration
instead of having Sack guess:
```javascript
container.registerConstructor('users', MySqlUserStore);
container.registerInstance('config', { version: '1.0.0' });
container.registerClosure('currentUser', function(users) {
return users.fetchLoggedInUser();
});
```
You can also register certain properties to be set every time a depedency is
created via any of the factory methods for a specific class.
```javascript
container.registerInjects(MyService, { securityToken: '123-123' });
```
### Resolving Objects

@@ -198,3 +176,3 @@

```javascript
container.register('users', new LocalStorageUsers());
container.register('users', LocalStorageUsers).asSingleton();
```

@@ -209,2 +187,25 @@

### Strong vs Weak Dependencies
By default, all registered dependencies are considered "strong", that is, they
cannot be overridden. Attempting to register a dependency with the same name as
another one will result in an error:
```javascript
container.register('server', express());
container.register('server', http.createServer());
> Error: Cannot override: config
```
Registering a dependency as weak allows it to be overriden later:
```javascript
container.register('config', {}).asWeak();
container.register('config', new ConfigStore());
// No error
```
## Architecture Patterns

@@ -211,0 +212,0 @@

var test = require('tape');
var Container = require('../lib/Container.js');

@@ -16,12 +17,2 @@

test('Resolve method throws', function(t) {
t.plan(1);
var r = new Container();
t.throws(function() {
r._resolve('not there');
}, 'Missing type');
});
test('Basic dep track', function(t) {

@@ -47,13 +38,2 @@ t.plan(5);

test('Throw on unmet dep', function(t) {
t.plan(1);
var r = new Container();
r.register('T', function(A, B, C) {});
t.throws(function() {
r.make('T');
}, 'no deps');
});
test('Make from ctor', function(t) {

@@ -78,3 +58,3 @@ t.plan(2);

function T() { t.ok(true, 'T ctor fired'); }
r.shared('t', T);
r.register('t', T).asSingleton();

@@ -98,3 +78,3 @@ t.strictEqual(r.make('t'), r.make('t'), 'Same instance created');

r.register('a', A);
r.shared('t', T);
r.register('t', T).asSingleton();

@@ -106,2 +86,3 @@ r.make('t');

test('Nop', function(t) {

@@ -117,2 +98,3 @@ t.plan(1);

test('Using closures', function(t) {

@@ -156,55 +138,1 @@ t.plan(3);

});
test('Self register', function(t) {
t.plan(2);
var c = new Container();
t.strictEqual(c.make('container'), c, 'got em');
t.strictEqual(c.make('container'), c, 'got em');
});
test('Factory style', function(t) {
t.plan(4);
function M() { }
var LOL = {};
var container = new Container();
container.registerFactory(M, function() {
t.pass('factory fired');
return LOL;
});
t.strictEqual(container.factory(M), LOL, 'made it'); // 2x
t.strictEqual(container.factory(M), LOL, 'made it'); // 2x
});
test('call method', function(t) {
t.plan(8);
var context = { };
var ret = {};
context.f = function(b, a) {
t.strictEqual(this, context);
t.ok(a instanceof A);
t.ok(a instanceof A);
return ret;
}
function A() { }
function B() { }
var container = new Container();
container.register('a', A);
container.register('b', B);
var r = container.call(context, context.f);
t.strictEqual(r, ret);
// with make defaulting out
t.strictEqual(ret, container.make(context, context.f));
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc