var fetchTemplateData = require('okutil').fetchTemplateData; var OKResource = require('okresource'); // Routes for views over collections have a special pattern // containing a free variable e.g. :id var UNBOUND_ROUTE_PATTERN = /:([a-zA-Z\-_]+)/; /** * OKView! * Is supplied DB queries and a template and is responsible * for resolving the queries, throwing the data into the templates, * and sending the response. */ function OKView(options) { if (!(this instanceof OKView)) return new OKView(options); options = options || {}; if (!options.template) throw new Error('No template provided to view.'); if (!options.meta) throw new Error('No meta resource provided to view'); if (!options.route) throw new Error('No route provided to view'); this.route = options.route; this._template = options.template; this._meta = options.meta; this._queries = options.queries || []; // Whether this is a view for a specific resource or its // resource will be resolved later this.unbound = !!UNBOUND_ROUTE_PATTERN.exec(this.route); this._middleware = createMiddleware(this); } OKView.prototype.middleware = function() { return this._middleware; }; OKView.prototype.render = function(req, res, data) { this._template.render(data).then(function(html) { res.send(html); }, errorHandler(req, res, data)); }; OKView.prototype.getTemplateData = function(options) { return fetchTemplateData(this._meta, this._queries, options); }; /** * Unbound views need different middleware to resolve requests */ function createMiddleware(view) { if (view.unbound) { return collectionMiddleware(view); } else { return singleMiddleware(view); } } // Note that these middleware do not call next // and should thus always be added at the end of the // middleware chain. /** * Creates middleware for a view which does not * yet have a resource id associated with it */ function collectionMiddleware(view) { var paramName = getParamName(view.route); return function(req, res, next) { view.getTemplateData({ id: req.params[paramName] }).then(function(data) { view.render(req, res, data); }, errorHandler(req, res, next)); }; } /** * Creates middleware for a view which already * has a resource id associated with it */ function singleMiddleware(view) { return function(req, res, next) { view.getTemplateData().then(function(data) { view.render(req, res, data); }, errorHandler(req, res, next)); }; } /** * TODO BS error handling for now */ function errorHandler(req, res, next) { return function(err) { res.send(err.stack); } } /** * Given a route with a free variable, return the * name of the variable, e.g. :id returns id */ function getParamName(route) { route = route || ''; var matches = UNBOUND_ROUTE_PATTERN.exec(route) || []; return matches[1]; } module.exports = OKView;