Created
October 14, 2017 19:55
-
-
Save pmuellr/3ffe68c6ee6437fe79d132c645ffa3ba to your computer and use it in GitHub Desktop.
Observable Object
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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]) | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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