small projects collection api fixes + doc for it

This commit is contained in:
oleg 2016-01-06 21:14:25 +03:00
parent 7dd920ce28
commit 14a14d3785
6 changed files with 177 additions and 53 deletions

2
app.js
View File

@ -243,7 +243,7 @@ Steppy(
app.projects.loadAll(this.slot()); app.projects.loadAll(this.slot());
}, },
function(err) { 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, var host = app.config.http.host,
port = app.config.http.port; port = app.config.http.port;

View File

@ -88,7 +88,7 @@ exports.init = function(app, callback) {
// related stat (last build date, avg build time, etc) // related stat (last build date, avg build time, etc)
if (changes.completed) { if (changes.completed) {
var projectsResource = app.dataio.resource('projects'); var projectsResource = app.dataio.resource('projects');
projectsResource.clientEmitSyncChange({name: build.project.name}); projectsResource.clientEmitSyncChange(build.project.name);
} }
buildsResource.clientEmitSync('change', { buildsResource.clientEmitSync('change', {

View File

@ -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.

View File

@ -10,11 +10,12 @@ var Steppy = require('twostep').Steppy,
EventEmitter = require('events').EventEmitter, EventEmitter = require('events').EventEmitter,
inherits = require('util').inherits; inherits = require('util').inherits;
/* /**
* Projects collection it's something similar to backbone collection. * Projects collection contains all currently loaded projects and provides
* But contrasting to backbone there is no model of a single project, when you * operations for manipulating with them.
* receive project from collection you just get a json. * All projects stored on disk in `baseDir` and loaded to memory so
* General id for the particular project is a `name` of that project. * 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) { function ProjectsCollection(params) {
this.db = params.db; this.db = params.db;
@ -28,7 +29,10 @@ exports.ProjectsCollection = ProjectsCollection;
inherits(ProjectsCollection, EventEmitter); 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) { ProjectsCollection.prototype.validateConfig = function(config, callback) {
Steppy( Steppy(
@ -78,7 +82,7 @@ ProjectsCollection.prototype.validateConfig = function(config, callback) {
ProjectsCollection.prototype._getProjectPath = function(name) { ProjectsCollection.prototype._getProjectPath = function(name) {
return path.join(this.baseDir, name); return path.join(this.baseDir, name);
} };
ProjectsCollection.prototype._loadConfig = function(dir, callback) { ProjectsCollection.prototype._loadConfig = function(dir, callback) {
var self = this; 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) { _(config.steps).each(function(step) {
if (!step.type) step.type = 'shell'; if (!step.type) step.type = 'shell';
if (!step.name && step.cmd) step.name = utils.prune(step.cmd, 40); 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) { ProjectsCollection.prototype.load = function(name, callback) {
callback = callback || _.noop;
var self = this, var self = this,
dir = self._getProjectPath(name); dir = self._getProjectPath(name);
Steppy( Steppy(
function() { function() {
if (self.get(name)) {
throw new Error(
'Can`t load already loaded project "' + name + '"'
);
}
self._loadConfig(dir, this.slot()); self._loadConfig(dir, this.slot());
}, },
function(err, config) { 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) { ProjectsCollection.prototype.unload = function(name, callback) {
callback = callback || _.noop; callback = callback || _.noop;
var self = this; 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) { ProjectsCollection.prototype.get = function(name) {
return _(this.configs).findWhere({name: 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; return this.configs;
}; };
ProjectsCollection.prototype.findWhere = function(params) { /**
return _(this.configs).findWhere(params); * Get project configs which match to predicate.
}; * Returns array of config objects or empty array if there is no matched
* project.
ProjectsCollection.prototype.where = function(params) { *
return _(this.configs).where(params); * @param {Function} predicate
}; */
ProjectsCollection.prototype.filter = function(predicate) {
ProjectsCollection.prototype.filter = function(iterator) { return _(this.configs).filter(predicate);
return _(this.configs).filter(iterator);
};
ProjectsCollection.prototype.pluck = function(attribute) {
return _(this.configs).pluck(attribute);
}; };
/** /**
* Loads all projects (from `this.baseDir`) * Calculate average build duration (in ms) for the given project.
*/ *
ProjectsCollection.prototype.loadAll = function(callback) { * @param {String} name
var self = this; * @param {Function} callback(err,duration)
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
*/ */
ProjectsCollection.prototype.getAvgBuildDuration = function(name, callback) { ProjectsCollection.prototype.getAvgBuildDuration = function(name, callback) {
var self = this; 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) { ProjectsCollection.prototype.remove = function(name, callback) {
callback = callback || _.noop;
var self = this; var self = this;
Steppy( 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) { ProjectsCollection.prototype.rename = function(name, newName, callback) {
callback = callback || _.noop;
var self = this; var self = this;
Steppy( Steppy(

View File

@ -10,6 +10,7 @@
"test": "npm run makeTestRepos && mocha --bail --reporter=spec --timeout 10000", "test": "npm run makeTestRepos && mocha --bail --reporter=spec --timeout 10000",
"dev": "gulp", "dev": "gulp",
"sync": "npm install && npm prune && bower install && bower prune", "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", "buildJs": "r.js -o static/js/requirejs/buid.js",
"buildClean": "rm static/index.html", "buildClean": "rm static/index.html",
"buildHtml": "jade views/index.jade --obj '{\"env\": \"production\"}' -o static/", "buildHtml": "jade views/index.jade --obj '{\"env\": \"production\"}' -o static/",
@ -56,6 +57,7 @@
}, },
"devDependencies": { "devDependencies": {
"bower": "1.4.1", "bower": "1.4.1",
"dox": "0.8.0",
"expect.js": "0.3.1", "expect.js": "0.3.1",
"gulp": "3.8.11", "gulp": "3.8.11",
"gulp-less": "3.0.3", "gulp-less": "3.0.3",

View File

@ -31,11 +31,11 @@ module.exports = function(app) {
res.send(filteredProjects); res.send(filteredProjects);
}); });
var getProject = function(params, callback) { var getProject = function(name, callback) {
var project; var project;
Steppy( Steppy(
function() { function() {
project = app.projects.findWhere(params.condition); project = app.projects.get(name);
app.projects.getAvgBuildDuration(project.name, this.slot()); 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 // and emits event about it change to clients
resource.clientEmitSyncChange = function(condition) { resource.clientEmitSyncChange = function(name) {
Steppy( Steppy(
function() { function() {
getProject({condition: condition}, this.slot()); getProject(name, this.slot());
}, },
function(err, project) { function(err, project) {
resource.clientEmitSync('change', {project: project}); resource.clientEmitSync('change', {project: project});
@ -106,7 +106,7 @@ module.exports = function(app) {
resource.use('read', function(req, res) { resource.use('read', function(req, res) {
Steppy( Steppy(
function() { function() {
getProject({condition: req.data}, this.slot()); getProject(req.data.name, this.slot());
}, },
function(err, project) { function(err, project) {
res.send(project); res.send(project);