From fd102ee597e2ad2ba8f0950ce1a16fd34029963d Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Mon, 18 Sep 2023 13:26:18 -0300 Subject: data: MORE composite wip --- src/data/things/composite.js | 44 +++---- src/data/things/thing.js | 265 +++++++++++++++++++------------------------ src/data/things/track.js | 45 +++++--- 3 files changed, 170 insertions(+), 184 deletions(-) diff --git a/src/data/things/composite.js b/src/data/things/composite.js index f2ca2c7..c33fc03 100644 --- a/src/data/things/composite.js +++ b/src/data/things/composite.js @@ -1349,7 +1349,7 @@ export function exposeConstant({ // for values like zero and the empty string! // -const availabilityCheckMode = { +const availabilityCheckModeInput = { validate: oneOf('null', 'empty', 'falsy'), defaultValue: 'null', }; @@ -1359,7 +1359,7 @@ export const withResultOfAvailabilityCheck = templateCompositeFrom({ inputs: { from: input(), - mode: input(availabilityCheckMode), + mode: input(availabilityCheckModeInput), }, outputs: { @@ -1403,7 +1403,7 @@ export const exposeDependencyOrContinue = templateCompositeFrom({ inputs: { dependency: input(), - mode: input(availabilityCheckMode), + mode: input(availabilityCheckModeInput), }, steps: () => [ @@ -1432,7 +1432,7 @@ export const exposeUpdateValueOrContinue = templateCompositeFrom({ annotation: `exposeUpdateValueOrContinue`, inputs: { - mode: input(availabilityCheckMode), + mode: input(availabilityCheckModeInput), }, steps: () => [ @@ -1450,7 +1450,7 @@ export const exitWithoutDependency = templateCompositeFrom({ inputs: { dependency: input(), - mode: input(availabilityCheckMode), + mode: input(availabilityCheckModeInput), value: input({null: true}), }, @@ -1479,7 +1479,7 @@ export const exitWithoutUpdateValue = templateCompositeFrom({ annotation: `exitWithoutUpdateValue`, inputs: { - mode: input(availabilityCheckMode), + mode: input(availabilityCheckModeInput), value: input({defaultValue: null}), }, @@ -1498,7 +1498,7 @@ export const raiseOutputWithoutDependency = templateCompositeFrom({ inputs: { dependency: input(), - mode: input(availabilityCheckMode), + mode: input(availabilityCheckModeInput), output: input({defaultValue: {}}), }, @@ -1527,7 +1527,7 @@ export const raiseOutputWithoutUpdateValue = templateCompositeFrom({ annotation: `raiseOutputWithoutUpdateValue`, inputs: { - mode: input(availabilityCheckMode), + mode: input(availabilityCheckModeInput), output: input({defaultValue: {}}), }, @@ -1562,19 +1562,21 @@ export const withPropertyFromObject = templateCompositeFrom({ }, outputs: { - into: { - dependencies: [ - input.staticDependency('object'), - input.staticValue('property'), - ], - - default: ({ - [input.staticDependency('object')]: object, - [input.staticValue('property')]: property, - }) => - (object.startsWith('#') - ? `${object}.${property}` - : `#${object}.${property}`), + dependencies: [ + input.staticDependency('object'), + input.staticValue('property'), + ], + + compute: ({ + [input.staticDependency('object')]: object, + [input.staticValue('property')]: property, + }) => { + return ( + (object && property + ? (object.startsWith('#') + ? `${object}.${property}` + : `#${object}.${property}`) + : '#value')); }, }, diff --git a/src/data/things/thing.js b/src/data/things/thing.js index 45e9123..a5f0b78 100644 --- a/src/data/things/thing.js +++ b/src/data/things/thing.js @@ -5,7 +5,7 @@ import {inspect} from 'node:util'; import {colors} from '#cli'; import find from '#find'; -import {empty, stitchArrays, unique} from '#sugar'; +import {stitchArrays, unique} from '#sugar'; import {filterMultipleArrays, getKebabCase} from '#wiki-data'; import {oneOf} from '#validators'; @@ -253,6 +253,18 @@ export function additionalFiles() { }; } +const thingClassInput = { + validate(thingClass) { + isType(thingClass, 'function'); + + if (!Object.hasOwn(thingClass, Thing.referenceType)) { + throw new TypeError(`Expected a Thing constructor, missing Thing.referenceType`); + } + + return true; + }, +}; + // A reference list! Keep in mind this is for general references to wiki // objects of (usually) other Thing subclasses, not specifically leitmotif // references in tracks (although that property uses referenceList too!). @@ -267,18 +279,7 @@ export const referenceList = templateCompositeFrom({ compose: false, inputs: { - class: input({ - validate(thingClass) { - isType(thingClass, 'function'); - - if (!Object.hasOwn(thingClass, Thing.referenceType)) { - throw new TypeError(`Expected a Thing constructor, missing Thing.referenceType`); - } - - return true; - }, - }), - + class: input(thingClassInput), find: input({type: 'function'}), // todo: validate @@ -300,127 +301,100 @@ export const referenceList = templateCompositeFrom({ steps: () => [ withResolvedReferenceList({ - list: '#updateValue', - data: '#composition.data', - find: '#composition.findFunction', + list: input.updateValue(), + data: input('data'), + find: input('find'), }), exposeDependency({dependency: '#resolvedReferenceList'}), ], -}) -export function referenceList({ - class: thingClass, - data, - find: findFunction, -}) { - return compositeFrom({ - annotation: `referenceList`, - - mapDependencies: { - '#composition.data': data, - }, - - constantDependencies: { - '#composition.findFunction': findFunction, - }, - - steps: () => [ - withUpdateValueAsDependency(), - ], - }); -} +}); // Corresponding function for a single reference. -export function singleReference({ - class: thingClass, - data, - find: findFunction, -}) { - if (!thingClass) { - throw new TypeError(`Expected a Thing class`); - } +export const singleReference = templateCompositeFrom({ + annotation: `singleReference`, - const {[Thing.referenceType]: referenceType} = thingClass; - if (!referenceType) { - throw new Error(`The passed constructor ${thingClass.name} doesn't define Thing.referenceType!`); - } + compose: false, - return compositeFrom({ - annotation: `singleReference`, + inputs: { + class: input(thingClassInput), + find: input({type: 'function'}), - update: { - validate: validateReference(referenceType), - }, + // todo: validate + data: input(), + }, - mapDependencies: { - '#composition.data': data, - }, + update: { + dependencies: [ + input.staticValue('class'), + ], - constantDependencies: { - '#composition.findFunction': findFunction, + compute({ + [input.staticValue('class')]: thingClass, + }) { + const {[Thing.referenceType]: referenceType} = thingClass; + return {validate: validateReference(referenceType)}; }, + }, - steps: () => [ - withUpdateValueAsDependency(), - - withResolvedReference({ - ref: '#updateValue', - data: '#composition.data', - find: '#composition.findFunction', - }), + steps: () => [ + withResolvedReference({ + ref: input.updateValue(), + data: input('data'), + find: input('findFunction'), + }), - exposeDependency({dependency: '#resolvedReference'}), - ], - }); -} + exposeDependency({dependency: '#resolvedReference'}), + ], +}); // Nice 'n simple shorthand for an exposed-only flag which is true when any // contributions are present in the specified property. -export function contribsPresent({ - contribs, -}) { - return compositeFrom({ - annotation: `contribsPresent`, +export const contribsPresent = templateCompositeFrom({ + annotation: `contribsPresent`, - mapDependencies: { - '#composition.contribs': contribs, - }, + compose: false, - steps: () => [ - withResultOfAvailabilityCheck({ - fromDependency: '#composition.contribs', - mode: 'empty', - }), + inputs: { + contribs: input({type: 'string'}), + }, - exposeDependency({dependency: '#availability'}), - ], - }); -} + steps: () => [ + withResultOfAvailabilityCheck({ + fromDependency: input('contribs'), + mode: input.value('empty'), + }), + + exposeDependency({dependency: '#availability'}), + ], +}); // Neat little shortcut for "reversing" the reference lists stored on other // things - for example, tracks specify a "referenced tracks" property, and // you would use this to compute a corresponding "referenced *by* tracks" // property. Naturally, the passed ref list property is of the things in the // wiki data provided, not the requesting Thing itself. -export function reverseReferenceList({data, list}) { - return compositeFrom({ - annotation: `reverseReferenceList`, +export const reverseReferenceList = templateCompositeFrom({ + annotation: `reverseReferenceList`, - mapDependencies: { - '#composition.data': data, - '#composition.list': list, - }, + compose: false, - steps: () => [ - withReverseReferenceList({ - data: '#composition.data', - list: '#composition.list', - }), + inputs: { + // todo: validate + data: input(), - exposeDependency({dependency: '#reverseReferenceList'}), - ], - }); -} + list: input({type: 'string'}), + }, + + steps: () => [ + withReverseReferenceList({ + data: input('data'), + list: input('list'), + }), + + exposeDependency({dependency: '#reverseReferenceList'}), + ], +}); // General purpose wiki data constructor, for properties like artistData, // trackData, etc. @@ -436,53 +410,50 @@ export function wikiData(thingClass) { // This one's kinda tricky: it parses artist "references" from the // commentary content, and finds the matching artist for each reference. // This is mostly useful for credits and listings on artist pages. -export function commentatorArtists() { - return compositeFrom({ - annotation: `commentatorArtists`, +export const commentatorArtists = templateCompositeFrom({ + annotation: `commentatorArtists`, + + compose: false, + + steps: () => [ + exitWithoutDependency({ + dependency: 'commentary', + mode: input.value('falsy'), + value: input.value([]), + }), - constantDependencies: { - '#composition.findFunction': find.artists, + { + dependencies: ['commentary'], + compute: (continuation, {commentary}) => + continuation({ + '#artistRefs': + Array.from( + commentary + .replace(/<\/?b>/g, '') + .matchAll(/(?.*?):<\/i>/g)) + .map(({groups: {who}}) => who), + }), }, - steps: () => [ - exitWithoutDependency({ - dependency: 'commentary', - mode: 'falsy', - value: [], - }), - - { - dependencies: ['commentary'], - compute: ({commentary}, continuation) => - continuation({ - '#artistRefs': - Array.from( - commentary - .replace(/<\/?b>/g, '') - .matchAll(/(?.*?):<\/i>/g)) - .map(({groups: {who}}) => who), - }), - }, + withResolvedReferenceList({ + list: '#artistRefs', + data: 'artistData', + find: input.value(find.artist), + }).outputs({ + '#resolvedReferenceList': '#artists', + }), + + { + flags: {expose: true}, - withResolvedReferenceList({ - list: '#artistRefs', - data: 'artistData', - into: '#artists', - find: '#composition.findFunction', - }), - - { - flags: {expose: true}, - - expose: { - dependencies: ['#artists'], - compute: ({'#artists': artists}) => - unique(artists), - }, + expose: { + dependencies: ['#artists'], + compute: ({'#artists': artists}) => + unique(artists), }, - ], - }); -} + }, + ], +}); // Compositional utilities diff --git a/src/data/things/track.js b/src/data/things/track.js index 870b991..b41dbb5 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -140,9 +140,11 @@ export class Track extends Thing { artistContribs: [ inheritFromOriginalRelease({property: 'artistContribs'}), - withResolvedContribs - .inputs({from: input.updateValue()}) - .outputs({into: '#artistContribs'}), + withResolvedContribs({ + from: input.updateValue(), + }).outputs({ + '#resolvedContribs': '#artistContribs', + }), exposeDependencyOrContinue({dependency: '#artistContribs'}), @@ -164,9 +166,11 @@ export class Track extends Thing { coverArtistContribs: [ exitWithoutUniqueCoverArt(), - withResolvedContribs - .inputs({from: input.updateValue()}) - .outputs({into: '#coverArtistContribs'}), + withResolvedContribs({ + from: input.updateValue(), + }).outputs({ + '#resolvedContribs': '#coverArtistContribs', + }), exposeDependencyOrContinue({dependency: '#coverArtistContribs'}), @@ -400,7 +404,7 @@ export const withPropertyFromAlbum = templateCompositeFrom({ annotation: `withPropertyFromAlbum`, inputs: { - property: input({type: 'string'}), + property: input.staticValue({type: 'string'}), notFoundMode: input({ validate: oneOf('exit', 'null'), @@ -409,12 +413,10 @@ export const withPropertyFromAlbum = templateCompositeFrom({ }, outputs: { - into: { - dependencies: [input.staticValue('property')], - default: ({ - [input.staticValue('property')]: property, - }) => '#album.' + property, - }, + dependencies: [input.staticValue('property')], + compute: ({ + [input.staticValue('property')]: property, + }) => ['#album.' + property], }, steps: () => [ @@ -422,9 +424,20 @@ export const withPropertyFromAlbum = templateCompositeFrom({ notFoundMode: input('notFoundMode'), }), - withPropertyFromObject - .inputs({object: '#album', property: input('property')}) - .outputs({into: 'into'}), + withPropertyFromObject({ + object: '#album', + property: input('property'), + }), + + { + dependencies: ['#value', input.staticValue('property')], + compute: (continuation, { + ['#value']: value, + [input.staticValue('property')]: property, + }) => continuation({ + ['#album.' + property]: value, + }), + }, ], }); -- cgit 1.3.0-6-gf8a5