|
1 | 1 | // Analogous implementation for withReverseReferenceList, for contributions.
|
2 |
| -// This is mostly duplicate code and both should be ported to the same |
3 |
| -// underlying data form later on. |
4 |
| -// |
5 |
| -// This implementation uses a global cache (via WeakMap) to attempt to speed |
6 |
| -// up subsequent similar accesses. |
7 |
| -// |
8 |
| -// This has absolutely not been rigorously tested with altering properties of |
9 |
| -// data objects in a wiki data array which is reused. If a new wiki data array |
10 |
| -// is used, a fresh cache will always be created. |
11 | 2 |
|
12 |
| -import {input, templateCompositeFrom} from '#composite'; |
13 |
| -import {sortByDate} from '#sort'; |
14 |
| -import {stitchArrays} from '#sugar'; |
| 3 | +import withReverseList_template from './helpers/withReverseList-template.js'; |
15 | 4 |
|
16 |
| -import {exitWithoutDependency, raiseOutputWithoutDependency} |
17 |
| - from '#composite/control-flow'; |
18 |
| -import {withFlattenedList, withMappedList} from '#composite/data'; |
19 |
| - |
20 |
| -import inputWikiData from './inputWikiData.js'; |
| 5 | +import {input} from '#composite'; |
21 | 6 |
|
22 |
| -// Mapping of reference list property to WeakMap. |
23 |
| -// Each WeakMap maps a wiki data array to another weak map, |
24 |
| -// which in turn maps each referenced thing to an array of |
25 |
| -// things referencing it. |
26 |
| -const caches = new Map(); |
| 7 | +import {withFlattenedList, withMappedList} from '#composite/data'; |
27 | 8 |
|
28 |
| -export default templateCompositeFrom({ |
| 9 | +export default withReverseList_template({ |
29 | 10 | annotation: `withReverseContributionList`,
|
30 | 11 |
|
31 |
| - inputs: { |
32 |
| - data: inputWikiData({allowMixedTypes: false}), |
33 |
| - list: input({type: 'string'}), |
34 |
| - }, |
35 |
| - |
36 |
| - outputs: ['#reverseContributionList'], |
37 |
| - |
38 |
| - steps: () => [ |
39 |
| - // Common behavior -- |
40 |
| - |
41 |
| - // Early exit with an empty array if the data list isn't available. |
42 |
| - exitWithoutDependency({ |
43 |
| - dependency: input('data'), |
44 |
| - value: input.value([]), |
45 |
| - }), |
46 |
| - |
47 |
| - // Raise an empty array (don't early exit) if the data list is empty. |
48 |
| - raiseOutputWithoutDependency({ |
49 |
| - dependency: input('data'), |
50 |
| - mode: input.value('empty'), |
51 |
| - output: input.value({'#reverseContributionList': []}), |
52 |
| - }), |
53 |
| - |
54 |
| - // Check for an existing cache record which corresponds to this |
55 |
| - // input('list') and input('data'). If it exists, query it for the |
56 |
| - // current thing, and raise that; if it doesn't, create it, put it |
57 |
| - // where it needs to be, and provide it so the next steps can fill |
58 |
| - // it in. |
59 |
| - { |
60 |
| - dependencies: [input('list'), input('data'), input.myself()], |
61 |
| - |
62 |
| - compute: (continuation, { |
63 |
| - [input('list')]: list, |
64 |
| - [input('data')]: data, |
65 |
| - [input.myself()]: myself, |
66 |
| - }) => { |
67 |
| - if (!caches.has(list)) { |
68 |
| - const cache = new WeakMap(); |
69 |
| - caches.set(list, cache); |
70 |
| - |
71 |
| - const cacheRecord = new WeakMap(); |
72 |
| - cache.set(data, cacheRecord); |
73 |
| - |
74 |
| - return continuation({ |
75 |
| - ['#cacheRecord']: cacheRecord, |
76 |
| - }); |
77 |
| - } |
78 |
| - |
79 |
| - const cache = caches.get(list); |
80 |
| - |
81 |
| - if (!cache.has(data)) { |
82 |
| - const cacheRecord = new WeakMap(); |
83 |
| - cache.set(data, cacheRecord); |
84 |
| - |
85 |
| - return continuation({ |
86 |
| - ['#cacheRecord']: cacheRecord, |
87 |
| - }); |
88 |
| - } |
89 |
| - |
90 |
| - return continuation.raiseOutput({ |
91 |
| - ['#reverseContributionList']: |
92 |
| - cache.get(data).get(myself) ?? [], |
93 |
| - }); |
94 |
| - }, |
95 |
| - }, |
96 |
| - |
97 |
| - // Unique behavior for contribution lists -- |
| 12 | + propertyInputName: 'list', |
| 13 | + outputName: '#reverseContributionList', |
98 | 14 |
|
| 15 | + customCompositionSteps: () => [ |
99 | 16 | {
|
100 | 17 | dependencies: [input('list')],
|
101 | 18 | compute: (continuation, {
|
@@ -125,77 +42,5 @@ export default templateCompositeFrom({
|
125 | 42 | }).outputs({
|
126 | 43 | '#mappedList': '#referencedThings',
|
127 | 44 | }),
|
128 |
| - |
129 |
| - // Common behavior -- |
130 |
| - |
131 |
| - // Actually fill in the cache record. Since we're building up a *reverse* |
132 |
| - // reference list, track connections in terms of the referenced thing. |
133 |
| - // Although we gather all referenced things into a set and provide that |
134 |
| - // for sorting purposes in the next step, we *don't* reprovide the cache |
135 |
| - // record, because we're mutating that in-place - we'll just reuse its |
136 |
| - // existing '#cacheRecord' dependency. |
137 |
| - { |
138 |
| - dependencies: ['#cacheRecord', '#referencingThings', '#referencedThings'], |
139 |
| - compute: (continuation, { |
140 |
| - ['#cacheRecord']: cacheRecord, |
141 |
| - ['#referencingThings']: referencingThings, |
142 |
| - ['#referencedThings']: referencedThings, |
143 |
| - }) => { |
144 |
| - const allReferencedThings = new Set(); |
145 |
| - |
146 |
| - stitchArrays({ |
147 |
| - referencingThing: referencingThings, |
148 |
| - referencedThings: referencedThings, |
149 |
| - }).forEach(({referencingThing, referencedThings}) => { |
150 |
| - for (const referencedThing of referencedThings) { |
151 |
| - if (cacheRecord.has(referencedThing)) { |
152 |
| - cacheRecord.get(referencedThing).push(referencingThing); |
153 |
| - } else { |
154 |
| - cacheRecord.set(referencedThing, [referencingThing]); |
155 |
| - allReferencedThings.add(referencedThing); |
156 |
| - } |
157 |
| - } |
158 |
| - }); |
159 |
| - |
160 |
| - return continuation({ |
161 |
| - ['#allReferencedThings']: |
162 |
| - allReferencedThings, |
163 |
| - }); |
164 |
| - }, |
165 |
| - }, |
166 |
| - |
167 |
| - // Sort the entries in the cache records, too, just by date - the rest of |
168 |
| - // sorting should be handled outside of withReverseContributionList, either |
169 |
| - // preceding (changing the 'data' input) or following (sorting the output). |
170 |
| - // Again we're mutating in place, so no need to reprovide '#cacheRecord' |
171 |
| - // here. |
172 |
| - { |
173 |
| - dependencies: ['#cacheRecord', '#allReferencedThings'], |
174 |
| - compute: (continuation, { |
175 |
| - ['#cacheRecord']: cacheRecord, |
176 |
| - ['#allReferencedThings']: allReferencedThings, |
177 |
| - }) => { |
178 |
| - for (const referencedThing of allReferencedThings) { |
179 |
| - if (cacheRecord.has(referencedThing)) { |
180 |
| - const referencingThings = cacheRecord.get(referencedThing); |
181 |
| - sortByDate(referencingThings); |
182 |
| - } |
183 |
| - } |
184 |
| - |
185 |
| - return continuation(); |
186 |
| - }, |
187 |
| - }, |
188 |
| - |
189 |
| - // Then just pluck out the current object from the now-filled cache record! |
190 |
| - { |
191 |
| - dependencies: ['#cacheRecord', input.myself()], |
192 |
| - compute: (continuation, { |
193 |
| - ['#cacheRecord']: cacheRecord, |
194 |
| - [input.myself()]: myself, |
195 |
| - }) => continuation({ |
196 |
| - ['#reverseContributionList']: |
197 |
| - cacheRecord.get(myself) ?? [], |
198 |
| - }), |
199 |
| - }, |
200 | 45 | ],
|
201 | 46 | });
|
0 commit comments