Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
d4476da
feat(layered-storage): add a new storage for options
Thomaash Dec 8, 2019
285977d
feat(layered-storage): return off function from on
Thomaash Dec 9, 2019
4b49dc1
docs(layered-storage): add some docs
Thomaash Dec 9, 2019
9fb537a
docs(layered-storage): add yet more docs
Thomaash Dec 11, 2019
357b951
docs(layered-storage): add docs to transactions
Thomaash Dec 12, 2019
353f220
style(layered-storage): rename revert to abort
Thomaash Dec 12, 2019
47bfa2f
docs(layered-storage): finish the docs
Thomaash Dec 12, 2019
bfb7012
test(layered-storage): check that off unbinds listeners
Thomaash Dec 12, 2019
b2f0408
fix(layered-storage): fix unwritten segments being undefined
Thomaash Dec 14, 2019
85ea039
fix(layered-storage): cache the values when accessed
Thomaash Dec 14, 2019
e5d429c
fix(layered-storage): throw for nonnumeric layers
Thomaash Dec 14, 2019
689c154
feat(layered-storage): add console dump method
Thomaash Dec 14, 2019
b6f13b8
fix(layered-storage): purge empty structures
Thomaash Dec 15, 2019
7c4350c
fix(layered-storage): do not create new structures on access
Thomaash Dec 15, 2019
d9eb64c
style: fix linting issues
Thomaash Dec 15, 2019
de132b4
feat(layered-storage): remove events
Thomaash Dec 17, 2019
24beaa2
style(layered-storage): remove unused import
Thomaash Dec 17, 2019
b1787b8
feat(layered-layout): add validation
Thomaash Dec 18, 2019
b64c4fd
perf(layered-storage): do not clean unaffacted cache
Thomaash Dec 23, 2019
4bf2f3a
perf(layered-storage): cache nonexistent values too
Thomaash Dec 25, 2019
80f189c
perf(layered-storage): iterate over cache when clearing it
Thomaash Dec 25, 2019
e87368a
feat(layered-storage): add expanders
Thomaash Dec 26, 2019
1ca06e1
feat(layered-storage): unify validator and expander API
Thomaash Dec 26, 2019
1fed169
feat(layered-storage): add segment cloning
Thomaash Dec 27, 2019
b9b559c
fix(layered-storage): use entries instead of pair
Thomaash Dec 27, 2019
9d61fa2
fix(layered-storage): sort debug output
Thomaash Dec 27, 2019
84f4d27
chore(layered-storage): fix some doc comments
Thomaash Jan 21, 2020
093040f
perf(layered-storage): improve performance
Thomaash Jan 21, 2020
fd2a42e
docs(layered-storage): update outdated comment
Thomaash Jan 21, 2020
84a0144
style(layered-storage): unify the way existence is checked in core
Thomaash Jan 21, 2020
cc0f866
feat(layered-storage): simplify
Thomaash Jan 26, 2020
cc7e879
fix(layered-storage): remove cyclic dep
Thomaash Jan 26, 2020
bf566ec
Merge branch 'master' of github.com:visjs/vis-util into layered-storage
Thomaash May 24, 2020
763c703
style: reformat
Thomaash May 24, 2020
28a1240
style: fix typo in config
Thomaash May 24, 2020
1153f4a
chore: restore package* from master
Thomaash May 24, 2020
41195bd
feat(layered-storage): add export to object
Thomaash May 24, 2020
6b3785b
fix(layered-storage): throw right away in transactions
Thomaash May 24, 2020
d229669
test(layered-storage): fix copy paste error
Thomaash May 24, 2020
d1c16b9
feat(layered-storage): add inheritance
Thomaash May 24, 2020
10367bb
feat(layered-storage): add a basic library of validators
Thomaash May 24, 2020
9175f38
feat(layered-storage)!: use sepparate input and output types
Thomaash May 31, 2020
9089b80
fix(layered-storage): expand keys recursively
Thomaash May 31, 2020
fb0fc8b
feat(layered-storage)!: add DELETE keyword to delete values through set
Thomaash May 31, 2020
ab56402
Merge branch 'master' of github.com:visjs/vis-util into layered-storage
Thomaash May 31, 2020
6c3c545
Merge branch 'master' of github.com:visjs/vis-util into layered-storage
Thomaash Jun 1, 2020
3a4b9f2
Merge branch 'master' of github.com:visjs/vis-util into layered-storage
Thomaash Jun 18, 2020
ab88903
Merge branch 'master' of github.com:visjs/vis-util into layered-storage
Thomaash Aug 9, 2020
edb0996
style(layered-storage): fix linting issues
Thomaash Aug 9, 2020
4d41b96
feat(layered-storage): add delete single layer from segment
Thomaash Aug 9, 2020
81fd0c3
Merge branch 'master' of github.com:visjs/vis-util into layered-storage
Thomaash Aug 9, 2020
15603e8
test: disable DTS testing for now
Thomaash Aug 9, 2020
97deb9b
style(eslint): fix automerging error
Thomaash Aug 9, 2020
501926e
fix(layered-storage): export it
Thomaash Aug 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(layered-storage): add inheritance
This can be used to implement groups from Vis Network. It actually
allows for multiple groups per single node which is already a wanted
feature that would be extremely difficult to implement using the
(anti)pattern that's currently in Vis Network for options.
  • Loading branch information
Thomaash committed May 24, 2020
commit d1c16b9fada00da95fb9e1383760f66ad6d83a8a
88 changes: 62 additions & 26 deletions src/layered-storage/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ export class LayeredStorageCore<
*/
private readonly _segments = new Set<Segment>();

/**
* Segment inheritance chains.
*
* @remarks
* The first element always has to be the segment itself:
* [this segment, ...ancestors, global segment]
*/
private readonly _inheritance = new Map<Segment, Segment[]>();

/**
* A list of validators for each key.
*/
Expand Down Expand Up @@ -204,65 +213,68 @@ export class LayeredStorageCore<
/**
* Retrieve a value.
*
* @param segment - Which segment to search through in addition to the global
* @param thisSegment - Which segment to search through in addition to the global
* segment which is used as the fallback on each level.
* @param key - The key corresponding to the requested value.
*
* @returns The value or undefined if it wasn't found.
*/
public get<Key extends Keys>(
segment: Segment,
thisSegment: Segment,
key: Key
): KV[Key] | undefined {
let segmentCache = this._topLevelCache.get(segment);
if (typeof segmentCache === "undefined") {
segmentCache = new Map();
this._topLevelCache.set(segment, segmentCache);
let thisSegmentCache = this._topLevelCache.get(thisSegment);
if (typeof thisSegmentCache === "undefined") {
thisSegmentCache = new Map();
this._topLevelCache.set(thisSegment, thisSegmentCache);
}

// Return cached value if it exists.
const cached = segmentCache.get(key);
const cached = thisSegmentCache.get(key);
if (typeof cached !== "undefined") {
// TODO: The non null assertion shouldn't be necessary.
return cached === null ? void 0 : cached!;
}

// Fetch the inheritance chain.
const segments = this._inheritance.get(thisSegment) ?? [
thisSegment,
this.globalSegment,
];

// Search the layers from highest to lowest priority.
for (const layerData of this._layerDatas) {
if (typeof layerData === "undefined") {
// Empty layer.
continue;
}

// Check the segment and quit if found.
const segmentData = layerData.get(segment);
if (typeof segmentData !== "undefined") {
const value = segmentData.get(key);
if (typeof value !== "undefined") {
// Save to the cache.
segmentCache.set(key, value);
// Search the inheritance chain.
for (const segment of segments) {
// Check the segment and quit if found.
const segmentData = layerData.get(segment);
if (typeof segmentData === "undefined") {
// Empty segment on this layer.
continue;
}

return value;
const value = segmentData.get(key);
if (typeof value === "undefined") {
// No value for this segment on this layer.
continue;
}
}

// Check the global segment and quit if found.
const globalData = layerData.get(this.globalSegment);
if (typeof globalData !== "undefined") {
const value = globalData.get(key);
if (typeof value !== "undefined") {
// Save to the cache.
segmentCache.set(key, value);
// Save to the cache.
thisSegmentCache.set(key, value);

return value;
}
return value;
}
}

// If nothing was found by now there are no values for the key.

// Save to the cache.
segmentCache.set(key, null);
thisSegmentCache.set(key, null);

// Return the empty value.
return;
Expand Down Expand Up @@ -376,6 +388,29 @@ export class LayeredStorageCore<
this._cleanCache(segment, key);
}

/**
* Set the inherance chain of given segment.
*
* @param segment - The segment that will inherit.
* @param segments - The segments from which will be inherited.
* @param global - Whether to inherit from global (as is the default) or not.
*/
public setInheritance(
segment: Segment,
segments: Segment[],
global = true
): void {
this._inheritance.set(
segment,
global
? [segment, ...segments, this.globalSegment]
: [segment, ...segments]
);

// Inheritance can affect anything, delete the whole cache for this segment.
this._topLevelCache.delete(segment);
}

/**
* Export data in an object format.
*
Expand Down Expand Up @@ -461,6 +496,7 @@ export class LayeredStorageCore<
}
this._topLevelCache.delete(segment);
this._segments.delete(segment);
this._inheritance.delete(segment);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/layered-storage/segment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ export class LayeredStorageSegment<
});
}

/**
* Set the inherance chain of this segment.
*
* @param segments - The segments from which this segment will inherit.
* @param global - Whether to inherit from global (as is the default) or not.
*/
public setInheritance(segments: Segment[], global = true): void {
this._core.setInheritance(this.segment, segments, global);
}

/**
* Create a new segmented instance for working with a single segment with a
* copy of another segments data.
Expand Down
2 changes: 2 additions & 0 deletions test/layered-storage/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { allCombined } from "./all-combined";
import { cloning } from "./cloning";
import { expanders } from "./expanders";
import { inheritance } from "./inheritance";
import { multipleKeys } from "./multiple-keys";
import { multipleLayers } from "./multiple-layers";
import { other } from "./other";
Expand All @@ -13,6 +14,7 @@ describe("Layered storage", function (): void {
allCombined();
cloning();
expanders();
inheritance();
multipleKeys();
multipleLayers();
other();
Expand Down
171 changes: 171 additions & 0 deletions test/layered-storage/inheritance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { LayeredStorage } from "../../src/layered-storage";
import { expect } from "chai";

interface KV {
"test.value": string;
"test.value1": string;
"test.value2": string;
"test.value3": string;
"test.value4": string;
"test.value5": string;
"test.value6": string;
"test.value7": string;
"unrelated.value": string;
}

/**
* Test that segments inherit from other segments.
*/
export function inheritance(): void {
describe("Inheritance", function (): void {
const a = Symbol("A");
const b = Symbol("B");
const c = Symbol("C");
const d = Symbol("D");
const e = Symbol("E");
const f = Symbol("F");

it("Default inheritance", function (): void {
const ls = new LayeredStorage<7, KV, keyof KV>();

ls.global.set(7, "test.value", "global");
ls.openSegment(a).set(7, "test.value", "A");
ls.openSegment(b).set(7, "test.value", "B");

expect(
ls.openSegment(c).get("test.value"),
"By default segments should inherit from the global segment"
).to.equal("global");
});

it("Disable global inheritance", function (): void {
const ls = new LayeredStorage<7, KV, keyof KV>();

ls.global.set(7, "test.value", "global");
ls.openSegment(a).set(7, "test.value", "A");
ls.openSegment(b).set(7, "test.value", "B");
ls.openSegment(c).setInheritance([], false);

expect(
ls.openSegment(c).has("test.value"),
"Nothing should be inherited if inheritance chain is empty and global disabled"
).to.be.false;
});

it("Other segment without global", function (): void {
const ls = new LayeredStorage<3 | 7, KV, keyof KV>();

ls.global.set(7, "test.value", "global");
ls.openSegment(a).set(7, "test.value", "A");
ls.openSegment(b).set(7, "test.value", "B");
ls.openSegment(c).setInheritance([a], false);

expect(
ls.openSegment(c).get("test.value"),
"The value should be inherited from A since C has none of it's own and global inheritance (even though global has higher priority in this case) is disabled"
).to.equal("A");
});

it("Other segment with global", function (): void {
const ls = new LayeredStorage<3 | 7, KV, keyof KV>();

ls.global.set(7, "test.value", "global");
ls.openSegment(a).set(3, "test.value", "A");
ls.openSegment(b).set(7, "test.value", "B");
ls.openSegment(c).setInheritance([a]);

expect(
ls.openSegment(c).get("test.value"),
"The value should be inherited from global since C has none of it's own (A has lower priority than global)"
).to.equal("global");
});

it("Multiple inheritance", function (): void {
const ls = new LayeredStorage<3 | 7, KV, keyof KV>();

ls.openSegment(f).setInheritance([e, d, c, b, a]);

ls.global.set(7, "test.value1", "global");
ls.openSegment(a).set(7, "test.value1", "A");
ls.openSegment(b).set(7, "test.value1", "B");
ls.openSegment(c).set(7, "test.value1", "C");
ls.openSegment(d).set(7, "test.value1", "D");
ls.openSegment(e).set(7, "test.value1", "E");
ls.openSegment(f).set(7, "test.value1", "F");

ls.global.set(7, "test.value2", "global");
ls.openSegment(a).set(7, "test.value2", "A");
ls.openSegment(b).set(7, "test.value2", "B");
ls.openSegment(c).set(7, "test.value2", "C");
ls.openSegment(d).set(7, "test.value2", "D");
ls.openSegment(e).set(7, "test.value2", "E");

ls.global.set(7, "test.value3", "global");
ls.openSegment(a).set(7, "test.value3", "A");
ls.openSegment(b).set(7, "test.value3", "B");
ls.openSegment(c).set(7, "test.value3", "C");
ls.openSegment(d).set(7, "test.value3", "D");

ls.global.set(7, "test.value4", "global");
ls.openSegment(a).set(7, "test.value4", "A");
ls.openSegment(b).set(7, "test.value4", "B");
ls.openSegment(c).set(7, "test.value4", "C");

ls.global.set(7, "test.value5", "global");
ls.openSegment(a).set(7, "test.value5", "A");
ls.openSegment(b).set(7, "test.value5", "B");

ls.global.set(7, "test.value6", "global");
ls.openSegment(a).set(7, "test.value6", "A");

ls.global.set(7, "test.value7", "global");

expect(ls.openSegment(f).get("test.value1")).to.equal("F");
expect(ls.openSegment(f).get("test.value2")).to.equal("E");
expect(ls.openSegment(f).get("test.value3")).to.equal("D");
expect(ls.openSegment(f).get("test.value4")).to.equal("C");
expect(ls.openSegment(f).get("test.value5")).to.equal("B");
expect(ls.openSegment(f).get("test.value6")).to.equal("A");
expect(ls.openSegment(f).get("test.value7")).to.equal("global");
});

it("Change inheritance", function (): void {
const ls = new LayeredStorage<3 | 7, KV, keyof KV>();

ls.global.set(7, "test.value", "global");
ls.openSegment(a).set(7, "test.value", "A");
ls.openSegment(b).set(7, "test.value", "B");

expect(
ls.openSegment(c).get("test.value"),
"C should follow default inheritance rules"
).to.equal("global");

ls.openSegment(c).setInheritance([a, b]);

expect(
ls.openSegment(c).get("test.value"),
"C should inherit from A then B then global"
).to.equal("A");

ls.openSegment(c).setInheritance([b]);

expect(
ls.openSegment(c).get("test.value"),
"C should inherit from B then global"
).to.equal("B");

ls.openSegment(c).setInheritance([]);

expect(
ls.openSegment(c).get("test.value"),
"C should inherit from global"
).to.equal("global");

ls.openSegment(c).setInheritance([], false);

expect(ls.openSegment(c).has("test.value"), "C should not inherit at all")
.to.be.false;
});
});
}