diff options
-rw-r--r-- | src/data/things/thing.js | 43 | ||||
-rw-r--r-- | src/data/things/track.js | 54 | ||||
-rw-r--r-- | test/unit/data/things/track.js | 119 |
3 files changed, 172 insertions, 44 deletions
diff --git a/src/data/things/thing.js b/src/data/things/thing.js index 15ec62c3..1c99a323 100644 --- a/src/data/things/thing.js +++ b/src/data/things/thing.js @@ -330,16 +330,15 @@ export default class Thing extends CacheableObject { // 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. - reverseReferenceList: (thingDataProperty, referencerRefListProperty) => ({ - flags: {expose: true}, - - expose: { - dependencies: ['this', thingDataProperty], - - compute: ({this: thing, [thingDataProperty]: thingData}) => - thingData?.filter(t => t[referencerRefListProperty].includes(thing)) ?? [], - }, - }), + reverseReferenceList({ + data, + refList, + }) { + return Thing.composite.from(`Thing.common.reverseReferenceList`, [ + Thing.composite.withReverseReferenceList({data, refList}), + Thing.composite.exposeDependency('#reverseReferenceList'), + ]); + }, // Corresponding function for single references. Note that the return value // is still a list - this is for matching all the objects whose single @@ -1535,5 +1534,29 @@ export default class Thing extends CacheableObject { }, ]); }, + + // Check out the info on Thing.common.reverseReferenceList! + // This is its composable form. + withReverseReferenceList({ + data, + to = '#reverseReferenceList', + refList: refListProperty, + }) { + return Thing.composite.from(`Thing.common.reverseReferenceList`, [ + Thing.composite.earlyExitWithoutDependency(data, {value: []}), + + { + dependencies: ['this'], + mapDependencies: {data}, + mapContinuation: {to}, + options: {refListProperty}, + + compute: ({this: thisThing, data, '#options': {refListProperty}}, continuation) => + continuation({ + to: data.filter(thing => thing[refListProperty].includes(thisThing)), + }), + }, + ]); + }, }; } diff --git a/src/data/things/track.js b/src/data/things/track.js index 0b34de20..bc9affbe 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -278,38 +278,15 @@ export class Track extends Thing { // counting the number of times a track has been referenced, for use in // the "Tracks - by Times Referenced" listing page (or other data // processing). - referencedByTracks: { - flags: {expose: true}, - - expose: { - dependencies: ['this', 'trackData'], - - compute: ({this: track, trackData}) => - trackData - ? trackData - .filter((t) => !t.originalReleaseTrack) - .filter((t) => t.referencedTracks?.includes(track)) - : [], - }, - }, + referencedByTracks: Track.composite.trackReverseReferenceList('referencedTracks'), // For the same reasoning, exclude re-releases from sampled tracks too. - sampledByTracks: { - flags: {expose: true}, - - expose: { - dependencies: ['this', 'trackData'], - - compute: ({this: track, trackData}) => - trackData - ? trackData - .filter((t) => !t.originalReleaseTrack) - .filter((t) => t.sampledTracks?.includes(track)) - : [], - }, - }, + sampledByTracks: Track.composite.trackReverseReferenceList('sampledTracks'), - featuredInFlashes: Thing.common.reverseReferenceList('flashData', 'featuredTracks'), + featuredInFlashes: Thing.common.reverseReferenceList({ + data: 'flashData', + refList: 'featuredTracks', + }), }); static composite = { @@ -575,6 +552,25 @@ export class Track extends Thing { }, ]); }, + + trackReverseReferenceList(refListProperty) { + return Thing.composite.from(`Track.composite.trackReverseReferenceList`, [ + Thing.composite.withReverseReferenceList({ + data: 'trackData', + refList: refListProperty, + originalTracksOnly: true, + }), + + { + flags: {expose: true}, + expose: { + dependencies: ['#reverseReferenceList'], + compute: ({'#reverseReferenceList': reverseReferenceList}) => + reverseReferenceList.filter(track => !track.originalReleaseTrack), + }, + }, + ]); + }, }; [inspect.custom](depth) { diff --git a/test/unit/data/things/track.js b/test/unit/data/things/track.js index 16162fb7..8939d964 100644 --- a/test/unit/data/things/track.js +++ b/test/unit/data/things/track.js @@ -6,6 +6,8 @@ import thingConstructors from '#things'; const { Album, Artist, + Flash, + FlashAct, Thing, Track, } = thingConstructors; @@ -43,6 +45,16 @@ function stubArtistAndContribs() { return {artist, contribs, badContribs}; } +function stubFlashAndAct(directory = 'zam') { + const flash = new Flash(); + flash.directory = directory; + + const flashAct = new FlashAct(); + flashAct.flashesByRef = [Thing.getReference(flash)]; + + return {flash, flashAct}; +} + t.test(`Track.album`, t => { t.plan(6); @@ -155,9 +167,9 @@ t.test(`Track.coverArtDate`, t => { const {artist, contribs} = stubArtistAndContribs(); const {XXX_decacheWikiData} = linkAndBindWikiData({ - trackData: [track], albumData: [album], artistData: [artist], + trackData: [track], }); track.coverArtistContribsByRef = contribs; @@ -202,9 +214,9 @@ t.test(`Track.coverArtFileExtension`, t => { const {artist, contribs} = stubArtistAndContribs(); const {XXX_decacheWikiData} = linkAndBindWikiData({ - trackData: [track], albumData: [album], artistData: [artist], + trackData: [track], }); t.equal(track.coverArtFileExtension, null, @@ -280,6 +292,32 @@ t.test(`Track.date`, t => { `date #3: is own dateFirstReleased`); }); +t.test(`Track.featuredInFlashes`, t => { + t.plan(2); + + const {track, album} = stubTrackAndAlbum('track1'); + + const {flash: flash1, flashAct: flashAct1} = stubFlashAndAct('flash1'); + const {flash: flash2, flashAct: flashAct2} = stubFlashAndAct('flash2'); + + const {XXX_decacheWikiData} = linkAndBindWikiData({ + albumData: [album], + trackData: [track], + flashData: [flash1, flash2], + flashActData: [flashAct1, flashAct2], + }); + + t.same(track.featuredInFlashes, [], + `featuredInFlashes #1: defaults to empty array`); + + flash1.featuredTracksByRef = ['track:track1']; + flash2.featuredTracksByRef = ['track:track1']; + XXX_decacheWikiData(); + + t.same(track.featuredInFlashes, [flash1, flash2], + `featuredInFlashes #2: matches flashes' featuredTracks`); +}); + t.test(`Track.hasUniqueCoverArt`, t => { t.plan(7); @@ -339,8 +377,8 @@ t.only(`Track.originalReleaseTrack`, t => { const {track: track2, album: album2} = stubTrackAndAlbum('track2'); const {wikiData, linkWikiDataArrays, XXX_decacheWikiData} = linkAndBindWikiData({ - trackData: [track1, track2], albumData: [album1, album2], + trackData: [track1, track2], }); t.equal(track2.originalReleaseTrack, null, @@ -366,8 +404,8 @@ t.test(`Track.otherReleases`, t => { const {track: track4, album: album4} = stubTrackAndAlbum('track4'); const {wikiData, linkWikiDataArrays, XXX_decacheWikiData} = linkAndBindWikiData({ - trackData: [track1, track2, track3, track4], albumData: [album1, album2, album3, album4], + trackData: [track1, track2, track3, track4], }); t.same(track1.otherReleases, [], @@ -376,7 +414,6 @@ t.test(`Track.otherReleases`, t => { track2.originalReleaseTrackByRef = 'track:track1'; track3.originalReleaseTrackByRef = 'track:track1'; track4.originalReleaseTrackByRef = 'track:track1'; - XXX_decacheWikiData(); t.same(track1.otherReleases, [track2, track3, track4], @@ -400,3 +437,75 @@ t.test(`Track.otherReleases`, t => { t.same(track4.otherReleases, [track1, track3, track2], `otherReleases #6: otherReleases of rerelease are original track then other rereleases (1/3)`); }); + +t.test(`Track.referencedByTracks`, t => { + t.plan(4); + + const {track: track1, album: album1} = stubTrackAndAlbum('track1'); + const {track: track2, album: album2} = stubTrackAndAlbum('track2'); + const {track: track3, album: album3} = stubTrackAndAlbum('track3'); + const {track: track4, album: album4} = stubTrackAndAlbum('track4'); + + const {XXX_decacheWikiData} = linkAndBindWikiData({ + albumData: [album1, album2, album3, album4], + trackData: [track1, track2, track3, track4], + }); + + t.same(track1.referencedByTracks, [], + `referencedByTracks #1: defaults to empty array`); + + track2.referencedTracksByRef = ['track:track1']; + track3.referencedTracksByRef = ['track:track1']; + XXX_decacheWikiData(); + + t.same(track1.referencedByTracks, [track2, track3], + `referencedByTracks #2: matches tracks' referencedTracks`); + + track4.sampledTracksByRef = ['track:track1']; + XXX_decacheWikiData(); + + t.same(track1.referencedByTracks, [track2, track3], + `referencedByTracks #3: doesn't match tracks' sampledTracks`); + + track3.originalReleaseTrackByRef = 'track:track2'; + XXX_decacheWikiData(); + + t.same(track1.referencedByTracks, [track2], + `referencedByTracks #4: doesn't include re-releases`); +}); + +t.test(`Track.sampledByTracks`, t => { + t.plan(4); + + const {track: track1, album: album1} = stubTrackAndAlbum('track1'); + const {track: track2, album: album2} = stubTrackAndAlbum('track2'); + const {track: track3, album: album3} = stubTrackAndAlbum('track3'); + const {track: track4, album: album4} = stubTrackAndAlbum('track4'); + + const {XXX_decacheWikiData} = linkAndBindWikiData({ + albumData: [album1, album2, album3, album4], + trackData: [track1, track2, track3, track4], + }); + + t.same(track1.sampledByTracks, [], + `sampledByTracks #1: defaults to empty array`); + + track2.sampledTracksByRef = ['track:track1']; + track3.sampledTracksByRef = ['track:track1']; + XXX_decacheWikiData(); + + t.same(track1.sampledByTracks, [track2, track3], + `sampledByTracks #2: matches tracks' sampledTracks`); + + track4.referencedTracksByRef = ['track:track1']; + XXX_decacheWikiData(); + + t.same(track1.sampledByTracks, [track2, track3], + `sampledByTracks #3: doesn't match tracks' referencedTracks`); + + track3.originalReleaseTrackByRef = 'track:track2'; + XXX_decacheWikiData(); + + t.same(track1.sampledByTracks, [track2], + `sampledByTracks #4: doesn't include re-releases`); +}); |