Verified Commit b6a12b51 authored by Aral Balkan's avatar Aral Balkan
Browse files

Tables now emit save event; add more write tests

parent 0bea4eec
......@@ -74,6 +74,24 @@ A data layer for simple [Small Web](https://ar.al/2020/08/07/what-is-the-small-w
- __No schema, no migrations__: again, this is meant to be a very simple persistence, query, and observation layer for local data. If you want schemas and migrations, take a look at nearly every other database out there. You might also want to see how well [ObjectModel](https://github.com/sylvainpolletvillard/ObjectModel) works alongside WhatDB.
## Events
Given that a core goal for WhatDB is to be transparent, you will mostly feel like you’re working with regular JavaScript collections (objects and arrays). At times, however, it might be useful to have access to the underlying abstractions like the table object. One of those instances is if you want to be notified of events.
To listen for an event, access the special `__table__` property of your collection. e.g.,
```js
db.people.__table__.addListener('save', table => {
console.log(`Table ${table.tableName} persisted to disk.`)
})
```
### Table events
| Event name | Description |
| ---------- | ------------------------------------- |
| save | The table has been persisted to disk. |
## Performance characteristics
With the caveat, once again, that this is __for small data sets__, it is still insightful to see the performance on my development machine (Intel i7-8550U (8) @ 4.000GHz, 16GB RAM) with what I would consider an order of magnitude larger data set than the limit you should be using with this module.
......
......@@ -33,6 +33,7 @@ class DataProxy {
// console.log('get', target, property, receiver)
// console.log(` 💾 ❨WhatDB?❩ Getting ${property} on ${this.table.tableName}`)
// Time.mark()
if (property === '__table__') return this.table
const result = Reflect.get(this.data, property, receiver)
// console.log(` 💾 ❨WhatDB?❩ ╰─ Reflection took ${Time.elapsed()} ms.`)
return result
......
......@@ -16,20 +16,24 @@ const fs = require('fs-extra')
const path = require('path')
const { promisify } = require('util')
const isProxy = require('util').types.isProxy
const EventEmitter = require('events')
const fastWriteAtomic = promisify(require('fast-write-atomic'))
const DataProxy = require('./DataProxy')
const { log, needsToBeProxified } = require('./util')
const Time = require('./Time')
const { emit } = require('process')
class WhatTable {
class WhatTable extends EventEmitter {
// Either loads the table at the passed table path (default) or, if
// a root object is passed, creates a new table at table path, populating
// it with the passed root object.
constructor(tablePath, rootObject = null) {
super()
this.tablePath = tablePath
this.tableFileName = tablePath.slice(tablePath.lastIndexOf(path.sep)+1)
this.tableName = this.tableFileName.replace('.json', '')
......@@ -106,6 +110,7 @@ class WhatTable {
await fastWriteAtomic(this.tablePath, tableContents)
log(` 💾 ❨WhatDB?❩ ╰─ Persisted in ${Time.elapsed()} ms.`)
this.isSaving = false
this.emit('save', this)
}
}
......
......@@ -61,17 +61,46 @@ test('persistence', t => {
//
// Property update.
//
db.people[0].age = 21
// Since writes are asynchronous, we wait a bit to give the table time to save.
setTimeout(() => {
t.strictEquals(JSON.stringify(db.people), JSON.stringify(people), 'original object and data in table are same after property update')
// Listen for the save event.
let actualWriteCount = 0
db.people.__table__.addListener('save', table => {
actualWriteCount++
if (actualWriteCount === 1) {
t.strictEquals(table.tableName, 'people', 'the correct table is saved')
t.strictEquals(expectedWriteCount, actualWriteCount, 'write 1: expected number of writes has taken place')
t.strictEquals(JSON.stringify(db.people), JSON.stringify(people), 'write 1: original object and data in table are same after property update')
const updatedTable = loadTable('db', 'people')
t.strictEquals(updatedTable, JSON.stringify(db.people, null, 2), 'write 1: persisted table matches in-memory table after property update')
//
// Update two properties within the same stack frame.
//
expectedWriteCount = 2
const updatedTable = loadTable('db', 'people')
t.strictEquals(updatedTable, JSON.stringify(db.people, null, 2), 'persisted table matches in-memory table after property update')
db.people[0].age = 43
db.people[1].age = 33
}
if (actualWriteCount === 2) {
t.strictEquals(expectedWriteCount, actualWriteCount, 'write 2: expected number of writes has taken place')
t.strictEquals(JSON.stringify(db.people), JSON.stringify(people), 'write 2: original object and data in table are same after property update')
const updatedTable = loadTable('db', 'people')
t.strictEquals(updatedTable, JSON.stringify(db.people, null, 2), 'write 2: persisted table matches in-memory table after property update')
t.end()
}
})
// Update a property
let expectedWriteCount = 1
db.people[0].age = 21
t.end()
}, 100)
// t.strictEquals(JSON.stringify(db.settings), loadTable('database', 'settings'), 'Settings table is as expected')
})
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment