summaryrefslogtreecommitdiff
path: root/app/node_modules/okview/index.js
diff options
context:
space:
mode:
authorSean Fridman <fridman@mail.sfsu.edu>2015-04-01 18:27:51 -0400
committerSean Fridman <fridman@mail.sfsu.edu>2015-04-01 18:27:51 -0400
commit2e041ff6cd1b40c26bf16d37777c2c1c5153a669 (patch)
tree1e427659faf343bf42795beae8980000436617b8 /app/node_modules/okview/index.js
parentae24790e8122296b06f88fa883b8e272c6454d46 (diff)
Bootstrappin young lad
Diffstat (limited to 'app/node_modules/okview/index.js')
-rw-r--r--app/node_modules/okview/index.js132
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;