Skip to content

Instantly share code, notes, and snippets.

@svidgen
Last active May 6, 2021 03:52
Show Gist options
  • Select an option

  • Save svidgen/2afd1c6d954ff354ffec898cf5b3d845 to your computer and use it in GitHub Desktop.

Select an option

Save svidgen/2afd1c6d954ff354ffec898cf5b3d845 to your computer and use it in GitHub Desktop.
example of the new observe/infect/curse implementation i'll be using in my DOM lib
/*
* Infects an object with a multi-cast property and method "mirroring".
*
* ```
* observe(source, ['property_a', 'property_b', 'method_a']).mirror(target);
* ```
*
* The `target` object will be invoked with the same calls/values as those
* made against the `source` on the listed properties and methods -- or
* ALL enumerable properties and methods if non are given.
*
* The only way to stop mirroring is to shatter the mirror:
*
* ```
* const mirror = observe(source, [...]).mirror(target);
*
* // later.
* mirror.shatter();
* ```
*/
function observe(o, props) {
if (typeof(props) === 'undefined') {
props = allPropertyNamesOf(o);
} else if (typeof(props) != 'Array') {
props = [props];
} else {
props = distinct(props);
}
o.__callbacks = o.__callbacks || {};
o.__targets = o.__targets || [];
props.forEach(function(p) {
if (p === '__callbacks') return;
if (p === '__targets') return;
if (typeof(o.__callbacks[p]) === 'function') return;
o.__callbacks[p] = callback(o, p);
if (typeof(o[p]) === 'function') {
const _originalFunction = o[p].bind(o);
o[p] = (...args) => {
try {
const rv = _originalFunction(...args);
o.__callbacks[p](...args);
return rv;
} catch (err) {
throw err;
}
};
} else {
var innerValue = o[p];
Object.defineProperty(o, p, {
set: function(v) {
innerValue = v;
o.__callbacks[p](v);
},
get: function() {
return innerValue;
}
});
// also initialize the value on the mirrored object.
o.__callbacks[p](o[p])
}
});
return {
mirror: target => {
o.__targets.indexOf(target) < 0 && o.__targets.push(target);
return {
shatter: () => {
o.__targets.splice(o.__targets.indexOf(target), 1);
}
};
}
};
};
function callback(s, name) {
return (...args) => {
s.__targets.forEach(target => {
try {
if (typeof(target[name]) === 'function') {
target[name](...args);
} else {
target[name] = args[0];
}
} catch (err) {
console.error(`${err}: Could not invoke ${name} on target`);
console.log('target =', target);
}
});
};
};
function distinct(items) {
return [...(new Set([...items]))];
};
function allPropertyNamesOf(o) {
var props = Object.getOwnPropertyNames(o);
for (; o != null; o = Object.getPrototypeOf(o)) {
props = [...props, ...Object.getOwnPropertyNames(o)];
}
return distinct(props);
};
var source = {a: '123'};
var target = {};
var mirror = observe(source).mirror(target);
source.a = '456';
console.log(target);
mirror.shatter();
source.a = '789';
console.log(target);
source = {f: (a) => console.log('logged form source', a)};
target1 = {f: (a) => console.log('logged form target1', a)};
target2 = {f: (a) => console.log('logged form target2', a)};
target3 = {f: (a) => console.log('logged form target3', a)};
var mirror1 = observe(source).mirror(target1);
var mirror2 = observe(source).mirror(target2);
var mirror3 = observe(source).mirror(target3);
source.f('u');
mirror1.shatter();
mirror2.shatter();
mirror3.shatter();
source = {a: 123};
target = {a: v => console.log('detected change', v)};
mirror = observe(source).mirror(target);
source.a += 1;
mirror.shatter();
{a: "456"}
{a: "456"}
logged form source u
logged form target1 u
logged form target2 u
logged form target3 u
detected change 124
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment