From 78969610d7d7f2cb8aae71fbb6667496f1f9e47a Mon Sep 17 00:00:00 2001 From: Zdenko Vujasinovic Date: Sat, 24 Feb 2018 18:50:58 +0100 Subject: [PATCH] observable object --- main.coffee | 49 ++++++++++++++++++++++++++++++++ test/observable.coffee | 63 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/main.coffee b/main.coffee index 1837a81..aab084f 100644 --- a/main.coffee +++ b/main.coffee @@ -146,6 +146,55 @@ module.exports = Observable = (value, context) -> size: -> magicDependency(self) value.length + if Object::toString.call(value) is '[object Object]' + # proxy object properties and add notifications to mutation events + defProp = (property) -> + Object.defineProperty self, property, + get: -> + magicDependency(self) + # if object property is Observable, e.g obj = Observable { a: Observable 1 } + if typeof value[property]?.observe is "function" + value[property]() + else + value[property] + set: (val) -> + # if object property is Observable, e.g obj = Observable { a: Observable 1 } + if typeof value[property]?.observe is "function" + value[property] val + else + value[property] = val + notify value + + defProp prop for own prop of value + + # Proxy object methods, e.g. Object.keys(obj) + [ + "keys" + "values" + "entries" + ].forEach (method) -> + Object.defineProperty self, method, + get: -> + magicDependency(self) + Object[method] value + + # Extra methods for object observables + extend self, + # Remove an element from the object and notify observers of changes. + remove: (object) -> + if returnValue = value[object] + delete value[object] + notify(value) + return returnValue + + extend: (obj) -> + magicDependency(self) + value = Object.assign {}, value, obj + defProp prop for own prop of obj + notify value + + # alias + self.assign = self.extend extend self, listeners: listeners diff --git a/test/observable.coffee b/test/observable.coffee index b416190..1d1cb6d 100644 --- a/test/observable.coffee +++ b/test/observable.coffee @@ -413,3 +413,66 @@ describe "Observable functions", -> done() model.lastName "Bro" + +describe "Observable object", -> + it "should proxy properties", -> + obj = Observable + a: 1 + + assert.equal obj.a, 1 + assert.equal obj().a, obj.a + + obj.a = 2 + + assert.equal obj.a, 2 + assert.equal obj().a, obj.a + + it "should work with observable properties", -> + obj = Observable + a: Observable 1 + + assert.equal obj.a, 1 + assert.equal obj().a(), obj.a + + obj().a 2 + + assert.equal obj.a, 2 + assert.equal obj().a(), obj.a + + obj.a = 3 + + assert.equal obj.a, 3 + assert.equal obj().a(), obj.a + + it "should compute object#extend as a dependency", -> + obj = Observable + a: 1 + + obj.extend b: 2 + + assert.equal obj.b, 2 + assert.equal obj().b, obj.b + + # alias + obj.assign c: 3 + + assert.equal obj.c, 3 + assert.equal obj().c, obj.c + + it "should compute object#remove as a dependency", -> + obj = Observable + a: 1 + b: 2 + + obj.remove "b" + + assert.equal obj.b, undefined + + it "should proxy object methods", -> + obj = Observable + a: 1 + b: 2 + + assert.deepEqual obj.keys, ["a", "b"] + assert.deepEqual obj.values, [1, 2] + assert.deepEqual obj.entries, [["a", 1], ["b", 2]]