« 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--package.json1
-rw-r--r--src/data/composite/things/contribution/index.js1
-rw-r--r--src/data/composite/things/contribution/withContributionArtist.js31
-rw-r--r--src/data/composite/wiki-data/withResolvedContribs.js57
-rw-r--r--src/data/things/contribution.js97
-rw-r--r--src/data/things/index.js2
-rw-r--r--test/unit/data/things/track.js12
7 files changed, 176 insertions, 25 deletions
diff --git a/package.json b/package.json
index 5ae6772f..ace852ad 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
         "#composite/wiki-data": "./src/data/composite/wiki-data/index.js",
         "#composite/wiki-properties": "./src/data/composite/wiki-properties/index.js",
         "#composite/things/album": "./src/data/composite/things/album/index.js",
+        "#composite/things/contribution": "./src/data/composite/things/contribution/index.js",
         "#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",
diff --git a/src/data/composite/things/contribution/index.js b/src/data/composite/things/contribution/index.js
new file mode 100644
index 00000000..c0506a2b
--- /dev/null
+++ b/src/data/composite/things/contribution/index.js
@@ -0,0 +1 @@
+export {default as withContributionArtist} from './withContributionArtist.js';
diff --git a/src/data/composite/things/contribution/withContributionArtist.js b/src/data/composite/things/contribution/withContributionArtist.js
new file mode 100644
index 00000000..9e588936
--- /dev/null
+++ b/src/data/composite/things/contribution/withContributionArtist.js
@@ -0,0 +1,31 @@
+import {input, templateCompositeFrom} from '#composite';
+import find from '#find';
+
+import {withPropertyFromObject} from '#composite/data';
+import {withResolvedReference} from '#composite/wiki-data';
+
+export default templateCompositeFrom({
+  annotation: `withOwnContributionArtist`,
+
+  inputs: {
+    ref: input({type: 'string'}),
+  },
+
+  outputs: ['#artist'],
+
+  steps: () => [
+    withPropertyFromObject({
+      object: 'thing',
+      property: input.value('artistData'),
+      internal: input.value(true),
+    }),
+
+    withResolvedReference({
+      ref: input('ref'),
+      data: '#thing.artistData',
+      find: input.value(find.artist),
+    }).outputs({
+      '#resolvedReference': '#artist',
+    }),
+  ],
+});
diff --git a/src/data/composite/wiki-data/withResolvedContribs.js b/src/data/composite/wiki-data/withResolvedContribs.js
index 95266382..7ff7b1bc 100644
--- a/src/data/composite/wiki-data/withResolvedContribs.js
+++ b/src/data/composite/wiki-data/withResolvedContribs.js
@@ -7,17 +7,11 @@
 import {input, templateCompositeFrom} from '#composite';
 import find from '#find';
 import {filterMultipleArrays, stitchArrays} from '#sugar';
+import thingConstructors from '#things';
 import {is, isContributionList} from '#validators';
 
-import {
-  raiseOutputWithoutDependency,
-} from '#composite/control-flow';
-
-import {
-  withPropertiesFromList,
-} from '#composite/data';
-
-import withResolvedReferenceList from './withResolvedReferenceList.js';
+import {raiseOutputWithoutDependency} from '#composite/control-flow';
+import {withPropertiesFromList} from '#composite/data';
 
 export default templateCompositeFrom({
   annotation: `withResolvedContribs`,
@@ -51,15 +45,6 @@ export default templateCompositeFrom({
       prefix: input.value('#contribs'),
     }),
 
-    withResolvedReferenceList({
-      list: '#contribs.artist',
-      data: 'artistData',
-      find: input.value(find.artist),
-      notFoundMode: input('notFoundMode'),
-    }).outputs({
-      ['#resolvedReferenceList']: '#contribs.artist',
-    }),
-
     {
       dependencies: ['#contribs.artist', '#contribs.annotation'],
 
@@ -68,11 +53,45 @@ export default templateCompositeFrom({
         ['#contribs.annotation']: annotation,
       }) {
         filterMultipleArrays(artist, annotation, (artist, _annotation) => artist);
+
         return continuation({
-          ['#resolvedContribs']:
+          ['#details']:
             stitchArrays({artist, annotation}),
         });
       },
     },
+
+    {
+      dependencies: ['#details', input.myself()],
+
+      compute: (continuation, {
+        ['#details']: details,
+        [input.myself()]: myself,
+      }) => continuation({
+        ['#contributions']:
+          details.map(details => {
+            const contrib = new thingConstructors.Contribution();
+
+            Object.assign(contrib, {
+              ...details,
+              thing: myself,
+            });
+
+            return contrib;
+          }),
+      }),
+    },
+
+    {
+      dependencies: ['#contributions'],
+
+      compute: (continuation, {
+        ['#contributions']: contributions,
+      }) => continuation({
+        ['#resolvedContribs']:
+          contributions
+            .filter(contrib => contrib.artist),
+      }),
+    },
   ],
 });
diff --git a/src/data/things/contribution.js b/src/data/things/contribution.js
new file mode 100644
index 00000000..dc7f2157
--- /dev/null
+++ b/src/data/things/contribution.js
@@ -0,0 +1,97 @@
+import {inspect} from 'node:util';
+
+import CacheableObject from '#cacheable-object';
+import {colors} from '#cli';
+import {input} from '#composite';
+import {empty} from '#sugar';
+import Thing from '#thing';
+import {isStringNonEmpty, isThing, validateReference} from '#validators';
+
+import {exposeDependency} from '#composite/control-flow';
+import {withResolvedReference} from '#composite/wiki-data';
+
+import {withContributionArtist} from '#composite/things/contribution';
+
+export class Contribution extends Thing {
+  static [Thing.getPropertyDescriptors] = () => ({
+    // Update & expose
+
+    thing: {
+      flags: {update: true, expose: true},
+      update: {validate: isThing},
+    },
+
+    artist: [
+      withContributionArtist({
+        ref: input.updateValue({
+          validate: validateReference('artist'),
+        }),
+      }),
+
+      exposeDependency({
+        dependency: '#artist',
+      }),
+    ],
+
+    annotation: {
+      flags: {update: true, expose: true},
+      update: {validate: isStringNonEmpty},
+    },
+  });
+
+  [inspect.custom](depth, options, inspect) {
+    const parts = [];
+    const accentParts = [];
+
+    parts.push(Thing.prototype[inspect.custom].apply(this));
+
+    if (this.annotation) {
+      accentParts.push(colors.green(`"${this.annotation}"`));
+    }
+
+    let artistRef;
+    if (depth >= 0) {
+      let artist;
+      try {
+        artist = this.artist;
+      } catch (_error) {
+        // Computing artist might crash for any reason - don't distract from
+        // other errors as a result of inspecting this contribution.
+      }
+
+      if (artist) {
+        artistRef =
+          colors.blue(Thing.getReference(artist));
+      }
+    } else {
+      artistRef =
+        colors.green(CacheableObject.getUpdateValue(this, 'artist'));
+    }
+
+    if (artistRef) {
+      accentParts.push(`by ${artistRef}`);
+    }
+
+    if (this.thing) {
+      if (depth >= 0) {
+        const newOptions = {
+          ...options,
+          depth:
+            (options.depth === null
+              ? null
+              : options.depth - 1),
+        };
+
+        accentParts.push(`to ${inspect(this.thing, newOptions)}`);
+      } else {
+        accentParts.push(`to ${colors.blue(Thing.getReference(this.thing))}`);
+      }
+    }
+
+    if (!empty(accentParts)) {
+      parts.push(` (${accentParts.join(', ')})`);
+    }
+
+    return parts.join('');
+  }
+}
diff --git a/src/data/things/index.js b/src/data/things/index.js
index 4f87f492..f18e283a 100644
--- a/src/data/things/index.js
+++ b/src/data/things/index.js
@@ -11,6 +11,7 @@ import Thing from '#thing';
 import * as albumClasses from './album.js';
 import * as artTagClasses from './art-tag.js';
 import * as artistClasses from './artist.js';
+import * as contributionClasses from './contribution.js';
 import * as flashClasses from './flash.js';
 import * as groupClasses from './group.js';
 import * as homepageLayoutClasses from './homepage-layout.js';
@@ -24,6 +25,7 @@ const allClassLists = {
   'album.js': albumClasses,
   'art-tag.js': artTagClasses,
   'artist.js': artistClasses,
+  'contribution.js': contributionClasses,
   'flash.js': flashClasses,
   'group.js': groupClasses,
   'homepage-layout.js': homepageLayoutClasses,
diff --git a/test/unit/data/things/track.js b/test/unit/data/things/track.js
index c6695b6f..74231e20 100644
--- a/test/unit/data/things/track.js
+++ b/test/unit/data/things/track.js
@@ -280,7 +280,7 @@ t.test(`Track.artistContribs`, t => {
 
   XXX_decacheWikiData();
 
-  t.same(track.artistContribs,
+  t.match(track.artistContribs,
     [{artist: artist1, annotation: `composition`}, {artist: artist2, annotation: null}],
     `artistContribs #2: inherits album artistContribs`);
 
@@ -288,7 +288,7 @@ t.test(`Track.artistContribs`, t => {
     {artist: `Artist 1`, annotation: `arrangement`},
   ];
 
-  t.same(track.artistContribs, [{artist: artist1, annotation: `arrangement`}],
+  t.match(track.artistContribs, [{artist: artist1, annotation: `arrangement`}],
     `artistContribs #3: resolves from own value`);
 
   track.artistContribs = [
@@ -297,7 +297,7 @@ t.test(`Track.artistContribs`, t => {
     {artist: `Artist 2`, annotation: `usual`},
   ];
 
-  t.same(track.artistContribs,
+  t.match(track.artistContribs,
     [{artist: artist1, annotation: `snooping`}, {artist: artist2, annotation: `usual`}],
     `artistContribs #4: filters out names without matches`);
 });
@@ -457,7 +457,7 @@ t.test(`Track.coverArtistContribs`, t => {
 
   XXX_decacheWikiData();
 
-  t.same(track.coverArtistContribs,
+  t.match(track.coverArtistContribs,
     [{artist: artist1, annotation: `lines`}, {artist: artist2, annotation: null}],
     `coverArtistContribs #2: inherits album trackCoverArtistContribs`);
 
@@ -465,7 +465,7 @@ t.test(`Track.coverArtistContribs`, t => {
     {artist: `Artist 1`, annotation: `collage`},
   ];
 
-  t.same(track.coverArtistContribs, [{artist: artist1, annotation: `collage`}],
+  t.match(track.coverArtistContribs, [{artist: artist1, annotation: `collage`}],
     `coverArtistContribs #3: resolves from own value`);
 
   track.coverArtistContribs = [
@@ -474,7 +474,7 @@ t.test(`Track.coverArtistContribs`, t => {
     {artist: `Artist 2`, annotation: `usual`},
   ];
 
-  t.same(track.coverArtistContribs,
+  t.match(track.coverArtistContribs,
     [{artist: artist1, annotation: `snooping`}, {artist: artist2, annotation: `usual`}],
     `coverArtistContribs #4: filters out names without matches`);