From f669d3af75b04b2dff484b4ce5b2088a7a064f50 Mon Sep 17 00:00:00 2001 From: Tobias Buschor Date: Thu, 25 Apr 2024 18:43:55 +0200 Subject: [PATCH] Update import URL for dump.js, refactor asyncHandler in multiple files, update mocha and chai versions in test files, fix nested key assignment in proxy object, update header level in intro.md, update library introduction and headings, start working on docs, add btc-driver, update uhtml version in imports, update README.md --- drivers/localStorage.js | 6 +++++ drivers/sql/Items/Db.js | 1 + drivers/sql/Items/Row.js | 3 +-- drivers/sql/Items/Table.js | 47 ++++++++++++++++++------------------ drivers/sql/Mysql.js | 3 ++- item.js | 19 +++++++-------- tests/denoMysql.js | 1 - tests/mocha.async.html | 4 +--- tests/mocha.html | 16 +++++++++++++ tests/tree1.html | 49 ++++++++++++++++++++++++++++++++++++++ tools/AsyncDataPoint.js | 10 +++----- 11 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 tests/tree1.html diff --git a/drivers/localStorage.js b/drivers/localStorage.js index 6ab39eb..0db468c 100644 --- a/drivers/localStorage.js +++ b/drivers/localStorage.js @@ -15,6 +15,12 @@ export function getStore(){ addEventListener('storage', e => { // does not trigger on source window! root.item(e.key).value = e.newValue; }); + root.loadItems = function(){ + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + root.item(key); + } + } } return root; } diff --git a/drivers/sql/Items/Db.js b/drivers/sql/Items/Db.js index 7c4c120..0febd90 100644 --- a/drivers/sql/Items/Db.js +++ b/drivers/sql/Items/Db.js @@ -42,5 +42,6 @@ export class Db extends Item { } } + static isPrimitive() { return false; } static ChildClass = Table; } diff --git a/drivers/sql/Items/Row.js b/drivers/sql/Items/Row.js index accba00..0542b70 100644 --- a/drivers/sql/Items/Row.js +++ b/drivers/sql/Items/Row.js @@ -12,8 +12,6 @@ export class Row extends Item { for (const field of fields) this.item(field.name); } - - // select // async selectAll() { // await this.loadItems(); @@ -105,6 +103,7 @@ export class Row extends Item { toString() { return this.key; } valueOf() { return this.key; } + static isPrimitive() { return false; } static ChildClass = Cell; } diff --git a/drivers/sql/Items/Table.js b/drivers/sql/Items/Table.js index b635b58..dd043b8 100644 --- a/drivers/sql/Items/Table.js +++ b/drivers/sql/Items/Table.js @@ -8,7 +8,7 @@ export const Table = class extends Item { this._fields = {}; } async loadItems() { - const rows = await this.parent.query("SELECT * FROM "+this.key);b // todo, just get primaries? + const rows = await this.parent.query("SELECT * FROM "+this.key); // todo, just get primaries? for (const data of rows) { const id = await this.rowId(data); const row = this.item(id); @@ -23,28 +23,28 @@ export const Table = class extends Item { } - async ensure(filter) { - console.error('used?') - const rows = await this.rows(filter); - for (const row of rows) return row; // return first - return this.insert(filter); // else insert, todo: filter? - } - async rows(filter /* limit? */) { - console.error('used?') - const where = await this.objectToWhere(filter); // todo - const all = await this.parent.query("SELECT * FROM "+this.key+" WHERE " + where); - const rows = []; - for (const data of all) { - const id = await this.rowId(data); - const row = this.item(id); - for (const i in data) { - console.log('asyncHandler?'); - row.cell(i).setFromMaster(data[i]); // todo: asyncHandler?? - } - rows.push( row ); - } - return rows; - } + // async ensure(filter) { + // console.error('used?') + // const rows = await this.rows(filter); + // for (const row of rows) return row; // return first + // return this.insert(filter); // else insert, todo: filter? + // } + // async rows(filter /* limit? */) { + // console.error('used?') + // const where = await this.objectToWhere(filter); // todo + // const all = await this.parent.query("SELECT * FROM "+this.key+" WHERE " + where); + // const rows = []; + // for (const data of all) { + // const id = await this.rowId(data); + // const row = this.item(id); + // for (const i in data) { + // console.log('asyncHandler?'); + // row.cell(i).setFromMaster(data[i]); // todo: asyncHandler?? + // } + // rows.push( row ); + // } + // return rows; + // } field(name) { @@ -190,5 +190,6 @@ export const Table = class extends Item { */ + static isPrimitive() { return false; } static ChildClass = Row; }; diff --git a/drivers/sql/Mysql.js b/drivers/sql/Mysql.js index 51a7af4..168fd2d 100644 --- a/drivers/sql/Mysql.js +++ b/drivers/sql/Mysql.js @@ -1,6 +1,7 @@ import { Item } from '../../item.js'; import { Db } from "./Items/Db.js"; -import { Client } from "https://deno.land/x/mysql@v2.10.0/mod.ts"; // Warning: v2.11.0 has a breaking bug! +//import { Client } from "https://deno.land/x/mysql@v2.10.0/mod.ts"; // Warning: v2.11.0 has a breaking bug! +import { Client } from "https://deno.land/x/mysql@v2.12.1/mod.ts"; // Warning: v2.11.0 has a breaking bug! class MysqlDb extends Db { async connect(){ diff --git a/item.js b/item.js index 6e4feed..ad6b076 100644 --- a/item.js +++ b/item.js @@ -53,7 +53,7 @@ export class Item extends EventTarget { $set(value){ const oldValue = this.#value; if (this.constructor.isPrimitive(value)) { - if (!this.#filled || oldValue !== value) { // TODO: we shoul use deepEqual as "primitive" can be an object + if (!this.#filled || !this.constructor.equals(oldValue, value)) { this.#value = value; // structuralClone(value); // TODO: should we clone the value? or should we just use the reference? (if its an object) this.#filled = true; if (!this.#isGetting) { @@ -84,15 +84,11 @@ export class Item extends EventTarget { return this.#value[key]; } remove(){ - if (this.#parent) { - delete this.#parent.#value[this.#key]; - dispatchEvent(this.#parent, 'change', { item: this.#parent, remove: this }); - } else { - throw new Error('cannot remove root item'); - } + if (!this.#parent) throw new Error('cannot remove root item'); + delete this.#parent.#value[this.#key]; + dispatchEvent(this.#parent, 'change', { item: this.#parent, remove: this }); } has(key){ return key in this.#value; } - //asyncHas(key){ return Promise.resolve(key in this.#value) } get proxy(){ return toProxy(this); } @@ -132,6 +128,9 @@ export class Item extends EventTarget { static isPrimitive(value){ return value !== Object(value) || 'toJSON' in value || value instanceof Promise; } + static equals(a, b){ // comparison function between old and new value in case of primitive + if (Object.is(a, b)) return true; // // TODO: we shoul use deepEqual as "primitive" can be an object + } static ChildClass; ChildClass = this.constructor.ChildClass; @@ -159,7 +158,7 @@ let currentEffect = null; * @param {function} fn - A function that executes imeediately and collects the containing items. * @return {function} A function to dispose the effect. */ -export function effect(fn){ +export function effect(fn){ // async? const outer = currentEffect; if (outer) { (outer.nested ??= new Set()).add(fn); @@ -167,7 +166,7 @@ export function effect(fn){ fn.parent = outer; } currentEffect = fn; - fn(); + fn(); // await, so that signals in async functions are collected? currentEffect = outer; return () => fn.disposed = true } diff --git a/tests/denoMysql.js b/tests/denoMysql.js index 2b9d7d8..8805f40 100644 --- a/tests/denoMysql.js +++ b/tests/denoMysql.js @@ -84,7 +84,6 @@ Deno.test("database table", async () => { const row = await table.insert({name: 'Tobias', age: 42}); assertEquals( row.key, '1' ); - const values = table.item(1).item('name').get(); const values2 = table.item(1).item('age').get(); console.log(await Promise.all([values, values2])); // generates only one query diff --git a/tests/mocha.async.html b/tests/mocha.async.html index dc4f4d3..d647eaf 100644 --- a/tests/mocha.async.html +++ b/tests/mocha.async.html @@ -43,7 +43,6 @@ asyncItem.createGetter = async function(...args){ getterRequested++; - console.log('getterRequested') await delay(10); return asyncRootValue; } @@ -160,7 +159,6 @@ asyncItem.value = {b:'new value2', c:'new value2'}; const c = await asyncItem.item('c').value; chai.expect(c).to.equal('new value2'); - console.log(asyncItem) }); /* @@ -232,7 +230,7 @@ describe('normal items with promises', () => { - it('Promise is promitive', async () => { + it('Promise is primitive', async () => { const i = item( Promise.resolve(2) ); const value = await i.value; chai.expect(value).to.equal(2); diff --git a/tests/mocha.html b/tests/mocha.html index b132c34..af2ad87 100644 --- a/tests/mocha.html +++ b/tests/mocha.html @@ -69,6 +69,22 @@ let i = item(date); chai.expect(i.value).to.equal(date); }); + + it('0 not equals -0', () => { + let i = item(0); + let changed = false; + i.addEventListener('change', e => changed = true); + i.value = -0; + chai.expect(changed).to.equal(true); + }); + it('NaN equals NaN', () => { + let i = item(NaN); + let changed = false; + i.addEventListener('change', e => changed = true); + i.value = NaN; + chai.expect(changed).to.equal(false); + }); + }); describe('object value', () => { diff --git a/tests/tree1.html b/tests/tree1.html new file mode 100644 index 0000000..30dc22e --- /dev/null +++ b/tests/tree1.html @@ -0,0 +1,49 @@ + + + + + + + + + + + +
+ + + +
+ + + diff --git a/tools/AsyncDataPoint.js b/tools/AsyncDataPoint.js index f4f9e1f..1684e71 100644 --- a/tools/AsyncDataPoint.js +++ b/tools/AsyncDataPoint.js @@ -1,6 +1,5 @@ /* -Ussage: - +// Ussage: const datapoint = new AsyncDataPoint({ get: () => fetch('https://example.com/todos/1').then(res => res.json()), set: value => fetch('https://example.com/todos/1', {method: 'PUT', body: JSON.stringify(value)}).then(res => res.json()) @@ -8,19 +7,16 @@ const datapoint = new AsyncDataPoint({ datapoint.set({title: 'foo', completed: true}); datapoint.get().then(value => console.log(value)); -// api +// API datapoint.onchange = ({value}) => console.log('value changed', value); datapoint.cacheDuration = 1000; // cache for 1 second datapoint.trustSendingValue = true; // trust sending value: until the sending is done, the value is the new value, despite the uncertainty that the server will fail datapoint.setDebouncePeriod = 5; // debounce period for setter in ms, default 5 -datapoint.setFromMaster({title: 'foo', completed: true}); // set value without saving it to the server +datapoint.setFromMaster({title: 'foo', completed: true}); // set value without saving it to the server (the value comes from the master through an other channel / trusted source) */ export class AsyncDataPoint { - // trustSendingValue = true; - // cacheDuration = 2000; // cache for 2 seconds, false = no cache, true = cache forever - // setDebouncePeriod = 5; // debounce period for setter in ms createGetter = null; // function that returns a promise createSetter = null; // function that returns a promise, if it failed, the promise must be rejected