Comparing version 1.1.0 to 1.2.0
156
index.js
@@ -14,31 +14,73 @@ 'use strict' | ||
function validate (method) { | ||
if (!_.isString(method.label) || (_.isEmpty(method.label))) { | ||
throw new SurmountError('One of the methods has no label', method) | ||
class Surmount { | ||
validate (method) { | ||
if ( | ||
(!_.isString(method.label)) || | ||
( _.isEmpty(method.label )) | ||
) { throw new SurmountError('One of the methods has no label', method) } | ||
else if ( | ||
(!_.isUndefined(method.dependency)) && | ||
(!_.isArray(method.dependency)) | ||
) { throw new SurmountError(`${method.label} dependencies are invalid`, method) } | ||
else if ( | ||
(!_.isFunction(method)) | ||
) { throw new SurmountError(`${method.label} is not a function`, method) } | ||
return [method.label, method] | ||
} | ||
else if (!(_.isUndefined(method.dependency)) && !(_.isArray(method.dependency))) { | ||
throw new SurmountError(`${method.label} dependencies are invalid`, method) | ||
} | ||
else if (!(_.isFunction(method))) { | ||
throw new SurmountError(`${method.label} is not a function`, method) | ||
} | ||
return [method.label, method] | ||
} | ||
class Surmount { | ||
constructor () { | ||
const methods = _(arguments) | ||
flatten (args) { | ||
return _(args) | ||
.values() | ||
.flattenDeep() | ||
.filter(_.isFunction) | ||
.map(validate) | ||
.map(this.validate) | ||
.fromPairs() | ||
.value() | ||
} | ||
if (_.isEmpty(methods)) { | ||
return Promise.resolve() | ||
exceptions (label, errors) { | ||
const methods = this.methods | ||
let err = _.fromPairs(_.map(_.intersection( | ||
methods[label].dependency, | ||
_.keys(errors) | ||
), | ||
function (_label) { | ||
return [_label, errors[_label]] | ||
})) | ||
if (_.isEmpty(err)) { | ||
err = false | ||
} | ||
function getMethod (label) { | ||
return [err] | ||
} | ||
promise (method, args, errors) { | ||
const label = method.label | ||
const err = this.exceptions(label, errors) | ||
return method.method(_.concat(err, args)) | ||
.catch(function (error) { | ||
errors[label] = error | ||
if (_.isError(error)) { | ||
return {[label]: error} | ||
} | ||
else { | ||
return error | ||
} | ||
}) | ||
.then(function (res) { | ||
return {[label]: res} | ||
}) | ||
} | ||
schedule () { | ||
const series = [] | ||
const methods = this.methods | ||
function extract (label) { | ||
const method = methods[label] | ||
@@ -56,4 +98,2 @@ | ||
const series = [] | ||
try { | ||
@@ -69,3 +109,3 @@ const graph = topolysis( | ||
for (const labels of graph) { | ||
series.push(_.map(labels, getMethod)) | ||
series.push(_.map(labels, extract)) | ||
} | ||
@@ -77,41 +117,34 @@ } | ||
return series | ||
} | ||
finalize (results) { | ||
return _.reduce( | ||
_(results) | ||
.flatten() | ||
.assign([]) | ||
.value(), | ||
function (sum, res) { return _.assign(sum, res) } | ||
) | ||
} | ||
constructor () { | ||
const methods = this.methods = this.flatten(arguments) | ||
if (_.isEmpty(methods)) { return Promise.resolve() } | ||
const series = this.schedule() | ||
const promise = _.bind(this.promise, this) | ||
const finalize = this.finalize | ||
return function () { | ||
const queue = _.cloneDeep(series) | ||
const errors = {} | ||
const args = _.values(arguments) | ||
const results = [] | ||
let pointer = 0 | ||
return new Promise(function (resolve, reject) { | ||
function walk (slice) { | ||
return Promise.all(_.map(slice, function (method) { | ||
let err = _.fromPairs(_.map(_.intersection( | ||
methods[method.label].dependency, | ||
_.keys(errors) | ||
), | ||
function (label) { | ||
return [label, errors[label]] | ||
})) | ||
if (_.isEmpty(err)) { | ||
err = false | ||
} | ||
return method.method(_.concat([err], args)) | ||
.catch(function (error) { | ||
errors[method.label] = error | ||
if (_.isError(error)) { | ||
const obj = {} | ||
obj[method.label] = error | ||
return obj | ||
} | ||
else { | ||
return error | ||
} | ||
}) | ||
.then(function (res) { | ||
const obj = {} | ||
obj[method.label] = res | ||
return obj | ||
}) | ||
function iterate (index) { | ||
return Promise.all(_.map(series[index], function (method) { | ||
return promise(method, args, errors) | ||
})) | ||
@@ -125,11 +158,8 @@ .then(function (res) { | ||
.finally(function () { | ||
if (queue.length !== 0) { | ||
walk(queue.shift()) | ||
if (series.length !== pointer) { | ||
++pointer | ||
iterate(pointer) | ||
} | ||
else { | ||
resolve( | ||
_.reduce(_(results).flatten().assign([]).value(), function (sum, res) { | ||
return _.assign(sum, res) | ||
}, []) | ||
) | ||
resolve(finalize(results)) | ||
} | ||
@@ -139,3 +169,3 @@ }) | ||
walk(queue.shift()) | ||
iterate(0) | ||
}) | ||
@@ -142,0 +172,0 @@ |
{ | ||
"name": "surmount", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "Minimal, well behaved dependency based scheduler", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/epiloque/surmount", |
@@ -28,9 +28,9 @@ # surmount | ||
* Optimized for parallel execution. | ||
* Propagates arguments to the functions. | ||
* Propagates errors to the affected functions. | ||
* Returns a promise of an object containing return values and/or errors. | ||
* Throws an error on circular dependencies. | ||
* Throws an error on missing dependencies. | ||
* Not an orchestrator, task registry or a build system. | ||
* Optimized for parallel execution | ||
* Propagates arguments to the functions | ||
* Propagates errors to the affected functions | ||
* Returns a promise of an object containing return values and/or errors | ||
* Throws an error on circular dependencies | ||
* Throws an error on missing dependencies | ||
* Not an orchestrator, task registry or a build system | ||
@@ -37,0 +37,0 @@ # Usage |
@@ -75,9 +75,3 @@ 'use strict' | ||
describe('fulfills the expectations', function () { | ||
const dress = new Surmount([ | ||
tie, | ||
shoes, | ||
jacket, | ||
shirt, | ||
pants | ||
]) | ||
const dress = new Surmount([tie, shoes], jacket, [shirt, pants]) | ||
@@ -84,0 +78,0 @@ const state = {} |
12700
368