Skip to content

Commit 221102e

Browse files
author
calvin-mbp-2019
committed
Added support for Observable/Listener of AfterDelete<T> events
1 parent 3137ce2 commit 221102e

File tree

4 files changed

+95
-27
lines changed

4 files changed

+95
-27
lines changed

Diff for: .vscode/launch.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"name": "Debug runner.ts",
1111
"skipFiles": ["<node_internals>/**"],
1212
"program": "${workspaceFolder}/src/runner.ts",
13-
// "preLaunchTask": "tsc: build - tsconfig.json",
13+
"preLaunchTask": "tsc: build - tsconfig.json",
1414
"outFiles": ["${workspaceFolder}/**/*.js"]
1515
}
1616
]

Diff for: src/gang-of-four/observer.ts

+40-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ export interface AfterSet<T> {
1717
createdAt: Date;
1818
}
1919

20+
export interface BeforeDelete<T> {
21+
existingValue: T | null;
22+
}
23+
24+
export interface AfterDelete<T> {
25+
existingValue: T;
26+
deletedAt: Date;
27+
}
28+
2029
/* this function returns an object with two members; i.e. two functions; namely
2130
* one to subscribe to events and one to publish an event that is observed by its
2231
* subscribed listeners. NOTE: that this function defines an inline type that could
@@ -55,12 +64,12 @@ export function createObserver<EventType>(): {
5564
}
5665

5766
export interface ObservableDatabase<T extends Indexable> extends Database<T> {
58-
set(value: T): T;
59-
get(id: string): T | null;
60-
6167
// The observable event hooks
6268
onBeforeSet(Listener: Listener<BeforeSet<T>>): () => void;
6369
onAfterSet(Listener: Listener<AfterSet<T>>): () => void;
70+
71+
onBeforeDelete(Listener: Listener<BeforeDelete<T>>): () => void;
72+
onAfterDelete(Listener: Listener<AfterDelete<T>>): () => void;
6473
}
6574

6675
export function createSingletonObservableDatabase<T extends Indexable>() {
@@ -106,6 +115,24 @@ export function createSingletonObservableDatabase<T extends Indexable>() {
106115
return this.dataStorage.get(id) ?? null;
107116
}
108117

118+
public delete(id: string): void {
119+
// WARNING: race condition can occur between check and actual delete
120+
const target = this.dataStorage.get(id);
121+
if (target) {
122+
this._beforeDeleteListeners.publish({
123+
existingValue: target,
124+
});
125+
}
126+
127+
const deleted = this.dataStorage.delete(id);
128+
if (target && deleted) {
129+
this._afterDeleteListeners.publish({
130+
existingValue: target,
131+
deletedAt: new Date(),
132+
});
133+
}
134+
}
135+
109136
// The observable event hooks
110137
private _beforeAddListeners = createObserver<BeforeSet<T>>();
111138
private _afterAddListeners = createObserver<AfterSet<T>>();
@@ -116,6 +143,16 @@ export function createSingletonObservableDatabase<T extends Indexable>() {
116143
onAfterSet(listener: Listener<AfterSet<T>>): () => void {
117144
return this._afterAddListeners.subscribe(listener);
118145
}
146+
147+
private _beforeDeleteListeners = createObserver<BeforeDelete<T>>();
148+
private _afterDeleteListeners = createObserver<AfterDelete<T>>();
149+
150+
onBeforeDelete(listener: Listener<BeforeDelete<T>>): () => void {
151+
return this._beforeDeleteListeners.subscribe(listener);
152+
}
153+
onAfterDelete(listener: Listener<AfterDelete<T>>): () => void {
154+
return this._afterDeleteListeners.subscribe(listener);
155+
}
119156
}
120157

121158
// NOTE: this may be odd but this does allow a class; rather than a function

Diff for: src/runner.ts

+52-21
Original file line numberDiff line numberDiff line change
@@ -116,30 +116,22 @@ console.log(
116116
"Adding Observable Design Pattern on top of Factory and Singleton Database Intance rather than previous instances."
117117
);
118118

119-
debugger;
120119
const RedisObservableDatabaseFromFactory =
121120
createSingletonObservableDatabase<Person>();
122-
// Add observer with a function that receives an instance that we destruture to just the value
123-
// RedisObservableDatabaseFromFactory.instance.onAfterSet(
124-
// ({ existingValue, newValue, createdAt }) => {
125-
// console.log(
126-
// "Observable callback triggered from Observable Database AfterSet event triggered"
127-
// );
128-
// console.table({ createdAt, existingValue, newValue });
129-
// }
130-
// );
131-
132-
RedisObservableDatabaseFromFactory.instance.onAfterSet((event) => {
133-
console.log(
134-
"Observable callback triggered from Observable Database AfterSet event triggered"
135-
);
136-
console.table({
137-
fromValue: event.existingValue,
138-
to: event.newValue,
139-
happenedAt: event.createdAt.toDateString(),
121+
122+
// Add observer with a function that receives an instance of <T> log for visibility; can also destruture to just the value
123+
const terminateAfterSetSubscription =
124+
RedisObservableDatabaseFromFactory.instance.onAfterSet((event) => {
125+
console.log(
126+
"Observable callback triggered from Observable Database AfterSet event triggered"
127+
);
128+
console.table({
129+
fromValue: event.existingValue,
130+
toValue: event.newValue,
131+
happenedAt: event.createdAt.toDateString(),
132+
});
133+
// console.table({ ...event });
140134
});
141-
// console.table({ ...event });
142-
});
143135

144136
// Set it initially with no prior value
145137
RedisObservableDatabaseFromFactory.instance.set(<Engineer>{
@@ -158,3 +150,42 @@ RedisObservableDatabaseFromFactory.instance.set(<Engineer>{
158150
role: "director of FSD.V2",
159151
level: 46,
160152
});
153+
154+
// Now terminate subscription and expect that no more events are dispatched for this AfterSet<T> listener
155+
terminateAfterSetSubscription();
156+
console.log(
157+
'AfterSet listener has unsubscribed and will not receive the "V3" updates from Observable Database and has no readily available reference to re-subscribe once termianted.'
158+
);
159+
// Set it again to see the prior value
160+
RedisObservableDatabaseFromFactory.instance.set(<Engineer>{
161+
id: karpathy,
162+
name: "Andrei Karpathy.V3",
163+
description: "ML/AI Engineer.V3",
164+
role: "director of FSD.V3",
165+
level: 46,
166+
});
167+
168+
console.log("\n\r");
169+
console.log("AfterDelete listener subscribed to track deletes.");
170+
171+
const terminateAfterDeleteSubscription =
172+
RedisObservableDatabaseFromFactory.instance.onAfterDelete((event) => {
173+
console.log(
174+
"Observable AfterDelete event callback triggered from Observable Database "
175+
);
176+
console.table({
177+
fromValue: event.existingValue,
178+
happenedAt: event.deletedAt.toDateString(),
179+
});
180+
});
181+
182+
// Delete the last modified record V3 instance of the reord
183+
RedisObservableDatabaseFromFactory.instance.delete(karpathy);
184+
185+
console.log(
186+
"Confirming the deleted record is no longer retrievable from Database."
187+
);
188+
const goner = RedisObservableDatabaseFromFactory.instance.get(karpathy);
189+
console.table({ goner });
190+
191+
terminateAfterDeleteSubscription();

Diff for: tsconfig.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"strict": true
4-
// "sourceMap": true
3+
"strict": true,
4+
"sourceMap": true
55
}
66
}

0 commit comments

Comments
 (0)