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

Commit 0ae03c6

Browse files
committed
Document updatable values
1 parent 9897f53 commit 0ae03c6

File tree

1 file changed

+66
-37
lines changed

1 file changed

+66
-37
lines changed

README.md

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,17 @@ context.get(key2); // 6
102102
[Context Value Target]: #context-value-target
103103

104104
The `provide()` method accepts not only a `ContextKey` instance, but arbitrary `ContextTarget`. The latter is just an
105-
object with `key` property containing a `ContextKey` to provide.
105+
object with `[ContextKey__symbol]` property containing a `ContextKey` to provide.
106106

107107

108108
This can be handy e.g. when providing an instance of some known type:
109109
```typescript
110-
import { ContextKey, ContextRegistry, SingleContextKey } from 'context-values';
110+
import { ContextKey, ContextKey__symbol, ContextRegistry, SingleContextKey } from 'context-values';
111111

112112
class MyService {
113113

114114
// MyService class (not instance) implements a `ContextRequest`
115-
static readonly key: ContextKey<MyService> = new SingleContextKey('my-service');
115+
static readonly [ContextKey__symbol]: ContextKey<MyService> = new SingleContextKey('my-service');
116116

117117
}
118118

@@ -138,11 +138,11 @@ This specifier defines a value (or, more precisely, the [value sources]). It may
138138
- `registry.provide({ a: key, by: (a, b) => calculateValue(a, b), with: [keyA, keyB] })` - evaluates the value based on
139139
other context values with keys `keyA` and `keyB`.
140140
- `registry.provide({ a: key, as: MyService })` - constructs the value as `new MyService(ctx)`, where `ctx` is the
141-
target context. The `a` property may be omitted if `MyService` has a static property `key`.
141+
target context. The `a` property may be omitted if `MyService` has a static `[ContextKey__symbol]` property.
142142
See [Context Value Target].
143143
- `registry.provide({ a: key, as: MyService, with: [keyA, keyB] })` - constructs the value as `new MyService(a, b)`,
144144
where `a` and `b` are context values with keys `keyA` and `keyB` respectively. The `a` property may be omitted if
145-
`MyService` has a static property `key`. See [Context Value Target].
145+
`MyService` has a static `[ContextKey__symbol]` property. See [Context Value Target].
146146
- `registry.provide({ a: key, via: otherKey })` - makes the value available under `otherKey` available under `key`.
147147
I.e. aliases it.
148148

@@ -187,7 +187,7 @@ The default value is evaluated by the function accepting a `ContextValues` insta
187187
import { ContextRegistry, SingleContextKey, MultiContextKey } from 'context-values';
188188

189189
const key1 = new SingleContextKey<string>('key1');
190-
const key2 = new SingleContextKey<number>('key2', ctx => ctx.get('key1').length);
190+
const key2 = new SingleContextKey<number>('key2', { byDefault: ctx => ctx.get('key1').length });
191191
const key3 = new MultiContextKey<number>('key3');
192192

193193
const registry = new ContextRegistry();
@@ -209,63 +209,58 @@ context.get(key2); // 999 - provided explicitly
209209

210210
### Custom Context Key
211211

212-
[ContextKey.merge()]: #custom-context-key
212+
[ContextKey.grow()]: #custom-context-key
213213

214214
It is possible to implement a custom `ContextKey`.
215215

216-
For that extend an `AbstractContextKey` that implements the boilerplate. The only method left to implement then is a
217-
`merge()` one.
218-
219-
The `merge()` method takes three parameters:
220-
- a `ContextValues` instance (to consult other context values if necessary),
221-
- a `ContextSources` instance (containing provided [value sources]), and
222-
- a `handleDefault` function responsible for the default value selection.
216+
For that extend e.g. a `SimpleContextKey` that implements the boilerplate. The only method left to implement then is a
217+
`grow()` one.
223218

224-
The method returns a context value constructed out of the provided value sources.
219+
The `grow()` method takes a single `ContextValueOpts` parameter containing the value construction options and returns
220+
a context value constructed out of the provided value sources.
225221

226222

227-
#### Value Sources
223+
#### Value Sources and Seeds
228224

229225
[value sources]: #value-sources
230226

231-
Instead of the values themselves, the registry allows to provide their sources. Those are used by [ContextKey.merge()]
232-
method to construct the value.
227+
Instead of the value itself, the registry allows to provide its sources. Those are combined into _value seed_.
228+
That is passed to [ContextKey.grow()] method to construct the value (or grow it from the seed).
233229

234-
There could be many sources per single value. And they could be of a type different from the final value.
230+
There could be multiple sources per single value. And they could be of different type.
235231

236-
The sources are passed to the `merge()` function as an [AIterable] instance. The latter is an enhanced `Iterable` with
237-
Array-like API, including `map()`, `flatMap()`, `forEach()`, and other methods.
232+
For example, the seed of `SimpleValueKey` and `MultiValueKey` is an [AIterable] instance. The latter is enhanced
233+
`Iterable` with Array-like API, including `map()`, `flatMap()`, `forEach()`, and other methods.
238234

239-
[AIterable]: https://www.npmjs.com/package/a-iterable
235+
[AIterable]: https://www.npmjs.com/package/a-iterable
240236

241237
```typescript
238+
import { AIterable } from 'a-iterable';
242239
import {
243-
AbstractContextKey,
244240
ContextRegistry,
245-
ContextSources,
241+
ContextValueOpts,
246242
ContextValues,
247-
Handler,
248-
DefaultContextValueHandler
243+
SimpleContextKey,
249244
} from 'context-values';
250245

251-
class ConcatContextKey<V> extends AbstractContextKey<V, string> {
246+
class ConcatContextKey<Src> extends SimpleContextKey<string, Src> {
252247

253248
constructor(name: string) {
254-
super(name);
249+
super(name);
255250
}
256251

257-
merge(
258-
context: ContextValues,
259-
sources: ContextSources<string>,
260-
handleDefault: DefaultContextValueHandler<string>): string | null | undefined {
252+
grow<Ctx extends ContextValues>(
253+
opts: ContextValueOpts<Ctx, string, Src, AIterable<Src>>,
254+
): string | null | undefined {
261255

262-
const result = sources.reduce((p, s) => p != null ? `${p}, ${s}` : `${s}`, null);
256+
const result = opts.seed.reduce((p, s) => p != null ? `${p}, ${s}` : `${s}`, null);
263257

264258
if (result != null) {
265259
return result;
266260
}
267-
268-
return handleDefault(() => ''); // No sources provided. Returning empty string, unless a fallback value provided.
261+
262+
// No sources provided. Returning empty string, unless a fallback value provided.
263+
return opts.byDefault(() => '');
269264
}
270265

271266
}
@@ -286,9 +281,43 @@ context.get(key2); // '' - empty string by default
286281
context.get(key2, { or: undefined }); // undefined - fallback value
287282
```
288283

289-
A context value for particular key is constructed at most once. Thus, the `merge()` method is called at most once per
284+
A context value for particular key is constructed at most once. Thus, the `grow()` method is called at most once per
290285
key.
291286

292-
A [context value specifier](#context-value-specifier) is consulted at most once per key. And only when the `merge()`
287+
A [context value specifier](#context-value-specifier) is consulted at most once per key. And only when the `grow()`
293288
method requested the source value. So, for example, if multiple sources specified for the same `SingleContextKey`, only
294289
the last one will be constructed and used as a context value. The rest of them won't be constructed at all.
290+
291+
292+
Updatable Context Values
293+
------------------------
294+
295+
A `SimpleContextKey`, and thus `SingleContextKey` and `MultiContextKey` extending it, imply that once the associated
296+
context value constructed, it no longer changes. I.e. event though more sources provided for the same key in
297+
`ContextRegistry`, they won't affect the already constructed value.
298+
299+
However, it is possible to update context values. For that a `ContextUpKey` abstract context value key implementation
300+
may be used, or `SingleContextUpKey` and `MultiContextUpKey` implementations.
301+
302+
They provide the values of [AfterEvent] registrar of value receivers. This registrar is a function accepting a callback
303+
that will be called with actual value each time it changes. E.g. when new value source is provided in `ContextRegistry`:
304+
305+
```typescript
306+
import {
307+
ContextRegistry,
308+
SingleContextUpKey,
309+
} from 'context-values';
310+
311+
const key = new SingleContextUpKey<string>('updatable-value');
312+
const registry = new ContextRegistry();
313+
314+
registry.provide({ a: key, is: 'initial' });
315+
316+
const values = registry.newValues();
317+
318+
values.get(key)(value => console.log(value)); // Log: 'initial'
319+
320+
registry.provide({ a: key, is: 'updated' }); // Log: 'updated'
321+
```
322+
323+
[AfterEvent]: https://www.npmjs.com/package/fun-events

0 commit comments

Comments
 (0)