src/BaseStatsCollector.js
import execMethod from './helpers/execMethod';
/**
* A stat collector that initially includes no collector functions.
* @example
* const statsCollector = new BaseStatsCollector();
* statsCollector.addCollector({name: 'custom', onUpdate: fn});
* statsCollector.update([1, 2, 3, 4, 5]);
* statsCollector.get();
*/
export default class BaseStatsCollector {
constructor() {
this._collectors = [];
this._reset = true;
this._state = {};
this._ignore = [];
}
/**
* Add a collector function that can process data
* when update() or get() is called.
* @param {Object} collector An object with the following properties:
* - **name** - [String] The name of the collector
* - **initialValue** - [any] The initial value if update() has never been called
* - **requirements** - [Array] An array of collector names that must be added before this collector
* - **onUpdate** - [Function]
* - **onGet** - [Function]
* @example
* const statCollector = new BaseStatsCollector();
* statCollector.addCollector({
* name: 'test-sum',
* initialValue: 0,
* onUpdate: function (prev, state, val) { return prev + val; }
* });
* @return {undefined}
*/
addCollector(collector) {
if (!this._reset) {
throw new Error('Cannot add a new collector unless you call reset() first');
}
if (typeof collector === 'object' && typeof collector.name === 'string') {
let reqIndex;
let requirements = collector.requirements;
if (!Array.isArray(requirements)) {
requirements = [];
}
this._collectors.forEach(function (currentCollector) {
if (currentCollector.name === collector.name) {
throw new Error('A collector with that name has already been added.');
}
reqIndex = requirements.indexOf(currentCollector.name);
if (reqIndex >= 0) {
requirements.splice(reqIndex, 1);
}
});
if (requirements.length) {
throw new Error(`Did not meet the requirements: ${requirements}`);
}
this._collectors.push(collector);
} else {
throw new Error('Collector must be an object with a name');
}
this.reset();
}
addIgnore(name) {
if (this._ignore.indexOf(name) === -1) {
this._ignore.push(name);
}
}
get() {
const self = this;
const result = {};
if (!this._reset) {
execMethod(this, 'onGet');
}
Object.keys(this._state).sort().forEach(function (key) {
if (self._ignore.indexOf(key) === -1) {
let val = self._state[key];
if (val) {
val = JSON.parse(JSON.stringify(val));
}
result[key] = val;
}
});
return result;
}
update(...values) {
const self = this;
values.forEach(function (value) {
if (Array.isArray(value)) {
value.forEach(function (v) {
self.update(v);
});
} else {
const v = parseFloat(value);
if (Number.isFinite(v)) {
execMethod(self, 'onUpdate', v);
}
}
});
}
reset() {
const self = this;
this._state = {};
this._collectors.forEach(function (collector) {
self._state[collector.name] = undefined;
});
this._reset = true;
}
}