diff options
| author | Sean Fridman <fridman@mail.sfsu.edu> | 2015-04-01 18:27:51 -0400 |
|---|---|---|
| committer | Sean Fridman <fridman@mail.sfsu.edu> | 2015-04-01 18:27:51 -0400 |
| commit | 2e041ff6cd1b40c26bf16d37777c2c1c5153a669 (patch) | |
| tree | 1e427659faf343bf42795beae8980000436617b8 /app/node_modules/okview/index.js | |
| parent | ae24790e8122296b06f88fa883b8e272c6454d46 (diff) | |
Bootstrappin young lad
Diffstat (limited to 'app/node_modules/okview/index.js')
| -rw-r--r-- | app/node_modules/okview/index.js | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/app/node_modules/okview/index.js b/app/node_modules/okview/index.js new file mode 100644 index 0000000..341ebdd --- /dev/null +++ b/app/node_modules/okview/index.js @@ -0,0 +1,132 @@ +var Q = require('q'); +var isarray = require('lodash.isarray'); +var pluralize = require('pluralize'); +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) { + return res.send(this._template.render(data)); +}; + +/** + * Takes queries backing this view and transforms them + * into a promise for an object with all the queried data, + * suitable to pass to the template. + * + * Lil bit convoluted, sorry. + */ +OKView.prototype.getTemplateData = function(options) { + var self = this; + var queries = this._queries; + return Q.promise(function(resolve, reject) { + return Q.all( + [self._meta.get()].concat(queries.map(function(query) { + return query.get(options); + }))) + .then(function(results) { + var meta = results.shift(); + var normalized = results.reduce(function(data, result, i) { + var name = queries[i].name; + if (isarray(result)) { + data[pluralize(name)] = result; + } else { + data[name] = result; + } + return data; + }, {meta: meta}); + resolve(normalized); + }, reject); + }); +}; + +/** + * 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; |
