Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
Add CompoundPathObserver
Browse files Browse the repository at this point in the history
CompoundPathObserver will eventually replace most uses of CompoundBinding in TemplateBinding.

R=arv
BUG=

Review URL: https://codereview.appspot.com/13242043
  • Loading branch information
rafaelw committed Aug 26, 2013
1 parent 058d172 commit 12f9212
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 15 deletions.
142 changes: 127 additions & 15 deletions src/observe.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,13 @@
},

getValueFrom: function(obj, observedSet) {
if (observedSet)
observedSet.reset();
for (var i = 0; i < this.length; i++) {
if (obj === undefined || obj === null) {
if (observedSet)
observedSet.cleanup();
if (obj === undefined || obj === null)
return;
}
if (observedSet)
observedSet.observe(obj);
obj = obj[this[i]];
}

return obj;
},

Expand Down Expand Up @@ -291,8 +285,6 @@
}

addToAll(this);
this.connect();
this.sync(true);
}

Observer.prototype = {
Expand Down Expand Up @@ -334,8 +326,10 @@
return;

this.sync(false);
this.reportArgs.push(this.token);
this.invokeCallback(this.reportArgs);
if (this.callback) {
this.reportArgs.push(this.token);
this.invokeCallback(this.reportArgs);
}
this.reportArgs = undefined;
},

Expand Down Expand Up @@ -435,6 +429,8 @@

function ObjectObserver(object, callback, target, token) {
Observer.call(this, object, callback, target, token);
this.connect();
this.sync(true);
}

ObjectObserver.prototype = createObject({
Expand Down Expand Up @@ -488,7 +484,7 @@
function ArrayObserver(array, callback, target, token) {
if (!Array.isArray(array))
throw Error('Provided object is not an Array');
Observer.call(this, array, callback, target, token);
ObjectObserver.call(this, array, callback, target, token);
}

ArrayObserver.prototype = createObject({
Expand Down Expand Up @@ -588,8 +584,6 @@
};

function PathObserver(object, pathString, callback, target, token, valueFn) {
this.valueFn = valueFn;

var path = getPath(pathString);
if (!path) {
// Invalid path.
Expand All @@ -612,8 +606,12 @@
return;
}

this.path = path;
Observer.call(this, object, callback, target, token);
this.valueFn = valueFn;
this.path = path;

this.connect();
this.sync(true);
}

PathObserver.prototype = createObject({
Expand All @@ -636,9 +634,15 @@
check: function() {
// Note: Extracting this to a member function for use here and below
// regresses dirty-checking path perf by about 25% =-(.
if (this.observedSet)
this.observedSet.reset();

var newValue = this.path.getValueFrom(this.object, this.observedSet)
this.value = this.valueFn ? this.valueFn(newValue) : newValue;

if (this.observedSet)
this.observedSet.cleanup();

if (areSameValue(this.value, this.oldValue))
return false;

Expand All @@ -648,8 +652,14 @@

sync: function(hard) {
if (hard) {
if (this.observedSet)
this.observedSet.reset();

var newValue = this.path.getValueFrom(this.object, this.observedSet)
this.value = this.valueFn ? this.valueFn(newValue) : newValue;

if (this.observedSet)
this.observedSet.cleanup();
}

this.oldValue = this.value;
Expand All @@ -671,6 +681,107 @@
path.setValueFrom(obj, value);
};

function CompoundPathObserver(callback, target, token, valueFn) {
Observer.call(this, undefined, callback, target, token);
this.valueFn = valueFn;

this.observed = [];
this.values = [];
this.started = false;
}

CompoundPathObserver.prototype = createObject({
__proto__: PathObserver.prototype,

addPath: function(object, pathString) {
if (this.started)
throw Error('Cannot add more paths once started.');

var path = getPath(pathString);
var value = undefined;

if (!path) {
// Invalid path.
} else if (!path.length) {
// 0-length path.
path = undefined;
value = object;
} else if (!isObject(object)) {
// non-object & non-0-length path.
path = undefined;
value = undefined;
}

this.observed.push(object, path);
this.values.push(value);
},

start: function() {
this.connect();
this.sync(true);
},

getValues: function() {
if (this.observedSet)
this.observedSet.reset();

var anyChanged = false;
for (var i = 0; i < this.observed.length; i = i+2) {
var path = this.observed[i+1];
if (!path)
continue;
var object = this.observed[i];
var value = path.getValueFrom(object, this.observedSet);
var oldValue = this.values[i/2];
if (!areSameValue(value, oldValue)) {
this.values[i/2] = value;
anyChanged = true;
}
}

if (this.observedSet)
this.observedSet.cleanup();

return anyChanged;
},

check: function() {
if (!this.getValues())
return;

this.value = this.valueFn(this.values);

if (areSameValue(this.value, this.oldValue))
return false;

this.reportArgs = [this.value, this.oldValue];
return true;
},

sync: function(hard) {
if (hard) {
this.getValues();
this.value = this.valueFn(this.values);
}

this.oldValue = this.value;
},

close: function() {
if (this.observed) {
for (var i = 0; i < this.observed.length; i = i + 2) {
var object = this.observed[i];
if (object && typeof object.close === 'function')
object.close();
}
this.observed = undefined;
this.values = undefined;
}

Observer.prototype.close.call(this);
}
});

var knownRecordTypes = {
'new': true,
'updated': true,
Expand Down Expand Up @@ -1176,5 +1287,6 @@
};
global.ObjectObserver = ObjectObserver;
global.PathObserver = PathObserver;
global.CompoundPathObserver = CompoundPathObserver;
global.Path = Path;
})(typeof global !== 'undefined' && global ? global : this);
48 changes: 48 additions & 0 deletions tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,54 @@ suite('PathObserver Tests', function() {
});
});


suite('CompoundPathObserver Tests', function() {

setup(doSetup);

teardown(doTeardown);

function assertPathChanges(expectNewValue, expectOldValue) {
observer.deliver();

assert.isTrue(callbackInvoked);

var newValue = callbackArgs[0];
var oldValue = callbackArgs[1];
assert.deepEqual(expectNewValue, newValue);
assert.deepEqual(expectOldValue, oldValue);

callbackArgs = undefined;
callbackInvoked = false;
}

test('CompoundPath Simple', function() {
var model = { a: 1, b: 2, c: 3 };

function valueFn(values) {
return values.reduce(function(last, cur) {
return typeof cur === 'number' ? last + cur : undefined;
}, 0);
}

observer = new CompoundPathObserver(callback, undefined, undefined,
valueFn);
observer.addPath(model, 'a');
observer.addPath(model, 'b');
observer.addPath(model, 'c');
observer.start();

assert.strictEqual(6, observer.value);

model.a = -10;
model.b = 20;
model.c = 30;
assertPathChanges(40, 6);

observer.close();
});
});

suite('ArrayObserver Tests', function() {

setup(doSetup);
Expand Down

0 comments on commit 12f9212

Please sign in to comment.