support for name spacing of reactive methods and objects/fields
support for namesapce and preface of get and setmaster
parent
4b76b80f07
commit
ce0e77399a
37
.eslintrc.js
37
.eslintrc.js
|
@ -1,37 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"modules": true,
|
|
||||||
"spread" : true,
|
|
||||||
"restParams" : true
|
|
||||||
},
|
|
||||||
// "plugins": [
|
|
||||||
// "unicorn"
|
|
||||||
// ],
|
|
||||||
"env": {
|
|
||||||
"es6": true,
|
|
||||||
"node": true,
|
|
||||||
"mocha": true
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2017,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"extends": "eslint:recommended",
|
|
||||||
"rules": {
|
|
||||||
"indent": [
|
|
||||||
"error",
|
|
||||||
2
|
|
||||||
],
|
|
||||||
// "unicorn/no-array-instanceof": "error",
|
|
||||||
"no-console": 0,
|
|
||||||
"semi": ["error", "never"],
|
|
||||||
"linebreak-style": [
|
|
||||||
"error",
|
|
||||||
"unix"
|
|
||||||
],
|
|
||||||
"quotes": [
|
|
||||||
"error",
|
|
||||||
"single"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,9 @@
|
||||||
import RxClass from '../src/rx-class.js'
|
import RxClass from '../src/rx-class.js'
|
||||||
|
|
||||||
|
const log = o => console.dir(o, { depth: 5 })
|
||||||
|
|
||||||
|
const rxopts = Symbol.for('rx-class-opts')
|
||||||
|
|
||||||
class Example extends RxClass {
|
class Example extends RxClass {
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
super(opts)
|
super(opts)
|
||||||
|
@ -9,51 +13,67 @@ class Example extends RxClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const opts = {
|
||||||
const example = new Example({
|
[rxopts]: {
|
||||||
_rx_ : {
|
|
||||||
handler: value => { console.log('-----------default subscription handler-------', value) },
|
handler: value => { console.log('-----------default subscription handler-------', value) },
|
||||||
skip: 1
|
skip: 1
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
example.foundation = null
|
console.log('options\n', opts)
|
||||||
|
|
||||||
console.log('-----EXAMPLE: TRAVERSE OF EXISTING OBJECT OR WHEN SETTING A PLAIN OBJECT-----------')
|
const example = new Example(opts)
|
||||||
console.log('instance before doing rx on properties\n')
|
|
||||||
console.log(example)
|
// console.log('-----EXAMPLE: TRAVERSE OF EXISTING OBJECT OR WHEN SETTING A PLAIN OBJECT-----------')
|
||||||
console.log('--------------------------')
|
// console.log('instance before doing rx on properties\n')
|
||||||
|
// // log(example)
|
||||||
|
// console.log('------------------------------------------')
|
||||||
|
|
||||||
// example.on('changed', (prop) => console.log('emitted----a property changed-----', prop))
|
// example.on('changed', (prop) => console.log('emitted----a property changed-----', prop))
|
||||||
// example.on('test', (val) => console.log('emitted value of test', val))
|
// example.on('test', (val) => console.log('emitted value of test', val))
|
||||||
|
|
||||||
console.log('traversing exitings object \'test\' and making all leaves reactive')
|
// console.log('----- traversing existing object \'test\' and making all leaves reactive ------\n')
|
||||||
console.log(example.test)
|
// example.rxAdd('test', { traverse: true })
|
||||||
example.rxAdd('test',{traverse:true})
|
// // log(example)
|
||||||
|
// console.log('------------------------------------------')
|
||||||
|
|
||||||
console.log('adding new property which is made reactive by default \'test.today.bong.why\'')
|
// console.log('now get value for bong directly and via get', example.test.today.bong, example.get('test.today.bong'))
|
||||||
example.set('test.today.bong.why','do not know')
|
// console.log('Note: using example.get will traverse bong and get actual values')
|
||||||
|
|
||||||
console.log('------\'test\', raw object ------ \n', example.$get('test'))
|
// console.log('adding new property which is made reactive by default \'test.today.bong.why\'')
|
||||||
console.log('------ \'test\', values object ------\n>', example.get('test'))
|
// example.set('test.today.bong.why', 'do not know')
|
||||||
console.log(example)
|
|
||||||
console.log('now removing reactivty from \'test\'')
|
|
||||||
example.rxRemove('test',{confirm:true})
|
|
||||||
console.log(example.$get('test'))
|
|
||||||
console.log('-------------------------')
|
|
||||||
|
|
||||||
console.log('now add a completely new object to root of \'foundation\' and make it reactive')
|
// console.log('------\'.bong\', raw object ------ \n', example.test.today.bong)
|
||||||
const obj = {giskard:'daneel', hari: {name:'seldon', planet:'Helcion'}}
|
// console.log('------ \'.bong\', values object ------\n>', example.get('test.today.bong'))
|
||||||
example.set('foundation',obj)
|
// console.log('------, note: raw leaf access returns value not [Getter/Setter] ==>', example.test.today.bong.why)
|
||||||
|
// console.log('\nnow removing reactivty from \'test\'')
|
||||||
|
// example.rxRemove('test', { confirm: true })
|
||||||
|
// console.log('------\'.test\', raw object ------ \n', example.test)
|
||||||
|
// console.log('--------------------------')
|
||||||
|
|
||||||
console.log('------\'foundation\', raw object ------ \n', example.$get('foundation'))
|
console.log('\n\nnow add a completely new object to root of \'foundation\' and make it reactive without traverse')
|
||||||
console.log('------ \'foundation\', values object ------\n>', example.get('foundation'))
|
example.foundation = { planets: ['Trantor', 'Helcion'], persons: { giskard: { alias: 'daneel' }, hari: { name: 'seldon', planet: 'Helcion' } } }
|
||||||
|
console.dir(example.foundation)
|
||||||
|
example.rxAdd('foundation.persons.hari.name')
|
||||||
|
// console.log('foundation', example.isRx('foundation'))
|
||||||
|
console.log(example.isRx('foundation.persons.hari'))
|
||||||
|
// console.log('------\'foundation\', raw object ------ \n')
|
||||||
|
// // log(example)
|
||||||
|
// console.log('get raw nested property raw or with get returns the same')
|
||||||
|
// console.log(example.foundation.hari, example.get('foundation.hari'))
|
||||||
|
|
||||||
console.log('--------instance after doing rx----\n')
|
// console.log('now change one of the nested properties and see if whole object foundation is reactive')
|
||||||
console.log(example)
|
|
||||||
console.log('--------------------------')
|
|
||||||
|
|
||||||
console.log('now change a reactive property directly like')
|
// example.foundation.hari.planet = 'Trantor'
|
||||||
console.log('example.foundation.giskard = \'daneel a robot\'')
|
// example.set('foundation.hari.planet', 'Trantor')
|
||||||
example.foundation.giskard = 'daneel a robot'
|
|
||||||
console.log('the new value extracted by the getter is',example.foundation.giskard)
|
// console.log(example.foundation)
|
||||||
|
// log(example)
|
||||||
|
// console.log('--------instance after doing rx----\n')
|
||||||
|
// console.log(example)
|
||||||
|
// console.log('--------------------------')
|
||||||
|
|
||||||
|
// console.log('now change a reactive property directly like')
|
||||||
|
// console.log('example.foundation.giskard = \'daneel a robot\'')
|
||||||
|
// example.foundation.giskard = 'daneel a robot'
|
||||||
|
// console.log('the new value extracted by the getter is', example.foundation.giskard)
|
||||||
|
|
|
@ -31,8 +31,9 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/uCOMmandIt/uci-utils#readme",
|
"homepage": "https://github.com/uCOMmandIt/uci-utils#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@uci-utils/bind-funcs": "^0.3.0",
|
||||||
"@uci-utils/logger": "^0.1.0",
|
"@uci-utils/logger": "^0.1.0",
|
||||||
"@uci-utils/obj-nested-prop": "^0.1.1",
|
"@uci-utils/obj-nested-prop": "^0.1.2",
|
||||||
"@uci-utils/type": "^0.6.2",
|
"@uci-utils/type": "^0.6.2",
|
||||||
"deep-equal": "^2.0.5",
|
"deep-equal": "^2.0.5",
|
||||||
"rxjs": "^7.0.0",
|
"rxjs": "^7.0.0",
|
||||||
|
|
264
src/rx-class.js
264
src/rx-class.js
|
@ -6,80 +6,89 @@ import { BehaviorSubject, from } from 'rxjs'
|
||||||
import { distinctUntilChanged, skip } from 'rxjs/operators'
|
import { distinctUntilChanged, skip } from 'rxjs/operators'
|
||||||
// uci modules
|
// uci modules
|
||||||
import { check } from '@uci-utils/type'
|
import { check } from '@uci-utils/type'
|
||||||
import { get, set, del, walkPath, getKeys, pathToString } from '@uci-utils/obj-nested-prop'
|
import bind from '@uci-utils/bind-funcs'
|
||||||
|
import { get as $get, set as $set, del as $del, walkPath, getKeys, pathToString } from '@uci-utils/obj-nested-prop'
|
||||||
import { logger } from '@uci-utils/logger'
|
import { logger } from '@uci-utils/logger'
|
||||||
// supporting
|
// supporting
|
||||||
import equal from 'deep-equal'
|
import equal from 'deep-equal'
|
||||||
import traverse from 'traverse'
|
import traverse from 'traverse'
|
||||||
|
|
||||||
const log = logger({ file: '/src/rx-class.js', package: '@uci-utils/rx-class', class: 'RxClass' })
|
const log = logger({ file: '/src/rx-class.js', package: '@uci-utils/rx-class', class: 'RxClass' })
|
||||||
|
|
||||||
// to pass rx options use 'Symbol.for('rx-class-opts')'
|
// to pass rx options use 'Symbol.for('rx-class-opts')'
|
||||||
class RxClass extends EventEmitter {
|
class RxClass extends EventEmitter {
|
||||||
// private fields
|
|
||||||
#rx
|
|
||||||
|
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
super(opts)
|
super(opts)
|
||||||
const rxopts = opts[Symbol.for('rx-class-opts')] || {}
|
const rxopts = opts[Symbol.for('rx-class-opts')] || {}
|
||||||
this.#rx = {
|
Object.defineProperty(this, '$$__rxns__$$', {
|
||||||
|
enumerable: false,
|
||||||
|
value: rxopts.namespace || 'rx'
|
||||||
|
})
|
||||||
|
this[this.$$__rxns__$$] = bind(methods, this)
|
||||||
|
this[this.$$__rxns__$$].props = {} // where reactive property info is stored including actual values
|
||||||
|
this[this.$$__rxns__$$].opts = {
|
||||||
emitter: rxopts.emitter != null ? rxopts.emitter : true,
|
emitter: rxopts.emitter != null ? rxopts.emitter : true,
|
||||||
event: rxopts.event === false ? false : rxopts.event || 'changed',
|
event: rxopts.event === false ? false : rxopts.event || 'changed',
|
||||||
handler: rxopts.handler, // default subscription handler
|
handler: rxopts.handler, // default subscription handler
|
||||||
props: {}, // where reactive property info is store including actual values
|
|
||||||
amendValue: rxopts.amendValue,
|
amendValue: rxopts.amendValue,
|
||||||
// amend_path: rxopts.amend_path, // by default amend value with path
|
// amend_path: rxopts.amend_path, // by default amend value with path
|
||||||
hook: rxopts.hook, // function called when setting
|
hook: rxopts.hook, // function called when setting
|
||||||
namespace: rxopts.namespace || 'rx', // TODO namespace to added to all public rx methods
|
namespace: this.$$__rxns__$$, // TODO namespace to added to all public rx methods
|
||||||
operators: new Map(), // TODO added to all rx pipes, in order
|
operators: new Map(), // TODO added to all rx pipes, in order
|
||||||
skip: rxopts.skip == null ? 1 : rxopts.skip
|
skip: rxopts.skip == null ? 1 : rxopts.skip
|
||||||
}
|
}
|
||||||
this.isRx.bind(this)
|
console.log(`${rxopts.getSetPrefix || ''}set`)
|
||||||
}
|
$set(this, [rxopts.getSetNamespace, `${rxopts.getSetPrefix || ''}set`], set.bind(this))
|
||||||
|
$set(this, [rxopts.getSetNamespace, `${rxopts.getSetPrefix || ''}get`], get.bind(this))
|
||||||
|
console.log(this)
|
||||||
|
} // end constructor
|
||||||
|
// reactive methods are bound at instantiation
|
||||||
|
} // end class
|
||||||
|
|
||||||
get (path) {
|
// helpers
|
||||||
const value = this.#get(path)
|
|
||||||
|
export default RxClass
|
||||||
|
export { RxClass, $set as set, $get as get, $del as del, getKeys, walkPath, pathToString }
|
||||||
|
|
||||||
|
function get (path) {
|
||||||
|
const value = _.get.call(this, path)
|
||||||
if (!check.isPlainObject(value)) return value
|
if (!check.isPlainObject(value)) return value
|
||||||
const obj = {} // return a copy with actual values instead of getters
|
const obj = {} // return a copy with actual values instead of getters
|
||||||
const self = this
|
const self = this
|
||||||
traverse(this.#get(path)).map(function () {
|
traverse(_.get.call(this, path)).map(function () {
|
||||||
if (this.isLeaf) {
|
if (this.isLeaf) {
|
||||||
self.#set(obj, this.path, this.node)
|
_.set.call(self, obj, this.path, this.node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
set (path, value, opts = {}) {
|
function set (path, value, opts = {}) {
|
||||||
console.log('set:', path, value, opts, !this.isRx(path))
|
console.log('set:', path, value, opts, !this[this.$$__rxns__$$].isRx(path))
|
||||||
if (!opts.noRx && !this.isRx(path)) { // travsere
|
if (!opts.noRx && !this[this.$$__rxns__$$].isRx(path)) { // travsere
|
||||||
opts = Object.assign(
|
opts = Object.assign(
|
||||||
(check.isPlainObject(value) && opts.traverse !== false)
|
(check.isPlainObject(value) && opts.traverse !== false)
|
||||||
? { values: value, traverse: true }
|
? { values: value, traverse: true }
|
||||||
: { value: value }
|
: { value: value }
|
||||||
, opts)
|
, opts)
|
||||||
console.log('adding rx from set', opts)
|
console.log('adding rx from set', opts)
|
||||||
this.rxAdd(path, opts)
|
this[this.$$__rxns__$$].add(path, opts)
|
||||||
} else {
|
} else {
|
||||||
const curValue = this.#get(path)
|
const curValue = _.get.call(this, path)
|
||||||
if (!equal(curValue, value) && value !== undefined) {
|
if (!equal(curValue, value) && value !== undefined) {
|
||||||
console.log('setting already reactive value', path, value)
|
console.log('setting already reactive value', path, value)
|
||||||
this.#set(path, value)
|
_.set.call(this, path, value)
|
||||||
console.log('value that was set', this.#get(path))
|
console.log('value that was set', _.get.call(this, path))
|
||||||
}
|
}
|
||||||
return this.#get(path)
|
return _.get.call(this, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getRaw () {
|
// public methods will be namespaced (default is rx.)
|
||||||
return get(this, [this.#rx.namespace, ...arguments])
|
const methods = {
|
||||||
}
|
add: function add (path, opts = {}) {
|
||||||
|
|
||||||
// if property exists it moves value to #rx.props and creates getter and setter and behavior subject
|
|
||||||
rxAdd (path, opts = {}) {
|
|
||||||
if (opts.traverse) { // will add rx to all leaves
|
if (opts.traverse) { // will add rx to all leaves
|
||||||
const obj = opts.values ? opts.values : this.#get(path)
|
const obj = opts.values ? opts.values : _.get.call(this, path)
|
||||||
const self = this
|
const self = this
|
||||||
// console.log('object to traverse', obj)
|
// console.log('object to traverse', obj)
|
||||||
traverse(obj).map(function () {
|
traverse(obj).map(function () {
|
||||||
|
@ -88,36 +97,36 @@ class RxClass extends EventEmitter {
|
||||||
const lpath = this.path
|
const lpath = this.path
|
||||||
lpath.unshift(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 })
|
self[this.$$__rxns__$$].add(lpath, { value: this.node })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
} // end traverse
|
} // end traverse
|
||||||
const value = this.#get(path) // current value
|
const value = _.get.call(this, path) // current value
|
||||||
if (value === undefined) {
|
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)
|
_.set.call(this, path, null)
|
||||||
}
|
}
|
||||||
if (this.isRx(path) && !opts.force) return true
|
if (this[this.$$__rxns__$$].isRx(path) && !opts.force) return true
|
||||||
console.log(path, 'making reactive')
|
console.log(path, 'making reactive')
|
||||||
const { parent, name } = this.#rxGetObj(path, '__parent__')
|
const { parent, name } = _.rxGetObj.call(this, path, '__parent__')
|
||||||
log.debug({
|
log.debug({
|
||||||
class: 'RxClass',
|
class: 'RxClass',
|
||||||
method: '#rxGetObj',
|
method: 'rx.add',
|
||||||
path: path,
|
path: path,
|
||||||
name: name,
|
name: name,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
msg: 'got name and parent'
|
msg: 'got name and parent'
|
||||||
})
|
})
|
||||||
this.#set(['#rx.props', path], {})
|
_.set.call(this, [this.$$__rxns__$$, 'props', path], {})
|
||||||
const rx = this.#get(['#rx.props', path])
|
const rx = _.get.call(this, [this.$$__rxns__$$, 'props', path])
|
||||||
// console.log('moving',opts.value != null ? opts.value : value,path)
|
// console.log('moving',opts.value != null ? opts.value : value,path)
|
||||||
rx.value = opts.value != null ? opts.value : value
|
rx.value = opts.value != null ? opts.value : value
|
||||||
rx.path = path
|
rx.path = path
|
||||||
rx.amendValue = opts.amendValue || this.#rx.amendValue || ((val) => val)
|
rx.amendValue = opts.amendValue || this[this.$$__rxns__$$].opts.amendValue || ((val) => val)
|
||||||
rx.obs = from(new BehaviorSubject(rx.amendValue(rx.value, rx.path)).pipe(
|
rx.obs = from(new BehaviorSubject(rx.amendValue(rx.value, rx.path)).pipe(
|
||||||
skip(opts.skip != null ? opts.skip : this.#rx.skip),
|
skip(opts.skip != null ? opts.skip : this[this.$$__rxns__$$].opts.skip),
|
||||||
distinctUntilChanged() // allow custom comparator
|
distinctUntilChanged() // allow custom comparator
|
||||||
// allow custom/additional operators here
|
// allow custom/additional operators here
|
||||||
// takeUntil(#get(this.#deleted,path))
|
// takeUntil(#get(this.#deleted,path))
|
||||||
|
@ -125,10 +134,10 @@ class RxClass extends EventEmitter {
|
||||||
// console.log(path,'---------------\n',Object.getOwnPropertyNames(Object.getPrototypeOf(rx.obs)),rx.obs.subscribe)
|
// console.log(path,'---------------\n',Object.getOwnPropertyNames(Object.getPrototypeOf(rx.obs)),rx.obs.subscribe)
|
||||||
rx.subs = {}
|
rx.subs = {}
|
||||||
rx.hook = opts.hook
|
rx.hook = opts.hook
|
||||||
if (check.isFunction(this.#rx.handler)) this.rxSubscribe(rx, this.#rx.handler, '_default_')
|
if (check.isFunction(this[this.$$__rxns__$$].opts.handler)) this[this.$$__rxns__$$].subscribe(rx, this[this.$$__rxns__$$].opts.handler, '_default_')
|
||||||
const subs = Object.entries(opts.subscribe || {})
|
const subs = Object.entries(opts.subscribe || {})
|
||||||
subs.forEach(sub => {
|
subs.forEach(sub => {
|
||||||
if (check.isFunction(sub[1])) this.rxSubscribe(rx, sub[1], sub[0])
|
if (check.isFunction(sub[1])) this[this.$$__rxns__$$].subscribe(rx, sub[1], sub[0])
|
||||||
})
|
})
|
||||||
const self = this
|
const self = this
|
||||||
Object.defineProperty(parent, name, {
|
Object.defineProperty(parent, name, {
|
||||||
|
@ -141,9 +150,9 @@ class RxClass extends EventEmitter {
|
||||||
rx.value = value
|
rx.value = value
|
||||||
value = rx.amendValue(value, path)
|
value = rx.amendValue(value, path)
|
||||||
rx.obs.next(value) // react
|
rx.obs.next(value) // react
|
||||||
if (self.#rx.emitter) { // emit
|
if (self[this.$$__rxns__$$].opts.emitter) { // emit
|
||||||
if (opts.event) self.emit(opts.event, value, path) // custom event
|
if (opts.event) self.emit(opts.event, value, path) // custom event
|
||||||
if (self.#rx.event) self.emit(self.#rx.event, value, path) // global event
|
if (self[this.$$__rxns__$$].opts.event) self.emit(self[this.$$__rxns__$$].opts.event, value, path) // global event
|
||||||
const spath = pathToString(path)
|
const spath = pathToString(path)
|
||||||
if (spath) self.emit(spath, value, path) // also emit path if is stringable.
|
if (spath) self.emit(spath, value, path) // also emit path if is stringable.
|
||||||
self.emit(name, value, path)
|
self.emit(name, value, path)
|
||||||
|
@ -151,52 +160,47 @@ class RxClass extends EventEmitter {
|
||||||
// any hook function that is already bound will use that context, otherwise this
|
// any hook function that is already bound will use that context, otherwise this
|
||||||
// hooks
|
// hooks
|
||||||
// TODO write with for loop and async await
|
// TODO write with for loop and async await
|
||||||
if (check.isFunction(self.#rx.hook)) self.#rx.hook.call(self, value) // global hook
|
if (check.isFunction(self[this.$$__rxns__$$].opts.hook)) self[this.$$__rxns__$$].opts.hook.call(self, value) // global hook
|
||||||
if (check.isFunction(rx.hook)) rx.hook.call(self, value) // property hook
|
if (check.isFunction(rx.hook)) rx.hook.call(self, value) // property hook
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// console.log('rxadd done', path, opts, this.#get(['#rx.props', path]))
|
// console.log('rxadd done', path, opts, _.get.call(this,[this.$$__rxns__$$,'props', path]))
|
||||||
return true
|
return true
|
||||||
}
|
},
|
||||||
|
|
||||||
// if name is only passed remove
|
amendValue: function amendValue (path, func) {
|
||||||
// rxHook (func, path) {
|
if (arguments.length === 0) { this[this.$$__rxns__$$].opts.amendValue = null; return }
|
||||||
// this.#rx.hooks[name] = func
|
if (typeof path === 'function') { this[this.$$__rxns__$$].opts.amendValue = path; return }
|
||||||
// }
|
const rx = _.rxGetObj.call(this, path)
|
||||||
|
|
||||||
rxAmendValue (path, func) {
|
|
||||||
if (arguments.length === 0) { this.#rx.amendValue = null; return }
|
|
||||||
if (typeof path === 'function') { this.#rx.amendValue = path; return }
|
|
||||||
const rx = this.#rxGetObj(path)
|
|
||||||
if (!check.isEmptyPlainObject(rx)) return false
|
if (!check.isEmptyPlainObject(rx)) return false
|
||||||
func = func || (val => val)
|
func = func || (val => val)
|
||||||
rx.amendValue = func === 'path' ? (value, path) => { return { value: value, path: path } } : func
|
rx.amendValue = func === 'path' ? (value, path) => { return { value: value, path: path } } : func
|
||||||
return !!rx.amendValue
|
return !!rx.amendValue
|
||||||
}
|
},
|
||||||
|
|
||||||
// if name is only passed remove
|
// if name is only passed remove
|
||||||
rxOperator (func, name) {
|
operator: function Operator (func, name) {
|
||||||
this.#rx.operators[name] = func
|
this[this.$$__rxns__$$].opts.operators[name] = func
|
||||||
}
|
},
|
||||||
|
|
||||||
// removes obser, subscriptions, and getter and setter and make back into plain value
|
// removes obser, subscriptions, and getter and setter and make back into plain value
|
||||||
rxRemove (path, opts = {}) {
|
remove: function remove (path, opts = {}) {
|
||||||
if (!opts.confirm) return false // must confirm to remove
|
if (!opts.confirm) return false // must confirm to remove
|
||||||
if (!this.isRx(path) && check.isPlainObject(this.#get(path))) {
|
if (!this[this.$$__rxns__$$].isRx(path) && check.isPlainObject(_.get.call(this, path))) {
|
||||||
const self = this
|
const self = this
|
||||||
traverse(this.#get(path)).map(function () {
|
traverse(_.get.call(this, path)).map(function () {
|
||||||
if (this.isLeaf) {
|
if (this.isLeaf) {
|
||||||
const lpath = this.path
|
const lpath = this.path
|
||||||
lpath.unshift(path)
|
lpath.unshift(path)
|
||||||
if (self.isRx(lpath)) self.rxRemove(lpath, { confirm: true })
|
if (self[this.$$__rxns__$$].isRx(lpath)) self[this.$$__rxns__$$].remove(lpath, { confirm: true })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// all done removing the leaves so remove branch
|
// all done removing the leaves so remove branch
|
||||||
this.#del(['#rx.props', path], true)
|
_.del.call(this, [this.$$__rxns__$$, 'props', path], true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const { parent, name, parentPath } = this.#rxGetObj(path, '__parent__')
|
const { parent, name, parentPath } = _.rxGetObj.call(this, path, '__parent__')
|
||||||
const rxparent = this.#get(['#rx.props', parentPath])
|
const rxparent = _.get.call(this, [this.$$__rxns__$$, 'props', parentPath])
|
||||||
const rx = rxparent[name]
|
const rx = rxparent[name]
|
||||||
if (!rx) return true // // not reactive nothing to remove
|
if (!rx) return true // // not reactive nothing to remove
|
||||||
Object.values(rx.subs).forEach(sub => sub.unsubscribe())
|
Object.values(rx.subs).forEach(sub => sub.unsubscribe())
|
||||||
|
@ -205,20 +209,19 @@ class RxClass extends EventEmitter {
|
||||||
delete rxparent[name]
|
delete rxparent[name]
|
||||||
// console.log('removed rx from', path)
|
// console.log('removed rx from', path)
|
||||||
return true
|
return true
|
||||||
}
|
},
|
||||||
|
|
||||||
rxGetObs (path) {
|
getObs: function getObs (path) {
|
||||||
return (this.#rxGetObj(path) || {}).obs
|
return (_.rxGetObj.call(this, path) || {}).obs
|
||||||
}
|
},
|
||||||
|
|
||||||
rxGetSubs (path, name) {
|
getSubs: function getSubs (path, name) {
|
||||||
const rx = this.#rxGetObj(path)
|
const rx = _.rxGetObj.call(this, path)
|
||||||
if (rx) return (name ? rx.subs[name] : Object.keys(rx.subs))
|
if (rx) return (name ? rx.subs[name] : Object.keys(rx.subs))
|
||||||
}
|
},
|
||||||
|
|
||||||
// pass rx as object or path
|
// pass rx as object or path
|
||||||
rxSubscribe (path, handler, name) {
|
subscribe: function subscribe (path, handler, name) {
|
||||||
const rx = this.#rxGetObj(path)
|
const rx = _.rxGetObj.call(this, path)
|
||||||
if (rx) {
|
if (rx) {
|
||||||
// TODO if no name generate a name and return with handle
|
// TODO if no name generate a name and return with handle
|
||||||
if (check.isFunction(handler)) {
|
if (check.isFunction(handler)) {
|
||||||
|
@ -229,10 +232,10 @@ class RxClass extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
},
|
||||||
|
|
||||||
rxRemoveSubs (path, name) {
|
removeSubs: function removeSubs (path, name) {
|
||||||
const rx = this.#rxGetObj(path)
|
const rx = _.rxGetObj.call(this, path)
|
||||||
// if (name && name!=='_default' && (rxObj||{}).path) console.log('rx object for',rxObj.path)
|
// if (name && name!=='_default' && (rxObj||{}).path) console.log('rx object for',rxObj.path)
|
||||||
if (rx) {
|
if (rx) {
|
||||||
if (name) {
|
if (name) {
|
||||||
|
@ -248,122 +251,71 @@ class RxClass extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
},
|
||||||
|
isGetSet: function isGetSet (path) {
|
||||||
isGetSet (path) {
|
|
||||||
const keys = getKeys(path)
|
const keys = getKeys(path)
|
||||||
const prop = keys.pop()
|
const prop = keys.pop()
|
||||||
const parent = this.#get(keys)
|
const parent = _.get.call(this, keys)
|
||||||
// console.log('isgetset', path, keys, prop, 'parent?', !!parent)
|
// console.log('isgetset', path, keys, prop, 'parent?', !!parent)
|
||||||
if (parent && prop) {
|
if (parent && prop) {
|
||||||
return !!(Object.getOwnPropertyDescriptor(parent, prop) || {}).get &&
|
return !!(Object.getOwnPropertyDescriptor(parent, prop) || {}).get &&
|
||||||
!!(Object.getOwnPropertyDescriptor(parent, prop) || {}).set
|
!!(Object.getOwnPropertyDescriptor(parent, prop) || {}).set
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
},
|
||||||
|
|
||||||
// is already reactive, checks for setter/getter and the corresponding rx object
|
// is already reactive, checks for setter/getter and the corresponding rx object
|
||||||
isRx (path) {
|
isRx: function isRx (path) {
|
||||||
// console.log('in isRX', path)
|
// console.log('in isRX', path)
|
||||||
const keys = getKeys(path)
|
const keys = getKeys(path)
|
||||||
const cnt = keys.length
|
const cnt = keys.length
|
||||||
for (let index = 0; index < cnt; index++) {
|
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]))
|
// console.log('testing rx', index, cnt, keys, !!_.get.call(this,keys), !!this.isGetSet(keys), !!_.get.call(this,[this.$$__rxns__$$,'props', keys]))
|
||||||
if (this.#get(keys) === undefined) return false
|
if (_.get.call(this, keys) === undefined) return false
|
||||||
if (this.isGetSet(keys) && this.#get(['#rx.props', keys])) return true
|
if (this[this.$$__rxns__$$].isGetSet(keys) && _.get.call(this, [this.$$__rxns__$$, 'props', keys])) return true
|
||||||
keys.pop()
|
keys.pop()
|
||||||
}
|
}
|
||||||
return false
|
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) {
|
// private methods
|
||||||
// // 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
|
const _ = {
|
||||||
|
rxGetObj: function rxGetObj (path, prop) {
|
||||||
// pass '__parent__' when getting objects for creating reactive prop
|
log.debug({ class: 'RxClass', method: '_.rxGetObj', path: path, prop: prop, msg: 'getting rx object' })
|
||||||
// otherwise it gets the rx prop object and/or one of it's props
|
|
||||||
#rxGetObj (path, prop) {
|
|
||||||
log.debug({ class: 'RxClass', method: '#rxGetObj', path: path, prop: prop, msg: 'getting rx object' })
|
|
||||||
let name, parent, parentPath, rx
|
let name, parent, parentPath, rx
|
||||||
// if (!path) parent = this
|
// if (!path) parent = this
|
||||||
if (check.isString(path) || check.isArray(path)) { // it's a normal path
|
if (check.isString(path) || check.isArray(path)) { // it's a normal path
|
||||||
const keys = getKeys(path)
|
const keys = getKeys(path)
|
||||||
name = (keys || []).pop()
|
name = (keys || []).pop()
|
||||||
parent = this.#get(keys)
|
parent = _.get.call(this, keys)
|
||||||
parentPath = keys
|
parentPath = keys
|
||||||
log.debug({ method: '#rxGetObj', path: path, prop: prop, key: name, parent: parent, msg: 'getting rx object' })
|
log.debug({ method: '_.rxGetObj', path: path, prop: prop, key: name, parent: parent, msg: 'getting rx object' })
|
||||||
if (parent === null) parent = {}
|
if (parent === null) parent = {}
|
||||||
if (prop === '__parent__') return { name: name, parent: parent, parentPath: parentPath }
|
if (prop === '__parent__') return { name: name, parent: parent, parentPath: parentPath }
|
||||||
rx = this.#get(['#rx.props', path])
|
rx = _.get.call(this, [this.$$__rxns__$$, 'props', path])
|
||||||
} else rx = check.isPlainObject(path) ? path : rx // if path was plain object assume it's already rx object
|
} else rx = check.isPlainObject(path) ? path : rx // if path was plain object assume it's already rx object
|
||||||
return prop ? (rx || {})[prop] : rx
|
return prop ? (rx || {})[prop] : rx
|
||||||
}
|
},
|
||||||
|
set: function set () {
|
||||||
#set () {
|
|
||||||
const args = [...arguments]
|
const args = [...arguments]
|
||||||
if (args.length < 2) return false
|
if (args.length < 2) return false
|
||||||
if (args.length === 2) args.unshift(this)
|
if (args.length === 2) args.unshift(this)
|
||||||
else if (!args[0]) return false
|
else if (!args[0]) return false
|
||||||
// console.log('in #set',args[1],args[2])
|
// console.log('in #set',args[1],args[2])
|
||||||
return set(...args)
|
return $set(...args)
|
||||||
}
|
},
|
||||||
|
get: function get () {
|
||||||
#get () {
|
|
||||||
const args = [...arguments]
|
const args = [...arguments]
|
||||||
if (args.length === 1) args.unshift(this)
|
if (args.length === 1) args.unshift(this)
|
||||||
return get(...args)
|
return $get(...args)
|
||||||
}
|
},
|
||||||
|
del: function del () {
|
||||||
#del () {
|
|
||||||
const args = [...arguments]
|
const args = [...arguments]
|
||||||
if (args.length < 2) return false
|
if (args.length < 2) return false
|
||||||
if (args[args.length - 1] !== true) return false
|
if (args[args.length - 1] !== true) return false
|
||||||
if (args.length === 2) args.unshift(this)
|
if (args.length === 2) args.unshift(this)
|
||||||
return del(...args)
|
return $del(...args)
|
||||||
}
|
}
|
||||||
} // end class
|
|
||||||
|
|
||||||
// helpers
|
}
|
||||||
|
|
||||||
export default RxClass
|
|
||||||
export { RxClass, set, get, del, getKeys, pathToString }
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import assert from 'assert'
|
import assert, { doesNotMatch } from 'assert'
|
||||||
import { RxClass } from '../src/rx-class.js'
|
import { RxClass } from '../src/rx-class.js'
|
||||||
|
|
||||||
const rxopts = Symbol.for('rx-class-opts')
|
const rxopts = Symbol.for('rx-class-opts')
|
||||||
|
@ -8,6 +8,8 @@ const obj = {
|
||||||
today: { bing: 'bing', bong: { who: 'first', what: 'second' } }
|
today: { bing: 'bing', bong: { who: 'first', what: 'second' } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const foundation = { planets: ['Trantor', 'Helcion'], persons: { giskard: { alias: 'daneel' }, hari: { name: 'seldon', planet: 'Helcion' } } }
|
||||||
|
|
||||||
class Test extends RxClass {
|
class Test extends RxClass {
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}) {
|
||||||
super(opts)
|
super(opts)
|
||||||
|
@ -17,121 +19,151 @@ class Test extends RxClass {
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
[rxopts]: {
|
[rxopts]: {
|
||||||
skip: 1,
|
// handler: () => {}
|
||||||
handler: () => {}
|
handler: (value) => { console.log('default subscription handler', value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const test = new Test(opts)
|
const test = new Test(opts)
|
||||||
|
opts[rxopts].namespace = 'rx2'
|
||||||
|
opts[rxopts].getSetPrefix = '_'
|
||||||
|
opts[rxopts].getSetNamespace = '$'
|
||||||
|
const test2 = new Test(opts)
|
||||||
|
|
||||||
describe('Reactive Class', function () {
|
describe('Reactive Class', function () {
|
||||||
it('Can be extended', function () {
|
it('Can be extended', function () {
|
||||||
assert.deepStrictEqual(test.obj, obj)
|
assert.deepStrictEqual(test.obj, obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Can get non rx deep value', function () {
|
// it('Can get non rx deep value', function () {
|
||||||
assert.strictEqual(test.obj.today.bing, 'bing')
|
// assert.strictEqual(test.obj.today.bing, 'bing')
|
||||||
assert.strictEqual(test.get('obj.today.bing'), 'bing')
|
// assert.strictEqual(test.get('obj.today.bing'), 'bing')
|
||||||
assert(!test.isRx('obj.today.bing'))
|
// assert(!test.isRx('obj.today.bing'))
|
||||||
})
|
// })
|
||||||
|
|
||||||
it('rx added to value', function () {
|
// it('can make a property reactive', function () {
|
||||||
test.rxAdd('obj.today.bing')
|
// test.rxAdd('obj.today.bing')
|
||||||
assert.strictEqual(test.obj.today.bing, 'bing')
|
// assert(test.isRx('obj.today.bing'))
|
||||||
assert.strictEqual(test.get('obj.today.bing'), 'bing')
|
// })
|
||||||
assert(test.isRx('obj.today.bing'))
|
|
||||||
|
it('can make a property containing object reactive', function () {
|
||||||
|
// test.foundation = foundation
|
||||||
|
test.set('foundation.test', foundation, { traverse: false })
|
||||||
|
test2.$._set('foundation.test', foundation, { traverse: false })
|
||||||
|
// console.log(test.foundation)
|
||||||
|
// test.rx.set('foundation.test.persons.jimbo', { name: 'jim', plant: 'remulak' })
|
||||||
|
// console.log(test.foundation.test)
|
||||||
|
assert(test.rx.isRx('foundation.test'))
|
||||||
|
assert(test2.rx2.isRx('foundation.test'))
|
||||||
|
// assert(test.rx.isRx('foundation.test.persons'))
|
||||||
|
// assert(test.rx.isRx('foundation.test.persons.giskard.alias'))
|
||||||
|
// assert(!test.rx.isRx('foundation.test.persons.bogus'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Emitted Events', function () {
|
// describe('Emitted Events', function () {
|
||||||
function etest (event, value, path, done) {
|
// function etest (event, value, path, done) {
|
||||||
test.once(event, (v, p) => {
|
// test.once(event, (v, p) => {
|
||||||
// console.log('fired:', event, value, path)
|
// // console.log('fired:', event, value, path)
|
||||||
assert.strictEqual(v, value)
|
// assert.strictEqual(v, value)
|
||||||
assert.strictEqual(p, path)
|
// assert.strictEqual(p, path)
|
||||||
clearTimeout(timeout)
|
// clearTimeout(timeout)
|
||||||
done()
|
// done()
|
||||||
})
|
// })
|
||||||
const timeout = setTimeout(() => {
|
// const timeout = setTimeout(() => {
|
||||||
assert(false, `Event ${event} did not fire in 1000 ms.`)
|
// assert(false, `Event ${event} did not fire in 1000 ms.`)
|
||||||
done()
|
// done()
|
||||||
}, 1000)
|
// }, 1000)
|
||||||
test.set(path, value)
|
// test.set(path, value)
|
||||||
}
|
// }
|
||||||
|
|
||||||
const path = 'obj.today.bing'
|
// const path = 'obj.today.bing'
|
||||||
it('should emit global default event', function (done) {
|
// it('should emit global default event', function (done) {
|
||||||
const event = 'changed'
|
// const event = 'changed'
|
||||||
etest(event, event, path, done)
|
// etest(event, event, path, done)
|
||||||
})
|
// })
|
||||||
it('should emit path if stringable', function (done) {
|
// it('should emit path if stringable', function (done) {
|
||||||
const event = path
|
// const event = path
|
||||||
etest(event, event, path, done)
|
// etest(event, event, path, done)
|
||||||
})
|
// })
|
||||||
|
|
||||||
it('should emit the key name of value being changed', function (done) {
|
// it('should emit the key name of value being changed', function (done) {
|
||||||
const event = path.split('.').pop()
|
// const event = path.split('.').pop()
|
||||||
etest(event, event, path, done)
|
// etest(event, event, path, done)
|
||||||
})
|
// })
|
||||||
|
|
||||||
it('should emit a custom set event for key/value change after force rxAdd', function (done) {
|
// it('should emit a custom set event for key/value change after force rxAdd', function (done) {
|
||||||
const event = 'testing'
|
// const event = 'testing'
|
||||||
test.rxAdd('obj.today.bing', { force: true, event: 'testing' })
|
// test.rxAdd('obj.today.bing', { force: true, event: 'testing' })
|
||||||
etest(event, event, path, done)
|
// etest(event, event, path, done)
|
||||||
})
|
// })
|
||||||
}) // end emits
|
// }) // end emits
|
||||||
|
|
||||||
describe('Subscriptions', function () {
|
// describe('Subscriptions', function () {
|
||||||
const subsHandler = (value, timeout, name, done, v) => {
|
// const subsHandler = (value, timeout, name, done, v) => {
|
||||||
assert.strictEqual(v, value)
|
// assert.strictEqual(v, value)
|
||||||
clearTimeout(timeout)
|
// clearTimeout(timeout)
|
||||||
test.rxRemoveSubs(name)
|
// test.rxRemoveSubs(name)
|
||||||
done()
|
// done()
|
||||||
}
|
// }
|
||||||
function subs (name, path, value, done, handler) {
|
// function subs (name, path, value, done, handler) {
|
||||||
const timeout = setTimeout(() => {
|
// const timeout = setTimeout(() => {
|
||||||
assert(false, 'subscription handler did not react in 1000 ms.')
|
// assert(false, 'subscription handler did not react in 1000 ms.')
|
||||||
done()
|
// done()
|
||||||
}, 1000)
|
// }, 1000)
|
||||||
const subscribe = handler ? { [handler]: subsHandler.bind(null, value, timeout, name, done) } : {}
|
// const subscribe = handler ? { [handler]: subsHandler.bind(null, value, timeout, name, done) } : {}
|
||||||
test.rxAdd(path, { force: true, subscribe: subscribe })
|
// test.rxAdd(path, { force: true, subscribe: subscribe })
|
||||||
if (!handler) {
|
// if (!handler) {
|
||||||
test.rxSubscribe('obj.today.bing',
|
// test.rxSubscribe('obj.today.bing',
|
||||||
subsHandler.bind(null, value, timeout, name, done)
|
// subsHandler.bind(null, value, timeout, name, done)
|
||||||
, name)
|
// , name)
|
||||||
}
|
// }
|
||||||
test.set(path, value)
|
// test.set(path, value)
|
||||||
}
|
// }
|
||||||
|
|
||||||
const path = 'obj.today.bing'
|
// const path = 'obj.today.bing'
|
||||||
it('should react to default subscription', function (done) {
|
// it('should react to default subscription', function (done) {
|
||||||
const name = '_default_'
|
// const name = '_default_'
|
||||||
subs(name, path, name, done, name)
|
// subs(name, path, name, done, name)
|
||||||
})
|
// })
|
||||||
it('should react to property/key subscription', function (done) {
|
// it('should react to property/key subscription', function (done) {
|
||||||
const name = 'keytest'
|
// const name = 'keytest'
|
||||||
subs(name, path, name, done, name)
|
// subs(name, path, name, done, name)
|
||||||
})
|
// })
|
||||||
it('should react to new subscription', function (done) {
|
// it('should react to new subscription', function (done) {
|
||||||
const name = 'atest'
|
// const name = 'atest'
|
||||||
subs(name, path, name, done)
|
// subs(name, path, name, done)
|
||||||
})
|
// })
|
||||||
}) // end subscriptions
|
// }) // end subscriptions
|
||||||
|
|
||||||
describe('TODO: Amend,Hooks,Operators', function () {
|
// describe('TODO: Amend,Hooks,Operators', function () {
|
||||||
it('should run a default hook', function (done) {
|
// it('should run a default hook', function (done) {
|
||||||
done()
|
// done()
|
||||||
})
|
// })
|
||||||
|
|
||||||
it('should run a custom hook', function (done) {
|
// it('should run a custom hook', function (done) {
|
||||||
done()
|
// done()
|
||||||
})
|
// })
|
||||||
|
|
||||||
it('should support custom rxjs operators', function (done) {
|
// it('should support custom rxjs operators', function (done) {
|
||||||
done()
|
// done()
|
||||||
})
|
// })
|
||||||
|
|
||||||
it('should amend a value that is set', function (done) {
|
// it('should amend a value that is set', function (done) {
|
||||||
done()
|
// done()
|
||||||
})
|
// })
|
||||||
}) // end hooks
|
// }) // end hooks
|
||||||
|
|
||||||
|
// describe('TODO: Misc', function () {
|
||||||
|
// it('get should return values of object nested props', function (done) {
|
||||||
|
// done()
|
||||||
|
// })
|
||||||
|
|
||||||
|
// it('set should make all nested leaves reactive if traverse is requested', function (done) {
|
||||||
|
// done()
|
||||||
|
// })
|
||||||
|
|
||||||
|
// it('should remove rx for property', function (done) {
|
||||||
|
// done()
|
||||||
|
// })
|
||||||
|
// }) // end hooks
|
||||||
|
|
Loading…
Reference in New Issue