diff options
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/data/composite/things/album/index.js | 1 | ||||
-rw-r--r-- | src/data/composite/things/album/withTrackSections.js | 127 | ||||
-rw-r--r-- | src/data/composite/things/album/withTracks.js | 40 | ||||
-rw-r--r-- | src/data/composite/things/track-section/index.js | 1 | ||||
-rw-r--r-- | src/data/composite/things/track-section/withAlbum.js | 22 | ||||
-rw-r--r-- | src/data/things/album.js | 187 | ||||
-rw-r--r-- | test/lib/wiki-data.js | 18 |
8 files changed, 182 insertions, 215 deletions
diff --git a/package.json b/package.json index 983c6796..88f351bc 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "#composite/things/flash": "./src/data/composite/things/flash/index.js", "#composite/things/flash-act": "./src/data/composite/things/flash-act/index.js", "#composite/things/track": "./src/data/composite/things/track/index.js", + "#composite/things/track-section": "./src/data/composite/things/track-section/index.js", "#content-dependencies": "./src/content/dependencies/index.js", "#content-function": "./src/content-function.js", "#cli": "./src/util/cli.js", diff --git a/src/data/composite/things/album/index.js b/src/data/composite/things/album/index.js index 8139f10e..8b5098f0 100644 --- a/src/data/composite/things/album/index.js +++ b/src/data/composite/things/album/index.js @@ -1,2 +1 @@ export {default as withTracks} from './withTracks.js'; -export {default as withTrackSections} from './withTrackSections.js'; diff --git a/src/data/composite/things/album/withTrackSections.js b/src/data/composite/things/album/withTrackSections.js deleted file mode 100644 index 0a1ebebc..00000000 --- a/src/data/composite/things/album/withTrackSections.js +++ /dev/null @@ -1,127 +0,0 @@ -import {input, templateCompositeFrom} from '#composite'; -import find from '#find'; -import {empty, filterMultipleArrays, stitchArrays} from '#sugar'; -import {isTrackSectionList} from '#validators'; - -import {exitWithoutDependency, exitWithoutUpdateValue} - from '#composite/control-flow'; -import {withResolvedReferenceList} from '#composite/wiki-data'; - -import { - fillMissingListItems, - withFlattenedList, - withPropertiesFromList, - withUnflattenedList, -} from '#composite/data'; - -export default templateCompositeFrom({ - annotation: `withTrackSections`, - - outputs: ['#trackSections'], - - steps: () => [ - exitWithoutDependency({ - dependency: 'ownTrackData', - value: input.value([]), - }), - - exitWithoutUpdateValue({ - mode: input.value('empty'), - value: input.value([]), - }), - - // TODO: input.updateValue description down here is a kludge. - withPropertiesFromList({ - list: input.updateValue({ - validate: isTrackSectionList, - }), - prefix: input.value('#sections'), - properties: input.value([ - 'tracks', - 'dateOriginallyReleased', - 'isDefaultTrackSection', - 'name', - 'color', - ]), - }), - - fillMissingListItems({ - list: '#sections.tracks', - fill: input.value([]), - }), - - fillMissingListItems({ - list: '#sections.isDefaultTrackSection', - fill: input.value(false), - }), - - fillMissingListItems({ - list: '#sections.name', - fill: input.value('Unnamed Track Section'), - }), - - fillMissingListItems({ - list: '#sections.color', - fill: input.dependency('color'), - }), - - withFlattenedList({ - list: '#sections.tracks', - }).outputs({ - ['#flattenedList']: '#trackRefs', - ['#flattenedIndices']: '#sections.startIndex', - }), - - withResolvedReferenceList({ - list: '#trackRefs', - data: 'ownTrackData', - notFoundMode: input.value('null'), - find: input.value(find.track), - }).outputs({ - ['#resolvedReferenceList']: '#tracks', - }), - - withUnflattenedList({ - list: '#tracks', - indices: '#sections.startIndex', - }).outputs({ - ['#unflattenedList']: '#sections.tracks', - }), - - { - dependencies: [ - '#sections.tracks', - '#sections.name', - '#sections.color', - '#sections.dateOriginallyReleased', - '#sections.isDefaultTrackSection', - '#sections.startIndex', - ], - - compute: (continuation, { - '#sections.tracks': tracks, - '#sections.name': name, - '#sections.color': color, - '#sections.dateOriginallyReleased': dateOriginallyReleased, - '#sections.isDefaultTrackSection': isDefaultTrackSection, - '#sections.startIndex': startIndex, - }) => { - filterMultipleArrays( - tracks, name, color, dateOriginallyReleased, isDefaultTrackSection, startIndex, - tracks => !empty(tracks)); - - return continuation({ - ['#trackSections']: - stitchArrays({ - tracks, - name, - color, - dateOriginallyReleased, - isDefaultTrackSection, - startIndex, - }), - }); - }, - }, - ], -}); diff --git a/src/data/composite/things/album/withTracks.js b/src/data/composite/things/album/withTracks.js index fff3d5ae..05f5b24d 100644 --- a/src/data/composite/things/album/withTracks.js +++ b/src/data/composite/things/album/withTracks.js @@ -1,9 +1,8 @@ import {input, templateCompositeFrom} from '#composite'; -import find from '#find'; import {exitWithoutDependency, raiseOutputWithoutDependency} from '#composite/control-flow'; -import {withResolvedReferenceList} from '#composite/wiki-data'; +import {withFlattenedList, withPropertyFromList} from '#composite/data'; export default templateCompositeFrom({ annotation: `withTracks`, @@ -11,41 +10,22 @@ export default templateCompositeFrom({ outputs: ['#tracks'], steps: () => [ - exitWithoutDependency({ - dependency: 'ownTrackData', - value: input.value([]), - }), - raiseOutputWithoutDependency({ dependency: 'trackSections', - mode: input.value('empty'), output: input.value({ - ['#tracks']: [], + '#tracks': [], }), }), - { - dependencies: ['trackSections'], - compute: (continuation, {trackSections}) => - continuation({ - '#trackRefs': trackSections - .flatMap(section => section.tracks ?? []), - }), - }, - - withResolvedReferenceList({ - list: '#trackRefs', - data: 'ownTrackData', - find: input.value(find.track), + withPropertyFromList({ + list: 'trackSections', + property: input.value('tracks'), }), - { - dependencies: ['#resolvedReferenceList'], - compute: (continuation, { - ['#resolvedReferenceList']: resolvedReferenceList, - }) => continuation({ - ['#tracks']: resolvedReferenceList, - }) - }, + withFlattenedList({ + list: '#trackSections.tracks', + }).outputs({ + ['#flattenedList']: '#tracks', + }), ], }); diff --git a/src/data/composite/things/track-section/index.js b/src/data/composite/things/track-section/index.js new file mode 100644 index 00000000..3202ed49 --- /dev/null +++ b/src/data/composite/things/track-section/index.js @@ -0,0 +1 @@ +export {default as withAlbum} from './withAlbum.js'; diff --git a/src/data/composite/things/track-section/withAlbum.js b/src/data/composite/things/track-section/withAlbum.js new file mode 100644 index 00000000..608cc0cd --- /dev/null +++ b/src/data/composite/things/track-section/withAlbum.js @@ -0,0 +1,22 @@ +// Gets the track section's album. This will early exit if ownAlbumData is +// missing. If there's no album whose list of track sections includes this one, +// the output dependency will be null. + +import {input, templateCompositeFrom} from '#composite'; + +import {withUniqueReferencingThing} from '#composite/wiki-data'; + +export default templateCompositeFrom({ + annotation: `withAlbum`, + + outputs: ['#album'], + + steps: () => [ + withUniqueReferencingThing({ + data: 'ownAlbumData', + list: input.value('trackSections'), + }).outputs({ + ['#uniqueReferencingThing']: '#album', + }), + ], +}); diff --git a/src/data/things/album.js b/src/data/things/album.js index f8354962..78ecb294 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -6,15 +6,17 @@ import {input} from '#composite'; import find from '#find'; import {traverse} from '#node-utils'; import {sortAlbumsTracksChronologically, sortChronologically} from '#sort'; -import {empty} from '#sugar'; +import {accumulateSum, empty} from '#sugar'; import Thing from '#thing'; -import {isDate} from '#validators'; +import {isColor, isDate, validateWikiData} from '#validators'; import {parseAdditionalFiles, parseContributors, parseDate, parseDimensions} from '#yaml'; -import {exposeDependency, exposeUpdateValueOrContinue} +import {exitWithoutDependency, exposeDependency, exposeUpdateValueOrContinue} from '#composite/control-flow'; -import {exitWithoutContribs} from '#composite/wiki-data'; +import {withPropertyFromObject} from '#composite/data'; +import {exitWithoutContribs, withDirectory, withResolvedReference} + from '#composite/wiki-data'; import { additionalFiles, @@ -31,16 +33,24 @@ import { referenceList, simpleDate, simpleString, + singleReference, urls, wikiData, } from '#composite/wiki-properties'; -import {withTracks, withTrackSections} from '#composite/things/album'; +import {withTracks} from '#composite/things/album'; +import {withAlbum} from '#composite/things/track-section'; export class Album extends Thing { static [Thing.referenceType] = 'album'; - static [Thing.getPropertyDescriptors] = ({ArtTag, Artist, Group, Track}) => ({ + static [Thing.getPropertyDescriptors] = ({ + ArtTag, + Artist, + Group, + Track, + TrackSection, + }) => ({ // Update & expose name: name('Unnamed Album'), @@ -111,10 +121,17 @@ export class Album extends Thing { commentary: commentary(), additionalFiles: additionalFiles(), - trackSections: [ - withTrackSections(), - exposeDependency({dependency: '#trackSections'}), - ], + trackSections: { + flags: {update: true, expose: true}, + + update: { + validate: + validateWikiData({ + referenceType: + TrackSection[Thing.referenceType], + }), + }, + }, artistContribs: contributionList(), coverArtistContribs: contributionList(), @@ -155,13 +172,6 @@ export class Album extends Thing { class: input.value(Group), }), - // Only the tracks which belong to this album. - // Necessary for computing the track list, so provide this statically - // or keep it updated. - ownTrackData: wikiData({ - class: input.value(Track), - }), - // Expose only commentatorArtists: commentatorArtists(), @@ -345,7 +355,7 @@ export class Album extends Thing { headerDocumentThing: Album, entryDocumentThing: document => ('Section' in document - ? TrackSectionHelper + ? TrackSection : Track), save(results) { @@ -353,49 +363,49 @@ export class Album extends Thing { const trackData = []; for (const {header: album, entries} of results) { - // We can't mutate an array once it's set as a property value, - // so prepare the track sections that will show up in a track list - // all the way before actually applying them. (It's okay to mutate - // an individual section before applying it, since those are just - // generic objects; they aren't Things in and of themselves.) const trackSections = []; - const ownTrackData = []; - let currentTrackSection = { + let currentTrackSection = new TrackSection(); + let currentTrackSectionTracks = []; + + Object.assign(currentTrackSection, { name: `Default Track Section`, isDefaultTrackSection: true, - tracks: [], - }; + }); const albumRef = Thing.getReference(album); const closeCurrentTrackSection = () => { - if (!empty(currentTrackSection.tracks)) { - trackSections.push(currentTrackSection); + if (empty(currentTrackSectionTracks)) { + return; } + + currentTrackSection.tracks = + currentTrackSectionTracks + .map(track => Thing.getReference(track)); + + currentTrackSection.ownTrackData = + currentTrackSectionTracks; + + currentTrackSection.ownAlbumData = + [album]; + + trackSections.push(currentTrackSection); }; for (const entry of entries) { - if (entry instanceof TrackSectionHelper) { + if (entry instanceof TrackSection) { closeCurrentTrackSection(); - - currentTrackSection = { - name: entry.name, - color: entry.color, - dateOriginallyReleased: entry.dateOriginallyReleased, - isDefaultTrackSection: false, - tracks: [], - }; - + currentTrackSection = entry; + currentTrackSectionTracks = []; continue; } + currentTrackSectionTracks.push(entry); + trackData.push(entry); entry.dataSourceAlbum = albumRef; - - ownTrackData.push(entry); - currentTrackSection.tracks.push(Thing.getReference(entry)); } closeCurrentTrackSection(); @@ -403,7 +413,6 @@ export class Album extends Thing { albumData.push(album); album.trackSections = trackSections; - album.ownTrackData = ownTrackData; } return {albumData, trackData}; @@ -416,15 +425,97 @@ export class Album extends Thing { }); } -export class TrackSectionHelper extends Thing { +export class TrackSection extends Thing { static [Thing.friendlyName] = `Track Section`; + static [Thing.referenceType] = `track-section`; + + static [Thing.getPropertyDescriptors] = ({Album, Track}) => ({ + // Update & expose - static [Thing.getPropertyDescriptors] = () => ({ name: name('Unnamed Track Section'), - color: color(), + + unqualifiedDirectory: directory(), + + color: [ + exposeUpdateValueOrContinue({ + validate: input.value(isColor), + }), + + withAlbum(), + + withPropertyFromObject({ + object: '#album', + property: input.value('color'), + }), + + exposeDependency({dependency: '#album.color'}), + ], + dateOriginallyReleased: simpleDate(), - isDefaultTrackGroup: flag(false), - }) + + isDefaultTrackSection: flag(false), + + album: [ + withAlbum(), + exposeDependency({dependency: '#album'}), + ], + + tracks: referenceList({ + class: input.value(Track), + data: 'ownTrackData', + find: input.value(find.track), + }), + + // Update only + + ownAlbumData: wikiData({ + class: input.value(Album), + }), + + ownTrackData: wikiData({ + class: input.value(Track), + }), + + // Expose only + + startIndex: [ + withAlbum(), + + withPropertyFromObject({ + object: '#album', + property: input.value('trackSections'), + }), + + { + dependencies: ['#album.trackSections', input.myself()], + compute: (continuation, { + ['#album.trackSections']: trackSections, + [input.myself()]: myself, + }) => continuation({ + ['#index']: + trackSections.indexOf(myself), + }), + }, + + exitWithoutDependency({ + dependency: '#index', + mode: input.value('index'), + value: input.value(0), + }), + + { + dependencies: ['#album.trackSections', '#index'], + compute: ({ + ['#album.trackSections']: trackSections, + ['#index']: index, + }) => + accumulateSum( + trackSections + .slice(0, index) + .map(section => section.tracks.length)), + }, + ], + }); static [Thing.yamlDocumentSpec] = { fields: { diff --git a/test/lib/wiki-data.js b/test/lib/wiki-data.js index d2d860ce..75b1170a 100644 --- a/test/lib/wiki-data.js +++ b/test/lib/wiki-data.js @@ -13,20 +13,20 @@ export function linkAndBindWikiData(wikiData, { .map(([key, value]) => [key, value.slice()])) : wikiData)); - // If albumData is present, automatically set albums' ownTrackData values - // by resolving track sections' references against the full array. This is - // just a nicety for working with albums throughout tests. + // If albumData is present, automatically set their sections' ownTrackData + // by resolving references against the full array. This is just a nicety + // for working with albums throughout tests. if (inferAlbumsOwnTrackData && wikiData.albumData && wikiData.trackData) { for (const album of wikiData.albumData) { const trackSections = CacheableObject.getUpdateValue(album, 'trackSections'); - const trackRefs = - trackSections.flatMap(section => section.tracks); - - album.ownTrackData = - trackRefs.map(ref => - find.track(ref, wikiData.trackData, {mode: 'error'})); + for (const trackSection of trackSections) { + trackSection.ownTrackData = + CacheableObject.getUpdateValue(trackSection, 'tracks') + .map(ref => + find.track(ref, wikiData.trackData, {mode: 'error'})); + } } } } |