Skip to content

Commit

Permalink
Release v0.2
Browse files Browse the repository at this point in the history
  • Loading branch information
divmgl committed Dec 8, 2015
1 parent f3a8d4d commit 4ed5ab2
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 277 deletions.
118 changes: 71 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,63 @@ Bootstrapping a simple server using Express.js:
var wire = require('nwire');
var config = require('./config');

wire(config, function(err, app){ // Composite root
if (err) throw err; // Something happened while building dependencies
app.packages.server.listen(3000);
wire(config, function(err, app){ // Composite root
if (err) throw err; // Handle errors
app.server.listen(3000); // Start your server
});
```
```js
// server.js
module.exports.needs = ['express'];
module.exports.fn = function($){
module.exports.needs = ['express']; // What your package needs
module.exports.fn = function($){ // Dependencies are injected through $
var app = $.express();

// Add your routes and configuration here
return app;

return app;
}
```
```js
// config.js
module.exports = {
url: __dirname, // Base URL
packages: { // Packages to be injected
'server': './server',
'express': 'express'
}
'server': require('./server'), // Provide packages
'express': require('express')
}
```

## Why?

Dependency injection shouldn't be complicated. `nwire.js` encourages loosely coupled functionality and simplifies the process of isolating your code for testing.
Dependency injection shouldn't be complicated. `nwire` encourages loosely coupled functionality and simplifies the process of isolating your code for testing.

## Creating packages
## Creating the container

### Package definition
You must feed `nwire` a configuration object containing the packages you wish to provide for injection.

Consider this sample configuration object.
```js
// config.js
module.exports = {
'app': require('./server'),
'redis-db': require('./db'),
'express': require('express'),
'morgan': require('morgan'),
'passport': require('passport')
};
```

Here we can see that the packages `app`, `redis-db`, `express`, `morgan`, and `passport` are registered and are ready to be injected in packages that need them. `nwire` will then inject all other four packages through the `imports` parameter for packages that contain the properties `fn` and `needs`.

```js
// server.js
module.exports.needs = ['redis-db', 'express', 'morgan', 'passport'];
module.exports.fn = function(import){ // You can use $ for short
// import now contains four properties each named after the injected packages
var app = import.express();
import["redis-db"].open();
}
```
## Creating packages
Packages are comprised of two properties: `fn` and `needs`.
Expand All @@ -62,63 +85,64 @@ module.exports.fn = function(imports) {
var login = function login(username, password, callback){
// Perform authentication here...
}
var logout = function logout(callback) { }
return {
var logout = function logout(callback) { /*...*/ }

return {
login: login,
logout: logout
logout: logout
}
}
```
This package resolves an object that exposes two functions: `login` and `logout`. The resolved object is then injected in other packages that require it through the `needs` property.
This package returns an object that exposes two functions: `login` and `logout`. The returned object is then injected in other packages that require it through the `needs` property.
```js
// server.js
module.exports.needs = ['auth'];
module.exports.fn = function(imports) { // You can use $ for short
var auth = imports.auth; // The auth module is injected
auth.login('testing', '123', function(err){
module.exports.fn = function($) {
$.auth.login('testing', '123', function(err, user){
// Handle whether user is authorized
});
}
```
If the `fn` property is not provided, nwire.js will not perform any dependency injection. If the `needs` property is not provided, the `imports` parameter will be empty.
If the `fn` and `needs` properties are not provided, `nwire` will not perform any dependency injection.
### Package discovery
## Running the test suite
In order to perform dependency injection, you must feed nwire.js a configuration object containing the `url` and `packages` properties.
```
$ npm install
$ npm test
```
The `url` property allows nwire.js to resolve packages without needing their absolute paths. In most configurations, assigning `__dirname` to the `url` property will do. If this property is not provided, nwire.js will attempt to resolve modules from within its own directory.
## Breaking changes from v0.1
The `packages` property assigns a name and location for every package. It must contain an object where property names define package names and property values are corresponding locations.
Release `v0.2` did away with string declarations for `config.js` files. This is to allow `nwire` applications to work with bundlers like Browserify and `system.js`. If your `config.js` file looked like this:
Consider this sample configuration object.
```js
// config.js
var path = require('path');
```javascript
module.exports = {
url: path.join(__dirname, 'src'),
url: __dirname,
packages: {
'app': './server',
'database': './db',
'express': 'express',
'morgan': 'morgan',
'passport': 'passport'
'app': './app'
}
};
}
```
Here we can see that the packages `app`, `database`, `express`, `morgan`, and `passport` are registered and are ready to be injected in packages that need them. Assuming that the `app` package looks like the following code, nwire.js will inject all other four packages through the `imports` parameter.
You will now need to use CommonJS (or equivalent) to load your application.
```js
// server.js
module.exports.needs = ['database', 'express', 'morgan', 'passport'];
module.exports.fn = function(import){
// import now contains four properties each named after the injected packages
var app = import.express();
```javascript
module.exports = {
'app': require('./app')
}
```
Also, packages are now properties of the container returned by `nwire` rather than living under a `packages` object.
```javascript
wire({ /*...config...*/}, function(err, app) {
// app.packages.server.bootstrap(3000);
app.server.bootstrap(3000);
});
```
## Suggestions and questions
If you have any suggestions or questions regarding this project, please open an issue. If you feel that you have a feature that would be useful to add, fork it and open a pull request.
If you have any suggestions or questions regarding this project, please open an issue. If you feel that you have a feature that would be useful to add, fork it and open a pull request.
11 changes: 4 additions & 7 deletions examples/expressjs/config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
module.exports = {
url: __dirname,
packages: {
'server': './server',
'express': 'express',
'winston': 'winston'
}
}
'server': require('./server'),
'express-app': require('express'),
'winston': require('winston')
}
4 changes: 2 additions & 2 deletions examples/expressjs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ var wire = require('../../');

wire(require('./config'), function(err, app) { // Composite root
if (err) throw err;
app.packages.server.bootstrap(1337);
});
app.server.bootstrap(3000);
});
6 changes: 3 additions & 3 deletions examples/expressjs/server.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports.needs = ['express', 'winston'];
module.exports.needs = ['express-app', 'winston'];
module.exports.fn = function($) {
var app = $.express();
var app = $["express-app"]();

var bootstrap = function(port) {
return app.listen(port, function() {
Expand All @@ -11,4 +11,4 @@ module.exports.fn = function($) {
return {
bootstrap: bootstrap
};
}
}
13 changes: 5 additions & 8 deletions examples/koajs/config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
module.exports = {
url: __dirname,
packages: {
'server': './server',
'koa': 'koa',
'winston': 'winston',
'http': 'http'
}
}
'server': require('./server'),
'koa': require('koa'),
'winston': require('winston'),
'http': require('http')
}
4 changes: 2 additions & 2 deletions examples/koajs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ var wire = require('../../');

wire(require('./config'), function(err, app) { // Composite root
if (err) throw err;
app.packages.server.bootstrap(1337);
});
app.server.bootstrap(3000);
});
114 changes: 33 additions & 81 deletions nwire.js
Original file line number Diff line number Diff line change
@@ -1,98 +1,50 @@
module.exports = function nwire(config, callback) {
'use strict';
var Container = function(config, callback) {
if (typeof callback !== "function")
throw new Error("Please provide a callback function.")

// Set up a wiring container that injects all necessary components into
// the packages provided
var Wiring = function() {
// Validate configuration object
if (!config || typeof config !== 'object')
throw "Please provide a valid configuration object.";
var $ = {}; // Resolved packages
var decls = config.packages || config;
if (!decls) return callback(null, $);

var path = require('path');
var base = config.url || '';
var definitions = config.packages || {};
var resolve = function(decl) {
if ($[decl]) return $[decl];

// Validate package definitions
if (!definitions || definitions instanceof Array ||
!(definitions instanceof Object)) definitions = {};
var mod = decls[decl];

var self = this;
self.packages = {};
if (mod.fn && typeof mod.fn === "function" &&
mod.needs && mod.needs instanceof Array && !mod.ignore) {
var needs = {};

var load = function(name, skipNeeds) { // Responsible for loading packages
if (typeof(name) !== 'string') throw "Invalid package definition.";
$[decl] = {};

if (!definitions[name]) return undefined;

// If a package already exists with the same name, do not attempt to
// overwrite it. Return the existing package.
var loaded = self.packages[name];
if (loaded) return loaded;

var pkg, imports = {};

var resolve = function(name) {
try { // Try to load a system module first
return require(name)
} catch (e) {
try { // Try to load an NPM module
return require(path.join(base, 'node_modules', name));
} catch (er) { // Try to load the module through the base directory
try {
return require(path.join(base, name));
} catch (err) {
return null;
}
}
}
}

pkg = resolve(definitions[name]);
if (!pkg) return null;

// If a package is dependent on other packages, it's time to load them.
if (pkg.hasOwnProperty('needs') && pkg.needs instanceof Array)
pkg.needs.forEach(function(dependencyName) {
var definition = resolve(definitions[dependencyName]);
var skip = false;

if (definitions && definition.needs)
for(var i = 0; i < definition.needs.length; i++){
var need = definition.needs[i];
if (need == name) skip = true;
}

if (!skipNeeds) {
self.packages[dependencyName] = load(dependencyName, skip);
Object.defineProperty(imports, dependencyName, {
get: function(){
return self.packages[dependencyName];
}
});
mod.needs.forEach(function(need) {
Object.defineProperty(needs, need, {
get: function() {
if (!decls[need]) return null;
return $[need] || resolve(need);
}
});
});

// If package implements the fn function then inject the necessary
// packages and replace the package signature with the object it
// returns
if (pkg.hasOwnProperty('fn')) pkg = pkg.fn(imports);

return pkg;
if (!mod.construct) $[decl] = mod.fn(needs);
else Object.defineProperty($, decl, {
get: function() {
return new mod.fn(needs);
}
});
}

for (var definition in definitions) {
if (!definition) continue;
var fn = load(definition);
if (fn) self.packages[definition] = fn;
}
$[decl] = $[decl] || mod;
return $[decl];
}

try {
var app = new Wiring();
if (!callback) return app;
callback(null, app);
for (var decl in decls) $[decl] = resolve(decl);
callback(null, $);
} catch (err) {
if (!callback) throw err;
callback(err);
callback(err, null);
}
}


module.exports = Container;
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nwire",
"version": "0.1.4",
"version": "0.2.0",
"description": "Simplified dependency injection in Node.js",
"main": "nwire.js",
"scripts": {
Expand All @@ -25,7 +25,6 @@
"homepage": "https://github.com/divmgl/nwire#readme",
"devDependencies": {
"chai": "^3.2.0",
"lodash": "^3.10.1",
"mocha": "^2.2.5"
}
}
7 changes: 0 additions & 7 deletions spec/fixtures/circular/config.js

This file was deleted.

Loading

0 comments on commit 4ed5ab2

Please sign in to comment.