Comparing version 0.2.0 to 0.2.1
var INHERIT = require('inherit'); | ||
module.exports = INHERIT({ | ||
module.exports = INHERIT(/** @lends APW.prototype */ { | ||
@@ -8,11 +8,9 @@ /** | ||
* | ||
* @constructor | ||
* @class APW | ||
* @constructs | ||
* @param {Arch} arch The Arch to work with. | ||
* @param {number} [maxWorkers] Maximum number of workers to run in one time. | ||
* @param {object} [ctx] The hash to mix with default context in node 'run' function. | ||
* @param {Number} [maxWorkers] Maximum number of workers to run simultaneously. | ||
* @param {Object} [ctx] The hash to mix with default context in node 'run' function. | ||
*/ | ||
__constructor: function(arch /*, maxWorkers, ctx*/) { | ||
var maxWorkers = arguments[1], | ||
ctx = arguments[2]; | ||
__constructor: function(arch, maxWorkers, ctx) { | ||
this.arch = arch; | ||
@@ -33,4 +31,4 @@ | ||
* | ||
* @param {string[]|string} targets IDs (or ID) of jobs to process. | ||
* @returns {promise} Promise of this process. | ||
* @param {String[]|String} targets IDs (or ID) of jobs to process. | ||
* @returns {Promise * Undefined} Promise of this process to complete. | ||
*/ | ||
@@ -41,9 +39,16 @@ process: function(targets) { | ||
}, { | ||
}, /** @lends APW */ { | ||
/** @type COA.api */ | ||
api: require('./coa').api, | ||
/** @type Arch */ | ||
Arch: require('./arch'), | ||
/** @type Plan */ | ||
Plan: require('./plan'), | ||
/** @type Workers */ | ||
Workers: require('./workers') | ||
}); |
297
lib/arch.js
@@ -5,8 +5,7 @@ var Q = require('qq'), | ||
Plan = require('./plan'), | ||
util = require('./util'), | ||
arraySlice = Array.prototype.slice, | ||
U = require('./util'), | ||
nodeIDSequence = 1; | ||
arraySlice = Array.prototype.slice; | ||
module.exports = INHERIT({ | ||
module.exports = INHERIT(/** @lends Arch.prototype */ { | ||
@@ -16,3 +15,4 @@ /** | ||
* | ||
* @constructor | ||
* @class Arch | ||
* @constructs | ||
*/ | ||
@@ -73,3 +73,3 @@ __constructor: function() { | ||
* @param {Object} context Call context. | ||
* @returns {promise} The result of Q.when().fin(). | ||
* @returns {Promise}. | ||
*/ | ||
@@ -86,45 +86,138 @@ withLock: function(cb, context) { | ||
/** | ||
* Add new node. | ||
* If id argument is absent, autogenerated ID will be used. | ||
* Return node by ID. | ||
* If there is no such node, return default node. | ||
* | ||
* @param {String} [id] Node ID. | ||
* @param {Object} node Node to add. | ||
* @param {String[]|String} [parents] Parent node IDs to link with. | ||
* @param {String[]|String} [children] Child node IDs to link with. | ||
* @returns {String} Node ID. | ||
* @param {String} id Node id | ||
* @returns {Object} Node object | ||
*/ | ||
setNode: function(id, node, parents, children) { | ||
if (id && typeof id !== 'string') { | ||
children = parents; | ||
parents = node; | ||
node = id; | ||
id = null; | ||
} | ||
if (!id) id = (node.getId && node.getId())? node.getId() : nodeIDSequence++; | ||
getNode: function(id) { | ||
return this.hasNode(id)? this.nodes[id] : this.getDefaultNode(id); | ||
}, | ||
this._assertNode('setNode call', id, parents, children); | ||
/** | ||
* Create and return default node with specified ID. | ||
* | ||
* Returned node does not belong to this Arch. | ||
* It will return rejected promise on run() call. | ||
* | ||
* @param {String} id ID of node to create. | ||
* @returns {Object} Default node object | ||
*/ | ||
getDefaultNode: function(id) { | ||
return { | ||
getId: function() { | ||
return id; | ||
}, | ||
run: function() { | ||
return Q.reject("** No rule to make target '" + id + "'"); | ||
} | ||
}; | ||
}, | ||
/** | ||
* Add new node to arch. | ||
* | ||
* @param {Object} node Node to add | ||
* @param {String[]|String} [parents] Node parents to link with | ||
* @param {String[]|String} [children] Node children to link with | ||
* @throws AssertionError In case of there is node with same id in the arch already | ||
* @return {Arch} Chainable API. | ||
*/ | ||
addNode: function(node, parents, children) { | ||
ASSERT.ok(!this.hasNode(node)); | ||
var id = node.getId(); | ||
this._assertNode('addNode call', id, parents, children); | ||
this.nodes[id] = node; | ||
// TODO: seems like copy-paste between parents and childs | ||
// Proposal: add private method for both link types management | ||
if (!this.parents[id] || (parents && parents !== true)) this.parents[id] = []; | ||
if (parents && parents !== true) { | ||
this.link([id], parents); | ||
// NOTE: we are letting here premature linking with | ||
// not already existant nodes | ||
this.parents[id] = this.parents[id] || []; | ||
this.children[id] = this.children[id] || []; | ||
parents && this.link([id], parents); | ||
children && this.link(children, [id]); | ||
return this; | ||
}, | ||
/** | ||
* Add new node or replace existing node having same id with specified. | ||
* | ||
* If node exists and parents and children arguments are not specified | ||
* then links with other nodes will left unchanged. | ||
* | ||
* @param {Object} node Node to add or replace | ||
* @param {String[]|String} [parents] Node parents to link with | ||
* @param {String[]|String} [children] Node children to link with | ||
* @return {Arch} Chainable API. | ||
*/ | ||
setNode: function(node, parents, children) { | ||
return this.hasNode(node) | ||
? this.replaceNode(node, parents, children) | ||
: this.addNode(node, parents, children); | ||
}, | ||
/** | ||
* Replace existing node in arch with specified. | ||
* | ||
* @param {Object} node Node to add or replace | ||
* @param {String[]|String} [parents] Node parents to link with | ||
* @param {String[]|String} [children] Node children to link with | ||
* @throws AssertionError In case of there is no node with same id in the arch | ||
* @return {Arch} Chainable API. | ||
*/ | ||
replaceNode: function(node, parents, children) { | ||
ASSERT.ok(this.hasNode(node)); | ||
var id = node.getId(); | ||
if (parents || children) { | ||
this.removeNode(id); | ||
return this.addNode(node, parents, children); | ||
} | ||
if (!this.children[id] || (children && children !== true)) this.children[id] = []; | ||
if (children && children !== true) { | ||
this.link(children, [id]); | ||
this.nodes[id] = node; | ||
return this; | ||
}, | ||
/** | ||
* Remove node from arch. | ||
* | ||
* @param {String|Object} id Node to remove (ID or node object with getId()) | ||
* @returns {Arch} Chainable API. | ||
*/ | ||
removeNode: function(id) { | ||
id = U.getNodeId(id); | ||
U.removeNode(this.children, this.parents, id); | ||
delete this.nodes[id]; | ||
for (var k in this.plans) { | ||
var p = this.plans[k]; | ||
if (p.hasNode(id)) p.removeNode(id); | ||
} | ||
return id; | ||
return this; | ||
}, | ||
/** | ||
* Check if node exists in this arch. | ||
* | ||
* @param {String|Object} id Node to check (ID or node object with getId()). | ||
* @returns {Boolean} True if exists, otherwise false. | ||
*/ | ||
hasNode: function(id) { | ||
return !!this.nodes[U.getNodeId(id)]; | ||
}, | ||
/** | ||
* Assert simple loops for node. | ||
* | ||
* @param {String} [id] Node ID. | ||
* @param {String[]|String} [parents] Parent node IDs to link with. | ||
* @param {String[]|String} [children] Child node IDs to link with. | ||
* @param {String} id Node ID. | ||
* @param {String[]|String} [parents] Parent nodes IDs to link with. | ||
* @param {String[]|String} [children] Children nodes IDs to link with. | ||
*/ | ||
@@ -135,4 +228,4 @@ _assertNode: function(place, id, parents, children) { | ||
if (children === true) children = []; | ||
parents = util.toArray(parents); | ||
children = util.toArray(children); | ||
parents = U.toArray(parents); | ||
children = U.toArray(children); | ||
ASSERT.ok(parents.indexOf(id) === -1, 'found loop in ' + place + ': id = ' + id + ', parents = ' + parents); | ||
@@ -146,33 +239,10 @@ ASSERT.ok(children.indexOf(id) === -1, 'found loop in ' + place + ': id = ' + id + ', children = ' + children); | ||
/** | ||
* Replace node 'body'. | ||
* | ||
* @param {String} id ID of node to replace. | ||
* @param {Object} node New node. | ||
* @returns {String} Node ID. | ||
*/ | ||
replaceNode: function(id, node) { | ||
var args = arraySlice.call(arguments); | ||
args = (args.length < 2? [null] : []).concat(args, [true, true]); | ||
return this.setNode.apply(this, args); | ||
}, | ||
/** | ||
* Check if node with such ID exists in this arch. | ||
* | ||
* @param {String} id Node ID to check. | ||
* @returns {boolean} True if exists, otherwise false. | ||
*/ | ||
hasNode: function(id) { | ||
return !!this.nodes[id]; | ||
}, | ||
/** | ||
* Check if node with such ID has parents with provided IDs. | ||
* | ||
* @param {String} id Child node ID to check. | ||
* @param {String|String[]} parents Parent IDs to check. | ||
* @returns {boolean} True if all IDs are parent for this child, otherwise false. | ||
* @param {String|Object} id Node to check (ID or node object). | ||
* @param {String|String[]} parents IDs of parents to check. | ||
* @returns {Boolean} True if all IDs are parent for this child, otherwise false. | ||
*/ | ||
hasParents: function(id, parents) { | ||
return util.hasLink(id, this, 'parents', parents); | ||
return U.hasLink(U.getNodeId(id), this, 'parents', parents); | ||
}, | ||
@@ -183,58 +253,50 @@ | ||
* | ||
* @param {String} id Parent node ID to check. | ||
* @param {String|Object} id Node to check (ID or node object). | ||
* @param {String|String[]} children Child IDs to check. | ||
* @returns {boolean} True if all IDs are children for this parent, otherwise false. | ||
* @returns {Boolean} True if all IDs are children for this parent, otherwise false. | ||
*/ | ||
hasChildren: function(id, children) { | ||
return util.hasLink(id, this, 'children', children); | ||
return U.hasLink(U.getNodeId(id), this, 'children', children); | ||
}, | ||
/** | ||
* Return node by ID. | ||
* If there are no such node, return default node. | ||
* Return node children IDs. | ||
* | ||
* @param {String} id Node id | ||
* @returns {Object} Node object | ||
* @param {String|Object} id Node (ID or node object). | ||
* @returns {Array} Children IDs. | ||
*/ | ||
getNode: function(id) { | ||
return this.hasNode(id)? this.nodes[id] : this.getDefaultNode(id); | ||
getChildren: function(id) { | ||
return [].concat(this.children[U.getNodeId(id)] || []); | ||
}, | ||
/** | ||
* Create and return stub node for ID. | ||
* NB: it is not belongs to this Arch, just utility function. | ||
* Return node parent IDs. | ||
* | ||
* @param {String} id ID of node to create. | ||
* @returns {Object} Object with .getId() and .run() inside. | ||
* @param {String|Object} id Node (ID or node object). | ||
* @returns {Array} Parent IDs. | ||
*/ | ||
getDefaultNode: function(id) { | ||
return { | ||
getId: function() { | ||
return id; | ||
}, | ||
run: function() { | ||
return Q.reject("** No rule to make target '" + id + "'"); | ||
} | ||
}; | ||
getParents: function(id) { | ||
return [].concat(this.parents[U.getNodeId(id)] || []); | ||
}, | ||
/** | ||
* Return node children IDs. | ||
* Link specified children to node. | ||
* | ||
* @param {String} id Node ID. | ||
* @returns {String[]} Children IDs. | ||
* @param {String|Object} id Node (ID or node object). | ||
* @param {String[]|String} children Children nodes IDs. | ||
* @return {Arch} | ||
*/ | ||
getChildren: function(id) { | ||
return [].concat(this.children[id] || []); | ||
addChildren: function(id, children) { | ||
return this.link(children, U.getNodeId(id)); | ||
}, | ||
/** | ||
* Return node parent IDs. | ||
* Link specified parents to node. | ||
* | ||
* @param {String} id Node ID. | ||
* @returns {String[]} Parent IDs. | ||
* @param {String|Object} id Node (ID or node object). | ||
* @param {String[]|String} parents Parents nodes IDs. | ||
* @return {Arch} | ||
*/ | ||
getParents: function(id) { | ||
return [].concat(this.parents[id] || []); | ||
addParents: function(id, parents) { | ||
return this.link(U.getNodeId(id), parents); | ||
}, | ||
@@ -250,4 +312,4 @@ | ||
link: function(children, parents) { | ||
parents = util.toArray(parents); | ||
util.toArray(children).forEach(function(child) { | ||
parents = U.toArray(parents); | ||
U.toArray(children).forEach(function(child) { | ||
this._link(child, parents); | ||
@@ -266,2 +328,4 @@ }, this); | ||
_link: function(child, parents) { | ||
child = U.getNodeId(child); | ||
var _parents = this.parents[child], | ||
@@ -271,2 +335,4 @@ children; | ||
parents.forEach(function(parent) { | ||
parent = U.getNodeId(parent); | ||
if (this.hasParents(child, parent)) return; | ||
@@ -294,4 +360,4 @@ | ||
unlink: function(id1, id2) { | ||
util.unlink(this.parents[id1], this.children[id1], id2); | ||
util.unlink(this.parents[id2], this.children[id2], id1); | ||
U.unlink(this.parents[id1], this.children[id1], id2); | ||
U.unlink(this.parents[id2], this.children[id2], id1); | ||
@@ -312,3 +378,3 @@ for (var k in this.plans) { | ||
* @param {String} id Tree root node ID. | ||
* @param {boolean} forced Remove child nodes with links to other parents too. | ||
* @param {Boolean} forced Remove child nodes with links to other parents too. | ||
* @returns {Arch} Chainable API. | ||
@@ -341,26 +407,7 @@ */ | ||
/** | ||
* Remove node from arch. | ||
* | ||
* @param {String} id Node ID to remove. | ||
* @returns {Arch} Chainable API. | ||
*/ | ||
removeNode: function(id) { | ||
util.removeNode(this.children, this.parents, id); | ||
delete this.nodes[id]; | ||
for (var k in this.plans) { | ||
var p = this.plans[k]; | ||
if (p.hasNode(id)) p.removeNode(id); | ||
} | ||
return this; | ||
}, | ||
/** | ||
* Create plan instance from this arch. | ||
* Targets are (is) node IDs to run. If you have A -> B -> C arch and it is needed to build 'A', you target is 'A'. | ||
* | ||
* @param {Array|String} targets Node IDs to build. | ||
* @returns {Plan} Created plan. | ||
* @param {String[]|String} targets Node IDs to build. | ||
* @returns {Plan} Newly created plan. | ||
*/ | ||
@@ -406,3 +453,3 @@ createPlan: function(targets) { | ||
/** | ||
* Dump this arch to Graphviz string for debug purposes. | ||
* Dump this arch to string in Graphviz format for debug purposes. | ||
* | ||
@@ -423,2 +470,3 @@ * @returns {String} Graphviz string representation of this arch. | ||
* @param {String} spaces Left indent spaces. | ||
* @returns {String} String representation of specified node. | ||
*/ | ||
@@ -435,5 +483,6 @@ nodeToString: function(id, spaces) { | ||
/** | ||
* Dump node with its children to Graphviz string. | ||
* Dump node with its children to string in Graphviz format. | ||
* | ||
* @param {String} id Node ID to dump. | ||
* @returns {String} Graphviz string representation of specified node. | ||
*/ | ||
@@ -440,0 +489,0 @@ nodeToGraphviz: function(id, done) { |
@@ -7,3 +7,3 @@ var INHERIT = require('inherit'), | ||
module.exports = INHERIT(EventEmitter, { | ||
module.exports = INHERIT(EventEmitter, /** @lends Plan.prototype */ { | ||
@@ -13,3 +13,4 @@ /** | ||
* | ||
* @constructor | ||
* @class Plan | ||
* @constructs | ||
* @param {Arch} arch Arch from this plan created. | ||
@@ -626,19 +627,1 @@ * @param {String[]|String} targets Root node IDs to build. | ||
}); | ||
/** | ||
* Convert array to object. | ||
* | ||
* @param {Array} a Array to convert. | ||
* @returns {Object} Converted array. | ||
*/ | ||
function arrayToObject(a) { | ||
var o = {}, | ||
n = 0; | ||
for (var i = 0; i < a.length; i++) { | ||
if (!o[a[i]]) n++; | ||
o[a[i]] = 1; | ||
} | ||
return o; | ||
} |
@@ -10,2 +10,30 @@ /** | ||
/** | ||
* Convert array to object. | ||
* | ||
* @param {Array} a Array to convert. | ||
* @returns {Object} Converted array. | ||
*/ | ||
exports.arrayToObject = function(a) { | ||
var o = {}, | ||
n = 0; | ||
for (var i = 0; i < a.length; i++) { | ||
if (!o[a[i]]) n++; | ||
o[a[i]] = 1; | ||
} | ||
return o; | ||
}; | ||
/** | ||
* Returns node ID. | ||
* | ||
* @param id {String|Object} Node ID or object. | ||
* @return {String} Node ID. | ||
*/ | ||
exports.getNodeId = function(id) { | ||
return typeof id == 'string'? id : id.getId(); | ||
}; | ||
/** | ||
* Unlink node from parents and children. | ||
@@ -12,0 +40,0 @@ * |
@@ -5,10 +5,11 @@ var Q = require('qq'), | ||
module.exports = INHERIT({ | ||
module.exports = INHERIT(/** @lends Workers.prototype */ { | ||
/** | ||
* Creates an instance of Plan. | ||
* Creates an instance of Workers. | ||
* | ||
* @constructor | ||
* @param {number} [maxWorkers] Maximum number of workers to run in one time. | ||
* @param {object} [ctx] The hash to mix with default context in node 'run' function. | ||
* @class Workers | ||
* @constructs | ||
* @param {Number} [maxWorkers] Maximum number of workers to run simultaneously. | ||
* @param {Object} [ctx] The hash to mix with default context in node 'run' function. | ||
*/ | ||
@@ -29,2 +30,15 @@ __constructor: function(maxWorkers, ctx) { | ||
this.defers = {}; | ||
var _this = this; | ||
process.once('exit', function() { | ||
if (Object.keys(_this.plans).length) { | ||
console.error('WARNING! There are %s unfinished build processes!', Object.keys(_this.plans).length); | ||
console.error( | ||
'This application exit is not planned. Please check', | ||
'that all nodes in arch resolve or reject promises', | ||
'they return.'); | ||
} | ||
}); | ||
}, | ||
@@ -36,3 +50,3 @@ | ||
* @param {Plan} plan Plan to start. | ||
* @returns {promise} Promise of this start. | ||
* @returns {Promise * Undefined} Promise of this start. | ||
*/ | ||
@@ -49,12 +63,12 @@ start: function(plan) { | ||
/** | ||
* Create Q.defer for plan ID. | ||
* Create deffered for the plan ID. | ||
* | ||
* @param {String} id Plan ID to defer. | ||
* @returns {defer} Defer of this plan ID. | ||
* @param {Boolean} [remove=false] Remove defer from registry just before return | ||
* @returns {Q.Defer} Defer of this plan ID. | ||
*/ | ||
getDefer: function(id) { | ||
if (!this.defers[id]) { | ||
this.defers[id] = Q.defer(); | ||
} | ||
return this.defers[id]; | ||
getDefer: function(id, remove) { | ||
var defer = this.defers[id] || (this.defers[id] = Q.defer()); | ||
remove && delete this.defers[id]; | ||
return defer; | ||
}, | ||
@@ -178,3 +192,11 @@ | ||
workPlan: function(job, plan) { | ||
var _this = this; | ||
var _this = this, | ||
onDone = function() { | ||
_this.activeWorkers--; | ||
_this.onJobFinished(job.id); | ||
_this.next(); | ||
}, | ||
onError = function(err) { | ||
_this.onJobFinished(job.id, err); | ||
}; | ||
@@ -184,12 +206,3 @@ this.activeWorkers++; | ||
this.work(job, plan) | ||
.then(function() { | ||
_this.activeWorkers--; | ||
_this.onJobFinished(job.id); | ||
_this.next(); | ||
}) | ||
.fail(function(err) { | ||
_this.onJobFinished(job.id, err); | ||
}) | ||
.then(onDone, onError) | ||
.end(); | ||
@@ -224,3 +237,3 @@ | ||
defer = this.getDefer(plan.getId()); | ||
defer = this.getDefer(plan.getId(), true); | ||
err? defer.reject(err) : defer.resolve(); | ||
@@ -238,3 +251,3 @@ } | ||
* @param {Plan} plan Plan from which this job is. | ||
* @returns {Q.call} The result of Q.call. | ||
* @returns {Promise} The result of Q.call. | ||
*/ | ||
@@ -263,3 +276,3 @@ work: function(job, plan) { | ||
* @param {String} planID Listener plan ID. | ||
* @returns {boolean} Was it new listener or not. | ||
* @returns {Boolean} Was it new listener or not. | ||
*/ | ||
@@ -266,0 +279,0 @@ addFinishListener: function(jobID, planID) { |
{ | ||
"name": "apw", | ||
"version": "0.2.0", | ||
"version": "0.2.1", | ||
"homepage": "http://github.com/bem/apw", | ||
@@ -5,0 +5,0 @@ "author": "Sergey Kryzhanovsky <skryzhanovsky@ya.ru> (http://github.com/afelix)", |
@@ -6,2 +6,16 @@ var VOWS = require('vows'), | ||
function createNode(id) { | ||
return { | ||
getId: function() { | ||
return id; | ||
}, | ||
run: function() { | ||
return 'test' + id; | ||
} | ||
}; | ||
} | ||
function getEmptyArch() { | ||
@@ -12,8 +26,47 @@ return new APW.Arch(); | ||
function getSimpleArch() { | ||
var arch = getEmptyArch(); | ||
/* | ||
A | ||
| | ||
B | ||
*/ | ||
return getEmptyArch() | ||
.addNode(createNode('A')) | ||
.addNode(createNode('B'), 'A'); | ||
} | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
function getArch1() { | ||
/* | ||
A | ||
/ \ | ||
B C | ||
\ / | ||
D | ||
*/ | ||
return getEmptyArch() | ||
.addNode(createNode('A')) | ||
.addNode(createNode('B'), 'A') | ||
.addNode(createNode('C'), 'A') | ||
.addNode(createNode('D'), ['B', 'C']); | ||
} | ||
return arch; | ||
function getArch2() { | ||
/* | ||
A | ||
/ \ | ||
B D | ||
| | | ||
C E | ||
\ / | ||
F | ||
| | ||
G | ||
*/ | ||
return getEmptyArch() | ||
.addNode(createNode('A')) | ||
.addNode(createNode('B'), 'A') | ||
.addNode(createNode('D'), 'A') | ||
.addNode(createNode('C'), 'B') | ||
.addNode(createNode('E'), 'D') | ||
.addNode(createNode('F'), ['C', 'E']) | ||
.addNode(createNode('G'), 'F'); | ||
} | ||
@@ -23,48 +76,100 @@ | ||
.addBatch({ | ||
'Node set / get': { | ||
'Arch getters': { | ||
topic: getSimpleArch, | ||
'children': function(arch) { | ||
assert.lengthOf(arch.children['A'], 1); | ||
assert.equal(arch.children['A'][0], 'B'); | ||
assert.lengthOf(arch.children['B'], 0); | ||
}, | ||
'parents': function(arch) { | ||
assert.lengthOf(arch.parents['B'], 1); | ||
assert.equal(arch.parents['B'][0], 'A'); | ||
assert.lengthOf(arch.parents['A'], 0); | ||
}, | ||
'getNode() A': function(arch) { | ||
var node = arch.getNode('A'); | ||
assert.equal(node.run, 'testA'); | ||
assert.equal(arch.getNode('A').run(), 'testA'); | ||
}, | ||
'getNode() default': function(arch) { | ||
var node = arch.getNode('XXX'); | ||
assert.equal(node.getId(), 'XXX'); | ||
assert.equal(arch.getNode('XXX').getId(), 'XXX'); | ||
}, | ||
'getChildren() A': function(arch) { | ||
var children = arch.getChildren('A'); | ||
assert.lengthOf(children, 1); | ||
assert.equal(children[0], 'B'); | ||
assert.equal(children.pop(), 'B'); | ||
}, | ||
'getParents() B': function(arch) { | ||
var parents = arch.getParents('B'); | ||
assert.lengthOf(parents, 1); | ||
assert.equal(parents[0], 'A'); | ||
assert.equal(parents.pop(), 'A'); | ||
} | ||
}, | ||
'Arch.addNode()': { | ||
topic: getSimpleArch, | ||
'new': function(arch) { | ||
arch.addNode(createNode('new'), 'A', 'B'); | ||
assert.ok(arch.hasNode('new')); | ||
assert.equal(arch.getChildren('new'), 'B'); | ||
assert.equal(arch.getParents('new'), 'A'); | ||
}, | ||
'already existent throws': function(arch) { | ||
assert.throws(function() { | ||
arch.addNode(createNode('A')); | ||
}); | ||
} | ||
}, | ||
'Arch.setNode()': { | ||
topic: getArch1, | ||
'new': function(arch) { | ||
arch.setNode(createNode('new'), 'A', 'D'); | ||
assert.ok(arch.hasNode('new')); | ||
assert.equal(arch.getParents('new'), 'A'); | ||
assert.equal(arch.getChildren('new'), 'D'); | ||
}, | ||
'already existent': function(arch) { | ||
var replaceNode = 'D'; | ||
arch.setNode({ | ||
getId: function() { | ||
return replaceNode; | ||
}, | ||
run: function() { | ||
return 'new ' + replaceNode; | ||
} | ||
}, 'A', 'B'); | ||
assert.ok(arch.hasNode(replaceNode)); | ||
assert.equal(arch.getNode(replaceNode).run(), 'new ' + replaceNode); | ||
assert.equal(arch.getParents(replaceNode), 'A'); | ||
assert.equal(arch.getChildren(replaceNode), 'B'); | ||
} | ||
}, | ||
'Arch.replaceNode()': { | ||
topic: getArch1, | ||
'new throws': function(arch) { | ||
assert.throws(function() { | ||
arch.replaceNode(createNode('new')); | ||
}); | ||
}, | ||
'already existent': function(arch) { | ||
var replaceNode = 'D'; | ||
arch.replaceNode({ | ||
getId: function() { | ||
return replaceNode; | ||
}, | ||
run: function() { | ||
return 'new ' + replaceNode; | ||
} | ||
}, 'A', 'B'); | ||
assert.ok(arch.hasNode(replaceNode)); | ||
assert.equal(arch.getNode(replaceNode).run(), 'new ' + replaceNode); | ||
assert.equal(arch.getParents(replaceNode), 'A'); | ||
assert.equal(arch.getChildren(replaceNode), 'B'); | ||
} | ||
}, | ||
'Node availability check': { | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A1', { run: 'testA1' }); | ||
arch.setNode('A2', { run: 'testA2' }); | ||
arch.setNode('B', { run: 'testB' }, 'A1'); | ||
arch.setNode('C', { run: 'testC' }, ['A1', 'A2']); | ||
return arch; | ||
return getEmptyArch() | ||
.addNode(createNode('A1')) | ||
.addNode(createNode('A2')) | ||
.addNode(createNode('B'), 'A1') | ||
.addNode(createNode('C'), ['A1', 'A2']); | ||
}, | ||
@@ -140,10 +245,7 @@ 'hasNode() A1': function(arch) { | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testA' }); | ||
arch.link('B', 'A'); | ||
arch.link('B', 'A'); | ||
return arch; | ||
return getEmptyArch() | ||
.addNode(createNode('A')) | ||
.addNode(createNode('B')) | ||
.link('B', 'A') | ||
.link('B', 'A'); | ||
}, | ||
@@ -160,3 +262,3 @@ 'link() B -> A': function(arch) { | ||
assert.equal(parents[0], 'A'); | ||
} | ||
} | ||
}, | ||
@@ -166,10 +268,5 @@ | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.unlink('B', 'A'); | ||
arch.unlink('B', 'A'); | ||
return arch; | ||
return getSimpleArch() | ||
.unlink('B', 'A') | ||
.unlink('B', 'A'); | ||
}, | ||
@@ -184,12 +281,3 @@ 'unlink() B - A': function(arch) { | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.setNode('C', { run: 'testC' }, 'A'); | ||
arch.setNode('D', { run: 'testD' }, ['B', 'C']); | ||
arch.removeTree('C'); | ||
return arch; | ||
return getArch1().removeTree('C'); | ||
}, | ||
@@ -206,12 +294,3 @@ 'removeTree() C unforced': function(arch) { | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.setNode('C', { run: 'testC' }, 'A'); | ||
arch.setNode('D', { run: 'testD' }, ['B', 'C']); | ||
arch.removeTree('C', true); | ||
return arch; | ||
return getArch1().removeTree('C', true); | ||
}, | ||
@@ -229,15 +308,3 @@ 'removeTree() C forced': function(arch) { | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.setNode('D', { run: 'testD' }, 'A'); | ||
arch.setNode('C', { run: 'testC' }, 'B'); | ||
arch.setNode('E', { run: 'testE' }, 'D'); | ||
arch.setNode('F', { run: 'testF' }, ['C', 'E']); | ||
arch.setNode('G', { run: 'testG' }, 'F'); | ||
arch.removeTree('D'); | ||
return arch; | ||
return getArch2().removeTree('D'); | ||
}, | ||
@@ -256,15 +323,3 @@ 'removeTree() D unforced': function(arch) { | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.setNode('D', { run: 'testD' }, 'A'); | ||
arch.setNode('C', { run: 'testC' }, 'B'); | ||
arch.setNode('E', { run: 'testE' }, 'D'); | ||
arch.setNode('F', { run: 'testF' }, ['C', 'E']); | ||
arch.setNode('G', { run: 'testG' }, 'F'); | ||
arch.removeTree('D', true); | ||
return arch; | ||
return getArch2().removeTree('D', true); | ||
}, | ||
@@ -284,11 +339,5 @@ 'removeTree() D forced': function(arch) { | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
var arch = getArch1(), | ||
plan = arch.createPlan('A'); | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.setNode('C', { run: 'testC' }, 'A'); | ||
arch.setNode('D', { run: 'testD' }, ['B', 'C']); | ||
var plan = arch.createPlan('A'); | ||
arch.removeTree('C'); | ||
@@ -295,0 +344,0 @@ |
@@ -6,2 +6,16 @@ var VOWS = require('vows'), | ||
function createNode(id) { | ||
return { | ||
getId: function() { | ||
return id; | ||
}, | ||
run: function() { | ||
return 'test' + id; | ||
} | ||
}; | ||
} | ||
function getEmptyArch() { | ||
@@ -12,9 +26,6 @@ return new APW.Arch(); | ||
function getSimpleArch() { | ||
var arch = getEmptyArch(); | ||
arch.setNode('A', { run: function() {} }); | ||
arch.setNode('B', { run: function() {} }, 'A'); | ||
arch.setNode('C', { run: function() {} }, 'B'); | ||
return arch; | ||
return getEmptyArch() | ||
.addNode(createNode('A')) | ||
.addNode(createNode('B'), 'A') | ||
.addNode(createNode('C'), 'B'); | ||
} | ||
@@ -71,11 +82,10 @@ | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
var arch = getEmptyArch() | ||
.addNode(createNode('A')) | ||
.addNode(createNode('B'), 'A') | ||
.addNode(createNode('C'), 'A') | ||
.addNode(createNode('D'), ['B', 'C']), | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.setNode('C', { run: 'testC' }, 'A'); | ||
arch.setNode('D', { run: 'testD' }, ['B', 'C']); | ||
plan = arch.createPlan('A'); | ||
var plan = arch.createPlan('A'); | ||
arch.removeTree('C'); | ||
@@ -95,11 +105,10 @@ | ||
topic: function() { | ||
var arch = getEmptyArch(); | ||
var arch = getEmptyArch() | ||
.addNode(createNode('A')) | ||
.addNode(createNode('B'), 'A') | ||
.addNode(createNode('C'), 'A') | ||
.addNode(createNode('D'), ['B', 'C']), | ||
arch.setNode('A', { run: 'testA' }); | ||
arch.setNode('B', { run: 'testB' }, 'A'); | ||
arch.setNode('C', { run: 'testC' }, 'A'); | ||
arch.setNode('D', { run: 'testD' }, ['B', 'C']); | ||
plan = arch.createPlan('A'); | ||
var plan = arch.createPlan('A'); | ||
arch.removeTree('C', true); | ||
@@ -140,2 +149,2 @@ | ||
suite.export(module); | ||
suite.export(module); |
@@ -8,78 +8,110 @@ var Q = require('qq'), | ||
function getArch(state) { | ||
var arch = new APW.Arch(); | ||
var createNode = function(id) { | ||
return { | ||
arch.setNode('0A', { run: function() { state.push('0A') } }); | ||
arch.setNode('1A', { run: function() { state.push('1A') } }); | ||
arch.setNode('1B', { run: function() { state.push('1B') } }, '1A'); | ||
arch.setNode('2A', { run: function() { state.push('2A') } }); | ||
arch.setNode('2B', { | ||
run: function(ctx) { | ||
state.push('2B'); | ||
ctx.arch.setNode('2C', { run: function() { state.push('2C') } }, '2A'); | ||
ctx.arch.setNode('2D', { run: function() { state.push('2D') } }, '2A'); | ||
} | ||
}, '2A'); | ||
getId: function() { | ||
return id; | ||
}, | ||
arch.setNode('3A', { run: function() { state.push('3A') } }); | ||
arch.setNode('3B', { | ||
run: function(ctx) { | ||
state.push('3B'); | ||
return ctx.arch.withLock(function() { | ||
ctx.arch.setNode('3C', { run: function() { state.push('3C') } }, '3A'); | ||
ctx.arch.setNode('3D', { run: function() { state.push('3D') } }, '3A'); | ||
}); | ||
} | ||
}, '3A'); | ||
run: function() { | ||
state.push(id); | ||
} | ||
arch.setNode('4A', { run: function() { state.push('4A') } }); | ||
arch.setNode('4B', { | ||
run: function(ctx) { | ||
ctx.plan.on('allDone', function(id) { | ||
state.push('4B'); | ||
}); | ||
} | ||
}, '4A'); | ||
}; | ||
}; | ||
arch.setNode('5A', { | ||
run: function(ctx) { | ||
state.push(ctx.plan); | ||
} | ||
}); | ||
arch.setNode('5B', { | ||
run: function(ctx) { | ||
ctx.plan.lock(); | ||
ctx.plan.link('5D', '5C'); | ||
ctx.plan.unlock(); | ||
} | ||
}, '5A'); | ||
arch.setNode('5C', { run: function() {} }, '5B'); | ||
arch.setNode('5D', { run: function() {} }, '5C'); | ||
return new APW.Arch() | ||
.addNode(createNode('0A')) | ||
arch.setNode('6A', { | ||
run: function(ctx) { | ||
state.push(ctx.plan); | ||
} | ||
}); | ||
arch.setNode('6B', { run: function() {} }, '6A'); | ||
arch.setNode('6C', { run: function() {} }, '6A'); | ||
arch.setNode('6D', { run: function() {} }, '6B'); | ||
arch.setNode('6E', { | ||
run: function(ctx) { | ||
ctx.plan.lock(); | ||
ctx.plan.link('6C', '6B'); | ||
ctx.plan.unlock(); | ||
} | ||
}, ['6B', '6C']); | ||
arch.setNode('6F', { run: function() {} }, '6D'); | ||
arch.setNode('6G', { run: function() {} }, '6D'); | ||
arch.setNode('6H', { run: function() {} }, '6E'); | ||
arch.setNode('6I', { run: function() {} }, '6H'); | ||
.addNode(createNode('1A')) | ||
.addNode(createNode('1B'), '1A') | ||
arch.setNode('7A', { run: function() { state.push('7A') } }); | ||
arch.setNode('7B', { run: function() { state.push('7B') } }, '7A'); | ||
arch.setNode('7C', { run: function() { state.push('7C') } }, '7B'); | ||
.addNode(createNode('2A')) | ||
.addNode({ | ||
getId: function() { | ||
return '2B'; | ||
}, | ||
run: function(ctx) { | ||
state.push('2B'); | ||
ctx.arch.addNode(createNode('2C'), '2A'); | ||
ctx.arch.addNode(createNode('2D'), '2A'); | ||
} | ||
}, '2A') | ||
return arch; | ||
.addNode(createNode('3A')) | ||
.addNode({ | ||
getId: function() { | ||
return '3B'; | ||
}, | ||
run: function(ctx) { | ||
state.push('3B'); | ||
return ctx.arch.withLock(function() { | ||
ctx.arch.addNode(createNode('3C'), '3A'); | ||
ctx.arch.addNode(createNode('3D'), '3A'); | ||
}); | ||
} | ||
}, '3A') | ||
.addNode(createNode('4A')) | ||
.addNode({ | ||
getId: function() { | ||
return '4B'; | ||
}, | ||
run: function(ctx) { | ||
ctx.plan.on('allDone', function(id) { | ||
state.push('4B'); | ||
}); | ||
} | ||
}, '4A') | ||
.addNode({ | ||
getId: function() { | ||
return '5A'; | ||
}, | ||
run: function(ctx) { | ||
state.push(ctx.plan); | ||
} | ||
}) | ||
.addNode({ | ||
getId: function() { | ||
return '5B'; | ||
}, | ||
run: function(ctx) { | ||
ctx.plan.lock(); | ||
ctx.plan.link('5D', '5C'); | ||
ctx.plan.unlock(); | ||
} | ||
}, '5A') | ||
.addNode(createNode('5C'), '5B') | ||
.addNode(createNode('5D'), '5C') | ||
.addNode({ | ||
getId: function() { | ||
return '6A'; | ||
}, | ||
run: function(ctx) { | ||
state.push(ctx.plan); | ||
} | ||
}) | ||
.addNode(createNode('6B'), '6A') | ||
.addNode(createNode('6C'), '6A') | ||
.addNode(createNode('6D'), '6B') | ||
.addNode({ | ||
getId: function() { | ||
return '6E'; | ||
}, | ||
run: function(ctx) { | ||
ctx.plan.lock(); | ||
ctx.plan.link('6C', '6B'); | ||
ctx.plan.unlock(); | ||
} | ||
}, ['6B', '6C']) | ||
.addNode(createNode('6F'), '6D') | ||
.addNode(createNode('6G'), '6D') | ||
.addNode(createNode('6H'), '6E') | ||
.addNode(createNode('6I'), '6H') | ||
.addNode(createNode('7A')) | ||
.addNode(createNode('7B'), '7A') | ||
.addNode(createNode('7C'), '7B'); | ||
} | ||
@@ -101,3 +133,3 @@ | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
@@ -119,3 +151,3 @@ 'correct run': function(error, state) { | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
@@ -138,3 +170,3 @@ 'correct run order': function(error, state) { | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
@@ -159,3 +191,3 @@ 'correct plan update on-the-fly': function(error, state) { | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
@@ -180,6 +212,6 @@ 'correct plan update on-the-fly': function(error, state) { | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
'there are no double done jobs': function(error, state) { | ||
var plan = state[0]; | ||
var plan = state.pop(); | ||
assert.isNull(error); | ||
@@ -207,7 +239,6 @@ assert.lengthOf(plan.doneJobs, 4); | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
'test': function(error, state) { | ||
assert.isNull(error); | ||
console.log(state); | ||
} | ||
@@ -224,6 +255,6 @@ }, | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
'there are no double done jobs': function(error, state) { | ||
var plan = state[0]; | ||
var plan = state.pop(); | ||
assert.isNull(error); | ||
@@ -246,3 +277,3 @@ assert.lengthOf(plan.doneJobs, 9); | ||
function(error) { _this.callback(error, null) } | ||
) | ||
).end(); | ||
}, | ||
@@ -249,0 +280,0 @@ 'allDone subscribers fired': function(error, state) { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
87679
2177
0