voting
Version:
Voter library for javascript
157 lines (148 loc) • 5 kB
JavaScript
/*jslint node:true*/
'use strict';
//var EventEmitter = require('events').EventEmitter;
var async = require('async');
/**
* Voter super class
* @param {{}} [opts] -
* @constructor
*/
var VoterClass = function (opts) {
opts = opts || {};
this.decisionBasis = opts.decisionBasis || [];
if (opts.hasOwnProperty('persistsEvaluation')) {
this.persistsEvaluation = opts.persistsEvaluation;
}
if (opts.hasOwnProperty('onlyOneArgument')) {
this.onlyOneArgument = opts.onlyOneArgument;
}
};
/*jslint unparam:true*/ // just for addAsyncBasis!
VoterClass.prototype = {
// Methods
/**
* Add a value or an asynchronous function as decision basis
* @param {Function|*} AsyncFnOrValue - Asynchronous function or value
* @returns {VoterClass}
*/
addDecisionBasis: function (AsyncFnOrValue) {
this.decisionBasis.push.apply(this.decisionBasis, arguments);
return this;
},
/**
* Add another voter. Its decision is decision basis for this voter.
* This is a kind of nested voter.
* @param {VoterClass} voter - Add a voter as
* @returns {VoterClass}
*/
addSubVoter: function (voter) {
this.addDecisionBasis(function (cb) {
voter.decide(cb);
});
return this;
},
/**
* Remove whole decision basis or a special entry from decision basis if a parameter is given.
* @param {Number} [n] - Remove just the given position of the decision basis
* @returns {VoterClass}
*/
clearBasis: function (n) {
if (n || n === 0) {
this.decisionBasis.splice(n, 1);
return this;
}
this.decisionBasis = [];
return this;
},
/**
* The default decide make from VoterClass (super class) returns an array of the values from the decisions basis.
* By default elements with null get spliced.
* @param {VoterClass~decideCallback} cb - Callback with decision
* @param {boolean} [obtainNull=false] - Should null values obtain?
* @returns {*}
*/
decide: function (cb, obtainNull) {
var asyncArr = [],
i,
mkAsyncFn,
self = this;
mkAsyncFn = function (n) {
return function (cb) {
self.evaluateBasis(n, cb);
};
};
for (i = 0; i < this.decisionBasis.length; i += 1) {
asyncArr.push(mkAsyncFn(i));
}
return async.parallel(asyncArr, function (err, data) {
var cleanedData = [];
if (err) {
return cb(err);
}
for (i = 0; i < data.length; i += 1) {
if (data[i] !== null || obtainNull) {
cleanedData.push(data[i]);
}
}
/**
* @callback VoterClass~decideCallback
* @param {Error|null} error - Error if there was one
* @param {*} decision - The decision that results the voter
*/
return cb(null, cleanedData);
});
},
/**
* Evaluate a decision basis.
* @param {number} n - Position of decision basis that should be evaluated
* @param {VoterClass~evaluateBasisCallback} cb - Callback from evaluation
* @returns {*}
*/
evaluateBasis: function (n, cb) {
var self = this,
target = this.decisionBasis[n];
if (typeof target === 'function') {
return target(function (err, data) {
var i;
if (err) {
return cb(err);
}
if (self.persistsEvaluation) {
if (self.onlyOneArgument || arguments.length === 1) {
self.decisionBasis[n] = data;
} else {
self.decisionBasis[n] = [];
for (i = 1; i < arguments.length; i += 1) {
self.decisionBasis[n].push(arguments[i]);
}
}
}
/**
* @callback VoterClass~evaluateBasisCallback
* @param {Error|null} error - Error if there was one
* @param {*} value - The value that evaluation results
*/
return cb(null, self.decisionBasis[n]);
});
}
return cb(null, target);
},
// Properties
/**
* List of decision basis
* @type {(Function|*)[]}
*/
decisionBasis: [],
/**
* Should the evaluated value replace the async function
*/
persistsEvaluation: true,
/**
* Should only the second argument from decision callback be used as value?
* If not it uses all parameters excerpts the first (error) one. If there is more then one it will group them in an
* array.
*/
onlyOneArgument: true
};
/*jslint unparam:false*/
module.exports = VoterClass;