Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Observable Object
'use strict'
module.exports = create
// Observable Objects are objects which contain properties for which you can
// observe changes.
//
// To create an ObservableObject, use:
// observableObject = ObservableObject.create()
//
// To set values of properties in an ObservableObject, use
// observableObject.set({key1: val1, key2: val2})
//
// To get values of properties in an ObservableObject, use
// observableObject.get()
// observableObject.get({key1: defVal1, key2: devVal2})
//
// The first form returns all the properties in a new object. The second
// form returns the object passed in, with any requested properties values
// reset, if they are in the observable object.
//
// To be notified of changes to properties of an ObservableObject, use
// observableObject.on({key1: fn(newV, oldV), key2: fn(newV, oldV)})
//
// The notification functions will be passed the new and old values.
const EventEmitter = require('events')
function create () {
return new ObservableObject()
}
class ObservableObject {
constructor () {
this._props = {}
this._events = new EventEmitter()
}
// get the values of the properties
get (values) {
// no args, return shallow copy of state
if (values == null) {
return Object.assign({}, this._props)
}
// otherwise, fill in values for the object passed in
for (let key in values) {
if (this._props.hasOwnProperty(key)) {
values[key] = this._props[key]
}
}
return values
}
// set the values of the properties
set (values) {
const oldValues = new Map()
for (let key in values) {
if (this._props[key] === values[key]) continue
oldValues.set(key, this._props[key])
this._props[key] = values[key]
}
for (let key of oldValues.keys()) {
this._events.emit(key, values[key], oldValues.get(key))
}
}
// set handlers for property changes
on (handlers) {
for (let key in handlers) {
this._events.on(key, handlers[key])
}
const self = this
return function revoker () {
for (let key in handlers) {
self._events.removeListener(key, handlers[key])
}
}
}
}
'use strict'
const utils = require('./lib/utils')
const runTest = utils.createTestRunner(__filename)
const ObservableObject = require('../lib/observable-object')
runTest(function test (t) {
const oo = new ObservableObject()
// get property values, using defaults if property not available
const values1 = oo.get({a: 1, b: 2})
t.equal(values1.a, 1, 'values1.a should be 1')
t.equal(values1.b, 2, 'values1.b should be 2')
// test setting, firing events, and getting
let newA, newB
let oldA, oldB
const revokeOn = oo.on({
a: (n, o) => { newA = n; oldA = o },
b: (n, o) => { newB = n; oldB = o }
})
oo.set({a: 3, b: 4})
t.equal(oldA, undefined, 'oldA should be undefined')
t.equal(oldB, undefined, 'oldB should be undefined')
t.equal(newA, 3, 'newA should be 3')
t.equal(newB, 4, 'newB should be 4')
const values2 = oo.get()
const values3 = oo.get({a: 1, b: 2})
t.equal(values2.a, 3, 'values2.a should be 3')
t.equal(values2.b, 4, 'values2.b should be 4')
t.equal(values3.a, 3, 'values3.a should be 3')
t.equal(values3.b, 4, 'values3.b should be 4')
oo.set({a: 5, b: 6})
t.equal(oldA, 3, 'oldA should be 3')
t.equal(oldB, 4, 'oldB should be 4')
t.equal(newA, 5, 'newA should be 5')
t.equal(newB, 6, 'newB should be 6')
revokeOn()
oo.set({a: 7, b: 8})
t.equal(oldA, 3, 'oldA should be 3')
t.equal(oldB, 4, 'oldB should be 4')
t.equal(newA, 5, 'newA should be 5')
t.equal(newB, 6, 'newB should be 6')
const values4 = oo.get()
const values5 = oo.get({a: 1, b: 2})
t.equal(values4.a, 7, 'values4.a should be 7')
t.equal(values4.b, 8, 'values4.b should be 8')
t.equal(values5.a, 7, 'values5.a should be 8')
t.equal(values5.b, 8, 'values5.b should be 8')
t.end()
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.