statefulobject
Version:
A simple object that has states and can switch between them (fully async)
67 lines (57 loc) • 2.32 kB
JavaScript
//--------------------------------- 80 chars -----------------------------------
const EventEmitter = require('events');
const argumentCountDispatch = (methods) => (...params) =>
methods[params.length](...params);
const bindMethod = (obj, methodName) => obj[methodName].bind(obj);
const takeSomeMethods = argumentCountDispatch({
4: (cls, ...methods) => class {
constructor() {this['_' + cls.name] = new cls();}
[methods[0]](..._) {return bindMethod(this['_' + cls.name], methods[0])(..._);}
[methods[1]](..._) {return bindMethod(this['_' + cls.name], methods[1])(..._);}
[methods[2]](..._) {return bindMethod(this['_' + cls.name], methods[2])(..._);}
}
});
class EventEmitterPromiseAllEmit extends EventEmitter {
emit(event, ...payload) {
return Promise.all(this.listeners(event).map(
(listener) => Promise.resolve(listener.apply(this, payload))
));
}
}
const getClassProtoChain = (cls) => {
if (!cls.prototype) return [];
const result = getClassProtoChain(Object.getPrototypeOf(cls));
result.unshift(cls.prototype);
return result;
}
// Basically cond, but with constructors instead of actions
const DispatchClass = (pairs) => {
if (!pairs.length) throw new Error('Array of predicate-class pairs is empty');
return class DispatchClass {
constructor(...data) {
for (const [predicate, cls] of pairs) {
if (predicate(...data)) {
const result = new cls(...data);
if (new.target != DispatchClass) { // we have an inheritance here
// We stich together stuff that inherited from DispatchClass and our cls
// Note that we're changing child classes, the same child classes.
// If child class can make the DC resolve into different classes from time to time,
// unexpected and interesting things can happen, so don't do that.
// TODO: fix this, or at least throw an error when it happens
const protoChain = getClassProtoChain(new.target).slice(0, -1); // slice to omit DispatchClass
protoChain.push(cls.prototype);
let tmp = result;
for (const proto of protoChain) {
tmp.__proto__ = proto;
tmp = tmp.__proto__;
}
}
return result;
}
}
throw new Error('No predicate matched the data');
}
}
}
module.exports = {takeSomeMethods, EventEmitterPromiseAllEmit, DispatchClass};