diff --git a/src/rx-class.js b/src/rx-class.js index 8efcb7c..16a4850 100644 --- a/src/rx-class.js +++ b/src/rx-class.js @@ -1,3 +1,4 @@ +// import methods from './public-methods.js' // native modules import { EventEmitter } from 'events' // reactive modules @@ -5,7 +6,7 @@ import { BehaviorSubject, from } from 'rxjs' import { distinctUntilChanged, skip } from 'rxjs/operators' // uci modules import { check } from '@uci-utils/type' -import { get, set, del, getKeys, pathToString } from '@uci-utils/obj-nested-prop' +import { get, set, del, walkPath, getKeys, pathToString } from '@uci-utils/obj-nested-prop' import { logger } from '@uci-utils/logger' // supporting import equal from 'deep-equal' @@ -17,7 +18,6 @@ const log = logger({ file: '/src/rx-class.js', package: '@uci-utils/rx-class', c class RxClass extends EventEmitter { // private fields #rx - #rxM constructor (opts = {}) { super(opts) @@ -32,8 +32,9 @@ class RxClass extends EventEmitter { hook: rxopts.hook, // function called when setting namespace: rxopts.namespace || 'rx', // TODO namespace to added to all public rx methods operators: new Map(), // TODO added to all rx pipes, in order - skip: rxopts.skip == null ? 0 : rxopts.skip + skip: rxopts.skip == null ? 1 : rxopts.skip } + this.isRx.bind(this) } get (path) { @@ -51,25 +52,29 @@ class RxClass extends EventEmitter { } set (path, value, opts = {}) { - // console.log(path,value,opts,!this.isRx(path)) - if (!opts.noRx && !this.isRx(path)) { - opts = Object.assign(check.isPlainObject(value) ? { values: value, traverse: true } : { value: value }, opts) - // console.log('set opts', opts) + console.log('set:', path, value, opts, !this.isRx(path)) + if (!opts.noRx && !this.isRx(path)) { // travsere + opts = Object.assign( + (check.isPlainObject(value) && opts.traverse !== false) + ? { values: value, traverse: true } + : { value: value } + , opts) + console.log('adding rx from set', opts) this.rxAdd(path, opts) } else { const curValue = this.#get(path) if (!equal(curValue, value) && value !== undefined) { - // console.log('in set', path,value) + console.log('setting already reactive value', path, value) this.#set(path, value) - // console.log('value that was set',this.#get(path)) + console.log('value that was set', this.#get(path)) } return this.#get(path) } } - // getRaw () { - // return get(this, [this.#rx.namespace, ...arguments]) - // } + getRaw () { + return get(this, [this.#rx.namespace, ...arguments]) + } // if property exists it moves value to #rx.props and creates getter and setter and behavior subject rxAdd (path, opts = {}) { @@ -78,23 +83,24 @@ class RxClass extends EventEmitter { const self = this // console.log('object to traverse', obj) traverse(obj).map(function () { + // console.log('traversing', this) if (this.isLeaf) { const lpath = this.path lpath.unshift(path) - // console.log(`#{!opts.values ? 'existing':'new'} leaf on path '#{lpath}' with value: #{this.node}`) + // console.log('#{!opts.values ? \'existing\':\'new\'} leaf on path \'#{lpath}\' with value: #{this.node}') self.rxAdd(lpath, { value: this.node }) } - return true } ) return true } // end traverse - if (this.isRx(path) && !opts.force) return true const value = this.#get(path) // current value if (value === undefined) { - // console.log ('no current property or value for',path,'creating temporary null') + // console.log('no current property or value for', path, 'creating temporary null') this.#set(path, null) } + if (this.isRx(path) && !opts.force) return true + console.log(path, 'making reactive') const { parent, name } = this.#rxGetObj(path, '__parent__') log.debug({ class: 'RxClass', @@ -149,6 +155,7 @@ class RxClass extends EventEmitter { if (check.isFunction(rx.hook)) rx.hook.call(self, value) // property hook } }) + // console.log('rxadd done', path, opts, this.#get(['#rx.props', path])) return true } @@ -183,7 +190,6 @@ class RxClass extends EventEmitter { lpath.unshift(path) if (self.isRx(lpath)) self.rxRemove(lpath, { confirm: true }) } - return true }) // all done removing the leaves so remove branch this.#del(['#rx.props', path], true) @@ -244,30 +250,74 @@ class RxClass extends EventEmitter { return false } - // // takes several formats for a path to an objects property and return only the . string - // formatObjPath (path) { - // if (path == null) return path - // if (Array.isArray(path)) { - // path = path.filter(Boolean).join('.') // Boolean filter strips empty strings or null/undefined - // } - // path = path.replace(/\/:,/g, '.') // replaces /:, with . - // return path - // } - - // checks for getter and the corresponding rx object - isRx (path) { - // console.log(path) - if (this.#get(path) === undefined) return false - const { parent, name, parentPath } = this.#rxGetObj(path, '__parent__') - // console.log(parent, name, parentPath) - const rxparent = this.#get(['#rx.props', parentPath]) || {} - const rx = rxparent[name] - // console.log(path, 'rxparent,rx', !!parent, !!name, !!rxparent, !!rx) - if (!rx) return false - // console.log(Object.getOwnPropertyDescriptor(parent, name).get) - return !!Object.getOwnPropertyDescriptor(parent, name).get + isGetSet (path) { + const keys = getKeys(path) + const prop = keys.pop() + const parent = this.#get(keys) + // console.log('isgetset', path, keys, prop, 'parent?', !!parent) + if (parent && prop) { + return !!(Object.getOwnPropertyDescriptor(parent, prop) || {}).get && + !!(Object.getOwnPropertyDescriptor(parent, prop) || {}).set + } + return false } + // is already reactive, checks for setter/getter and the corresponding rx object + isRx (path) { + // console.log('in isRX', path) + const keys = getKeys(path) + const cnt = keys.length + for (let index = 0; index < cnt; index++) { + // console.log('testing rx', index, cnt, keys, !!this.#get(keys), !!this.isGetSet(keys), !!this.#get(['#rx.props', keys])) + if (this.#get(keys) === undefined) return false + if (this.isGetSet(keys) && this.#get(['#rx.props', keys])) return true + keys.pop() + } + return false + // // console.log('in rx every', lpath) + // // if (this.#get(['#rx.props', ...lpath]) === undefined) { + // // return false + // // } + // // + // console.log(parent, name, parentPath) + // const rxparent = this.#get(['#rx.props', parentPath]) || {} + // const rx = rxparent[name] + // console.log(path, 'rxparent,rx', !!parent, !!name, !!rxparent, !!rx) + // if (!rx) return true + // else { + // console.log(Object.getOwnPropertyDescriptor(parent, name).get) + // isRx = !!Object.getOwnPropertyDescriptor(parent, name).get + // return false + // } + // }) + // return isRx + } + + // isRx(path) { + // // console.log(path) + // let isRx = false + // const lpath = [] + // getKeys(path).every(key => { + // lpath.push(key) + // console.log('in rx every', lpath, this.#get(lpath)) + // if (this.#get(lpath) === undefined) { + // return false + // } + // // const { parent, name, parentPath } = this.#rxGetObj(lpath, '__parent__') + // // console.log(parent, name, parentPath) + // const rxparent = this.#get(['#rx.props', parentPath]) || {} + // const rx = rxparent[name] + // console.log(path, 'rxparent,rx', !!parent, !!name, !!rxparent, !!rx) + // if (!rx) return true + // else { + // console.log(Object.getOwnPropertyDescriptor(parent, name).get) + // isRx = !!Object.getOwnPropertyDescriptor(parent, name).get + // return false + // } + // }) + // return isRx + // } + // PRIVATE METHODS // pass '__parent__' when getting objects for creating reactive prop