Skip to content

Instantly share code, notes, and snippets.

@richsilv
Last active August 29, 2015 14:10
Show Gist options
  • Save richsilv/7d66269aab3552449a4c to your computer and use it in GitHub Desktop.
Save richsilv/7d66269aab3552449a4c to your computer and use it in GitHub Desktop.
Meteor Reactive Object
// initial - Initial value (must be an object)
// name - a name to help identify the object if you're using objectStore
// objectStore - a central store to push all initialised objects into (for debugging)
// maxInvalidate - the maximum number of times to invalidate computations which depend
// on this object. Avoids and helps diagnose infinite invalidation loops in testing.
//
// note that setting a sub-key on a key which is currently literal-valued will delete
// the existing value
ReactiveObject = function(initial, name, objectStore, maxInvalidate) {
if (!initial || typeof initial !== 'object') throw new Meteor.Error('literal_initial',
'You must seed the ReactiveObject with a JS object, not a literal. If you need a reactive literal, use ReactiveVar');
this.value = initial;
this.count = 0;
this.dep = new Deps.Dependency();
this.name = name;
this.maxInvalidate = maxInvalidate || 0;
if (objectStore && name && typeof objectStore === 'object') objectStore[name] = this;
};
ReactiveObject.prototype.get = function() {
this.dep.depend();
return $.extend(true, {}, this.value);
};
ReactiveObject.prototype.set = function(newValue){
if (!_.isEqual(this.value, newValue)) {
this.value = newValue;
changeDepIfUnderLimit.call(this);
}
return this.value;
};
ReactiveObject.prototype.getKey = function(key) {
this.dep.depend();
var val = _.reduce(key.split('.'), function(curVal, prop) { return curVal && curVal[prop]}, this.value);
return typeof val === 'object' ? val && $.extend(true, {}, val) : val;
};
ReactiveObject.prototype.setKey = function(key, newValue) {
var split = key.split('.'),
splitLastInd = split.length - 1,
lastKey = split[splitLastInd];
node = _.reduce(split, function(curVal, prop, ind) {
if (ind < splitLastInd) {
if (!curVal[prop] || typeof curVal[prop] !== 'object') curVal[prop] = {};
return curVal[prop];
}
else return curVal;
}, this.value);
if (node && node[lastKey] !== newValue && !_.isEqual(node, newValue)) {
node[lastKey] = newValue;
changeDepIfUnderLimit.call(this);
}
return node[lastKey];
};
function changeDepIfUnderLimit() {
if (this.count < this.maxInvalidate || !this.maxInvalidate)
this.dep.changed();
else {
console.log((this.name || 'Unnamed ReactiveObject') + ' reached invalidation limit of ' + this.maxInvalidate);
console.log(arguments.callee.caller);
console.trace();
}
this.count++;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment