« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/data/things/thing.js43
-rw-r--r--src/data/things/track.js54
-rw-r--r--test/unit/data/things/track.js119
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`);
+});