From c47c4ccc6056e901a5d0df8fb68724ce84d0fc32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 16 Apr 2019 22:31:46 +0200 Subject: [PATCH] [REF] observer: turn the observer into a class --- src/observer.ts | 139 +++++++++++++++++++---------------------- src/store.ts | 8 ++- tests/observer.test.ts | 30 ++++----- 3 files changed, 84 insertions(+), 93 deletions(-) diff --git a/src/observer.ts b/src/observer.ts index 1487536f..5ecd71c4 100644 --- a/src/observer.ts +++ b/src/observer.ts @@ -1,12 +1,6 @@ //------------------------------------------------------------------------------ // Observer //------------------------------------------------------------------------------ -export interface Observer { - rev: number; - allowMutations: boolean; - observe: (val: any) => void; - set: (target: any, key: number | string, value: any) => void; -} const methodsToPatch = [ "push", @@ -49,72 +43,11 @@ for (let method of methodsToPatch) { }; } -export function makeObserver(): Observer { - const observer: Observer = { - rev: 1, - allowMutations: true, - observe: observe, - set: set - }; +export class Observer { + rev: number = 1; + allowMutations: boolean = true; - function set(target: any, key: number | string, value: any) { - addProp(target, key, value); - target.__owl__.rev++; - observer.rev++; - } - - function addProp( - obj: T, - key: string | number, - value: any - ) { - Object.defineProperty(obj, key, { - enumerable: true, - get() { - return value; - }, - set(newVal) { - if (!observer.allowMutations) { - throw new Error( - `State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")` - ); - } - if (newVal !== value) { - unobserve(value); - value = newVal; - observe(newVal, obj); - obj.__owl__.rev!++; - observer.rev++; - let parent = obj; - do { - parent.__owl__.deepRev++; - } while ((parent = parent.__owl__.parent)); - } - } - }); - observe(value, obj); - } - - function observeObj(obj: T, parent?: any) { - const keys = Object.keys(obj); - obj.__owl__ = { rev: 1, deepRev: 1, parent }; - Object.defineProperty(obj, "__owl__", { enumerable: false }); - for (let key of keys) { - addProp(obj, key, obj[key]); - } - } - - function observeArr(arr: Array, parent?: any) { - (arr).__owl__ = { rev: 1, deepRev: 1, parent }; - Object.defineProperty(arr, "__owl__", { enumerable: false }); - (arr).__proto__ = Object.create(ModifiedArrayProto); - (arr).__proto__.__observer__ = observer; - for (let i = 0; i < arr.length; i++) { - observe(arr[i], arr); - } - } - - function observe(value: any, parent?: any) { + observe(value: any, parent?: any) { if (value === null) { // fun fact: typeof null === 'object' return; @@ -127,17 +60,73 @@ export function makeObserver(): Observer { return; } if (Array.isArray(value)) { - observeArr(value, parent); + this._observeArr(value, parent); } else { - observeObj(value, parent); + this._observeObj(value, parent); } } - function unobserve(target: any) { + set(target: any, key: number | string, value: any) { + this._addProp(target, key, value); + target.__owl__.rev++; + this.rev++; + } + + unobserve(target: any) { if (target !== null && typeof target === "object") { delete target.__owl__; } } - return observer; + _observeObj(obj: T, parent?: any) { + const keys = Object.keys(obj); + obj.__owl__ = { rev: 1, deepRev: 1, parent }; + Object.defineProperty(obj, "__owl__", { enumerable: false }); + for (let key of keys) { + this._addProp(obj, key, obj[key]); + } + } + + _observeArr(arr: Array, parent?: any) { + (arr).__owl__ = { rev: 1, deepRev: 1, parent }; + Object.defineProperty(arr, "__owl__", { enumerable: false }); + (arr).__proto__ = Object.create(ModifiedArrayProto); + (arr).__proto__.__observer__ = this; + for (let i = 0; i < arr.length; i++) { + this.observe(arr[i], arr); + } + } + + _addProp( + obj: T, + key: string | number, + value: any + ) { + var self = this; + Object.defineProperty(obj, key, { + enumerable: true, + get() { + return value; + }, + set(newVal) { + if (!self.allowMutations) { + throw new Error( + `State cannot be changed outside a mutation! (key: "${key}", val: "${newVal}")` + ); + } + if (newVal !== value) { + self.unobserve(value); + value = newVal; + self.observe(newVal, obj); + obj.__owl__.rev!++; + self.rev++; + let parent = obj; + do { + parent.__owl__.deepRev++; + } while ((parent = parent.__owl__.parent)); + } + } + }); + this.observe(value, obj); + } } diff --git a/src/store.ts b/src/store.ts index fabd409c..03d2546f 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,6 +1,6 @@ import { Component } from "./component"; import { EventBus } from "./event_bus"; -import { makeObserver, Observer } from "./observer"; +import { Observer } from "./observer"; //------------------------------------------------------------------------------ // Store Definition @@ -27,6 +27,7 @@ export class Store extends EventBus { debug: boolean; env: any; observer: Observer; + set: any; constructor(config: StoreConfig, options: StoreOption = {}) { super(); @@ -35,13 +36,14 @@ export class Store extends EventBus { this.actions = config.actions; this.mutations = config.mutations; this.env = config.env; - this.observer = makeObserver(); + this.observer = new Observer(); this.observer.allowMutations = false; this.observer.observe(this.state); if (this.debug) { this.history.push({ state: this.state }); } + this.set = this.observer.set.bind(this.observer); } dispatch(action, payload?: any): Promise | void { @@ -79,7 +81,7 @@ export class Store extends EventBus { { commit: this.commit.bind(this), state: this.state, - set: this.observer.set + set: this.set }, payload ); diff --git a/tests/observer.test.ts b/tests/observer.test.ts index f4d80932..1e7dc908 100644 --- a/tests/observer.test.ts +++ b/tests/observer.test.ts @@ -1,8 +1,8 @@ -import { makeObserver } from "../src/observer"; +import { Observer } from "../src/observer"; describe("observer", () => { test("properly observe objects", () => { - const observer = makeObserver(); + const observer = new Observer(); const obj: any = {}; observer.observe(obj); @@ -26,7 +26,7 @@ describe("observer", () => { }); test("properly handle null or undefined", () => { - const observer = makeObserver(); + const observer = new Observer(); const obj: any = { a: null, b: undefined }; observer.observe(obj); @@ -45,7 +45,7 @@ describe("observer", () => { }); test("can change values in array", () => { - const observer = makeObserver(); + const observer = new Observer(); const obj: any = { arr: [1, 2] }; observer.observe(obj); @@ -62,7 +62,7 @@ describe("observer", () => { }); test("various object property changes", () => { - const observer = makeObserver(); + const observer = new Observer(); const obj: any = { a: 1 }; observer.observe(obj); expect(obj.__owl__.rev).toBe(1); @@ -81,7 +81,7 @@ describe("observer", () => { }); test("properly observe arrays", () => { - const observer = makeObserver(); + const observer = new Observer(); const arr: any = []; observer.observe(arr); expect(arr.__owl__.rev).toBe(1); @@ -117,7 +117,7 @@ describe("observer", () => { }); test("object pushed into arrays are observed", () => { - const observer = makeObserver(); + const observer = new Observer(); const arr: any = []; observer.observe(arr); expect(observer.rev).toBe(1); @@ -134,7 +134,7 @@ describe("observer", () => { }); test("properly observe arrays in object", () => { - const observer = makeObserver(); + const observer = new Observer(); const state: any = { arr: [] }; observer.observe(state); expect(state.arr.__owl__.rev).toBe(1); @@ -148,7 +148,7 @@ describe("observer", () => { }); test("properly observe objects in array", () => { - const observer = makeObserver(); + const observer = new Observer(); const state: any = { arr: [{ something: 1 }] }; observer.observe(state); expect(state.arr.__owl__.rev).toBe(1); @@ -160,7 +160,7 @@ describe("observer", () => { }); test("properly observe objects in object", () => { - const observer = makeObserver(); + const observer = new Observer(); const state: any = { a: { b: 1 } }; observer.observe(state); expect(state.__owl__.rev).toBe(1); @@ -172,7 +172,7 @@ describe("observer", () => { }); test("properly unobserve objects in object", () => { - const observer = makeObserver(); + const observer = new Observer(); const state: any = { a: { b: 1 } }; observer.observe(state); expect(state.__owl__.rev).toBe(1); @@ -184,7 +184,7 @@ describe("observer", () => { }); test("reobserve new object values", () => { - const observer = makeObserver(); + const observer = new Observer(); const obj: any = { a: 1 }; observer.observe(obj); expect(obj.__owl__.rev).toBe(1); @@ -200,7 +200,7 @@ describe("observer", () => { }); test("deep observe misc changes", () => { - const observer = makeObserver(); + const observer = new Observer(); const state: any = { o: { a: 1 }, arr: [1], n: 13 }; observer.observe(state); expect(state.__owl__.rev).toBe(1); @@ -221,7 +221,7 @@ describe("observer", () => { }); test("properly handle already observed state", () => { - const observer = makeObserver(); + const observer = new Observer(); const obj1: any = { a: 1 }; const obj2: any = { b: 1 }; observer.observe(obj1); @@ -240,7 +240,7 @@ describe("observer", () => { }); test("accept cycles in observed state", () => { - const observer = makeObserver(); + const observer = new Observer(); const obj1: any = {}; const obj2: any = { b: obj1, key: 1 }; obj1.a = obj2;