polite-plugin-manager
v0.0.6
Published
Stuning plugin manager for NodeJS
Downloads
86
Maintainers
Readme
polite-plugin-manager
A cool plugin manager for your NodeJS projects!
Polite Plugin Manager is an atomic library with which add plugin capabilities to any NodeJS project enhancing behavior scalability.
Key points during the developent of this library were:
- to be ridicolous easy to declare hooks
- to be ridicolous easy to register plugin packages
- to be ridicolous easy to handle asynchronous plugins
I really think all these points have been reached very well.
I've also added a really fine grained control on:
- plugins initialization cycle
- series / parallel / waterfall behaviors
- stoppable plugin chain
What's Next
I want to better explore plugins package organization to let a plugin package to spread on multiple files.
I also want to explore in-plugin package test support to let every plugin to be fully testable before it's release.
Tests
To grant code quality I wrote many test for this module!
After run npm install
to load all dev-dependencies you should run:
// run tests
grunt
// run tests and show a test coverage report
grunt specs
Install
The latest version of this package is always published to the NPM repository so it easy to install it by:
npm install polite-plugin-manager --save
After that you can load an instance into your modules to be able to declare hooks or to load packages:
var PluginManager = require('polite-plugin-manager');
// register all plugins within a folder then start the manager
PluginManager.registerMany('/path/to/plugins/').start(function() {
... your app code ...
// run all hook's registered callbacks
PluginManager.run('hook-name');
... your app code ...
});
Hooks
Register Callbacks to Hooks
PluginManager.registerHook('hook-name', function(arg1) {
arg1 += 1;
});
Run Hook's Callbacks
var num = 0;
PluginManager.run('hook-name', num, function() {
console.log(num); // 1
});
Packages
Register a Package
PluginManager.registerPackage('/path/to/plugin-package/');
Register Many Packages
PluginManager.registerPackage('/path/to/plugins/');
Inside that folders you can place all you plugin-package
folders. Polite Plugin Manager will load'em all!
Package Structure
/path/to/plugin-package/index.js
module.exports = function(packageContext) {
return {
// package identity properties
name: 'optional package name',
priority: 500, // default 100
init: function() {
... initialization stuff ...
},
// register hooks callbacks
'hook-name': function() {
... hook callback stuff ...
},
'another-hook-name': function() {
... hook callback stuff ...
}
};
};
Package Initialization
You plugin's package init()
function executes when PluginManager.start()
.
If you need to run asynchronous logic then you need to reference to a done()
callback to use when your stuff are done:
init: function() {
var done = this.async();
... async stuff ...
done();
}
Hook Types
run('hook-name', [arg1], [...], [argN], callback)
Run registered callbacks in series.
Execution order is granted to be the exact registration order (priority
package property). Each callback starts only after the previous one ends.
It support both sinchronous and asynchronous callbacks(see below how to implement an asynchronous callback).
parallel('hook-name', [arg1], [...], [argN], callback)
Run registered callbacks in parallel.
It support both sinchronous and asyncronous callbacks(see below how to implement an asynchronous callback) but it is highly recommend to avoid sinchronous callbacks in this hook types because they would slow down execution!
waterfall('hook-name')
// use a waterfall hook as normal function:
var sum = PluginManager.waterfall('sum', 0);
A waterfall callback must always return a value. That value will become the first argument of the following registered callback.
Waterfall's callbacks are always synchronous!
PluginManager.registerHook('sum', function(tot) {
tot += 1;
return tot;
});
Advanced Topics
Asynchronous Callbacks
Asynchronous callbacks needs to explicitly communicate the end of their stuff.
PluginManager.registerHook('hook-name', function() {
var done = this.async();
doAsyncFunction(function() {
done();
});
});
If your callback do not uses any asynchronous APIs but it is meant to run by a parallel hook then it is highly recommend to make it asynchronous anyway to avoid block other callbacks startup!
A very simple way to obtain this behavior is the setTimeout
function:
PluginManager.registerHook('hook-name', function() {
var done = this.async();
setTimeout(function() {
done();
}, 0);
});
This callback will release control immediately letting other callbacks to begin their execution.
If you want to learn more about Asynchronous Javascript programmin you can click here!
(temporary link)
Prevent Following Callbacks Execution
synchronous
PluginManager.registerHook('hook-name', function() {
... do something ...
this.stop();
});
asynchronous
PluginManager.registerHook('hook-name', function() {
var done = this.async();
fs.exists('/test/path', function(exists) {
if (exists) {
// prevent other callbacks to run:
done(true);
} else {
// allow other callbacks to run
done();
}
});
});
you can detect if an asyncronous callback queque have been stopped:
PluginManager.run('foo', function(err) {
if (err === true) {
// stopped with standard stop API
} else if (err instanceof Error) {
// stopped by some callback's error
}
});
Detect Empty Hook
If you feel important to know if some plugins have been executed under certain hooks you can:
/**
* explicitly detection:
*/
if (PluginManager.isEmpty('hook-name')) {
// yes it is!
} else {
// no, something will be executed if you run it!
}
/**
* after execution detection:
*/
var hasExecutedCallbacks = PluginManager.run('foo', function(err) {
if (err === valse) {
// no callbacks were executed
}
});
if (hasExecutedCallbacks === false) {
// no callbacks were executed
}