roots-records
Version:
dynamic content functionality for roots
239 lines (201 loc) • 7.26 kB
JavaScript
// Generated by CoffeeScript 1.10.0
(function() {
var RootsUtil, W, _, fs, node, path, rest;
fs = require('fs');
rest = require('rest');
path = require('path');
W = require('when');
node = require('when/node');
_ = require('lodash');
RootsUtil = require('roots-util');
module.exports = function(opts) {
var Records;
return Records = (function() {
/**
* Creates a locals object if one isn't set.
* @constructor
*/
var add_to_locals, apply_hook, compile_single_views, fetch, resolve_data, resolve_file, resolve_url;
function Records(roots) {
var base, base1;
this.roots = roots;
this.util = new RootsUtil(this.roots);
(base = this.roots.config).locals || (base.locals = {});
(base1 = this.roots.config.locals).records || (base1.records = {});
}
/**
* Setup extension method loops through objects and
* returns a promise to get all data and store.
*/
Records.prototype.setup = function() {
var conf, fetch_records, key;
fetch_records = (function() {
var results;
results = [];
for (key in opts) {
conf = opts[key];
results.push(fetch.call(this, key, conf));
}
return results;
}).call(this);
return W.all(fetch_records).then(function(res) {
return W.map(res, apply_hook);
}).tap((function(_this) {
return function(res) {
return W.map(res, add_to_locals.bind(_this));
};
})(this)).tap((function(_this) {
return function(res) {
return W.map(res, compile_single_views.bind(_this));
};
})(this));
};
/**
* Fetches the JSON data from a url, file, or data and returns it neatly as
* an object containing the key, options, and resolved data.
*
* @param {String} key - name of the record being fetched
* @param {Object} opts - options provided under the key
* @returns {Promise|Object} - a promise for an object with a `key`,
* `options`, and `data` values
*/
fetch = function(key, opts) {
var data_promise;
data_promise = (function() {
switch (false) {
case !_.has(opts, 'url'):
return resolve_url(opts);
case !_.has(opts, 'file'):
return resolve_file.call(this, opts);
case !_.has(opts, 'data'):
return resolve_data(opts);
default:
throw new Error("You must provide a 'url', 'file', or 'data' key");
}
}).call(this);
return data_promise.then(function(data) {
return {
key: key,
options: opts,
data: data
};
});
};
/**
* Makes a request to the provided url, returning the response body as JSON.
*
* @param {Object} opts - the key's parameters
*/
resolve_url = function(opts) {
var client, conf, error_code, mime;
mime = require('rest/interceptor/mime');
error_code = require('rest/interceptor/errorCode');
client = rest.wrap(mime).wrap(error_code);
if (typeof opts.url === 'string') {
conf = {
path: opts.url
};
} else {
conf = opts.url;
}
return client(conf).then(function(res) {
if (!res.entity) {
throw new Error("URL has not returned any content");
}
if (typeof res.entity !== 'object') {
throw new Error("URL did not return JSON");
}
return res.entity;
});
};
/**
* Reads the file based on a path relative to the project root, returns the
* results as JSON.
*
* @param {Object} obj - the key's parameters
*/
resolve_file = function(opts) {
return node.call(fs.readFile.bind(fs), path.join(this.roots.root, opts.file), 'utf8').then(function(contents) {
return JSON.parse(contents);
});
};
/**
* Ensures data provided is an object, then resolves it through.
*
* @param {Object} opts - the key's parameters
*/
resolve_data = function(opts) {
var type;
type = typeof opts.data;
if (type !== 'object') {
throw new Error("Data provided is a " + type + " but must be an object");
}
return W.resolve(opts.data);
};
/**
* If a hook was provided in the config, runs the response through the hook.
*
* @param {String} obj - record object with a `key`, `options`, and `data`
*/
apply_hook = function(obj) {
if (!obj.options.hook) {
return obj;
}
obj.data = obj.options.hook(obj.data);
return obj;
};
/**
* Given a resolved records object, adds it to the view's locals.
*
* @param {Object} obj - records object, containing a `key` and `data`
*/
add_to_locals = function(obj) {
return this.roots.config.locals.records[obj.key] = obj.data;
};
/**
* Given a records object, if that object has `template` and `out` keys, and
* its data is an array, iterates through its data, creating a single view
* for each item in the array using the template provided in the `template`
* value, and writing to the path provided in the `out` value.
*
* @param {Object} obj - record object with a `key`, `options`, and `data`
*/
compile_single_views = function(obj) {
var obj_opts;
obj_opts = obj.options;
if (!obj_opts.template && !obj_opts.out) {
return;
}
if (obj_opts.template && !obj_opts.out) {
throw new Error("You must also provide an 'out' option");
}
if (obj_opts.out && !obj_opts.template) {
throw new Error("You must also provide a 'template' option");
}
if (!Array.isArray(obj.data)) {
throw new Error("'" + obj.key + "' data must be an array");
}
return W.map(obj.data, (function(_this) {
return function(item) {
var compiler, compiler_opts, output_path, ref, tpl, tpl_path;
tpl = _.isFunction(obj_opts.template) ? obj_opts.template(item) : obj_opts.template;
tpl_path = path.join(_this.roots.root, tpl);
output_path = (obj_opts.out(item)) + ".html";
compiler = _.find(_this.roots.config.compilers, function(c) {
return _.contains(c.extensions, path.extname(tpl_path).substring(1));
});
compiler_opts = _.extend(_this.roots.config.locals, (ref = _this.roots.config[compiler.name]) != null ? ref : {}, {
_path: output_path
}, {
item: item
});
return compiler.renderFile(tpl_path, compiler_opts).then(function(res) {
return _this.util.write(output_path, res.result);
});
};
})(this));
};
return Records;
})();
};
}).call(this);