From 17240e9029b0c0e1ba227d828d86f50d4d9cba83 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Thu, 11 Sep 2014 12:20:20 +0200 Subject: [PATCH 01/21] support for disabled modules --- lib/mean.js | 114 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/lib/mean.js b/lib/mean.js index ae1fd87..e7f85cd 100644 --- a/lib/mean.js +++ b/lib/mean.js @@ -92,10 +92,12 @@ Meanio.prototype.serve = function(options, callback) { Mean.register('https', httpsServer); httpsServer.listen(config.https.port); } + var disabled = {}; + for (var i in config.disabled_modules) { + disabled[config.disabled_modules[i]] = true; + } - findModules(function() { - enableModules(); - }); + findModules(enableModules, disabled); aggregate('js', null); @@ -410,53 +412,70 @@ function modulePath(name, plus) { return path.join(process.cwd(), modules[name].source, name, plus); } -function findModules(callback) { +function readModuleFileDone (fileDefer, file, source, fileErr, data) { + if (data) { + try { + var json = JSON.parse(data.toString()); + if (json.mean) { + modules[file] = { + version: json.version, + source: source + }; + } + } catch (err) { + fileDefer.reject(err); + } + } + fileDefer.resolve(); +} - function searchSource(source) { - var deferred = Q.defer(); - fs.readdir(path.join(process.cwd(), source), function(err, files) { - if (err || !files || !files.length) { +function fileForEachProcess (source, promises, file) { + var fileDefer = Q.defer(); + fs.readFile(path.join(process.cwd(), source, file, 'package.json'), readModuleFileDone.bind(null, fileDefer, file, source)); + promises.push(fileDefer.promise); +} - if (err && err.code !== 'ENOENT') { - console.log(err); - } else { - return deferred.resolve(); - } - return deferred.reject(err); - } +function processDirFilesFromSearchSource(disabled, source, deferred, err, files) { + if (err || !files || !files.length) { - var promises = []; - files.forEach(function(file) { - var fileDefer = Q.defer(); - fs.readFile(path.join(process.cwd(), source, file, 'package.json'), function(fileErr, data) { - if (data) { - try { - var json = JSON.parse(data.toString()); - if (json.mean) { - modules[file] = { - version: json.version, - source: source - }; - } - } catch (err) { - fileDefer.reject(err); - } - } - fileDefer.resolve(); - }); - promises.push(fileDefer.promise); - }); - return deferred.resolve(Q.all(promises)); - }); - return deferred.promise; + if (err && err.code !== 'ENOENT') { + console.log(err); + } else { + return deferred.resolve(); + } + return deferred.reject(err); } - Q.all([searchSource('node_modules'), searchSource('packages'),searchSource('packages/core'),searchSource('packages/custom'),searchSource('packages/contrib')]).done(function() { - events.emit('modulesFound'); - callback(); - }, function(error) { - console.error('Error loading modules. ' + error); - callback(); - }); + + var promises = []; + for (var i in disabled) { + var index = files.indexOf(disabled[i]); + if (index > -1) continue; + files.splice(index, 1); + } + + files.forEach(fileForEachProcess.bind(null, source, promises)); + return deferred.resolve(Q.all(promises)); +} + +function searchSourceForFindModules(disabled, source) { + var deferred = Q.defer(); + fs.readdir(path.join(process.cwd(), source), processDirFilesFromSearchSource.bind (null, disabled, source, deferred)); + return deferred.promise; +} + +function findModulesDone (callback) { + events.emit('modulesFound'); + callback(); +} + +function findModulesError (callback, error) { + console.error('Error loading modules. ' + error); + callback(); +} + +function findModules(callback, disabled) { + Q.all([searchSourceForFindModules(disabled, 'node_modules'), searchSourceForFindModules(disabled, 'packages'),searchSourceForFindModules(disabled, 'packages/core'),searchSourceForFindModules(disabled, 'packages/custom'),searchSourceForFindModules(disabled, 'packages/contrib')]) + .done(findModulesDone.bind(null, callback), findModulesError.bind(null, callback)); } function sortAggregateAssetsByWeight() { @@ -482,7 +501,7 @@ function sortByWeight(group, ext) { }).join('\n'); } -function enableModules(callback) { +function enableModules() { var name, remaining = 0; for (name in modules) { remaining++; @@ -496,7 +515,6 @@ function enableModules(callback) { remaining--; if (!remaining) { events.emit('modulesEnabled'); - if (callback) callback(modules); } } } From 3ac12f2738afd5b03c60601801b7c3ca187925f3 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Thu, 11 Sep 2014 16:25:17 +0200 Subject: [PATCH 02/21] introducing support for multiple mongodb connections --- lib/mean.js | 150 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 43 deletions(-) diff --git a/lib/mean.js b/lib/mean.js index e7f85cd..351188c 100644 --- a/lib/mean.js +++ b/lib/mean.js @@ -53,15 +53,87 @@ function Meanio() { this.version = require('../package').version; } -Meanio.prototype.serve = function(options, callback) { +function connectDbOk (defer, s) { + defer.resolve(s); +} +function connectDbFailed(defer, s) { + defer.reject(s); +} + +function connectDb (alias, path) { + var defer = Q.defer(); + var connection = mongoose.createConnection (path); + + connection.once ('connected', connectDbOk.bind(null, defer, {path:path, alias: alias, connection: connection})); + connection.once('error', connectDbFailed.bind(null, defer, {})); + return defer.promise; +} + +function doBootstrap (options, callback, database, err, config) { + // Bootstrap Models, Dependencies, Routes and the app as an express app + var app = require('./bootstrap')(options, database); + + // Listen on http.port (or port as fallback for old configs) + var httpServer = http.createServer(app); + Mean.register('http', httpServer); + httpServer.listen(config.http ? config.http.port : config.port, config.hostname); + + if (config.https && config.https.port) { + var httpsOptions = { + key: fs.readFileSync(config.https.ssl.key), + cert: fs.readFileSync(config.https.ssl.cert) + }; + + var httpsServer = https.createServer(httpsOptions, app); + Mean.register('https', httpsServer); + httpsServer.listen(config.https.port); + } + var disabled = {}; + for (var i in config.disabled_modules) { + disabled[config.disabled_modules[i]] = true; + } + + findModules(enableModules, disabled); + + aggregate('js', null); + + Mean.name = config.app.name; + Mean.app = app; + + menus = new Mean.Menus(); + Mean.menus = menus; + + callback(app, config); +} + +function dataBasesReady (options, callback, database, connection_pool) { + var alias_map = {}; + for (var i in connection_pool) { + if (connection_pool[i].state !== 'fulfilled') continue; + alias_map[connection_pool[i].value.alias] = connection_pool[i].value.connection; + } + + mongoose.get_MEANIODB_connection = function (alias) { + if ('default' === alias || !alias_map[alias]) { + return database; + } + return alias_map[alias]; + } + mongoose.alias_MEANIODB_exists = function (alias) { + return (alias === 'default') || !alias || alias in alias_map; + } + + Mean.config = new Config(doBootstrap.bind(null, options, callback, database)); + Mean.active = true; + Mean.options = options; +} +Meanio.prototype.serve = function(options, callback) { if (this.active) return this; // Initializing system variables var defaultConfig = util.loadConfig(); - mongoose.set('debug', defaultConfig.mongoose && defaultConfig.mongoose.debug); - var database = mongoose.connect(defaultConfig.db || '', defaultConfig.dbOptions || {}, function(err) { if (err) { console.error('Error:', err.message); @@ -73,45 +145,11 @@ Meanio.prototype.serve = function(options, callback) { connection: database }); - Mean.config = new Config(function(err, config) { - // Bootstrap Models, Dependencies, Routes and the app as an express app - var app = require('./bootstrap')(options, database); - - // Listen on http.port (or port as fallback for old configs) - var httpServer = http.createServer(app); - Mean.register('http', httpServer); - httpServer.listen(config.http ? config.http.port : config.port, config.hostname); - - if (config.https && config.https.port) { - var httpsOptions = { - key: fs.readFileSync(config.https.ssl.key), - cert: fs.readFileSync(config.https.ssl.cert) - }; - - var httpsServer = https.createServer(httpsOptions, app); - Mean.register('https', httpsServer); - httpsServer.listen(config.https.port); - } - var disabled = {}; - for (var i in config.disabled_modules) { - disabled[config.disabled_modules[i]] = true; - } - - findModules(enableModules, disabled); - - aggregate('js', null); - - Mean.name = config.app.name; - Mean.app = app; - - menus = new Mean.Menus(); - Mean.menus = menus; - - callback(app, config); - }); - - Mean.active = true; - Mean.options = options; + var db_promisses = []; + for (var i in defaultConfig.dbs) { + db_promisses.push (connectDb(i, defaultConfig.dbs[i])); + } + Q.allSettled (db_promisses).then(dataBasesReady.bind(this, options, callback, database)); }); }; @@ -305,13 +343,39 @@ Meanio.prototype.Menus = function() { }; }; +function filterDBAliases (v) { + return mongoose.alias_MEANIODB_exists(v); +} + +function applyModels (schema, model, collection, dbalias) { + mongoose.get_MEANIODB_connection(dbalias).model(model, schema, collection); +} + +function requireModel (path) { + var mdl = require(path); + if (!mdl.register) return; + for (var i in mdl.register) { + var itm = mdl.register[i]; + if (!itm.schema) { + throw "No schema in reqister model request, can not move on ..."; + } + if (!itm.model) { + throw "No model in register model request, can not move on ..."; + } + if (!itm.dbs || itm.dbs.length === 0) { + itm.dbs = ['default']; + } + _.uniq(itm.dbs.filter(filterDBAliases)).forEach (applyModels.bind(null, itm.schema, itm.model, itm.collection)); + } +} + function Module(name) { this.name = lowerCaseFirstLetter(name); this.menus = menus; this.config = config; // bootstrap models - util.walk(modulePath(this.name, 'server'), 'model', null, require); + util.walk(modulePath(this.name, 'server'), 'model', null, requireModel); } From a6f03041bc4f3cee31b0df46cb69982ba4041fcb Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Fri, 12 Sep 2014 00:35:48 +0200 Subject: [PATCH 03/21] typo --- lib/mean.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mean.js b/lib/mean.js index 351188c..4f24b25 100644 --- a/lib/mean.js +++ b/lib/mean.js @@ -513,7 +513,7 @@ function processDirFilesFromSearchSource(disabled, source, deferred, err, files) var promises = []; for (var i in disabled) { var index = files.indexOf(disabled[i]); - if (index > -1) continue; + if (index < 0) continue; files.splice(index, 1); } From 204720cc73aa0f78f3e0d0f758932f4d2d8a2455 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Fri, 12 Sep 2014 14:21:07 +0200 Subject: [PATCH 04/21] original idea of core modules implemented as far as could be ... for now ... --- lib/bootstrap.js | 55 ++++-- lib/config.js | 141 ++++++++++++++ lib/db.js | 85 ++++++++ lib/mean.js | 493 +++-------------------------------------------- lib/menu.js | 48 +++++ lib/module.js | 172 +++++++++++++++++ 6 files changed, 514 insertions(+), 480 deletions(-) create mode 100644 lib/config.js create mode 100644 lib/db.js create mode 100644 lib/menu.js create mode 100644 lib/module.js diff --git a/lib/bootstrap.js b/lib/bootstrap.js index 4028517..69316a2 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -3,16 +3,22 @@ var express = require('express'), Grid = require('gridfs-stream'), errorHandler = require('errorhandler'), - config = require('meanio').loadConfig(), fs = require('fs'), + http = require('http'), + https = require('https'), + Q = require('q'), + path = require('path'), appPath = process.cwd(); -var mean = require('meanio'); -module.exports = function(options, db) { + +module.exports = function(Meanio) { + + var db = Meanio.Singleton.get('database').connection; + var config = Meanio.Singleton.config.clean; // Register app dependency; - mean.register('app', function(access, database) { + Meanio.Singleton.register('app', function(access, database) { require(appPath + '/config/express')(app, access.passport, database.connection); return app; }); @@ -27,7 +33,7 @@ module.exports = function(options, db) { res.setHeader('content-type', 'text/javascript'); - mean.aggregated('js', req.query.group ? req.query.group : 'footer', function(data) { + Meanio.Singleton.aggregated('js', req.query.group ? req.query.group : 'footer', function(data) { res.send(data); }); @@ -67,23 +73,23 @@ module.exports = function(options, db) { app.get('/modules/aggregated.css', function(req, res) { res.setHeader('content-type', 'text/css'); - mean.aggregated('css', req.query.group ? req.query.group : 'header', function(data) { + Meanio.Singleton.aggregated('css', req.query.group ? req.query.group : 'header', function(data) { res.send(data); }); }); app.use('/bower_components', express.static(config.root + '/bower_components')); - mean.events.on('modulesEnabled', function() { + Meanio.Singleton.events.on('modulesEnabled', function() { - for (var name in mean.modules) { - app.use('/' + name, express.static(config.root + '/' + mean.modules[name].source + '/' + name.toLowerCase() + '/public')); + for (var name in Meanio.Singleton.modules) { + app.use('/' + name, express.static(config.root + '/' + Meanio.Singleton.modules[name].source + '/' + name.toLowerCase() + '/public')); } // We are going to catch everything else here app.route('*').get(function(req, res, next) { - if (!mean.template) return next(); - mean.template(req, res, next); + if (!Meanio.Singleton.template) return next(); + Meanio.Singleton.template(req, res, next); }); // Assume "not found" in the error msgs is a 404. this is somewhat @@ -116,5 +122,30 @@ module.exports = function(options, db) { } }); - return app; + // Listen on http.port (or port as fallback for old configs) + var httpServer = http.createServer(app); + Meanio.Singleton.register('http', httpServer); + httpServer.listen(config.http ? config.http.port : config.port, config.hostname); + + if (config.https && config.https.port) { + var httpsOptions = { + key: fs.readFileSync(config.https.ssl.key), + cert: fs.readFileSync(config.https.ssl.cert) + }; + + var httpsServer = https.createServer(httpsOptions, app); + Meanio.Singleton.register('https', httpsServer); + httpsServer.listen(config.https.port); + } + var disabled = {}; + for (var i in config.disabled_modules) { + disabled[config.disabled_modules[i]] = true; + } + + Meanio.Singleton.Module.bootstrapModules(disabled); + + Meanio.Singleton.aggregate('js', null); + Meanio.Singleton.name = config.app.name; + Meanio.Singleton.app = app; + Meanio.Singleton.menus = new (Meanio.Singleton.Menus)(); }; diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 0000000..ae3f778 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,141 @@ +function mergeConfig(original, saved) { + var clean = {}; + for (var index in saved) { + clean[index] = saved[index].value; + if (original[index]) { + original[index].value = saved[index].value; + } else { + original[index] = { + value: saved[index].value + }; + } + original[index]['default'] = original[index]['default'] || saved[index]['default']; + } + return { + diff: original, + clean: clean + }; +} + +function createConfig(Meanio){ + function Config(defaultconfig, callback) { + this.verbose = {}; + this.original = JSON.flatten(defaultconfig, { + default: true + }); + this.clean = null; + this.diff = null; + this.flat = null; + this.createConfigurations(defaultconfig); + } + + Config.prototype.loadSettings = function(callback) { + var Package = this.loadPackageModel(); + if (!Package){ + return callback ? callback(this.original) : undefined; + } + Package.findOne({ + name: 'config' + }, this.onPackageRead.bind(this,callback)); + }; + + Config.prototype.updateSettings = function(name, settings, callback) { + var Package = this.loadPackageModel(); + if (!Package){ + return callback ? callback(new Error('Failed to update settings')) : undefined; + } + Package.findOneAndUpdate({ + name: name + }, { + $set: { + settings: settings, + updated: new Date() + } + }, { + upsert: true, + multi: false + }, function(err, doc) { + if (err) { + console.log(err); + return callback(new Error('Failed to update settings')); + } + return callback(null, doc); + }); + } + + Config.prototype.getSettings = function(name, callback) { + var Package = this.loadPackageModel(); + if (!Package){ + return callback ? callback(new Error('Failed to retrieve settings')) : undefined; + } + Package.findOne({ + name: name + }, function(err, doc) { + if (err) { + console.log(err); + return callback(new Error('Failed to retrieve settings')); + } + return callback(null, doc); + }); + } + + Config.prototype.loadPackageModel = function() { + var database = Meanio.Singleton.get('database'); + if (!database || !database.connection) { + return null; + } + if (!database.connection.models.Package) { + require('../modules/package')(database); + } + return database.connection.model('Package'); + }; + + Config.prototype.onPackageRead = function(callback,err,doc){ + console.log(callback,err,doc); + if (err) { + return callback ? callback(err) : undefined; + } + + if(!(doc&&doc.settings)){ + return callback ? callback() : undefined; + } + this.createConfigurations(doc.settings); + if (callback) callback(); + }; + + Config.prototype.createConfigurations = function(config){ + var saved = JSON.flatten(config, {}); + var merged = mergeConfig(this.original, saved); + var clean = JSON.unflatten(merged.clean, {}); + var diff = JSON.unflatten(merged.diff, {}); + this.verbose = { + clean: clean, + diff: diff, + flat: merged + }; + this.clean = clean; + this.diff = diff; + this.flat = merged; + }; + + Config.prototype.update = function(settings, callback) { + var Package = this.loadPackageModel(); + if (!Package) return callback(new Error('failed to load data model')); + Package.findOneAndUpdate({ + name: 'config' + }, { + $set: { + settings: settings, + updated: new Date() + } + }, { + upsert: true, + new: true, + multi: false + }, this.onPackageRead.bind(this,callback)); + }; + + Meanio.Config = Config; +} + +module.exports = createConfig; diff --git a/lib/db.js b/lib/db.js new file mode 100644 index 0000000..5f03551 --- /dev/null +++ b/lib/db.js @@ -0,0 +1,85 @@ +var mongoose = require ('mongoose'), + Q = require('q'), + _ = require('lodash'); + +function filterDBAliases (v) { + return mongoose.alias_MEANIODB_exists(v); +} + +function applyModels (schema, model, collection, dbalias) { + mongoose.get_MEANIODB_connection(dbalias).model(model, schema, collection); +} + +function connectDb (alias, path) { + var defer = Q.defer(); + var connection = mongoose.createConnection (path); + connection.once ('connected', connectDbOk.bind(null, defer, {path:path, alias: alias, connection: connection})); + connection.once('error', connectDbFailed.bind(null, defer, {})); + return defer.promise; +} + +function connectDbOk (defer, s) { + defer.resolve(s); +} + +function connectDbFailed(defer, s) { + defer.reject(s); +} + +function dataBasesReady (done, connection_pool) { + var alias_map = {}; + for (var i in connection_pool) { + if (connection_pool[i].state !== 'fulfilled') continue; + alias_map[connection_pool[i].value.alias] = connection_pool[i].value.connection; + } + mongoose.get_MEANIODB_connection = function (alias) { + if ('default' === alias || !alias_map[alias]) { + return database; + } + return alias_map[alias]; + } + mongoose.alias_MEANIODB_exists = function (alias) { + return (alias === 'default') || !alias || alias in alias_map; + } + done(); +} + +function supportDB(Meanio) { + Meanio.connectDBs = function (defaultConfig, done) { + mongoose.set('debug', defaultConfig.mongoose && defaultConfig.mongoose.debug); + var database = mongoose.connect(defaultConfig.db || '', defaultConfig.dbOptions || {}, function(err) { + if (err) { + console.error('Error:', err.message); + return console.error('**Could not connect to MongoDB. Please ensure mongod is running and restart MEAN app.**'); + } + + // Register database dependency + Meanio.Singleton.register('database', { + connection:database + }); + + var db_promises = []; + for (var i in defaultConfig.dbs) { + db_promises.push (connectDb(i, defaultConfig.dbs[i])); + } + Q.allSettled (db_promises).then(dataBasesReady.bind(this, done)); + }); + }; + + Meanio.applyModels = function (model_register) { + for (var i in model_register) { + var itm = model_register[i]; + if (!itm.schema) { + throw "No schema in reqister model request, can not move on ..."; + } + if (!itm.model) { + throw "No model in register model request, can not move on ..."; + } + if (!itm.dbs || itm.dbs.length === 0) { + itm.dbs = ['default']; + } + _.uniq(itm.dbs.filter(filterDBAliases)).forEach(applyModels.bind(null, itm.schema, itm.model, itm.collection)); + } + }; +} +module.exports = supportDB; diff --git a/lib/mean.js b/lib/mean.js index 68e7405..7168444 100644 --- a/lib/mean.js +++ b/lib/mean.js @@ -1,24 +1,28 @@ 'use strict'; var swig = require('swig'), - mongoose = require('mongoose'), container = require('dependable').container(), - fs = require('fs'), - path = require('path'), util = require('./util'), - http = require('http'), - https = require('https'), - _ = require('lodash'), - EventEmitter = require('events').EventEmitter, - Q = require('q'); + EventEmitter = require('events').EventEmitter; var events = new EventEmitter(), - allMenus = [], middleware = { before: {}, after: {} }; +function doBootstrap (callback, err) { + if (err) { + console.log('Bootstrap cowardly retreating due to error:' ,err); + return callback ? callback() : undefined; + } + Meanio.Singleton.config.loadSettings(function(){ + // Bootstrap Models, Dependencies, Routes and the app as an express app + require('./bootstrap')(Meanio); + callback(Meanio.Singleton.app, Meanio.Singleton.config.clean); + }); +} + function Meanio() { if (this.active) return; Meanio.Singleton = this; @@ -27,219 +31,14 @@ function Meanio() { } Meanio.events = events; -function connectDbOk (defer, s) { - defer.resolve(s); -} -function connectDbFailed(defer, s) { - defer.reject(s); -} - -function connectDb (alias, path) { - var defer = Q.defer(); - var connection = mongoose.createConnection (path); - - connection.once ('connected', connectDbOk.bind(null, defer, {path:path, alias: alias, connection: connection})); - connection.once('error', connectDbFailed.bind(null, defer, {})); - return defer.promise; -} - -function doBootstrap (options, callback, database, err, config) { - // Bootstrap Models, Dependencies, Routes and the app as an express app - var app = require('./bootstrap')(options, database); - - // Listen on http.port (or port as fallback for old configs) - var httpServer = http.createServer(app); - Meanio.Singleton.register('http', httpServer); - httpServer.listen(config.http ? config.http.port : config.port, config.hostname); - - if (config.https && config.https.port) { - var httpsOptions = { - key: fs.readFileSync(config.https.ssl.key), - cert: fs.readFileSync(config.https.ssl.cert) - }; - - var httpsServer = https.createServer(httpsOptions, app); - Meanio.Singleton.register('https', httpsServer); - httpsServer.listen(config.https.port); - } - var disabled = {}; - for (var i in config.disabled_modules) { - disabled[config.disabled_modules[i]] = true; - } - - findModules(enableModules, disabled); - - Meanio.Singleton.aggregate('js', null); - - Meanio.Singleton.name = config.app.name; - Meanio.Singleton.app = app; - Meanio.Singleton.menus = new Meanio.Singleton.Menus(); - - callback(app, config); -} - -function dataBasesReady (options, callback, database, connection_pool) { - var alias_map = {}; - for (var i in connection_pool) { - if (connection_pool[i].state !== 'fulfilled') continue; - alias_map[connection_pool[i].value.alias] = connection_pool[i].value.connection; - } - - mongoose.get_MEANIODB_connection = function (alias) { - if ('default' === alias || !alias_map[alias]) { - return database; - } - return alias_map[alias]; - } - mongoose.alias_MEANIODB_exists = function (alias) { - return (alias === 'default') || !alias || alias in alias_map; - } - - Meanio.Singleton.config = new Config(doBootstrap.bind(null, options, callback, database)); - Meanio.Singleton.active = true; - Meanio.Singleton.options = options; -} - Meanio.prototype.serve = function(options, callback) { if (this.active) return this; - - // Initializing system variables - var defaultConfig = util.loadConfig(); - mongoose.set('debug', defaultConfig.mongoose && defaultConfig.mongoose.debug); - var database = mongoose.connect(defaultConfig.db || '', defaultConfig.dbOptions || {}, function(err) { - if (err) { - console.error('Error:', err.message); - return console.error('**Could not connect to MongoDB. Please ensure mongod is running and restart MEAN app.**'); - } - - // Register database dependency - Meanio.Singleton.register('database', { - connection: database - }); - - var db_promisses = []; - for (var i in defaultConfig.dbs) { - db_promisses.push (connectDb(i, defaultConfig.dbs[i])); - } - Q.allSettled (db_promisses).then(dataBasesReady.bind(this, options, callback, database)); - }); + Meanio.Singleton.options = options; + Meanio.Singleton.config = new (Meanio.Config)(util.loadConfig()); + Meanio.Singleton.active = true; + Meanio.connectDBs(Meanio.Singleton.config.clean, doBootstrap.bind(null, callback)); }; -Meanio.prototype.loadConfig = util.loadConfig; - -function Config(callback) { - - if (this.config) return this.config; - - function update(settings, callback) { - - var Package = loadPackageModel(); - - if (!Package) return callback(new Error('failed to load data model')); - - Package.findOneAndUpdate({ - name: 'config' - }, { - $set: { - settings: settings, - updated: new Date() - } - }, { - upsert: true, - multi: false - }, function(err, doc) { - if (err) { - console.log(err); - return callback(new Error('Failed to update settings')); - } - - loadSettings(Meanio.Singleton.config); - - return callback(null, doc.settings); - }); - } - - function loadSettings(Config, callback) { - - var Package = loadPackageModel(); - - var defaultConfig = util.loadConfig(); - console.log('o cemu se ovde radi?', Package, defaultConfig); - - if (!Package) return defaultConfig; - - Package.findOne({ - name: 'config' - }, function(err, doc) { - - var original = JSON.flatten(defaultConfig, { - default: true - }); - - var saved = JSON.flatten(doc ? doc.settings : defaultConfig, {}); - - var merged = mergeConfig(original, saved); - - var clean = JSON.unflatten(merged.clean, {}); - - var diff = JSON.unflatten(merged.diff, {}); - - Config.verbose = { - clean: clean, - diff: diff, - flat: merged - }; - - Config.clean = clean; - Config.diff = diff; - Config.flat = merged; - if (callback) callback(err, clean); - }); - } - - function mergeConfig(original, saved) { - - var clean = {}; - - for (var index in saved) { - clean[index] = saved[index].value; - if (original[index]) { - original[index].value = saved[index].value; - } else { - original[index] = { - value: saved[index].value - }; - } - - original[index]['default'] = original[index]['default'] || saved[index]['default']; - } - - return { - diff: original, - clean: clean - }; - } - - function loadPackageModel() { - - var database = container.get('database'); - if (!database || !database.connection) { - return null; - } - - if (!database.connection.models.Package) { - require('../modules/package')(database); - } - - return database.connection.model('Package'); - } - - - loadSettings(this, callback); - this.update = update; - -} - Meanio.prototype.status = function() { return { active: this.active, @@ -248,7 +47,10 @@ Meanio.prototype.status = function() { }; Meanio.prototype.register = container.register; - +Meanio.prototype.get = container.get; +Meanio.prototype.loadConfig = function(){ + return this.config.clean; +}; Meanio.prototype.resolve = container.resolve; //confusing names, need to be refactored asap @@ -264,255 +66,6 @@ Meanio.modules = []; //instance property Meanio.prototype.modules = Meanio.modules; -Meanio.prototype.Menus = function() { - this.add = function(options) { - if (!Array.isArray(options)) options = [options]; - - options.forEach(function(opt) { - opt.menu = opt.menu || 'main'; - opt.roles = opt.roles || ['anonymous']; - allMenus[opt.menu] = allMenus[opt.menu] || []; - allMenus[opt.menu].push(opt); - }); - return Meanio.Singleton.menus; - }; - - this.get = function(options) { - var allowed = []; - options = options || {}; - options.menu = options.menu || 'main'; - options.roles = options.roles || ['anonymous']; - options.defaultMenu = options.defaultMenu || []; - - var items = options.defaultMenu.concat(allMenus[options.menu] || []); - items.forEach(function(item) { - - var hasRole = false; - options.roles.forEach(function(role) { - if (role === 'admin' || item.roles.indexOf('anonymous') !== -1 || item.roles.indexOf(role) !== -1) { - hasRole = true; - } - }); - - if (hasRole) { - allowed.push(item); - } - }); - return allowed; - }; -}; - -function filterDBAliases (v) { - return mongoose.alias_MEANIODB_exists(v); -} - -function applyModels (schema, model, collection, dbalias) { - mongoose.get_MEANIODB_connection(dbalias).model(model, schema, collection); -} - -function requireModel (path) { - var mdl = require(path); - if (!mdl.register) return; - for (var i in mdl.register) { - var itm = mdl.register[i]; - if (!itm.schema) { - throw "No schema in reqister model request, can not move on ..."; - } - if (!itm.model) { - throw "No model in register model request, can not move on ..."; - } - if (!itm.dbs || itm.dbs.length === 0) { - itm.dbs = ['default']; - } - _.uniq(itm.dbs.filter(filterDBAliases)).forEach (applyModels.bind(null, itm.schema, itm.model, itm.collection)); - } -} - -function Module(name) { - this.name = lowerCaseFirstLetter(name); - this.menus = Meanio.Singleton.menus; - this.config = Meanio.Singleton.config; - - // bootstrap models - util.walk(modulePath(this.name, 'server'), 'model', null, requireModel); - -} - -Module.prototype.render = function(view, options, callback) { - swig.renderFile(modulePath(this.name, '/server/views/' + view + '.html'), options, callback); -}; - -Module.prototype.setDefaultTemplate = function(template) { - Meanio.Singleton.template = template; -}; - -Module.prototype.routes = function() { - var args = Array.prototype.slice.call(arguments); - var that = this; - util.walk(modulePath(this.name, 'server'), 'route', 'middlewares', function(route) { - require(route).apply(that, [that].concat(args)); - }); -}; - -Module.prototype.register = function(callback) { - container.register(this.name, callback); -}; - -Module.prototype.angularDependencies = function(dependencies) { - this.angularDependencies = dependencies; - Meanio.modules[this.name].angularDependencies = dependencies; -}; - - -function updateSettings(Package, name, settings, callback) { - Package.findOneAndUpdate({ - name: name - }, { - $set: { - settings: settings, - updated: new Date() - } - }, { - upsert: true, - multi: false - }, function(err, doc) { - if (err) { - console.log(err); - return callback(new Error('Failed to update settings')); - } - return callback(null, doc); - }); -} - -function getSettings(Package, name, callback) { - Package.findOne({ - name: name - }, function(err, doc) { - if (err) { - console.log(err); - return callback(new Error('Failed to retrieve settings')); - } - return callback(null, doc); - }); -} - -Module.prototype.settings = function() { - - if (!arguments.length) return; - - var database = container.get('database'); - if (!database || !database.connection) { - return { - err: true, - message: 'No database connection' - }; - } - - if (!database.connection.models.Package) { - require('../modules/package')(database); - } - - var Package = database.connection.model('Package'); - if (arguments.length === 2) return updateSettings(Package, this.name, arguments[0], arguments[1]); - if (arguments.length === 1 && typeof arguments[0] === 'object') return updateSettings(Package, this.name, arguments[0], function() {}); - if (arguments.length === 1 && typeof arguments[0] === 'function') return getSettings(Package, this.name, arguments[0]); - -}; - -Meanio.prototype.Module = Module; - -function lowerCaseFirstLetter(string) { - return string.charAt(0).toLowerCase() + string.slice(1); -} - -function modulePath(name, plus) { - return path.join(process.cwd(), Meanio.modules[name].source, name, plus); -} - -function readModuleFileDone (fileDefer, file, source, fileErr, data) { - if (data) { - try { - var json = JSON.parse(data.toString()); - if (json.mean) { - Meanio.modules[file] = { - version: json.version, - source: source - }; - } - } catch (err) { - fileDefer.reject(err); - } - } - fileDefer.resolve(); -} - -function fileForEachProcess (source, promises, file) { - var fileDefer = Q.defer(); - fs.readFile(path.join(process.cwd(), source, file, 'package.json'), readModuleFileDone.bind(null, fileDefer, file, source)); - promises.push(fileDefer.promise); -} - -function processDirFilesFromSearchSource(disabled, source, deferred, err, files) { - if (err || !files || !files.length) { - if (err && err.code !== 'ENOENT') { - console.log(err); - } else { - return deferred.resolve(); - } - return deferred.reject(err); - } - - var promises = []; - for (var i in disabled) { - var index = files.indexOf(disabled[i]); - if (index < 0) continue; - files.splice(index, 1); - } - - files.forEach(fileForEachProcess.bind(null, source, promises)); - return deferred.resolve(Q.all(promises)); -} - -function searchSourceForFindModules(disabled, source) { - var deferred = Q.defer(); - fs.readdir(path.join(process.cwd(), source), processDirFilesFromSearchSource.bind (null, disabled, source, deferred)); - return deferred.promise; -} - -function findModulesDone (callback) { - events.emit('modulesFound'); - callback(); -} - -function findModulesError (callback, error) { - console.error('Error loading modules. ' + error); - callback(); -} - -function findModules(callback, disabled) { - Q.all([searchSourceForFindModules(disabled, 'node_modules'), searchSourceForFindModules(disabled, 'packages'),searchSourceForFindModules(disabled, 'packages/core'),searchSourceForFindModules(disabled, 'packages/custom'),searchSourceForFindModules(disabled, 'packages/contrib')]) - .done(findModulesDone.bind(null, callback), findModulesError.bind(null, callback)); -} - - -function enableModules() { - var name, remaining = 0; - for (name in Meanio.modules) { - remaining++; - require(modulePath(name, 'app.js')); - } - - for (name in Meanio.modules) { - name = name; - container.resolve.apply(container, [name]); - container.get(name); - remaining--; - if (!remaining) { - events.emit('modulesEnabled'); - } - } -} - Meanio.prototype.chainware = { add: function(event, weight, func) { @@ -556,6 +109,10 @@ Meanio.prototype.chainware = { } }; +(require('./config'))(Meanio); +(require('./menu'))(Meanio); +(require('./module'))(Meanio); (require('./aggregation'))(Meanio); +(require('./db'))(Meanio); module.exports = exports = new Meanio(); diff --git a/lib/menu.js b/lib/menu.js new file mode 100644 index 0000000..2ba2dc7 --- /dev/null +++ b/lib/menu.js @@ -0,0 +1,48 @@ +var allMenus = []; + +function supportMenus(Meanio){ + function Menus() { + }; + Menus.prototype.add = function(options) { + console.log('adding menu',options); + if (!Array.isArray(options)) options = [options]; + + options.forEach(function(opt) { + opt.menu = opt.menu || 'main'; + opt.roles = opt.roles || ['anonymous']; + allMenus[opt.menu] = allMenus[opt.menu] || []; + allMenus[opt.menu].push(opt); + }); + console.log('finally',allMenus); + return this; + }; + + Menus.prototype.get = function(options) { + var allowed = []; + options = options || {}; + options.menu = options.menu || 'main'; + options.roles = options.roles || ['anonymous']; + options.defaultMenu = options.defaultMenu || []; + + var items = options.defaultMenu.concat(allMenus[options.menu] || []); + items.forEach(function(item) { + + var hasRole = false; + options.roles.forEach(function(role) { + if (role === 'admin' || item.roles.indexOf('anonymous') !== -1 || item.roles.indexOf(role) !== -1) { + hasRole = true; + } + }); + + if (hasRole) { + allowed.push(item); + } + }); + console.log('getting menu for',options,'=>',allowed); + return allowed; + }; + Meanio.prototype.Menus = Menus; + +}; + +module.exports = supportMenus; diff --git a/lib/module.js b/lib/module.js new file mode 100644 index 0000000..73199a4 --- /dev/null +++ b/lib/module.js @@ -0,0 +1,172 @@ +var Q = require('q'), + fs = require('fs'), + path = require('path'), + util = require('./util'); + +function lowerCaseFirstLetter(string) { + return string.charAt(0).toLowerCase() + string.slice(1); +} + + +function supportModules(Meanio){ + + function findModulesDone (callback) { + Meanio.Singleton.events.emit('modulesFound'); + callback(); + } + + function findModulesError (callback, error) { + console.error('Error loading modules. ' + error); + callback(); + } + + + function searchSourceForFindModules(disabled, source) { + var deferred = Q.defer(); + fs.readdir(path.join(process.cwd(), source), processDirFilesFromSearchSource.bind (null, disabled, source, deferred)); + return deferred.promise; + } + + function findModules(callback, disabled) { + Q.all([searchSourceForFindModules(disabled, 'node_modules'), searchSourceForFindModules(disabled, 'packages'),searchSourceForFindModules(disabled, 'packages/core'),searchSourceForFindModules(disabled, 'packages/custom'),searchSourceForFindModules(disabled, 'packages/contrib')]) + .done(findModulesDone.bind(null, callback), findModulesError.bind(null, callback)); + } + + function readModuleFileDone (fileDefer, file, source, fileErr, data) { + if (data) { + try { + var json = JSON.parse(data.toString()); + if (json.mean) { + Meanio.modules[file] = { + version: json.version, + source: source + }; + } + } catch (err) { + fileDefer.reject(err); + } + } + fileDefer.resolve(); + } + + function fileForEachProcess (source, promises, file) { + var fileDefer = Q.defer(); + fs.readFile(path.join(process.cwd(), source, file, 'package.json'), readModuleFileDone.bind(null, fileDefer, file, source)); + promises.push(fileDefer.promise); + } + + function processDirFilesFromSearchSource(disabled, source, deferred, err, files) { + if (err || !files || !files.length) { + if (err && err.code !== 'ENOENT') { + console.log(err); + } else { + return deferred.resolve(); + } + return deferred.reject(err); + } + + var promises = []; + for (var i in disabled) { + var index = files.indexOf(i); + if (index < 0) continue; + files.splice(index, 1); + } + + files.forEach(fileForEachProcess.bind(null, source, promises)); + return deferred.resolve(Q.all(promises)); + } + + function enableModules() { + var name, remaining = 0; + for (name in Meanio.modules) { + remaining++; + require(modulePath(name, 'app.js')); + } + + for (name in Meanio.modules) { + name = name; + Meanio.Singleton.resolve(name); + Meanio.Singleton.get(name); + remaining--; + if (!remaining) { + Meanio.Singleton.events.emit('modulesEnabled'); + } + } + } + + + + function requireModel (path) { + var mdl = require(path); + if (mdl.register) {Meanio.applyModels(mdl.register);} + } + + function Module(name) { + this.name = lowerCaseFirstLetter(name); + this.menus = Meanio.Singleton.menus; + this.config = Meanio.Singleton.config; + + // bootstrap models + util.walk(modulePath(this.name, 'server'), 'model', null, requireModel); + } + + Module.prototype.render = function(view, options, callback) { + swig.renderFile(modulePath(this.name, '/server/views/' + view + '.html'), options, callback); + }; + + Module.prototype.setDefaultTemplate = function(template) { + Meanio.Singleton.template = template; + }; + + Module.prototype.routes = function() { + var args = Array.prototype.slice.call(arguments); + var that = this; + util.walk(modulePath(this.name, 'server'), 'route', 'middlewares', function(route) { + require(route).apply(that, [that].concat(args)); + }); + }; + + Module.prototype.register = function(callback) { + Meanio.Singleton.register(this.name, callback); + }; + + Module.prototype.angularDependencies = function(dependencies) { + this.angularDependencies = dependencies; + Meanio.modules[this.name].angularDependencies = dependencies; + }; + + Module.prototype.settings = function() { + + if (!arguments.length) return; + + var database = Meanio.Singleton.get('database'); + if (!database || !database.connection) { + return { + err: true, + message: 'No database connection' + }; + } + + if (!database.connection.models.Package) { + require('../modules/package')(database); + } + + var Package = database.connection.model('Package'); + if (arguments.length === 2) return updateSettings(Package, this.name, arguments[0], arguments[1]); + if (arguments.length === 1 && typeof arguments[0] === 'object') return updateSettings(Package, this.name, arguments[0], function() {}); + if (arguments.length === 1 && typeof arguments[0] === 'function') return getSettings(Package, this.name, arguments[0]); + + }; + Module.bootstrapModules = function(disabled){ + findModules(enableModules, disabled); + }; + + Meanio.prototype.Module = Module; + + function modulePath(name, plus) { + return path.join(process.cwd(), Meanio.modules[name].source, name, plus); + } + +} + +module.exports = supportModules; From 52b37a6d51e9352129aca5b495015dc4f0c71844 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Fri, 12 Sep 2014 16:46:15 +0200 Subject: [PATCH 05/21] introduced lazy mongoose model creation --- lib/config.js | 1 - lib/db.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++- lib/menu.js | 6 ++--- lib/module.js | 1 + 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/lib/config.js b/lib/config.js index ae3f778..0326312 100644 --- a/lib/config.js +++ b/lib/config.js @@ -91,7 +91,6 @@ function createConfig(Meanio){ }; Config.prototype.onPackageRead = function(callback,err,doc){ - console.log(callback,err,doc); if (err) { return callback ? callback(err) : undefined; } diff --git a/lib/db.js b/lib/db.js index 5f03551..71fff5b 100644 --- a/lib/db.js +++ b/lib/db.js @@ -1,5 +1,6 @@ var mongoose = require ('mongoose'), Q = require('q'), + Schema = mongoose.Schema, _ = require('lodash'); function filterDBAliases (v) { @@ -8,6 +9,7 @@ function filterDBAliases (v) { function applyModels (schema, model, collection, dbalias) { mongoose.get_MEANIODB_connection(dbalias).model(model, schema, collection); + return mongoose.get_MEANIODB_connection(dbalias).model(model); } function connectDb (alias, path) { @@ -44,6 +46,44 @@ function dataBasesReady (done, connection_pool) { done(); } +var lazyModelsMap = {}; + +function createModelStructure (schema, model, collection, db) { + db = db || 'default'; + if (!lazyModelsMap[db]) lazyModelsMap[db] = {}; + if (!lazyModelsMap[db][model]) lazyModelsMap[db][model] = {pre:[], post:[], virtual: [], indices: []}; + + var mc = lazyModelsMap[db][model]; + mc.collection = collection; + mc.fields = _.merge (mc.fields || {}, schema.fields); + mc.methods = _.assign (mc.methods || {}, schema.methods); + mc.statics = _.assign (mc.statics || {}, schema.statics); + + if (schema.options) mc.options = _.assign (mc.options || {}, schema.options); + if (schema.indices) Array.prototype.push (mc.indices, schema.indices); + if (schema.pre) mc.pre.push (schema.pre); + if (schema.virtual) mc.virtual.push (schema.virtual); +} + +function bindIndices (s, i) { + s.index(i); +} +function bindVirtuals (s, vr) { + for (var name in vr) { + var v = s.virtual(name); + console.log('create virtual ', name); + if (vr[name].get) v.get(vr[name].get); + if (vr[name].set) v.set(vr[name].set); + } +} + +function bindHook (s, type, rec) { + for (var name in rec) { + console.log('create hook', name); + s[type](name, rec[name]); + } +} + function supportDB(Meanio) { Meanio.connectDBs = function (defaultConfig, done) { mongoose.set('debug', defaultConfig.mongoose && defaultConfig.mongoose.debug); @@ -66,6 +106,25 @@ function supportDB(Meanio) { }); }; + Meanio.createModels = function () { + for (var db in lazyModelsMap) { + for (var model in lazyModelsMap[db]) { + var rec = lazyModelsMap[db][model]; + //console.log('for db', db,' model ',model, 'is about to be created:',rec); + var s = new Schema(rec.fields, rec.options); + s.methods = rec.methods; + s.statics = rec.statics; + rec.virtual.forEach(bindVirtuals.bind(null, s)); + rec.pre.forEach(bindHook.bind(null, s, 'pre')); + rec.post.forEach(bindHook.bind(null, s, 'post')); + rec.indices.forEach(bindIndices.bind(null, s)); + var m = applyModels(s, model, rec.collection, db); + Meanio.Singleton.events.emit ('lazy_model_ready', {model: model, db: db, model:m}); + } + } + Meanio.Singleton.events.emit('lazy_models_ready'); + } + Meanio.applyModels = function (model_register) { for (var i in model_register) { var itm = model_register[i]; @@ -78,7 +137,13 @@ function supportDB(Meanio) { if (!itm.dbs || itm.dbs.length === 0) { itm.dbs = ['default']; } - _.uniq(itm.dbs.filter(filterDBAliases)).forEach(applyModels.bind(null, itm.schema, itm.model, itm.collection)); + if (itm.schema instanceof mongoose.Schema) { + _.uniq(itm.dbs.filter(filterDBAliases)).forEach(applyModels.bind(null, itm.schema, itm.model, itm.collection)); + continue; + } + + //ok, now form structures for lazy model creation + itm.dbs.forEach( createModelStructure.bind(null, itm.schema, itm.model, itm.collection) ); } }; } diff --git a/lib/menu.js b/lib/menu.js index 2ba2dc7..b29e674 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -4,7 +4,7 @@ function supportMenus(Meanio){ function Menus() { }; Menus.prototype.add = function(options) { - console.log('adding menu',options); + //console.log('adding menu',options); if (!Array.isArray(options)) options = [options]; options.forEach(function(opt) { @@ -13,7 +13,7 @@ function supportMenus(Meanio){ allMenus[opt.menu] = allMenus[opt.menu] || []; allMenus[opt.menu].push(opt); }); - console.log('finally',allMenus); + //console.log('finally',allMenus); return this; }; @@ -38,7 +38,7 @@ function supportMenus(Meanio){ allowed.push(item); } }); - console.log('getting menu for',options,'=>',allowed); + //console.log('getting menu for',options,'=>',allowed); return allowed; }; Meanio.prototype.Menus = Menus; diff --git a/lib/module.js b/lib/module.js index 73199a4..f9287a7 100644 --- a/lib/module.js +++ b/lib/module.js @@ -89,6 +89,7 @@ function supportModules(Meanio){ Meanio.Singleton.get(name); remaining--; if (!remaining) { + Meanio.createModels(); Meanio.Singleton.events.emit('modulesEnabled'); } } From ccfe2fe59bdc1fe9ea3c5cc0ef9e3d3c4f7a3637 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Tue, 16 Sep 2014 14:03:37 +0200 Subject: [PATCH 06/21] force aggregation to respect config.debug parameter and not to minify code if debug is set --- lib/aggregation.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/aggregation.js b/lib/aggregation.js index de61da2..944704a 100644 --- a/lib/aggregation.js +++ b/lib/aggregation.js @@ -105,15 +105,14 @@ Aggregator.prototype.pushAggregatedData = function(ext, filename, data) { if (ext === 'js') { var code = this.options.global ? data.toString() + '\n' : '(function(){' + data.toString() + '})();'; - - var ugly = uglify.minify(code, { + var ugly = this.debug ? {code:code} : uglify.minify(code, { fromString: true, mangle: false }); aggregated[group][ext].weights[filename] = { weight: weight, - data: !this.debug ? ugly.code : code + data: ugly.code }; } else { group = this.options.group || 'header'; @@ -149,7 +148,8 @@ function supportAggregate(Meanio) { Meanio.aggregate(type, asset, options, Meanio.Singleton.config.clean.debug); }; - Meanio.onModulesFoundAggregate = function(ext, options, debug) { + Meanio.onModulesFoundAggregate = function(ext, options) { + var debug = Meanio.Singleton.config.clean.debug; var aggregator = new Aggregator(options, false, debug); for (var name in Meanio.modules) { aggregator.readFiles(ext, path.join(process.cwd(), Meanio.modules[name].source, name.toLowerCase(), 'public')); From e87b45246ab0443e77fed696b2f671cada149d5c Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Tue, 16 Sep 2014 14:09:00 +0200 Subject: [PATCH 07/21] introducing support for submenu items ... --- lib/menu.js | 138 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 125 insertions(+), 13 deletions(-) diff --git a/lib/menu.js b/lib/menu.js index b29e674..637ebc5 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -1,28 +1,139 @@ -var allMenus = []; +'use strict'; +var _ = require('lodash'); + +//helper functions +function extractNames (v) { + return v.name; +} + +function get_get (roles,v) { + return v.get(roles); +}; + +function remove_nulls (v) { + return v; +} + +//MenuItem class +function MenuItem (options) { + options = _.assign ({name: null, title:null, link:null, roles:null}, options); + if (!options.name) options.name = options.link; + this.name = options.name; + this.title = options.title; + this.link = options.link; + this.roles = options.roles; + this.submenus = []; +} + +MenuItem.hasRole = function (role, roles) { + return (roles.indexOf(role) > -1); +} + +MenuItem.prototype.props = function () { + return { + name: this.name, + title:this.title, + link:this.link, + roles:this.roles + }; +} + +MenuItem.prototype.findOrCreate = function (path) { + if (!path.length) return this; + var p = path.shift(); + var index = this.list().indexOf(p); + if (index > -1) return this.submenus[index].findOrCreate(path); + var n = new MenuItem(); + n.name = p; + this.submenus.push (n); + return n.findOrCreate(path); +} + +MenuItem.prototype.list = function () { + return this.submenus.map(extractNames); +} + +MenuItem.prototype.get = function (roles, path) { + roles = roles ? roles.slice() : []; + if (roles.indexOf('anonymous') < 0) { + roles.push ('authenticated'); + } + var list = this.list(); + if (path) { + if (!path.length) return this; + var n = path.shift(); + var index = list.indexOf (n); + return this.submenus[index] ? this.submenus[index].get(roles,path) : undefined; + } + + if ( + this.roles && + !MenuItem.hasRole('admin', roles) //allow admin to see all menu items ... + ) { + if (!_.intersection(this.roles, roles).length) return undefined; + } + + var ret = { + roles: this.roles || null, + link : this.link || null, + title:this.title || null, + name : this.name || null, + submenus : this.submenus.map(get_get.bind(null, roles)).filter(remove_nulls), + }; + return ret; +} + +MenuItem.prototype.add = function (mi) { + var index = this.list().indexOf(mi.name); + var itm; + if (index > -1) { + var ts = mi.props(); + itm = this.submenus[index]; + for (var i in ts) itm[i] = ts[i]; + }else{ + itm = mi; + this.submenus.push (itm); + } + return itm; +} + +var allMenus = new MenuItem (), + _ = require('lodash'); + +function mapSubmenuNames (v) { + return v.name; +} function supportMenus(Meanio){ function Menus() { }; + Menus.prototype.add = function(options) { - //console.log('adding menu',options); - if (!Array.isArray(options)) options = [options]; - - options.forEach(function(opt) { - opt.menu = opt.menu || 'main'; - opt.roles = opt.roles || ['anonymous']; - allMenus[opt.menu] = allMenus[opt.menu] || []; - allMenus[opt.menu].push(opt); - }); - //console.log('finally',allMenus); + if (options instanceof Array) { + options.forEach( Menus.prototype.add.bind(this) ); + return this; + }; + + options = _.assign({ + path : 'main', + roles: ['anonymous'], + }, + options); + options.path = options.path.replace(/^\//, ''); + var item = allMenus.findOrCreate(options.path.split('/')); + item.add(new MenuItem(options)); return this; }; Menus.prototype.get = function(options) { - var allowed = []; options = options || {}; options.menu = options.menu || 'main'; options.roles = options.roles || ['anonymous']; - options.defaultMenu = options.defaultMenu || []; + var sm = allMenus.get(options.roles, options.menu.split('/')); + var ret = sm.get(options.roles); + return ret ? ret.submenus : []; +/* + var items = options.defaultMenu.concat(allMenus[options.menu] || []); items.forEach(function(item) { @@ -40,6 +151,7 @@ function supportMenus(Meanio){ }); //console.log('getting menu for',options,'=>',allowed); return allowed; + */ }; Meanio.prototype.Menus = Menus; From 039c855514d043c2942f6676d073fb20630855c5 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Wed, 17 Sep 2014 12:26:47 +0200 Subject: [PATCH 08/21] inconsistency fixed --- lib/menu.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/menu.js b/lib/menu.js index 637ebc5..1e0cb23 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -22,7 +22,7 @@ function MenuItem (options) { this.title = options.title; this.link = options.link; this.roles = options.roles; - this.submenus = []; + this.submenus = options.submenus || []; } MenuItem.hasRole = function (role, roles) { @@ -55,7 +55,7 @@ MenuItem.prototype.list = function () { MenuItem.prototype.get = function (roles, path) { roles = roles ? roles.slice() : []; - if (roles.indexOf('anonymous') < 0) { + if (roles.indexOf('anonymous') < 0 && roles.indexOf('authenticated') < 0) { roles.push ('authenticated'); } var list = this.list(); @@ -73,14 +73,13 @@ MenuItem.prototype.get = function (roles, path) { if (!_.intersection(this.roles, roles).length) return undefined; } - var ret = { + return new MenuItem ({ roles: this.roles || null, link : this.link || null, title:this.title || null, name : this.name || null, submenus : this.submenus.map(get_get.bind(null, roles)).filter(remove_nulls), - }; - return ret; + }); } MenuItem.prototype.add = function (mi) { From 6e5873e4173887c8c3bf919efe3979e2a4ba9922 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Wed, 17 Sep 2014 13:08:31 +0200 Subject: [PATCH 09/21] defaultMenu problem fixed in order to be backward compatible --- lib/menu.js | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/menu.js b/lib/menu.js index 1e0cb23..0971b50 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -25,6 +25,21 @@ function MenuItem (options) { this.submenus = options.submenus || []; } +function mapDoStrip (v) { + return v ? v.strip() : undefined; +} + +MenuItem.prototype.strip = function () { + return { + name: this.name, + title:this.title, + link: this.link, + roles:this.roles, + submenus: this.submenus.map(mapDoStrip) + }; +} + + MenuItem.hasRole = function (role, roles) { return (roles.indexOf(role) > -1); } @@ -58,6 +73,7 @@ MenuItem.prototype.get = function (roles, path) { if (roles.indexOf('anonymous') < 0 && roles.indexOf('authenticated') < 0) { roles.push ('authenticated'); } + var list = this.list(); if (path) { if (!path.length) return this; @@ -66,11 +82,10 @@ MenuItem.prototype.get = function (roles, path) { return this.submenus[index] ? this.submenus[index].get(roles,path) : undefined; } - if ( - this.roles && - !MenuItem.hasRole('admin', roles) //allow admin to see all menu items ... - ) { - if (!_.intersection(this.roles, roles).length) return undefined; + if(!MenuItem.hasRole('admin', roles)) { + if ( this.roles) { + if (!_.intersection(this.roles, roles).length) return undefined; + } } return new MenuItem ({ @@ -128,9 +143,15 @@ function supportMenus(Meanio){ options = options || {}; options.menu = options.menu || 'main'; options.roles = options.roles || ['anonymous']; + options.defaultMenu = options.defaultMenu || []; + var sm = allMenus.get(options.roles, options.menu.split('/')); + if (!sm) { + //no menu at all + return options.defaultMenu; + } var ret = sm.get(options.roles); - return ret ? ret.submenus : []; + return ret ? options.defaultMenu.concat(ret.submenus.map(mapDoStrip)) : options.defaultMenu; /* From d28282285f3ef0924c30b7fddcf1775268e43791 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Wed, 17 Sep 2014 13:41:15 +0200 Subject: [PATCH 10/21] naming convention --- lib/bootstrap.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bootstrap.js b/lib/bootstrap.js index 69316a2..0e2c3d5 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -138,8 +138,8 @@ module.exports = function(Meanio) { httpsServer.listen(config.https.port); } var disabled = {}; - for (var i in config.disabled_modules) { - disabled[config.disabled_modules[i]] = true; + for (var i in config.disabledModules) { + disabled[config.disabledModules[i]] = true; } Meanio.Singleton.Module.bootstrapModules(disabled); From aaa1b780e348a9fb8de4b3c22828c68c53c69424 Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Sat, 20 Sep 2014 15:16:11 +0300 Subject: [PATCH 11/21] fixed jshint issues - removed anonymous function() definition within a loop, with no 'code' variable being passed to --- lib/cli.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cli.js b/lib/cli.js index 5040580..4da83ba 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -743,9 +743,7 @@ function packagesMeanJson(source) { for (var dep in data.dependencies) { shell.cd(process.cwd()); - shell.exec('node node_modules/meanio/bin/mean-install ' + dep + '@' + data.dependencies[dep], function(code) { - if (code) console.log(code); - }); + shell.exec('node node_modules/meanio/bin/mean-install ' + dep + '@' + data.dependencies[dep], console.log); } }); From 0b365b73c5a95befa643d8317c8055d2d397e404 Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Sat, 20 Sep 2014 15:22:16 +0300 Subject: [PATCH 12/21] cleaning up jshint semi-colon issues --- lib/menu.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/menu.js b/lib/menu.js index 0971b50..5fac346 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -8,7 +8,7 @@ function extractNames (v) { function get_get (roles,v) { return v.get(roles); -}; +} function remove_nulls (v) { return v; @@ -37,12 +37,12 @@ MenuItem.prototype.strip = function () { roles:this.roles, submenus: this.submenus.map(mapDoStrip) }; -} +}; MenuItem.hasRole = function (role, roles) { return (roles.indexOf(role) > -1); -} +}; MenuItem.prototype.props = function () { return { @@ -51,7 +51,7 @@ MenuItem.prototype.props = function () { link:this.link, roles:this.roles }; -} +}; MenuItem.prototype.findOrCreate = function (path) { if (!path.length) return this; @@ -62,11 +62,11 @@ MenuItem.prototype.findOrCreate = function (path) { n.name = p; this.submenus.push (n); return n.findOrCreate(path); -} +}; MenuItem.prototype.list = function () { return this.submenus.map(extractNames); -} +}; MenuItem.prototype.get = function (roles, path) { roles = roles ? roles.slice() : []; @@ -95,7 +95,7 @@ MenuItem.prototype.get = function (roles, path) { name : this.name || null, submenus : this.submenus.map(get_get.bind(null, roles)).filter(remove_nulls), }); -} +}; MenuItem.prototype.add = function (mi) { var index = this.list().indexOf(mi.name); @@ -109,7 +109,7 @@ MenuItem.prototype.add = function (mi) { this.submenus.push (itm); } return itm; -} +}; var allMenus = new MenuItem (), _ = require('lodash'); @@ -120,13 +120,13 @@ function mapSubmenuNames (v) { function supportMenus(Meanio){ function Menus() { - }; + } Menus.prototype.add = function(options) { if (options instanceof Array) { options.forEach( Menus.prototype.add.bind(this) ); return this; - }; + } options = _.assign({ path : 'main', @@ -175,6 +175,6 @@ function supportMenus(Meanio){ }; Meanio.prototype.Menus = Menus; -}; +} module.exports = supportMenus; From 9b305081881ed626d62d76549c6452269777fad8 Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Sat, 20 Sep 2014 15:25:01 +0300 Subject: [PATCH 13/21] adding javascript implicit strict --- lib/module.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/module.js b/lib/module.js index f9287a7..933a62e 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,3 +1,5 @@ +'use strict'; + var Q = require('q'), fs = require('fs'), path = require('path'), From bf8e9947445302a945757fbde1aa221faaf1e9ae Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Sat, 20 Sep 2014 15:29:21 +0300 Subject: [PATCH 14/21] cleaning up jshints issues --- lib/config.js | 6 ++++-- lib/db.js | 14 +++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/config.js b/lib/config.js index 0326312..4a6f2c3 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,3 +1,5 @@ +'use strict'; + function mergeConfig(original, saved) { var clean = {}; for (var index in saved) { @@ -61,7 +63,7 @@ function createConfig(Meanio){ } return callback(null, doc); }); - } + }; Config.prototype.getSettings = function(name, callback) { var Package = this.loadPackageModel(); @@ -77,7 +79,7 @@ function createConfig(Meanio){ } return callback(null, doc); }); - } + }; Config.prototype.loadPackageModel = function() { var database = Meanio.Singleton.get('database'); diff --git a/lib/db.js b/lib/db.js index 71fff5b..d6d0801 100644 --- a/lib/db.js +++ b/lib/db.js @@ -1,3 +1,5 @@ +'use strict'; + var mongoose = require ('mongoose'), Q = require('q'), Schema = mongoose.Schema, @@ -39,10 +41,12 @@ function dataBasesReady (done, connection_pool) { return database; } return alias_map[alias]; - } + }; + mongoose.alias_MEANIODB_exists = function (alias) { return (alias === 'default') || !alias || alias in alias_map; - } + }; + done(); } @@ -123,16 +127,16 @@ function supportDB(Meanio) { } } Meanio.Singleton.events.emit('lazy_models_ready'); - } + }; Meanio.applyModels = function (model_register) { for (var i in model_register) { var itm = model_register[i]; if (!itm.schema) { - throw "No schema in reqister model request, can not move on ..."; + throw 'No schema in reqister model request, can not move on ...'; } if (!itm.model) { - throw "No model in register model request, can not move on ..."; + throw 'No model in register model request, can not move on ...'; } if (!itm.dbs || itm.dbs.length === 0) { itm.dbs = ['default']; From 0ff02e111f24219334a34e2cd3171c695383c542 Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Sat, 20 Sep 2014 15:31:44 +0300 Subject: [PATCH 15/21] adding my own CLA to the list --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b14c569..8175a95 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,3 +35,4 @@ If you become aware of any facts or circumstances related to the representation * Taylor Thomas {thomastaylor312} * David Büttner {zloKOMAtic} * Andrija Petrovic {andrija-hers} +* Liran Tal {lirantal} \ No newline at end of file From ca2383db3c39be0b25a8d138dcedc25188e51a70 Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Sat, 20 Sep 2014 15:33:42 +0300 Subject: [PATCH 16/21] fixing spellings --- CONTRIBUTING.md | 4 ++-- lib/aggregation.js | 2 +- lib/templates/app.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8175a95..bee9711 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ The mean-cli project requires agreeing to the following CLA (contributor license agreement) for developers that would like to contribute code to the project. -We appreciate your time, energy and will to contributre, as the company behind the project - Linnovate Technologies LTD that has invested considerable time and money, we believe we need to retain rights and control over this part of the project so it can move forward with a sane governing model, and so we can not merge contributions from authors who do not accept the current CLA. +We appreciate your time, energy and will to contribute, as the company behind the project - Linnovate Technologies LTD that has invested considerable time and money, we believe we need to retain rights and control over this part of the project so it can move forward with a sane governing model, and so we can not merge contributions from authors who do not accept the current CLA. Please read the following text carefully, and if you agree to it's content: @@ -14,7 +14,7 @@ Once we merge the file you can gladly contribute to the project! ## Agreement Content -By "commiting" to this file, you ("you" or "You") hereby grant to Linnovate and to recipients of software distributed through Linnovate a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to use, make, have made, offer to sell, sell, import, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works as part of Linnovate. +By "committing" to this file, you ("you" or "You") hereby grant to Linnovate and to recipients of software distributed through Linnovate a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to use, make, have made, offer to sell, sell, import, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works as part of Linnovate. You, the contributor agree to the following… diff --git a/lib/aggregation.js b/lib/aggregation.js index d144552..fa1d485 100644 --- a/lib/aggregation.js +++ b/lib/aggregation.js @@ -148,7 +148,7 @@ function supportAggregate(Meanio) { callback(aggregated[group][ext].data); }; - // Allows redbuilding aggregated data + // Allows rebuilding aggregated data Meanio.prototype.rebuildAggregated = function() { sortAggregateAssetsByWeight(); }; diff --git a/lib/templates/app.js b/lib/templates/app.js index 082654c..b137a8f 100644 --- a/lib/templates/app.js +++ b/lib/templates/app.js @@ -42,7 +42,7 @@ __class__.register(function(app, auth, database) { 'anotherSettings': 'some value' }); - // Get settings. Retrieves latest saved settigns + // Get settings. Retrieves latest saved settings __class__.settings(function(err, settings) { //you now have the settings object }); From e30494f450b46be15f0d5556020f216ef9d5e9ba Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Sun, 21 Sep 2014 23:36:49 +0200 Subject: [PATCH 17/21] few details and few comments --- lib/bootstrap.js | 6 +----- lib/db.js | 1 + lib/module.js | 8 +++++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/bootstrap.js b/lib/bootstrap.js index 0e2c3d5..d2629c5 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -137,12 +137,8 @@ module.exports = function(Meanio) { Meanio.Singleton.register('https', httpsServer); httpsServer.listen(config.https.port); } - var disabled = {}; - for (var i in config.disabledModules) { - disabled[config.disabledModules[i]] = true; - } - Meanio.Singleton.Module.bootstrapModules(disabled); + Meanio.Singleton.Module.bootstrapModules(); Meanio.Singleton.aggregate('js', null); Meanio.Singleton.name = config.app.name; diff --git a/lib/db.js b/lib/db.js index 71fff5b..f02409e 100644 --- a/lib/db.js +++ b/lib/db.js @@ -138,6 +138,7 @@ function supportDB(Meanio) { itm.dbs = ['default']; } if (itm.schema instanceof mongoose.Schema) { + ///filter out eventual duplicates in dbs array and nonexisting aliases as well _.uniq(itm.dbs.filter(filterDBAliases)).forEach(applyModels.bind(null, itm.schema, itm.model, itm.collection)); continue; } diff --git a/lib/module.js b/lib/module.js index f9287a7..4270ba6 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,5 +1,6 @@ var Q = require('q'), fs = require('fs'), + _ = require('lodash'), path = require('path'), util = require('./util'); @@ -27,7 +28,8 @@ function supportModules(Meanio){ return deferred.promise; } - function findModules(callback, disabled) { + function findModules(callback) { + var disabled = _.toArray(Meanio.Singleton.config.clean); Q.all([searchSourceForFindModules(disabled, 'node_modules'), searchSourceForFindModules(disabled, 'packages'),searchSourceForFindModules(disabled, 'packages/core'),searchSourceForFindModules(disabled, 'packages/custom'),searchSourceForFindModules(disabled, 'packages/contrib')]) .done(findModulesDone.bind(null, callback), findModulesError.bind(null, callback)); } @@ -158,8 +160,8 @@ function supportModules(Meanio){ if (arguments.length === 1 && typeof arguments[0] === 'function') return getSettings(Package, this.name, arguments[0]); }; - Module.bootstrapModules = function(disabled){ - findModules(enableModules, disabled); + Module.bootstrapModules = function(){ + findModules(enableModules); }; Meanio.prototype.Module = Module; From 9597bcad729150ddf82acf6230f57fb85bc5900b Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Mon, 22 Sep 2014 12:07:20 +0200 Subject: [PATCH 18/21] fixing some errors ... --- lib/bootstrap.js | 2 -- lib/cli.js | 2 +- lib/config.js | 2 +- lib/db.js | 6 +++--- lib/mean.js | 3 +-- lib/menu.js | 36 +++++++++++------------------------- lib/module.js | 33 +++++++++++++++++++++++++++++++++ test/lib/mean_test.js | 23 ++++++++++++++++++++--- 8 files changed, 70 insertions(+), 37 deletions(-) diff --git a/lib/bootstrap.js b/lib/bootstrap.js index d2629c5..ce34004 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -6,8 +6,6 @@ var express = require('express'), fs = require('fs'), http = require('http'), https = require('https'), - Q = require('q'), - path = require('path'), appPath = process.cwd(); diff --git a/lib/cli.js b/lib/cli.js index 4da83ba..8821236 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -607,7 +607,7 @@ function getPackage(name, callback) { }); } -var install = exports.install = function(module, options) { +exports.install = function(module, options) { requiresRoot(function() { loadPackageJson('./node_modules/meanio/package.json', function(err, data) { diff --git a/lib/config.js b/lib/config.js index 4a6f2c3..662a4c7 100644 --- a/lib/config.js +++ b/lib/config.js @@ -20,7 +20,7 @@ function mergeConfig(original, saved) { } function createConfig(Meanio){ - function Config(defaultconfig, callback) { + function Config(defaultconfig) { this.verbose = {}; this.original = JSON.flatten(defaultconfig, { default: true diff --git a/lib/db.js b/lib/db.js index dede685..bdf91fd 100644 --- a/lib/db.js +++ b/lib/db.js @@ -30,7 +30,7 @@ function connectDbFailed(defer, s) { defer.reject(s); } -function dataBasesReady (done, connection_pool) { +function dataBasesReady (done, database, connection_pool) { var alias_map = {}; for (var i in connection_pool) { if (connection_pool[i].state !== 'fulfilled') continue; @@ -106,7 +106,7 @@ function supportDB(Meanio) { for (var i in defaultConfig.dbs) { db_promises.push (connectDb(i, defaultConfig.dbs[i])); } - Q.allSettled (db_promises).then(dataBasesReady.bind(this, done)); + Q.allSettled (db_promises).then(dataBasesReady.bind(this, done, database)); }); }; @@ -123,7 +123,7 @@ function supportDB(Meanio) { rec.post.forEach(bindHook.bind(null, s, 'post')); rec.indices.forEach(bindIndices.bind(null, s)); var m = applyModels(s, model, rec.collection, db); - Meanio.Singleton.events.emit ('lazy_model_ready', {model: model, db: db, model:m}); + Meanio.Singleton.events.emit ('lazy_model_ready', {model: m, db: db}); } } Meanio.Singleton.events.emit('lazy_models_ready'); diff --git a/lib/mean.js b/lib/mean.js index 7168444..3204ad4 100644 --- a/lib/mean.js +++ b/lib/mean.js @@ -1,7 +1,6 @@ 'use strict'; -var swig = require('swig'), - container = require('dependable').container(), +var container = require('dependable').container(), util = require('./util'), EventEmitter = require('events').EventEmitter; diff --git a/lib/menu.js b/lib/menu.js index 5fac346..c639834 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -17,7 +17,7 @@ function remove_nulls (v) { //MenuItem class function MenuItem (options) { options = _.assign ({name: null, title:null, link:null, roles:null}, options); - if (!options.name) options.name = options.link; + options.name = options.name || options.link || options.title; this.name = options.name; this.title = options.title; this.link = options.link; @@ -113,20 +113,26 @@ MenuItem.prototype.add = function (mi) { var allMenus = new MenuItem (), _ = require('lodash'); +function Menus() { +} -function mapSubmenuNames (v) { - return v.name; +function arguments_menu_items_processor (instance, item) { + Menus.prototype.add.call(instance, item); } function supportMenus(Meanio){ - function Menus() { - } + Menus.prototype.add = function(options) { + if (arguments.length === 0) return this; if (options instanceof Array) { options.forEach( Menus.prototype.add.bind(this) ); return this; } + if (arguments.length > 1) { + Array.prototype.forEach.call(arguments, arguments_menu_items_processor.bind(null, this)); + return this; + } options = _.assign({ path : 'main', @@ -152,26 +158,6 @@ function supportMenus(Meanio){ } var ret = sm.get(options.roles); return ret ? options.defaultMenu.concat(ret.submenus.map(mapDoStrip)) : options.defaultMenu; -/* - - - var items = options.defaultMenu.concat(allMenus[options.menu] || []); - items.forEach(function(item) { - - var hasRole = false; - options.roles.forEach(function(role) { - if (role === 'admin' || item.roles.indexOf('anonymous') !== -1 || item.roles.indexOf(role) !== -1) { - hasRole = true; - } - }); - - if (hasRole) { - allowed.push(item); - } - }); - //console.log('getting menu for',options,'=>',allowed); - return allowed; - */ }; Meanio.prototype.Menus = Menus; diff --git a/lib/module.js b/lib/module.js index 73b9981..07a42b2 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,6 +1,7 @@ 'use strict'; var Q = require('q'), + swig = require('swig'), fs = require('fs'), _ = require('lodash'), path = require('path'), @@ -139,6 +140,38 @@ function supportModules(Meanio){ this.angularDependencies = dependencies; Meanio.modules[this.name].angularDependencies = dependencies; }; + function updateSettings(Package, name, settings, callback) { + Package.findOneAndUpdate({ + name: name + }, { + $set: { + settings: settings, + updated: new Date() + } + }, { + upsert: true, + multi: false + }, function(err, doc) { + if (err) { + console.log(err); + return callback(true, 'Failed to update settings'); + } + return callback(null, doc); + }); + } + + function getSettings(Package, name, callback) { + Package.findOne({ + name: name + }, function(err, doc) { + if (err) { + console.log(err); + return callback(true, 'Failed to retrieve settings'); + } + return callback(null, doc); + }); + } + Module.prototype.settings = function() { diff --git a/test/lib/mean_test.js b/test/lib/mean_test.js index 48d27e6..76d8154 100644 --- a/test/lib/mean_test.js +++ b/test/lib/mean_test.js @@ -18,16 +18,21 @@ describe('mean.js', function() { it('creates an empty menu array', function() { menus.get().should.be.an.Array.and.be.empty; }); + it('adds a menu', function() { menus.add({ title: 'test' }); + menus.get().should.eql([{ title: 'test', - menu: 'main', - roles: ['anonymous'] + name: 'test', + link:null, + roles: ['anonymous'], + submenus:[], }]); }); + it('adds 2 menus', function() { menus.get().should.be.an.Array.and.have.length(1); // 1 anonymous menus.add({ @@ -37,9 +42,21 @@ describe('mean.js', function() { title: 'mocha', roles: ['mocha'] }); + + //since user is authenticated, the menu for anonymous wont be shown to him menus.get({ roles: ['authenticated'] - }).should.be.an.Array.and.have.length(2); // anonymous, authenticated + }).should.be.an.Array.and.have.length(1); //authenticated + + menus.get({ + roles: ['mocha'] + }).should.be.an.Array.and.have.length(2); //authenticated and mocha + }); + + //anonymous is anonymous, authenticated is authenticated ... If you need a submenu + //item for everybody, add submenu item for role 'all' + it('add menu item for ALL users', function () { + menus.get().should.be.an.Array.and.have.length(1); //anonymous }); it('properly weight js footer menus', function(done) { From 6fbcc503bedd6c1e14ff3f5d9e67c696ca82a7d7 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Mon, 22 Sep 2014 13:02:11 +0200 Subject: [PATCH 19/21] introduced implicit role named "all", test updated --- lib/menu.js | 3 ++- test/lib/mean_test.js | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/menu.js b/lib/menu.js index c639834..6a7c205 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -73,6 +73,7 @@ MenuItem.prototype.get = function (roles, path) { if (roles.indexOf('anonymous') < 0 && roles.indexOf('authenticated') < 0) { roles.push ('authenticated'); } + if (roles.indexOf('all') < 0) roles.push('all'); var list = this.list(); if (path) { @@ -83,7 +84,7 @@ MenuItem.prototype.get = function (roles, path) { } if(!MenuItem.hasRole('admin', roles)) { - if ( this.roles) { + if (this.roles) { if (!_.intersection(this.roles, roles).length) return undefined; } } diff --git a/test/lib/mean_test.js b/test/lib/mean_test.js index 76d8154..66711a9 100644 --- a/test/lib/mean_test.js +++ b/test/lib/mean_test.js @@ -57,6 +57,14 @@ describe('mean.js', function() { //item for everybody, add submenu item for role 'all' it('add menu item for ALL users', function () { menus.get().should.be.an.Array.and.have.length(1); //anonymous + menus.add({ + title:'check_all', + roles:['all'] + }); + + menus.get().should.be.an.Array.and.have.length(2); //anonymous and all + menus.get({roles:['mocha']}).should.be.an.Array.and.have.length(3); //authenticated, mocha and all + menus.get({roles:['authenticated']}).should.be.an.Array.and.have.length(2); //authenticated and all }); it('properly weight js footer menus', function(done) { From dac84e0a627fc4846ff65195b0511c037c289d09 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Mon, 22 Sep 2014 13:34:28 +0200 Subject: [PATCH 20/21] menu item for "all" can be specified as null in add_menu as well --- test/lib/mean_test.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/lib/mean_test.js b/test/lib/mean_test.js index 66711a9..43a9d7f 100644 --- a/test/lib/mean_test.js +++ b/test/lib/mean_test.js @@ -65,6 +65,16 @@ describe('mean.js', function() { menus.get().should.be.an.Array.and.have.length(2); //anonymous and all menus.get({roles:['mocha']}).should.be.an.Array.and.have.length(3); //authenticated, mocha and all menus.get({roles:['authenticated']}).should.be.an.Array.and.have.length(2); //authenticated and all + + menus.add({ + title:'check_all2', + roles:null + }); + + menus.get().should.be.an.Array.and.have.length(3); //anonymous and all + menus.get({roles:['mocha']}).should.be.an.Array.and.have.length(4); //authenticated, mocha and all + menus.get({roles:['authenticated']}).should.be.an.Array.and.have.length(3); //authenticated and all + }); it('properly weight js footer menus', function(done) { From ece912a8893e8e7120ca603060ab56a6bdeb5198 Mon Sep 17 00:00:00 2001 From: Veljko Popovic Date: Mon, 22 Sep 2014 14:04:41 +0200 Subject: [PATCH 21/21] link as name? remove / from link string --- lib/menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/menu.js b/lib/menu.js index 6a7c205..cbaff19 100644 --- a/lib/menu.js +++ b/lib/menu.js @@ -17,7 +17,7 @@ function remove_nulls (v) { //MenuItem class function MenuItem (options) { options = _.assign ({name: null, title:null, link:null, roles:null}, options); - options.name = options.name || options.link || options.title; + options.name = options.name || (options.link ? options.link.replace('/','_') : undefined) || options.title; this.name = options.name; this.title = options.title; this.link = options.link;