Skip to content
This repository was archived by the owner on Sep 17, 2023. It is now read-only.

Commit 6044e36

Browse files
committed
Allow to remove context value specifiers
1 parent 4da8938 commit 6044e36

File tree

6 files changed

+97
-6
lines changed

6 files changed

+97
-6
lines changed

src/context-registry.spec.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { AIterable } from 'a-iterable';
2+
import { ContextKeyError } from './context-key-error';
23
import { ContextRegistry } from './context-registry';
34
import { ContextValues } from './context-values';
4-
import { ContextKeyError } from './context-key-error';
55
import { MultiContextKey, SingleContextKey } from './simple-context-key';
66
import Mock = jest.Mock;
77

@@ -59,6 +59,41 @@ describe('ContextRegistry', () => {
5959

6060
expect(values.get(key)).toBe(value);
6161
});
62+
it('selects the last value if more than one provided', () => {
63+
64+
const value1 = 'value1';
65+
const value2 = 'value2';
66+
67+
registry.provide({ a: key, is: value1 });
68+
registry.provide({ a: key, is: value2 });
69+
70+
expect(values.get(key)).toBe(value2);
71+
});
72+
it('removes the value specifier', () => {
73+
74+
const value1 = 'value1';
75+
const value2 = 'value2';
76+
77+
registry.provide({ a: key, is: value1 });
78+
registry.provide({ a: key, is: value2 })();
79+
80+
expect(values.get(key)).toBe(value1);
81+
});
82+
it('retains the constructed value when specifier removed', () => {
83+
84+
const value1 = 'value1';
85+
const value2 = 'value2';
86+
87+
registry.provide({ a: key, is: value1 });
88+
89+
const remove = registry.provide({ a: key, is: value2 });
90+
91+
expect(values.get(key)).toBe(value2);
92+
93+
remove();
94+
remove();
95+
expect(values.get(key)).toBe(value2);
96+
});
6297
it('throws if there is neither default nor fallback value', () => {
6398
expect(() => values.get(new SingleContextKey(key.name))).toThrowError(ContextKeyError);
6499
expect(() => values.get(new SingleContextKey(key.name), {})).toThrowError(ContextKeyError);

src/context-registry.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,15 @@ export class ContextRegistry<Ctx extends ContextValues = ContextValues> {
5454
* @typeparam Src Source value type.
5555
* @typeparam Seed Value seed type.
5656
* @param spec Context value specifier.
57+
*
58+
* @returns A function that removes the given context value specifier when called.
5759
*/
58-
provide<Deps extends any[], Src, Seed>(spec: ContextValueSpec<Ctx, any, Deps, Src, Seed>): void {
60+
provide<Deps extends any[], Src, Seed>(spec: ContextValueSpec<Ctx, any, Deps, Src, Seed>): () => void {
5961

6062
const { a: { [ContextKey__symbol]: { seedKey } }, by } = contextValueSpec(spec);
6163
const [seeder] = this._seeding<Src, Seed>(seedKey);
6264

63-
seeder.provide(by);
65+
return seeder.provide(by);
6466
}
6567

6668
/**

src/context-seeder.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ export interface ContextSeeder<Ctx extends ContextValues, Src, Seed> {
2323
* Provides context value.
2424
*
2525
* @param provider Context value provider.
26+
*
27+
* @returns A function that removes the given context value `provider` when called.
2628
*/
27-
provide(provider: ContextValueProvider<Ctx, Src>): void;
29+
provide(provider: ContextValueProvider<Ctx, Src>): () => void;
2830

2931
/**
3032
* Creates context value seed for target `context`.

src/context-up-key.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,41 @@ describe('ContextUpKey', () => {
3636
registry.provide({ a: key, is: afterEventOf(value) });
3737
expect(readValue(values.get(key))).toBe(value);
3838
});
39+
it('selects the last value if more than one provided', () => {
40+
41+
const value1 = 'value1';
42+
const value2 = 'value2';
43+
44+
registry.provide({ a: key, is: value1 });
45+
registry.provide({ a: key, is: value2 });
46+
47+
expect(readValue(values.get(key))).toBe(value2);
48+
});
49+
it('removes the value specifier', () => {
50+
51+
const value1 = 'value1';
52+
const value2 = 'value2';
53+
54+
registry.provide({ a: key, is: value1 });
55+
registry.provide({ a: key, is: value2 })();
56+
57+
expect(readValue(values.get(key))).toBe(value1);
58+
});
59+
it('updates the value when specifier removed', () => {
60+
61+
const value1 = 'value1';
62+
const value2 = 'value2';
63+
64+
registry.provide({ a: key, is: value1 });
65+
66+
const remove = registry.provide({ a: key, is: value2 });
67+
68+
expect(readValue(values.get(key))).toBe(value2);
69+
70+
remove();
71+
remove();
72+
expect(readValue(values.get(key))).toBe(value1);
73+
});
3974
it('throws if there is neither default nor fallback value', () => {
4075
expect(() => readValue(values.get(key))).toThrowError(ContextKeyError);
4176
expect(() => readValue(values.get(key, {})!)).toThrowError(ContextKeyError);

src/context-up-key.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,17 @@ class ContextUpSeeder<Ctx extends ContextValues, Src>
2222

2323
private readonly _providers: ValueTracker<ContextValueProvider<Ctx, Src | EventKeeper<Src[]>>[]> = trackValue([]);
2424

25-
provide(provider: ContextValueProvider<Ctx, Src | EventKeeper<Src[]>>): void {
25+
provide(provider: ContextValueProvider<Ctx, Src | EventKeeper<Src[]>>): () => void {
2626
this._providers.it = [...this._providers.it, provider];
27+
return () => {
28+
29+
const providers = this._providers.it;
30+
const found = providers.indexOf(provider);
31+
32+
if (found >= 0) {
33+
this._providers.it = providers.slice(0, found).concat(providers.slice(found + 1));
34+
}
35+
};
2736
}
2837

2938
seed(context: Ctx, initial: AfterEvent<Src[]> = afterEventOf<Src[]>()): AfterEvent<Src[]> {

src/simple-context-key.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ class SimpleContextSeeder<Ctx extends ContextValues, Src> implements ContextSeed
1313

1414
private readonly _providers: ContextValueProvider<Ctx, Src>[] = [];
1515

16-
provide(provider: ContextValueProvider<Ctx, Src>): void {
16+
provide(provider: ContextValueProvider<Ctx, Src>): () => void {
1717
this._providers.push(provider);
18+
return () => {
19+
20+
const found = this._providers.indexOf(provider);
21+
22+
if (found >= 0) {
23+
this._providers.splice(found, 1);
24+
}
25+
};
1826
}
1927

2028
seed(context: Ctx, initial: AIterable<Src> = AIterable.from(overNone())): AIterable<Src> {

0 commit comments

Comments
 (0)