-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Start loading the nested plugins after the current function completes #29
Merged
Merged
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b09cb93
Added async-await to use()
mcollina ae76290
Added promise-based test.
mcollina 469daa4
Add test for delayed use
mcollina 892c9fb
Start loading the nested plugins after the current function completes
mcollina 435080b
Updated docs.
mcollina d8c72e0
Merge branch 'async-await' into load-plugins-after-callback
mcollina 41ec482
Updated README
mcollina fb170bf
Addressing feedbacks
mcollina 4b82da2
throw if use() is called after avvio is booted.
mcollina File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ npm install avvio --save | |
<a name="example"></a> | ||
## Example | ||
|
||
The example below can be found [here][example] and ran using `node example.js`. | ||
The example below can be found [here][example] and ran using `node example.js`. | ||
It demonstrates how to use `avvio` to load functions / plugins in order. | ||
|
||
|
||
|
@@ -43,32 +43,26 @@ avvio | |
cb() | ||
}) | ||
|
||
avvio.use(third, (err) => { | ||
if (err) { | ||
console.log('something bad happened') | ||
console.log(err) | ||
} | ||
|
||
console.log('third plugin loaded') | ||
}) | ||
avvio.use(third) | ||
|
||
avvio.ready(function () { | ||
console.log('application booted!') | ||
}) | ||
|
||
function first (instance, opts, cb) { | ||
console.log('first loaded', opts) | ||
instance.use(second, cb) | ||
instance.use(second) | ||
cb() | ||
} | ||
|
||
function second (instance, opts, cb) { | ||
console.log('second loaded') | ||
process.nextTick(cb) | ||
} | ||
|
||
function third (instance, opts, cb) { | ||
// async/await or Promise support | ||
async function third (instance, opts) { | ||
console.log('third loaded') | ||
cb() | ||
} | ||
``` | ||
|
||
|
@@ -89,8 +83,8 @@ function third (instance, opts, cb) { | |
|
||
### avvio([instance], [started]) | ||
|
||
Starts the avvio sequence. | ||
As the name suggest, `instance` is the object representing your application. | ||
Starts the avvio sequence. | ||
As the name suggest, `instance` is the object representing your application. | ||
Avvio will add the functions `use`, `after` and `ready` to the instance. | ||
|
||
```js | ||
|
@@ -102,7 +96,8 @@ server.use(function first (s, opts, cb) { | |
// s is the same of server | ||
s.use(function second (s, opts, cb) { | ||
cb() | ||
}, cb) | ||
}) | ||
cb() | ||
}).after(function (err, cb) { | ||
// after first and second are finished | ||
cb() | ||
|
@@ -136,9 +131,9 @@ app.on('start', () => { | |
------------------------------------------------------- | ||
<a name="use"></a> | ||
|
||
### app.use(func, [opts], [cb]) | ||
### app.use(func, [opts]) | ||
|
||
Loads one or more functions asynchronously. | ||
Loads one or more functions asynchronously. | ||
The function **must** have the signature: `instance, options, done` | ||
|
||
Plugin example: | ||
|
@@ -151,48 +146,50 @@ app.use(plugin) | |
``` | ||
`done` must be called only once, when your plugin is ready to go. | ||
|
||
async/await is also supported: | ||
|
||
```js | ||
async function plugin (server, opts) { | ||
await sleep(10) | ||
} | ||
app.use(plugin) | ||
``` | ||
|
||
`use` returns the instance on which `use` is called, to support a chainable API. | ||
|
||
If you need to add more than a function and you don't need to use a different options object or callback, you can pass an array of functions to `.use`. | ||
```js | ||
app.use([first, second, third], opts, cb) | ||
app.use([first, second, third], opts) | ||
``` | ||
The functions will be loaded in the same order as they are inside the array. | ||
|
||
<a name="error-handling"></a> | ||
#### Error handling | ||
The third argument of the plugin, the `done` function can accept an error parameter, if you pass it, you **must** handle that error. You have two ways to do it: | ||
1. the callback of the use function | ||
```js | ||
app.use(function (instance, opts, done) { | ||
done(new Error('error')) | ||
}, opts, function (err) { | ||
if (err) throw err | ||
}) | ||
``` | ||
2. the next `ready` or `after` callback | ||
|
||
In order to handle errors in the loading plugins, you must use the | ||
`.reayd()` method, like so: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
```js | ||
app.use(function (instance, opts, done) { | ||
done(new Error('error')) | ||
done(new Error('error')) | ||
}, opts) | ||
|
||
app.ready(function (err) { | ||
if (err) throw err | ||
if (err) throw err | ||
}) | ||
``` | ||
|
||
*Note if you pass a callback to `use` without an error parameter, the error will be automatically passed to the next `ready` or `after` callback.* | ||
|
||
------------------------------------------------------- | ||
<a name="after"></a> | ||
|
||
### app.after(func(error, [context], [done]), [cb]) | ||
|
||
Calls a function after all the previously defined plugins are loaded, including | ||
all their dependencies. The `'start'` event is not emitted yet. | ||
all their dependencies. The `'start'` event is not emitted yet. | ||
|
||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
3. If three parameters are given to the callback, the first will be the `error` object, the second will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, and the third the `done` callback. | ||
|
||
```js | ||
|
@@ -226,11 +223,11 @@ Returns the instance on which `after` is called, to support a chainable API. | |
|
||
### app.ready(func(error, [context], [done])) | ||
|
||
Calls a function after all the plugins and `after` call are completed, but before `'start'` is emitted. `ready` callbacks are executed one at a time. | ||
Calls a function after all the plugins and `after` call are completed, but before `'start'` is emitted. `ready` callbacks are executed one at a time. | ||
|
||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
3. If three parameters are given to the callback, the first will be the `error` object, the second will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, and the third the `done` callback. | ||
|
||
```js | ||
|
@@ -281,8 +278,8 @@ boot(app, { | |
|
||
### app.override(server, plugin, options) | ||
|
||
Allows to override the instance of the server for each loading plugin. | ||
It allows the creation of an inheritance chain for the server instances. | ||
Allows to override the instance of the server for each loading plugin. | ||
It allows the creation of an inheritance chain for the server instances. | ||
The first parameter is the server instance and the second is the plugin function while the third is the options object that you give to use. | ||
|
||
```js | ||
|
@@ -306,7 +303,8 @@ app.use(function first (s1, opts, cb) { | |
assert(s1 !== server) | ||
assert(server.isPrototypeOf(s1)) | ||
assert(s1.count === 1) | ||
s1.use(second, cb) | ||
s1.use(second) | ||
cb() | ||
|
||
function second (s2, opts, cb) { | ||
assert(s2 !== s1) | ||
|
@@ -324,8 +322,8 @@ app.use(function first (s1, opts, cb) { | |
Registers a new callback that will be fired once then `close` api is called. | ||
|
||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `context`. | ||
2. If two parameters are given to the callback, the first will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `context`. | ||
2. If two parameters are given to the callback, the first will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, the second will be the `done` callback. | ||
|
||
```js | ||
const server = {} | ||
|
@@ -353,8 +351,8 @@ Returns the instance on which `onClose` is called, to support a chainable API. | |
Starts the shotdown procedure, the callback is called once all the registered callbacks with `onClose` has been executed. | ||
|
||
The callback changes basing on the parameters your are giving: | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
1. If one parameter is given to the callback, that parameter will be the `error` object. | ||
2. If two parameters are given to the callback, the first will be the `error` object, the second will be the `done` callback. | ||
3. If three parameters are given to the callback, the first will be the `error` object, the second will be the top level `context` unless you have specified both server and override, in that case the `context` will be what the override returns, and the third the `done` callback. | ||
|
||
```js | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ const fastq = require('fastq') | |
const EE = require('events').EventEmitter | ||
const inherits = require('util').inherits | ||
const Plugin = require('./plugin') | ||
const debug = require('debug')('avvio') | ||
|
||
function wrap (server, opts, instance) { | ||
const expose = opts.expose || {} | ||
|
@@ -108,11 +109,12 @@ inherits(Boot, EE) | |
// the root node is responsible for emitting 'start' | ||
Boot.prototype._init = function () { | ||
if (this._current.length === 0) { | ||
const main = new Plugin(this, (s, opts, done) => { | ||
const main = new Plugin(this, function root (s, opts, done) { | ||
// we need to wait any call to use() to happen | ||
process.nextTick(done) | ||
}, {}, noop) | ||
Plugin.loadPlugin.call(this, main, (err) => { | ||
debug('root plugin ready') | ||
if (err) { | ||
this._error = err | ||
if (this._readyQ.length() === 0) { | ||
|
@@ -165,8 +167,12 @@ Boot.prototype._addPlugin = function (plugin, opts, callback) { | |
|
||
const obj = new Plugin(this, plugin, opts, callback) | ||
|
||
if (current.loaded) { | ||
throw new Error(`Impossible to load (${obj.name}) plugin because the parent (${current.name}) was already loaded`) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
// we add the plugin to be loaded at the end of the current queue | ||
current.q.push(obj, (err) => { | ||
current.enqueue(obj, (err) => { | ||
if (err) { | ||
this._error = err | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you removed
[cb]
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because it's very bad to use it anyway, but we still support it for backward compat.