Skip to content

Commit

Permalink
Merge pull request hapijs#993 from spumko/issue/991
Browse files Browse the repository at this point in the history
Plugin context
  • Loading branch information
geek committed Jul 22, 2013
2 parents 6afd0c3 + 9150e44 commit 176b26e
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 11 deletions.
27 changes: 27 additions & 0 deletions docs/Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
- [`plugin.require(name, options, callback)`](#pluginrequirename-options-callback)
- [`plugin.require(names, callback)`](#pluginrequirenames-callback)
- [`plugin.loader(require)`](#pluginloader-require)
- [`plugin.context(context)`](#plugincontext-context)
- [Selectable methods and properties](#selectable-methods-and-properties)
- [`plugin.select(labels)`](#pluginselectlabels)
- [`plugin.length`](#pluginlength)
Expand Down Expand Up @@ -404,6 +405,8 @@ The following options are available when adding a route:
- `config` - additional route configuration (the `config` options allows splitting the route information from its implementation):
- `handler` - an alternative location for the route handler function. Same as the `handler` option in the parent level. Can only
include one handler per route.
- `context` - any value passed back to the provided handler (via the `request.context` variable) when called. Can only be used with
`handler` function values.
<p></p>
- `pre` - an array with prerequisites methods which are executed in serial or in parallel before the handler is called and are
described in [Route prerequisites](#route-prerequisites).
Expand Down Expand Up @@ -1183,6 +1186,7 @@ Registers an extension function in one of the available [extension points](#requ
in the order added.
- `after` - a string or array of strings of plugin names this method must executed before (on the same event). Otherwise, extension methods are executed
in the order added.
- `context` - any value passed back to the provided method (via the `request.context` variable) when called.

```javascript
var Hapi = require('hapi');
Expand Down Expand Up @@ -3059,6 +3063,29 @@ exports.register = function (plugin, options, next) {
};
```
#### `plugin.context(context)`
Sets a global plugin context used as the default context when adding a route or an extension using the plugin interface (if no
explicit context is provided as an option).
```javascript
var handler = function () {

this.reply(this.context.message);
};

exports.register = function (plugin, options, next) {

var context = {
message: 'hello'
};

plugin.context(context);
plugin.route({ method: 'GET', path: '/', handler: internals.handler });
next();
};
```
### Selectable methods and properties
The plugin interface selectable methods and properties are those available both on the `plugin` object received via the
Expand Down
12 changes: 9 additions & 3 deletions lib/ext.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ internals.Ext.prototype._add = function (event, func, options, plugin) {

// Validate rules

var pluginName = (plugin ? plugin.name : null);
var before = [].concat(options.before || []);
var after = [].concat(options.after || []);
var group = plugin || '?';
var group = pluginName || '?';

Utils.assert(before.indexOf(group) === -1, 'Plugin ext cannot come before itself:', group);
Utils.assert(before.indexOf('?') === -1, 'Plugin ext cannot come before unassociated exts');
Expand All @@ -63,7 +64,8 @@ internals.Ext.prototype._add = function (event, func, options, plugin) {
before: before,
after: after,
group: group,
func: fn
func: fn,
plugin: plugin
};

self._events[event].push(ext);
Expand All @@ -72,7 +74,7 @@ internals.Ext.prototype._add = function (event, func, options, plugin) {
// Insert event

var error = this.sort(event);
Utils.assert(!error, event, 'extension', (plugin ? 'add by ' + plugin : ''), 'created a dependencies error');
Utils.assert(!error, event, 'extension', (pluginName ? 'added by ' + pluginName : ''), 'created a dependencies error');
};


Expand All @@ -87,6 +89,8 @@ internals.Ext.prototype.invoke = function (request, event, callback) {

Async.forEachSeries(handlers, function (ext, next) {

request.context = (ext.plugin ? ext.plugin.context : undefined);

internals.Ext.runProtected(log, event, next, function (enter, exit) {

enter(function () {
Expand All @@ -97,6 +101,8 @@ internals.Ext.prototype.invoke = function (request, event, callback) {
},
function (err) {

delete request.context;

return callback(err);
});
};
Expand Down
20 changes: 14 additions & 6 deletions lib/pack.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,14 @@ internals.Pack.prototype._register = function (plugin, permissions, options, cal

// Setup environment

this._env[plugin.name] = {
var env = {
name: plugin.name,
path: plugin.path
path: plugin.path,
context: null
};

this._env[plugin.name] = env;

// Add plugin to servers lists

this.list[plugin.name] = plugin;
Expand Down Expand Up @@ -185,7 +188,7 @@ internals.Pack.prototype._register = function (plugin, permissions, options, cal
if (permissions.route) {
methods.route = function (options) {

self._applySync(selection.servers, Server.prototype._route, [options, self._env[plugin.name]]);
self._applySync(selection.servers, Server.prototype._route, [options, env]);
};
}

Expand All @@ -206,7 +209,7 @@ internals.Pack.prototype._register = function (plugin, permissions, options, cal
if (permissions.ext) {
methods.ext = function () {

self._applySync(selection.servers, Server.prototype._ext, [arguments[0], arguments[1], arguments[2], plugin.name]);
self._applySync(selection.servers, Server.prototype._ext, [arguments[0], arguments[1], arguments[2], env]);
};
}

Expand Down Expand Up @@ -254,16 +257,21 @@ internals.Pack.prototype._register = function (plugin, permissions, options, cal
root._requireFunc = requireFunc;
};

root.context = function (context) {

env.context = context;
};

if (permissions.events) {
root.events = self.events;
}

if (permissions.views) {
root.views = function (options) {

Utils.assert(!self._env[plugin.name].views, 'Cannot set plugin views manager more than once');
Utils.assert(!env.views, 'Cannot set plugin views manager more than once');
options.basePath = options.basePath || plugin.path;
self._env[plugin.name].views = new Views(options);
env.views = new Views(options);
};
}

Expand Down
3 changes: 3 additions & 0 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ internals.Request.handler = function (request, next) {

isFinalized = true;

delete request.context;
delete request.reply;

// Check for Error result
Expand All @@ -557,6 +558,8 @@ internals.Request.handler = function (request, next) {

// Decorate request

request.context = (request.route.context || request._route.env.context);

request.reply = function (result) {

return Response._generate(result, finalize);
Expand Down
43 changes: 43 additions & 0 deletions lib/response/cached.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,46 @@ exports = module.exports = internals.Cached = function (item, ttl) {
};

Utils.inherits(internals.Cached, Generic);


/*
Generic
_code (200)
header() -> _headers
encoding() -> _flags (encoding)
ttl() -> _flags (ttl) - ttl cached but ignored
Text
_code (200)
_payload
_headers (Content-Type)
_flags (encoding)
Redirection
_code (301, 302, 307, 308)
_payload
_headers (Content-Type)
_flags (encoding, location)
Empty
_code (200)
Object
_code (200)
_payload
_headers (Content-Type)
_flags (encoding)
Cached
_code
_payload
_headers
_flags + (ttl)
*/
1 change: 1 addition & 0 deletions lib/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ exports = module.exports = internals.Route = function (options, server, env) {
this.settings.handler = this.settings.handler || options.handler;

Utils.assert((typeof this.settings.handler === 'function') ^ !!this.settings.handler.proxy ^ !!this.settings.handler.file ^ !!this.settings.handler.directory ^ !!this.settings.handler.view ^ (this.settings.handler === 'notFound'), 'Handler must be a function or equal notFound or be an object with a proxy, file, directory, or view');
Utils.assert(!this.settings.context || typeof this.settings.handler === 'function', 'Cannot set route context when handler is not a function');

var schemaError = Schema.routeOptions(options);
Utils.assert(!schemaError, 'Invalid route options for', options.path, ':', schemaError);
Expand Down
1 change: 1 addition & 0 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ exports.routeConfig = function (config) {

internals.routeConfigSchema = {
handler: [T.Object(), T.Function(), T.String().valid('notFound')],
context: T.Object(),
payload: [T.String().valid('stream', 'raw', 'parse', 'try'),
T.Object({
mode: T.String().valid(['stream', 'raw', 'parse', 'try']),
Expand Down
14 changes: 14 additions & 0 deletions test/integration/pack.js
Original file line number Diff line number Diff line change
Expand Up @@ -669,4 +669,18 @@ describe('Pack', function () {
done();
});
});

it('sets plugin context', function (done) {

var server = new Hapi.Server();
server.pack.require('./pack/--context', function (err) {

expect(err).to.not.exist;
server.inject({ method: 'GET', url: '/' }, function (res) {

expect(res.result).to.equal('in context throughout');
done();
});
});
});
});
35 changes: 35 additions & 0 deletions test/integration/pack/--context/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Declare internals

var internals = {};


// Plugin registration

exports.register = function (plugin, options, next) {

var context = {
value: 'in context',
suffix: ' throughout'
};

plugin.route({ method: 'GET', path: '/', handler: internals.handler });

plugin.ext('onPreResponse', internals.ext);

plugin.context(context); // Call last to test late binding

next();
};


internals.handler = function () {

this.reply(this.context.value);
};


internals.ext = function (request, next) {

request.response()._payload.push(request.context.suffix);
next();
};
7 changes: 7 additions & 0 deletions test/integration/pack/--context/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "--context",
"description": "Test plugin module",
"version": "0.0.1",
"private": true,
"main": "lib/index"
}
4 changes: 2 additions & 2 deletions test/unit/ext.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('Ext', function () {
var ext = new Ext();
scenario.forEach(function (record, i) {

ext._add('onRequest', generateExt(record.id), { before: record.before, after: record.after }, record.group);
ext._add('onRequest', generateExt(record.id), { before: record.before, after: record.after }, { name: record.group });
});

var request = {
Expand Down Expand Up @@ -129,7 +129,7 @@ describe('Ext', function () {
expect(function () {

testDeps(scenario, function (result) { });
}).to.throw('onRequest extension add by c created a dependencies error');
}).to.throw('onRequest extension added by c created a dependencies error');

done();
});
Expand Down

0 comments on commit 176b26e

Please sign in to comment.