var path = require('path'); var OKQuery = require('okquery'); var OKView = require('okview'); var OKDB = require('okdb'); var OKResource = require('okresource') var OKTemplate = require('oktemplate'); var OKServer = require('okserver'); var OKRestEndpoint = require('okrest'); /** * OKCMS! * Basically takes configuration and gives you a server. */ function OKCMS(options) { options = options || {}; var root = this._root = options.root || 'www'; // Reduce resource config into unique set this._resourceConfig = options.resources || []; this._views = options.views || { '/': { template: 'index' } }; this._resourceRoot = options.resourceRoot || '/api'; this._server = new OKServer({root: root}); this._templates = new OKTemplate({root: root}); var db = this._db = options.db || new OKDB(); // Special query for getting meta info this._meta = options.meta || { name: 'meta', get: function() { return db.getMeta(); } }; this._init(); } OKCMS.prototype.listen = function listen(port, options) { options = options || {}; this._server.listen(port); }; OKCMS.prototype._init = function _init() { var self = this; // Create resources instances from config and add CRUD views on them // NOTE Does not support subresources this._resources = this._resourceConfig.map(function (config) { var resource = self._createResource(config); var route = path.join(self._resourceRoot, resource.name); self._server.addView(route, resource.view()); return resource; }); // Add HTML views Object.keys(this._views) // Make sure more specific routes are processed first // TODO This is not semantically correct (bro) .sort(function(a, b) { return a.length < b.length; }) // For each route / viewOptions pair, add a view .forEach(function eachRoute(route) { var options = self._views[route]; // View should know what its route is. options.route = route; var view = self._createView(options); // Add view at route self._server.addView(route, view); }); }; /** * Takes a resource configuration object and returns a resource instance */ OKCMS.prototype._createResource = function(options) { options = options || {}; var resourceClass = options.resource; // No resource? Bail! if (!resourceClass) throw new Error('OKCMS: No resource class given'); // Default to 'select all' id options.id = options.id || '*'; // Some dependency injection options.db = this._db; // Clean unpassed options delete options.resource; // Return resource instance return resourceClass(options); }; /** * Takes a view configuration object and returns a view instance */ OKCMS.prototype._createView = function _createView(options) { options = options || {}; var self = this; var viewClass = options.view || OKView; var template = this._templates.getTemplate(options.template); var queryConfig = options.data || []; // No template? Bail! if (!template) throw new Error('OKCMS: No template "' + options.template + '"'); // Inject them dependencies options.template = template; options.meta = this._meta; options.queries = this._createQueries(queryConfig); // Clean options not passed to view delete options.view; // Return view instance return viewClass(options); }; /** * Takes a query configuration and returns a list of queries */ OKCMS.prototype._createQueries = function(options) { options = options || []; var db = this._db; if (!options.length) options = [options]; return options.map(function(option) { if (!option.name) throw new Error('No document name provided to query'); option.id = option.id || '*'; return new OKQuery(db, { name: option.name, id: option.id }); }); }; module.exports = { createApp: function(options) { return new OKCMS(options); }, OKResource: OKResource, OKView: OKView, OKRestEndpoint: OKRestEndpoint };