From 91c3dd9aecc362c994954c0afdebacd197812309 Mon Sep 17 00:00:00 2001 From: Daniel Cachapa Date: Sat, 16 Sep 2023 16:34:51 +0200 Subject: [PATCH] Add `isDeleted` flag to watched change events --- CHANGELOG.md | 4 ++++ lib/src/map_crdt/map_crdt.dart | 10 ++++++---- lib/src/map_crdt/map_crdt_base.dart | 4 +++- pubspec.yaml | 2 +- test/map_crdt_test.dart | 10 +++++++++- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9937ab6..f6a7898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.1.2 + +- Add `isDeleted` flag to watched change events + ## 5.1.1 - Remove timezone drift from `Hlc.toString` diff --git a/lib/src/map_crdt/map_crdt.dart b/lib/src/map_crdt/map_crdt.dart index 78d6770..8921318 100644 --- a/lib/src/map_crdt/map_crdt.dart +++ b/lib/src/map_crdt/map_crdt.dart @@ -8,7 +8,8 @@ import 'record.dart'; /// datasets. It is incredibly inefficient. class MapCrdt extends MapCrdtBase { final Map> _recordMaps; - final Map> + final Map> _changeControllers; @override @@ -37,13 +38,14 @@ class MapCrdt extends MapCrdtBase { void putRecords(Map> dataset) { dataset.forEach((table, records) { _recordMaps[table]!.addAll(records); - records.forEach((key, value) => - _changeControllers[table]!.add((key: key, value: value.value))); + records.forEach((key, record) => _changeControllers[table]! + .add((key: key, value: record.value, isDeleted: record.isDeleted))); }); } @override - Stream<({String key, dynamic value})> watch(String table, {String? key}) { + Stream<({String key, dynamic value, bool isDeleted})> watch(String table, + {String? key}) { if (!tables.contains(table)) throw 'Unknown table: $table'; return key == null ? _changeControllers[table]!.stream diff --git a/lib/src/map_crdt/map_crdt_base.dart b/lib/src/map_crdt/map_crdt_base.dart index 1af4eb0..6486a56 100644 --- a/lib/src/map_crdt/map_crdt_base.dart +++ b/lib/src/map_crdt/map_crdt_base.dart @@ -7,6 +7,8 @@ import '../hlc.dart'; import '../types.dart'; import 'record.dart'; +typedef WatchEvent = ({String key, dynamic value, bool isDeleted}); + /// A CRDT backed by a simple in-memory hashmap. /// Useful for testing, or for applications which only require small, ephemeral /// datasets. It is incredibly inefficient. @@ -97,7 +99,7 @@ abstract class MapCrdtBase extends Crdt { /// Returns a stream of changes. /// Use the optional [key] parameter to filter events or leave it empty to get /// all changes. - Stream<({String key, dynamic value})> watch(String table, {String? key}); + Stream watch(String table, {String? key}); @override CrdtChangeset getChangeset({ diff --git a/pubspec.yaml b/pubspec.yaml index fdd1597..9f8ad9f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: crdt description: Dart implementation of Conflict-free Replicated Data Types (CRDTs). -version: 5.1.1 +version: 5.1.2 homepage: https://github.com/cachapa/crdt repository: https://github.com/cachapa/crdt issue_tracker: https://github.com/cachapa/crdt/issues diff --git a/test/map_crdt_test.dart b/test/map_crdt_test.dart index b921ce3..cc412ff 100644 --- a/test/map_crdt_test.dart +++ b/test/map_crdt_test.dart @@ -364,10 +364,18 @@ void main() { test('Single change', () async { // ignore: unawaited_futures - expectLater(crdt.watch('table'), emits((key: 'x', value: 1))); + expectLater( + crdt.watch('table'), emits((key: 'x', value: 1, isDeleted: false))); await crdt.put('table', 'x', 1); }); + test('Deleted', () async { + // ignore: unawaited_futures + expectLater( + crdt.watch('table'), emits((key: 'x', value: 1, isDeleted: true))); + await crdt.put('table', 'x', 1, true); + }); + test('Enforce table existence', () { expect( () => crdt.watch('not_table'),