Skip to content

Commit

Permalink
Introduced renderer to DRY up controllers (#9235)
Browse files Browse the repository at this point in the history
refs #5091, #9192

- Renderer figures out templates, contexts, and does a render call
- Templating is now handled with a single function
- Context call is made in the renderer

Note:  to make this work, all controllers now define a little bit of config, currently stored in res._route. (That's a totally temporary location, as is res._template... when a sensible naming convention reveals itself I'll get rid of the weird _). This exposes a type and for custom routes a template name & default.
  • Loading branch information
ErisDS committed Nov 10, 2017
1 parent e41d0c7 commit 98f5ae0
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 148 deletions.
25 changes: 13 additions & 12 deletions core/server/apps/amp/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ var path = require('path'),

// Dirty requires
errors = require('../../../errors'),
templates = require('../../../controllers/frontend/templates'),
postLookup = require('../../../controllers/frontend/post-lookup'),
setResponseContext = require('../../../controllers/frontend/context'),
renderer = require('../../../controllers/frontend/renderer'),

templateName = 'amp',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs');
templateName = 'amp';

function _renderer(req, res, next) {
// Note: this is super similar to the config middleware used in channels
// @TODO refactor into to something explicit & DRY this up
res._route = {
type: 'custom',
templateName: templateName,
defaultTemplate: path.resolve(__dirname, 'views', templateName + '.hbs')
};

// Renderer begin
// Format data
var data = req.body || {};
Expand All @@ -22,14 +28,8 @@ function _renderer(req, res, next) {
return next(new errors.NotFoundError({message: i18n.t('errors.errors.pageNotFound')}));
}

// Context
setResponseContext(req, res, data);

// Template
res.template = templates.pickTemplate(templateName, defaultTemplate);

// Render Call
return res.render(res.template, data);
return renderer(req, res, data);
}

// This here is a controller.
Expand All @@ -49,7 +49,8 @@ function getPostData(req, res, next) {
}

// AMP frontend route
ampRouter.route('/')
ampRouter
.route('/')
.get(
getPostData,
_renderer
Expand Down
23 changes: 12 additions & 11 deletions core/server/apps/private-blogging/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ var path = require('path'),
express = require('express'),
middleware = require('./middleware'),
bodyParser = require('body-parser'),
templates = require('../../../controllers/frontend/templates'),
setResponseContext = require('../../../controllers/frontend/context'),
renderer = require('../../../controllers/frontend/renderer'),
brute = require('../../../middleware/brute'),

templateName = 'private',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs'),

privateRouter = express.Router();

function _renderer(req, res) {
// Note: this is super similar to the config middleware used in channels
// @TODO refactor into to something explicit & DRY this up
res._route = {
type: 'custom',
templateName: templateName,
defaultTemplate: path.resolve(__dirname, 'views', templateName + '.hbs')
};

// Renderer begin
// Format data
var data = {};
Expand All @@ -20,18 +26,13 @@ function _renderer(req, res) {
data.error = res.error;
}

// Context
setResponseContext(req, res);

// Template
res.template = templates.pickTemplate(templateName, defaultTemplate);

// Render Call
return res.render(res.template, data);
return renderer(req, res, data);
}

// password-protected frontend route
privateRouter.route('/')
privateRouter
.route('/')
.get(
middleware.isPrivateSessionAuth,
_renderer
Expand Down
26 changes: 13 additions & 13 deletions core/server/apps/subscribers/lib/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,26 @@ var path = require('path'),
api = require('../../../api'),
errors = require('../../../errors'),
validator = require('../../../data/validation').validator,
templates = require('../../../controllers/frontend/templates'),
postLookup = require('../../../controllers/frontend/post-lookup'),
setResponseContext = require('../../../controllers/frontend/context'),
renderer = require('../../../controllers/frontend/renderer'),

templateName = 'subscribe',
defaultTemplate = path.resolve(__dirname, 'views', templateName + '.hbs');
templateName = 'subscribe';

// In future we'd have a more complex controller here - showing if someone already subscribed?!
function _renderer(req, res) {
// Note: this is super similar to the config middleware used in channels
// @TODO refactor into to something explicit & DRY this up
res._route = {
type: 'custom',
templateName: templateName,
defaultTemplate: path.resolve(__dirname, 'views', templateName + '.hbs')
};

// Renderer begin
// Format data
var data = req.body;

// Context
setResponseContext(req, res);

// Template
res.template = templates.pickTemplate(templateName, defaultTemplate);

// Render Call
return res.render(res.template, data);
return renderer(req, res, data);
}

/**
Expand Down Expand Up @@ -106,7 +105,8 @@ function storeSubscriber(req, res, next) {
}

// subscribe frontend route
subscribeRouter.route('/')
subscribeRouter
.route('/')
.get(
_renderer
)
Expand Down
6 changes: 6 additions & 0 deletions core/server/controllers/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ var utils = require('../utils'),
// It renders entries = individual posts or pages
// The "route" is handled in site/routes.js
module.exports = function entryController(req, res, next) {
// Note: this is super similar to the config middleware used in channels
// @TODO refactor into to something explicit
res._route = {
type: 'entry'
};

// Query database to find post
return postLookup(req.path).then(function then(lookup) {
// Format data 1
Expand Down
14 changes: 3 additions & 11 deletions core/server/controllers/frontend/render-channel.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
var debug = require('ghost-ignition').debug('channels:render'),
formatResponse = require('./format-response'),
setResponseContext = require('./context'),
templates = require('./templates');
renderer = require('./renderer');

module.exports = function renderChannel(req, res) {
debug('renderChannel called');
return function renderChannel(result) {
// Renderer begin
// Format data 2
// Do final data formatting and then render
result = formatResponse.channel(result);

// Context
setResponseContext(req, res);

// Template
res.template = templates.channel(res.locals.channel);
var data = formatResponse.channel(result);

// Render Call
debug('Rendering view: ' + res.template);
res.render(res.template, result);
return renderer(req, res, data);
};
};
14 changes: 3 additions & 11 deletions core/server/controllers/frontend/render-entry.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
var debug = require('ghost-ignition').debug('channels:render-post'),
templates = require('./templates'),
formatResponse = require('./format-response'),
setResponseContext = require('./context');
renderer = require('./renderer');
/*
* Sets the response context around an entry (post or page)
* and renders it with the correct template.
Expand All @@ -13,16 +12,9 @@ module.exports = function renderEntry(req, res) {
return function renderEntry(entry) {
// Renderer begin
// Format data 2 - 1 is in preview/entry
var response = formatResponse.entry(entry);

// Context
setResponseContext(req, res, response);

// Template
res.template = templates.entry(entry);
var data = formatResponse.entry(entry);

// Render Call
debug('Rendering view: ' + res.template);
res.render(res.template, response);
return renderer(req, res, data);
};
};
16 changes: 16 additions & 0 deletions core/server/controllers/frontend/renderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var debug = require('ghost-ignition').debug('renderer'),
setContext = require('./context'),
templates = require('./templates');

module.exports = function renderer(req, res, data) {
// Context
setContext(req, res, data);

// Template
templates.setTemplate(req, res, data);

// Render Call
debug('Rendering template: ' + res._template + ' for: ' + req.originalUrl);
debug('res.locals', res.locals);
res.render(res._template, data);
};
75 changes: 48 additions & 27 deletions core/server/controllers/frontend/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
var _ = require('lodash'),
path = require('path'),
config = require('../../config'),
themes = require('../../themes');
themes = require('../../themes'),
_private = {};

/**
* ## Get Error Template Hierarchy
Expand All @@ -18,7 +19,7 @@ var _ = require('lodash'),
* @param {integer} statusCode
* @returns {String[]}
*/
function getErrorTemplateHierarchy(statusCode) {
_private.getErrorTemplateHierarchy = function getErrorTemplateHierarchy(statusCode) {
var errorCode = _.toString(statusCode),
templateList = ['error'];

Expand All @@ -29,7 +30,7 @@ function getErrorTemplateHierarchy(statusCode) {
templateList.unshift('error-' + errorCode);

return templateList;
}
};

/**
* ## Get Channel Template Hierarchy
Expand All @@ -43,7 +44,7 @@ function getErrorTemplateHierarchy(statusCode) {
* @param {Object} channelOpts
* @returns {String[]}
*/
function getChannelTemplateHierarchy(channelOpts) {
_private.getChannelTemplateHierarchy = function getChannelTemplateHierarchy(channelOpts) {
var templateList = ['index'];

if (channelOpts.name && channelOpts.name !== 'index') {
Expand All @@ -59,7 +60,7 @@ function getChannelTemplateHierarchy(channelOpts) {
}

return templateList;
}
};

/**
* ## Get Entry Template Hierarchy
Expand All @@ -72,7 +73,7 @@ function getChannelTemplateHierarchy(channelOpts) {
* @param {Object} postObject
* @returns {String[]}
*/
function getEntryTemplateHierarchy(postObject) {
_private.getEntryTemplateHierarchy = function getEntryTemplateHierarchy(postObject) {
var templateList = ['post'],
slugTemplate = 'post-' + postObject.slug;

Expand All @@ -88,7 +89,7 @@ function getEntryTemplateHierarchy(postObject) {
templateList.unshift(slugTemplate);

return templateList;
}
};

/**
* ## Pick Template
Expand All @@ -99,7 +100,7 @@ function getEntryTemplateHierarchy(postObject) {
* @param {Array|String} templateList
* @param {String} fallback - a fallback template
*/
function pickTemplate(templateList, fallback) {
_private.pickTemplate = function pickTemplate(templateList, fallback) {
var template;

if (!_.isArray(templateList)) {
Expand All @@ -119,29 +120,49 @@ function pickTemplate(templateList, fallback) {
}

return template;
}
};

function getTemplateForEntry(postObject) {
var templateList = getEntryTemplateHierarchy(postObject),
_private.getTemplateForEntry = function getTemplateForEntry(postObject) {
var templateList = _private.getEntryTemplateHierarchy(postObject),
fallback = templateList[templateList.length - 1];
return pickTemplate(templateList, fallback);
}
return _private.pickTemplate(templateList, fallback);
};

function getTemplateForChannel(channelOpts) {
var templateList = getChannelTemplateHierarchy(channelOpts),
_private.getTemplateForChannel = function getTemplateForChannel(channelOpts) {
var templateList = _private.getChannelTemplateHierarchy(channelOpts),
fallback = templateList[templateList.length - 1];
return pickTemplate(templateList, fallback);
}
return _private.pickTemplate(templateList, fallback);
};

function getTemplateForError(statusCode) {
var templateList = getErrorTemplateHierarchy(statusCode),
_private.getTemplateForError = function getTemplateForError(statusCode) {
var templateList = _private.getErrorTemplateHierarchy(statusCode),
fallback = path.resolve(config.get('paths').defaultViews, 'error.hbs');
return pickTemplate(templateList, fallback);
}

module.exports = {
channel: getTemplateForChannel,
entry: getTemplateForEntry,
error: getTemplateForError,
pickTemplate: pickTemplate
return _private.pickTemplate(templateList, fallback);
};

module.exports.setTemplate = function setTemplate(req, res, data) {
var routeConfig = res._route || {};

if (res._template) {
return;
}

if (req.err) {
res._template = _private.getTemplateForError(res.statusCode);
return;
}

switch (routeConfig.type) {
case 'custom':
res._template = _private.pickTemplate(routeConfig.templateName, routeConfig.defaultTemplate);
break;
case 'channel':
res._template = _private.getTemplateForChannel(res.locals.channel);
break;
case 'entry':
res._template = _private.getTemplateForEntry(data.post);
break;
default:
res._template = 'index';
}
};
6 changes: 6 additions & 0 deletions core/server/controllers/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ module.exports = function previewController(req, res, next) {
include: 'author,tags'
};

// Note: this is super similar to the config middleware used in channels
// @TODO refactor into to something explicit
res._route = {
type: 'entry'
};

api.posts.read(params).then(function then(result) {
// Format data 1
var post = result.posts[0];
Expand Down
Loading

0 comments on commit 98f5ae0

Please sign in to comment.