Skip to content
Permalink
Browse files

Pass request to global context function when available

Resolves #59.
  • Loading branch information
jagoda committed Apr 1, 2016
1 parent 43155a6 commit 7f6354e8d575cde6ed5213be9af77c746f385eaf
Showing with 139 additions and 13 deletions.
  1. +11 −5 API.md
  2. +4 −4 lib/manager.js
  3. +86 −1 test/index.js
  4. +38 −3 test/manager.js
16 API.md
@@ -28,7 +28,7 @@ Initializes the server views manager where:
- `compile()` - the rendering function. The required function signature depends on the
`compileMode` settings (see below). If `compileMode` is `'sync'`, the signature is
`compile(template, options)`, the return value is a function with signature
`function(context, options)` (the compiled sync template), and the method is allowed to throw errors. If
`function(context, options)` (the compiled sync template), and the method is allowed to throw errors. If
`compileMode` is `'async'`, the signature is `compile(template, options, next)`
where `next` has the signature `function(err, compiled)`, `compiled` is a
function with signature `function(context, options, callback)` (the compiled async template) and `callback` has the
@@ -90,10 +90,16 @@ Initializes the server views manager where:
- `compileMode` - specify whether the engine `compile()` method is `'sync'` or `'async'`.
Defaults to `'sync'`.
- `context` - a global context used with all templates. The global context option can be either
an object or a function that takes no arguments and returns a context object. When rendering
views, the global context will be merged with any context object specified on the handler or
using [`reply.view()`](https://github.com/hapijs/hapi/blob/master/API.md#replyviewtemplate-context-options).
When multiple context objects are used, values from the global context always have lowest precedence.
an object or a function that takes the [`request`](https://github.com/hapijs/hapi/blob/master/API.md#request-properties)
as its only argument and returns a context object. The
[`request`](https://github.com/hapijs/hapi/blob/master/API.md#request-properties) object is only provided when using
the [view handler](#the-view-handler) or [`reply.view()`](#replyviewtemplate-context-options). When using
[`server.render()`](#serverrendertemplate-context-options-callback) or
[`request.render()`](#requestrendertemplate-context-options-callback), the
[`request`](https://github.com/hapijs/hapi/blob/master/API.md#request-properties) argument will be `null`. When rendering
views, the global context will be merged with any context object specified on the handler or using
[`reply.view()`](#replyviewtemplate-context-options). When multiple context objects are used, values from the global
context always have lowest precedence.

When [`server.views()`](https://github.com/hapijs/hapi/blob/master/API.md#serverviewsoptions) is called within a
plugin, the views manager is only available to [plugins](https://github.com/hapijs/hapi/blob/master/API.md#plugins)
@@ -272,7 +272,7 @@ internals.Manager.prototype.render = function (filename, context, options, callb
return callback(err);
}

this._render(compiled, context, (err, rendered) => {
this._render(compiled, context, null, (err, rendered) => {

if (err) {
return callback(err);
@@ -443,10 +443,10 @@ internals.Manager.prototype._path = function (template, settings, isLayout, next
};


internals.Manager.prototype._render = function (compiled, context, callback) {
internals.Manager.prototype._render = function (compiled, context, request, callback) {

if (this._context) {
let base = typeof this._context === 'function' ? this._context() : this._context;
let base = typeof this._context === 'function' ? this._context(request) : this._context;
if (context) {
base = Hoek.shallow(base);
const keys = Object.keys(context);
@@ -559,7 +559,7 @@ internals.marshal = function (response, callback) {

const manager = response.source.manager;

manager._render(response.source.compiled, response.source.context, (err, rendered) => {
manager._render(response.source.compiled, response.source.context, response.request, (err, rendered) => {

if (err) {
return callback(err);
@@ -157,7 +157,7 @@ describe('handler()', () => {
});
});

it('handles a global context', (done) => {
it('handles a global context object', (done) => {

const server = new Hapi.Server();
server.connection();
@@ -179,6 +179,32 @@ describe('handler()', () => {
});
});

it('passes the request to a global context function', (done) => {

const server = new Hapi.Server();
server.connection();
server.register(Vision, Hoek.ignore);
server.views({
engines: { html: require('handlebars') },
path: __dirname + '/templates',

context: (request) => {

return {
message: request ? request.route.path : 'default'
};
}
});

server.route({ method: 'GET', path: '/', handler: { view: { template: 'valid/testContext' } } });
server.inject('/', (res) => {

expect(res.result).to.contain('<h1></h1>');
expect(res.result).to.contain('<h1>/</h1>');
done();
});
});

it('overrides the global context with the default handler context', (done) => {

const server = new Hapi.Server();
@@ -483,6 +509,65 @@ describe('render()', () => {
});
});
});

it('does not pass the request to the global context function (server)', (done) => {

const server = new Hapi.Server();
server.connection();
server.register(Vision, Hoek.ignore);
server.views({
engines: { html: require('handlebars') },
path: __dirname + '/templates/valid',

context: (request) => {

return {
message: request ? request.route.path : 'default'
};
}
});

server.render('testContext', null, null, (err, result) => {

expect(err).not.to.exist();
expect(result).to.contain('<h1>default</h1>');
done();
});
});

it('does not pass the request to the global context function (request)', (done) => {

const server = new Hapi.Server();
server.connection();
server.register(Vision, Hoek.ignore);
server.views({
engines: { html: require('handlebars') },
path: __dirname + '/templates/valid',

context: (request) => {

return {
message: request ? request.route.path : 'default'
};
}
});

const handler = (request, reply) => {

request.render('testContext', null, null, (err, rendered) => {

expect(err).not.to.exist();
reply(rendered);
});
};

server.route({ method: 'GET', path: '/', handler: handler });
server.inject({ method: 'GET', url: '/' }, (response) => {

expect(response.result).to.contain('<h1>default</h1>');
done();
});
});
});

describe('views()', () => {
@@ -1133,16 +1133,16 @@ describe('Manager', () => {
});
});

it('renders with a global context function', (done) => {
it('renders with a global context function (no request)', (done) => {

const testView = new Manager({
engines: { html: require('handlebars') },
path: __dirname + '/templates',

context: function () {
context: function (request) {

return {
message: 'default message',
message: request ? request.route.path : 'default message',

query: {
test: 'global'
@@ -1932,5 +1932,40 @@ describe('Manager', () => {
done();
});
});

it('passes the response object to the global context function', (done) => {

const server = new Hapi.Server();
server.register(Vision, Hoek.ignore);
server.connection();
server.views({
engines: { html: Handlebars },
path: __dirname + '/templates/valid',

context: function (request) {

return {
message: request ? request.route.path : 'default message',

query: {
test: 'global'
}
};
}
});

const handler = function (request, reply) {

return reply.view('testContext');
};

server.route({ method: 'GET', path: '/', handler: handler });
server.inject('/', (res) => {

expect(res.payload).to.contain('<h1>/</h1>');
expect(res.payload).to.contain('<h1>global</h1>');
done();
});
});
});
});

0 comments on commit 7f6354e

Please sign in to comment.
You can’t perform that action at this time.