From 205f9a2ccd8576b587f5ed612e16d8b74889596b Mon Sep 17 00:00:00 2001 From: David Kebler Date: Mon, 10 Feb 2020 21:42:45 -0800 Subject: [PATCH] 0.1.10 add makeCombination, fix bug in makeObserver, refactor failed, add sending boolean event as array where first element is ready value to check --- .npmignore | 1 + package.json | 8 ++++---- src/ready.js | 57 +++++++++++++++++++++++++++++++++++----------------- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/.npmignore b/.npmignore index f16fc41..2f680a8 100644 --- a/.npmignore +++ b/.npmignore @@ -2,3 +2,4 @@ tests/ test/ *.test.js testing/ +examples/ diff --git a/package.json b/package.json index 1228ec2..7c36fe5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@uci-utils/ready", - "version": "0.1.7", + "version": "0.1.10", "description": "A Class to Observe the reduced to boolean combined state of a map of observables", "main": "src/ready.js", "scripts": { @@ -22,7 +22,7 @@ }, "homepage": "https://github.com/uCOMmandIt/uci-utils#readme", "dependencies": { - "@uci-utils/to-boolean": "^0.1.1", + "@uci-utils/to-boolean": "^0.1.5", "is-observable": "^2.0.0", "is-plain-object": "^3.0.0", "p-is-promise": "^3.0.0", @@ -31,7 +31,7 @@ "devDependencies": { "chai": "^4.2.0", "esm": "^3.2.25", - "mocha": "^6.2.2", - "nodemon": "^1.19.4" + "mocha": "^7.0.1", + "nodemon": "^2.0.2" } } diff --git a/src/ready.js b/src/ready.js index 966e58b..f79c958 100644 --- a/src/ready.js +++ b/src/ready.js @@ -8,12 +8,14 @@ import isPlainObject from 'is-plain-object' import { createBoolean } from '@uci-utils/to-boolean' class Ready extends Map { - constructor(opts) { + constructor(opts={}) { super(opts.observables) this.emitter = isEmitter(opts.emitter) ? opts.emitter : null const toBool = createBoolean(opts.boolean) this.toBoolean = (value) => { + // can emit plain object with ready prop, or array with first value as ready value or just ready value if (isPlainObject(value)) value = (value.ready || value.online || value.active || false) + if (Array.isArray(value)) value=value[0] return toBool(value) } this.condition = opts.condition || ( (ev) => this.toBoolean(ev) ) @@ -27,19 +29,26 @@ class Ready extends Map { if (opts.verbose||process.env.UCI_READY_VERBOSE==='true') this.logger.subscribe(console.log) this.handler = opts.handler || ((ready) => {console.log('default handler', ready)}) this._first = true // tracks first emission + console.log('@uci-utils/ready package tag 0.1.10') } get observers(){return Array.from(this.keys())} get combinations(){return Array.from(this._combinations.keys())} get all() { return this._all} - get failure () { - let ret = null - let failed = this.state.some(obs=> { - ret = obs[0] - return obs[1]===false - }) - return !failed ? '__none__' : ret + get failed() { + // console.log('making failures from',this.state) + let failed = this.state + .filter(([,ready]) => { + // console.log('in filter', ready) + // return !await this.getValue(name) + // ret = obs[0] + return !ready + }) + .map(obs=> {return {name:obs[0], details: this.getObserverDetails(obs[0])} }) + // console.log('failed as filtered', failed) + + return failed.length ? failed : '__none__' } getObserverDetails(name) { return (this.get(name)||{}).details} @@ -67,9 +76,10 @@ class Ready extends Map { getValue(name) { // NOT recommended. if there is any issue will return false let obs = this.getObserver(name) return new Promise(resolve => { - setTimeout(()=>resolve(false),50) + setTimeout(()=>resolve(false),100) if (isObservable(obs)){ const sub = obs.subscribe(val => { + console.log('in value subscriber', name,val) resolve(val) }) sub.unsubscribe() @@ -85,13 +95,18 @@ class Ready extends Map { condition = condition || this.condition if (isEmitter(obs) && (event || name)) obs = fromEvent(obs, event || name ) // it's an emitter if (isPromise(obs)) obs = from(obs) // it's a promise - if (!obs && this.emitter && (event || name)) obs = fromEvent(this.emitter,(event || name)) + if ((!obs || typeof obs==='string') && this.emitter && (event || name || obs)) obs = fromEvent(this.emitter,(event || name || obs)) if (!obs || !isObservable(obs)) return false let xobs = obs.pipe( tap(val => this.log(`${name} emitted/resolved the value =>${JSON.stringify(val)}`)), + // tap(val => console.log(`${name} emitted/resolved the value =>${JSON.stringify(val)}`)), // TODO retain plain object if it has a ready property map(condition), + // tap(val => console.log(`boolean: ${val}`)), tap(val => this.log(`boolean: ${val}`)), + map(val => { + // if (opts.reverse) console.log('reversing', val, opts.reverse ? !val : val) + return opts.reverse ? !val : val}), startWith(false), // shareReplay({refCount:true, bufferSize:1}), shareReplay(1), @@ -131,6 +146,19 @@ class Ready extends Map { // TODO update affected combinations } + // TODO have combineObservers call this + makeCombination(observers,list){ + observers = observers.filter(obs=>obs._readyType) + if (!observers.length) return false + let combination = combineLatest(observers).pipe( + tap(states => { if (list) this.log(list.map((name,index) => [name,states[index]]))}), + map(states => states.reduce((res,state) => {return res && state},true)), + changed(), //filters out emission if it is unchanged from last TODO replace with distinctUntilChanged + shareReplay(1) + ) + return combination + } + // TODO add option for whether changed is enabled (default) or not and store with combo for remake combineObservers(name,list,opts={}) { if (!name || typeof(name)!=='string') return false // name required @@ -141,14 +169,7 @@ class Ready extends Map { else return false } let observers = list.map(name=>{return name._readyType ? name : this.getObserver(name)}) - observers = observers.filter(obs=>obs._readyType) - if (!observers.length) return false - let combination = combineLatest(observers).pipe( - tap(states => { this.log(list.map((name,index) => [name,states[index]]))}), - map(states => states.reduce((res,state) => {return res && state},true)), - changed(), //filters out emission if it is unchanged from last - shareReplay(1) - ) + const combination = this.makeCombination(observers,list) this._combinations.set(name, combination) // if name passed then save combo in Map return combination }