npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

zazeninjector

v1.0.0

Published

Simple dependency injection library using promises

Downloads

9

Readme

ZenInjector

A simple library for dependency injection with support for ES6 generators.

Dependency what ?

With dependency injection, one doesn't care how to retrieve dependencies as long as they comply with a given interface. This allow you to decouple each components from each others. You don't need to know which implementation of express or fs you're using. This allow easy mocking of objects for testing for example. Using dependency injection, you create once a container which will manage all the components of your application, and make them available where they are needed.

Use cases

You should use zeninjector when

  • You want to decouple your components from each others
  • You want to integrate into one namespace a lot of components
  • You need to dynamically add components to your code without branching the whole repo.
  • You want to be able to do (more) unit tests on your code.

Example

Below, a typicall example of any node.js program:

// file module1.js
var dep1 = require('./dep1');
var dep2 = require('./dep2');
module.exports.myModuleName = dep1 + dep2;

Two flavors

You can rewrite this snippet of code with zeninjector using it programmatically or with annotations.

Programmatic usage

Create the IOC container:

var Zeninjector = require('zeninjector');
var container = new Zeninjector();

Register a module with some dependencies:

container.register('myModuleName', function(dep1, dep2) {
  return dep1+dep2;
});

// alternative syntax
container.register('myModuleName', ['dep1', 'dep2', function(dep1, dep2) {
  // with this syntax, you can name the function's arguments whatever you like
  return dep1+dep2;
}]);

Directly register a module with no dependency:

container.registerAndExport('myConfigObject', {
  env: 'test',
  port: 8080
});

// it works great with `require` too
container.registerAndExport('Promise', require('bluebird'));

Resolve a module with its dependencies:

container.resolve('myModuleName').then(function(module) {
    // here module is equal to `dep1+dep2`
})

Zeninjector#resolve returns a bluebird promise, thus the need to have a then.

Inject dependencies to an anonymous function, because sometimes you just want to access some objects once, but still want all the goodness of the IOC.

container.inject(function(myConfigObject, dep1) {
  console.log('current env %s, dep1 equals: %s', myConfigObject.env, dep1);
});

// this also works
container.inject(['myConfigObject', 'dep1', function(config, dep) {
  // ...
}]);

Annotations

You still have to create the container:

var Zeninjector = require('zeninjector');
var container = new Zeninjector();

Scan your projects to automatically register all dependencies:

var scan = container.scan([__dirname+'/lib/**/*.js', __dirname+'/ext/**/*.js']);

This returns a bluebird promise. Once it's done, you can use Zeninjector#inject and Zeninjector.resolve to get a reference to your defined objects.

Define a module with dependencies:

//@autoinject
module.exports.myModuleName = function(dep1, dep2) {
  return dep1+dep2;
};

With custom name and custom dependencies:

//@autoinject(name=myModuleName; dependencies=dep1,dep2)
module.exports.thisNameIsIrrelevant = function(a, b) {
  return a+b;
};

This example has exactly the same effect as the previous one.

Automatically exports (similar to Zeninjector#registerAndExport):

//@autoexport
module.exports.myConfigObject = {
  env: 'test'
};

Asynchronous definitions

Let's have a look at an example where you need to connect to a database:

// file db.js
var MongoClient = require('mongodb').MongoClient;
var db;
module.exports.connect = function connect(callback) {
  if(db) {
    process.setImmediate(function() { callback(null, db); });
  } else {
      MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, _db) {
        db = _db;
        callback(err, db);
      });
  }
};

There is a problem here, everytime one wants to connect to the database, connect has to be called first and your code ends up in another callback (hello callback hell). With zeninjector, a module can returns a promise, and the result of this promise will be injected as dependecy:

var MongoClient = require('mongodb').MongoClient;

// @autoinject
module.exports.db = function(Promise) {
  // inject a Promise library
  var connect = Promise.promisify(MongoClient.connect);
  return connect("mongodb://localhost:27017/exampleDb");
}
// @autoinject
module.exports.myOtherModule = function(db) {
  // db here is the database object ready to be used
}

With generators

resolve and inject returns a promise so it can easily be used in coroutines. Below is the asynchronous example written with generators:

var Promise = require('bluebird');
Promise.coroutine(function* () {
  yield container.scan('**/*.js'); // scan the db.js file
  
  try {
    var db = yield container.resolve('db');
    var foo = db.collection('foo');
    //...
  } catch(err) {
    console.error('Got error:', err);
  }
})();

This example requires node >=0.11.4 with the flag --harmony-generators.

Running tests

npm install && npm test

API

new Zeninjector(Object options) -> container

The options object currently supports:

  • logger an optional logger (default to the console). The logger must implement the methodes trace, debug, info, warn, error and fatal.
  • verbose to add more logs with the level DEBUG. Defaults to false.

.setLogger(Logger logger) -> container

Set a custom logger after creation. The logger object is expected to have the following functions: trace, debug, info, warn, error and fatal;


.register(String name, FunctionDefinition) -> undefined

This function will register the dependency name. FunctionDefinition can be a function or an array of Strings, with its last element being a function. The array variant is the same as require.js. If only a function is provided, the name of the arguments will be used to fetch the dependencies, same is angularJS implicit dependencies.

When this module is required, the given FunctionDefinition function will be called and it's return value will be used as the value of the module name. If it returns a promise, the resolved value of the promise will be taken.


.registerAndExport(String name, Any value) -> value

This is a shorthand to container.register(name, function() { return value; });


.isRegistered(String moduleName) -> Boolean

Return true if and only if there is a registered module with the given moduleName.


.resolve(String name) -> promise

This will activate the define function for the dependency with name. The returned promise will resolve to the return value of the define function.


.inject(FunctionDefinition) -> Promise

This will invoke the given function with its arguments already resolved.


.scan(Array patterns) -> promise

Scan takes an array of file glob patterns (or a single string) and returns a promise which resolves when all the files have been scanned. Scan will look for //@autoinject and //@autoexport inside every files, and take the following function's name as the module name. This method allow to manage large project without having to pass around the container object and do the registration by yourself.

Annotations can be used to define a custom name for the module and specify dependencies explicitely:

//@autoinject(name=customName, dependencies=dep1,dep2)
// dependencies are comma (,) separated

The following patterns are supported to declare your module with annotations:

module.exports = {
  //@autoinject
  a: function() {}
}

//@autoinject
module.exports.b = function() {
  return 'baz';
};

//@autoinject
var c = function() {};

//@autoinject

function d() {};

//@autoinject
function e() {};
// /!\ this will raise an error if you try to require it
// .scan's promise will be rejected here.


function f() {}; // will NOT be exported

//@autoinject
module.exports = function g() {};

License

MIT