diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index 2f84d9a9119..865be15e207 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -10,12 +10,17 @@ function firstKey(path) { return path.match(FIRST_KEY)[0]; } -function isObject(obj) { - return obj && (typeof obj === 'object'); +function isWatchable(obj) { + if (!obj) { + return; + } + + let type = typeof obj; + return type === 'object' || type === 'function'; } function isVolatile(obj) { - return !(isObject(obj) && obj.isDescriptor && obj._cacheable); + return !(isWatchable(obj) && obj.isDescriptor && obj._cacheable); } function Chains() { } @@ -42,7 +47,7 @@ export function flushPendingChains() { } function addChainWatcher(obj, keyName, node) { - if (!isObject(obj)) { + if (!isWatchable(obj)) { return; } @@ -63,7 +68,7 @@ function addChainWatcher(obj, keyName, node) { } function removeChainWatcher(obj, keyName, node) { - if (!isObject(obj)) { + if (!isWatchable(obj)) { return; } diff --git a/packages/ember-metal/lib/streams/key-stream.js b/packages/ember-metal/lib/streams/key-stream.js index b95dfc4fe60..bf18e5e24d7 100644 --- a/packages/ember-metal/lib/streams/key-stream.js +++ b/packages/ember-metal/lib/streams/key-stream.js @@ -60,7 +60,8 @@ merge(KeyStream.prototype, { if (object !== this.observedObject) { this._clearObservedObject(); - if (object && typeof object === 'object') { + var type = typeof object; + if (object && (type === 'object' || type === 'function')) { addObserver(object, this.key, this, this.notify); this.observedObject = object; } diff --git a/packages/ember-metal/tests/streams/key-stream-test.js b/packages/ember-metal/tests/streams/key-stream-test.js index 54a8588d42c..6d7e5e4ffb3 100644 --- a/packages/ember-metal/tests/streams/key-stream-test.js +++ b/packages/ember-metal/tests/streams/key-stream-test.js @@ -48,6 +48,23 @@ QUnit.test('is notified when the observed object\'s property is mutated', functi equal(nameStream.value(), 'wycats', 'Stream value is correct'); }); +QUnit.test('is notified when properties on functions are mutated', function() { + var fn = function() {}; + fn.foo = 'mmun'; + source = new Stream(function() { return fn; }); + + var nameStream = source.get('foo'); + nameStream.subscribe(incrementCount); + + equal(count, 0, 'Subscribers called correct number of times'); + equal(nameStream.value(), 'mmun', 'Stream value is correct'); + + set(fn, 'foo', 'wycats'); + + equal(count, 1, 'Subscribers called correct number of times'); + equal(nameStream.value(), 'wycats', 'Stream value is correct'); +}); + QUnit.test('is notified when the source stream\'s value changes to a new object', function() { var nameStream = source.get('name'); nameStream.subscribe(incrementCount); diff --git a/packages/ember-metal/tests/watching/watch_test.js b/packages/ember-metal/tests/watching/watch_test.js index 70b3a4a5a67..a21021a6480 100644 --- a/packages/ember-metal/tests/watching/watch_test.js +++ b/packages/ember-metal/tests/watching/watch_test.js @@ -271,3 +271,22 @@ testBoth('watching "length" property on an array', function(get, set) { equal(get(arr, 'length'), 10, 'should get new value'); equal(arr.length, 10, 'property should be accessible on arr'); }); + +testBoth('watching properties on a function', function(get, set) { + var obj = { + foo: function() {} + }; + addListeners(obj, 'foo.baz'); + + watch(obj, 'foo.baz'); + equal(get(obj, 'foo.baz'), undefined, 'should have original prop'); + + set(obj, 'foo.baz', 'bar'); + equal(willCount, 1, 'should have invoked willCount'); + equal(didCount, 1, 'should have invoked didCount'); + + equal(get(obj, 'foo.baz'), 'bar', 'should get new value'); + equal(obj.foo.baz, 'bar', 'property should be accessible on obj'); +}); + +