From 14a14d378565d0a9c09a65fa63fad463a88572cf Mon Sep 17 00:00:00 2001 From: oleg Date: Wed, 6 Jan 2016 21:14:25 +0300 Subject: [PATCH] small projects collection api fixes + doc for it --- app.js | 2 +- distributor.js | 2 +- .../developing-plugins/projects-collection.md | 70 +++++++++ lib/project.js | 142 ++++++++++++------ package.json | 2 + resources/projects.js | 12 +- 6 files changed, 177 insertions(+), 53 deletions(-) create mode 100644 docs/developing-plugins/projects-collection.md diff --git a/app.js b/app.js index c42dbcb..64de59c 100644 --- a/app.js +++ b/app.js @@ -243,7 +243,7 @@ Steppy( app.projects.loadAll(this.slot()); }, function(err) { - logger.log('Loaded projects: ', app.projects.pluck('name')); + logger.log('Loaded projects: ', _(app.projects.getAll()).pluck('name')); var host = app.config.http.host, port = app.config.http.port; diff --git a/distributor.js b/distributor.js index 7fda602..b1b4174 100644 --- a/distributor.js +++ b/distributor.js @@ -88,7 +88,7 @@ exports.init = function(app, callback) { // related stat (last build date, avg build time, etc) if (changes.completed) { var projectsResource = app.dataio.resource('projects'); - projectsResource.clientEmitSyncChange({name: build.project.name}); + projectsResource.clientEmitSyncChange(build.project.name); } buildsResource.clientEmitSync('change', { diff --git a/docs/developing-plugins/projects-collection.md b/docs/developing-plugins/projects-collection.md new file mode 100644 index 0000000..ae0b0be --- /dev/null +++ b/docs/developing-plugins/projects-collection.md @@ -0,0 +1,70 @@ + - [ProjectsCollection()](#projectscollection) + - [ProjectsCollection.validateConfig():Function)](#projectscollectionvalidateconfigconfigobjectcallbackerrconfigfunction) + - [ProjectsCollection.load()]:Function)](#projectscollectionloadnamestringcallbackerrfunction) + - [ProjectsCollection.loadAll()]:Function)](#projectscollectionloadallcallbackerrfunction) + - [ProjectsCollection.unload()]:Function)](#projectscollectionunloadnamestringcallbackerrfunction) + - [ProjectsCollection.get()](#projectscollectiongetnamestring) + - [ProjectsCollection.getAll()](#projectscollectiongetall) + - [ProjectsCollection.filter()](#projectscollectionfilterpredicatefunction) + - [ProjectsCollection.getAvgBuildDuration():Function)](#projectscollectiongetavgbuilddurationnamestringcallbackerrdurationfunction) + - [ProjectsCollection.remove()]:Function)](#projectscollectionremovenamestringcallbackerrfunction) + - [ProjectsCollection.rename()]:Function)](#projectscollectionrenamenamestringcallbackerrfunction) + +## ProjectsCollection() + + Projects collection contains all currently loaded projects and provides + operations for manipulating with them. + All projects stored on disk in `baseDir` and loaded to memory so + they can be received (by `get`, `getAll` and other methods) in a sync way. + Note that id for the particular project is a `name` of that project. + +## ProjectsCollection.validateConfig(config:Object, callback(err,config):Function) + + Validate and return given config + +## ProjectsCollection.load(name:String, [callback(err)]:Function) + + Load project to collection. + `projectLoaded` event with loaded config as argument will be emitted after + load. + +## ProjectsCollection.loadAll([callback(err)]:Function) + + Load all projects (from `this.baseDir`). + Calls `load` for every project in a base dir. + +## ProjectsCollection.unload(name:String, [callback(err)]:Function) + + Unload project from collection + `projectUnloaded` event with unloaded config as argument will be emitted + after unload. + +## ProjectsCollection.get(name:String) + + Get project config by name. + Returns config object or undefined if project is not found. + +## ProjectsCollection.getAll() + + Get configs for all currently loaded projects. + Returns array of config objects. + +## ProjectsCollection.filter(predicate:Function) + + Get project configs which match to predicate. + Returns array of config objects or empty array if there is no matched + project. + +## ProjectsCollection.getAvgBuildDuration(name:String, callback(err,duration):Function) + + Calculates average build duration (in ms) for the given project + +## ProjectsCollection.remove(name:String, [callback(err)]:Function) + + Remove project by name. + Calls `unload`, removes project from disk and db. + +## ProjectsCollection.rename(name:String, [callback(err)]:Function) + + Rename project. + Renames project on disk and db, also changes name for loaded project. diff --git a/lib/project.js b/lib/project.js index d93eb17..3137531 100644 --- a/lib/project.js +++ b/lib/project.js @@ -10,11 +10,12 @@ var Steppy = require('twostep').Steppy, EventEmitter = require('events').EventEmitter, inherits = require('util').inherits; -/* - * Projects collection it's something similar to backbone collection. - * But contrasting to backbone there is no model of a single project, when you - * receive project from collection you just get a json. - * General id for the particular project is a `name` of that project. +/** + * Projects collection contains all currently loaded projects and provides + * operations for manipulating with them. + * All projects stored on disk in `baseDir` and loaded to memory so + * they can be received (by `get`, `getAll` and other methods) in a sync way. + * Note that id for the particular project is a `name` of that project. */ function ProjectsCollection(params) { this.db = params.db; @@ -28,7 +29,10 @@ exports.ProjectsCollection = ProjectsCollection; inherits(ProjectsCollection, EventEmitter); /** - * Validates and returns given `config` to the `callback`(err, config) + * Validate and return given config. + * + * @param {Object} config + * @param {Function} callback(err,config) */ ProjectsCollection.prototype.validateConfig = function(config, callback) { Steppy( @@ -78,7 +82,7 @@ ProjectsCollection.prototype.validateConfig = function(config, callback) { ProjectsCollection.prototype._getProjectPath = function(name) { return path.join(this.baseDir, name); -} +}; ProjectsCollection.prototype._loadConfig = function(dir, callback) { var self = this; @@ -102,7 +106,7 @@ ProjectsCollection.prototype._loadConfig = function(dir, callback) { }); } - // apply defaults to not yet validated config + // apply defaults _(config.steps).each(function(step) { if (!step.type) step.type = 'shell'; if (!step.name && step.cmd) step.name = utils.prune(step.cmd, 40); @@ -115,14 +119,26 @@ ProjectsCollection.prototype._loadConfig = function(dir, callback) { }; /** - * Loads project to collection + * Load project to collection. + * `projectLoaded` event with loaded config as argument will be emitted after + * load. + * + * @param {String} name + * @param {Function} [callback(err)] */ ProjectsCollection.prototype.load = function(name, callback) { + callback = callback || _.noop; var self = this, dir = self._getProjectPath(name); Steppy( function() { + if (self.get(name)) { + throw new Error( + 'Can`t load already loaded project "' + name + '"' + ); + } + self._loadConfig(dir, this.slot()); }, function(err, config) { @@ -140,6 +156,38 @@ ProjectsCollection.prototype.load = function(name, callback) { ); }; +/** + * Load all projects (from `this.baseDir`). + * Calls `load` for every project in a base dir. + * + * @param {Function} [callback(err)] + */ +ProjectsCollection.prototype.loadAll = function(callback) { + callback = callback || _.noop; + var self = this; + + Steppy( + function() { + fs.readdir(self.baseDir, this.slot()); + }, + function(err, dirs) { + var loadGroup = this.makeGroup(); + _(dirs).each(function(dir) { + self.load(dir, loadGroup.slot()); + }); + }, + callback + ); +}; + +/** + * Unload project from collection + * `projectUnloaded` event with unloaded config as argument will be emitted + * after unload. + * + * @param {String} name + * @param {Function} [callback(err)] + */ ProjectsCollection.prototype.unload = function(name, callback) { callback = callback || _.noop; var self = this; @@ -163,52 +211,40 @@ ProjectsCollection.prototype.unload = function(name, callback) { ); }; +/** + * Get project config by name. + * Returns config object or undefined if project is not found. + * + * @param {String} name + */ ProjectsCollection.prototype.get = function(name) { return _(this.configs).findWhere({name: name}); }; -ProjectsCollection.prototype.getAll = function(name) { +/** + * Get configs for all currently loaded projects. + * Returns array of config objects. + */ +ProjectsCollection.prototype.getAll = function() { return this.configs; }; -ProjectsCollection.prototype.findWhere = function(params) { - return _(this.configs).findWhere(params); -}; - -ProjectsCollection.prototype.where = function(params) { - return _(this.configs).where(params); -}; - -ProjectsCollection.prototype.filter = function(iterator) { - return _(this.configs).filter(iterator); -}; - -ProjectsCollection.prototype.pluck = function(attribute) { - return _(this.configs).pluck(attribute); +/** + * Get project configs which match to predicate. + * Returns array of config objects or empty array if there is no matched + * project. + * + * @param {Function} predicate + */ +ProjectsCollection.prototype.filter = function(predicate) { + return _(this.configs).filter(predicate); }; /** - * Loads all projects (from `this.baseDir`) - */ -ProjectsCollection.prototype.loadAll = function(callback) { - var self = this; - - Steppy( - function() { - fs.readdir(self.baseDir, this.slot()); - }, - function(err, dirs) { - var loadGroup = this.makeGroup(); - _(dirs).each(function(dir) { - self.load(dir, loadGroup.slot()); - }); - }, - callback - ); -}; - -/* - * Calculates average build duration for the given project + * Calculate average build duration (in ms) for the given project. + * + * @param {String} name + * @param {Function} callback(err,duration) */ ProjectsCollection.prototype.getAvgBuildDuration = function(name, callback) { var self = this; @@ -236,7 +272,15 @@ ProjectsCollection.prototype.getAvgBuildDuration = function(name, callback) { ); }; +/** + * Remove project by name. + * Calls `unload`, removes project from disk and db. + * + * @param {String} name + * @param {Function} [callback(err)] + */ ProjectsCollection.prototype.remove = function(name, callback) { + callback = callback || _.noop; var self = this; Steppy( @@ -269,7 +313,15 @@ ProjectsCollection.prototype.remove = function(name, callback) { ); }; +/** + * Rename project. + * Renames project on disk and db, also changes name for loaded project. + * + * @param {String} name + * @param {Function} [callback(err)] + */ ProjectsCollection.prototype.rename = function(name, newName, callback) { + callback = callback || _.noop; var self = this; Steppy( diff --git a/package.json b/package.json index 5e19dc6..b2600f6 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "test": "npm run makeTestRepos && mocha --bail --reporter=spec --timeout 10000", "dev": "gulp", "sync": "npm install && npm prune && bower install && bower prune", + "docProjectsCollection": "dox --api --skipSingleStar < lib/project.js > docs/developing-plugins/projects-collection.md", "buildJs": "r.js -o static/js/requirejs/buid.js", "buildClean": "rm static/index.html", "buildHtml": "jade views/index.jade --obj '{\"env\": \"production\"}' -o static/", @@ -56,6 +57,7 @@ }, "devDependencies": { "bower": "1.4.1", + "dox": "0.8.0", "expect.js": "0.3.1", "gulp": "3.8.11", "gulp-less": "3.0.3", diff --git a/resources/projects.js b/resources/projects.js index 3dd4a79..2957752 100644 --- a/resources/projects.js +++ b/resources/projects.js @@ -31,11 +31,11 @@ module.exports = function(app) { res.send(filteredProjects); }); - var getProject = function(params, callback) { + var getProject = function(name, callback) { var project; Steppy( function() { - project = app.projects.findWhere(params.condition); + project = app.projects.get(name); app.projects.getAvgBuildDuration(project.name, this.slot()); @@ -84,12 +84,12 @@ module.exports = function(app) { ); }; - // resource custom method which finds project by condition + // resource custom method which finds project by name // and emits event about it change to clients - resource.clientEmitSyncChange = function(condition) { + resource.clientEmitSyncChange = function(name) { Steppy( function() { - getProject({condition: condition}, this.slot()); + getProject(name, this.slot()); }, function(err, project) { resource.clientEmitSync('change', {project: project}); @@ -106,7 +106,7 @@ module.exports = function(app) { resource.use('read', function(req, res) { Steppy( function() { - getProject({condition: req.data}, this.slot()); + getProject(req.data.name, this.slot()); }, function(err, project) { res.send(project);