« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/data/things/album.js9
-rw-r--r--src/data/things/composite.js92
-rw-r--r--src/data/things/homepage-layout.js29
-rw-r--r--src/data/things/track.js33
4 files changed, 97 insertions, 66 deletions
diff --git a/src/data/things/album.js b/src/data/things/album.js
index c0042ae2..ec133a34 100644
--- a/src/data/things/album.js
+++ b/src/data/things/album.js
@@ -53,11 +53,12 @@ export class Album extends Thing {
 
     coverArtDate: [
       exitWithoutContribs({contribs: 'coverArtistContribs'}),
-      exposeUpdateValueOrContinue(),
-      exposeDependency({
-        dependency: 'date',
-        update: {validate: isDate},
+
+      exposeUpdateValueOrContinue({
+        validate: input.value(isDate),
       }),
+
+      exposeDependency({dependency: 'date'}),
     ],
 
     coverArtFileExtension: [
diff --git a/src/data/things/composite.js b/src/data/things/composite.js
index c33fc03c..011f307e 100644
--- a/src/data/things/composite.js
+++ b/src/data/things/composite.js
@@ -1279,34 +1279,21 @@ export function debugComposite(fn) {
 
 // Exposes a dependency exactly as it is; this is typically the base of a
 // composition which was created to serve as one property's descriptor.
-// Since this serves as a base, specify a value for {update} to indicate
-// that the property as a whole updates (and some previous compositional
-// step works with that update value). Set {update: true} to only enable
-// the update flag, or set update to an object to specify a descriptor
-// (e.g. for custom value validation).
 //
 // Please note that this *doesn't* verify that the dependency exists, so
 // if you provide the wrong name or it hasn't been set by a previous
 // compositional step, the property will be exposed as undefined instead
 // of null.
 //
-export function exposeDependency({
-  dependency,
-  update = false,
-}) {
+export function exposeDependency({dependency}) {
   return {
     annotation: `exposeDependency`,
-    flags: {expose: true, update: !!update},
+    flags: {expose: true},
 
     expose: {
       mapDependencies: {dependency},
       compute: ({dependency}) => dependency,
     },
-
-    update:
-      (typeof update === 'object'
-        ? update
-        : null),
   };
 }
 
@@ -1314,25 +1301,16 @@ export function exposeDependency({
 // is typically the base of a composition serving as a particular property
 // descriptor. It generally follows steps which will conditionally early
 // exit with some other value, with the exposeConstant base serving as the
-// fallback default value. Like exposeDependency, set {update} to true or
-// an object to indicate that the property as a whole updates.
-export function exposeConstant({
-  value,
-  update = false,
-}) {
+// fallback default value.
+export function exposeConstant({value}) {
   return {
     annotation: `exposeConstant`,
-    flags: {expose: true, update: !!update},
+    flags: {expose: true},
 
     expose: {
       options: {value},
       compute: ({'#options': {value}}) => value,
     },
-
-    update:
-      (typeof update === 'object'
-        ? update
-        : null),
   };
 }
 
@@ -1427,12 +1405,24 @@ export const exposeDependencyOrContinue = templateCompositeFrom({
 
 // Exposes the update value of an {update: true} property as it is,
 // or continues if it's unavailable. See withResultOfAvailabilityCheck
-// for {mode} options!
+// for {mode} options! Also provide {validate} here to conveniently
+// set a custom validation check for this property's update value.
 export const exposeUpdateValueOrContinue = templateCompositeFrom({
   annotation: `exposeUpdateValueOrContinue`,
 
   inputs: {
     mode: input(availabilityCheckModeInput),
+    validate: input({type: 'function', null: true}),
+  },
+
+  update: {
+    dependencies: [input.staticValue('validate')],
+    compute: ({
+      [input.staticValue('validate')]: validate,
+    }) =>
+      (validate
+        ? {validate}
+        : {}),
   },
 
   steps: () => [
@@ -1757,6 +1747,52 @@ export function fillMissingListItems({
   }
 }
 
+// Filters particular values out of a list. Note that this will always
+// completely skip over null, but can be used to filter out any other
+// primitive or object value.
+export const excludeFromList = templateCompositeFrom({
+  annotation: `excludeFromList`,
+
+  inputs: {
+    list: input(),
+
+    item: input({null: true}),
+    items: input({validate: isArray, null: true}),
+  },
+
+  outputs: {
+    dependencies: [input.staticDependency('list')],
+    compute: ({
+      [input.staticDependency('list')]: list,
+    }) => [list ?? '#list'],
+  },
+
+  steps: [
+    {
+      dependencies: [
+        input.staticDependency('list'),
+        input('list'),
+        input('item'),
+        input('items'),
+      ],
+
+      compute: (continuation, {
+        [input.staticDependency('list')]: listName,
+        [input('list')]: listContents,
+        [input('item')]: excludeItem,
+        [input('items')]: excludeItems,
+      }) => continuation({
+        [listName ?? '#list']:
+          listContents.filter(item => {
+            if (excludeItem !== null && item === excludeItem) return false;
+            if (!empty(excludeItems) && exclueItems.includes(item)) return false;
+            return true;
+          }),
+      }),
+    },
+  ],
+});
+
 // Flattens an array with one level of nested arrays, providing as dependencies
 // both the flattened array as well as the original starting indices of each
 // successive source array.
diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js
index 007e0236..677a2756 100644
--- a/src/data/things/homepage-layout.js
+++ b/src/data/things/homepage-layout.js
@@ -109,10 +109,21 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow {
 
     sourceGroup: compositeFrom(`HomepageLayoutAlbumsRow.sourceGroup`, [
       {
-        transform: (value, continuation) =>
-          (value === 'new-releases' || value === 'new-additions'
-            ? value
-            : continuation(value)),
+        flags: {expose: true, update: true, compose: true},
+
+        update: {
+          validate:
+            oneOf(
+              is('new-releases', 'new-additions'),
+              validateReference(Group[Thing.referenceType])),
+        },
+
+        expose: {
+          transform: (value, continuation) =>
+            (value === 'new-releases' || value === 'new-additions'
+              ? value
+              : continuation(value)),
+        },
       },
 
       withResolvedReference({
@@ -121,15 +132,7 @@ export class HomepageLayoutAlbumsRow extends HomepageLayoutRow {
         find: input.value(find.group),
       }),
 
-      exposeDependency({
-        dependency: '#resolvedReference',
-        update: input.value({
-          validate:
-            oneOf(
-              is('new-releases', 'new-additions'),
-              validateReference(Group[Thing.referenceType])),
-        }),
-      }),
+      exposeDependency({dependency: '#resolvedReference'}),
     ]),
 
     sourceAlbums: referenceList({
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 28caf1de..135e6d1f 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -121,15 +121,13 @@ export class Track extends Thing {
     coverArtFileExtension: [
       exitWithoutUniqueCoverArt(),
 
-      exposeUpdateValueOrContinue(),
+      exposeUpdateValueOrContinue({
+        validate: input.value(isFileExtension),
+      }),
 
       withPropertyFromAlbum({property: 'trackCoverArtFileExtension'}),
       exposeDependencyOrContinue({dependency: '#album.trackCoverArtFileExtension'}),
-
-      exposeConstant({
-        value: 'jpg',
-        update: {validate: isFileExtension},
-      }),
+      exposeConstant({value: 'jpg'}),
     ],
 
     // Date of cover art release. Like coverArtFileExtension, this represents
@@ -140,13 +138,12 @@ export class Track extends Thing {
       withHasUniqueCoverArt(),
       exitWithoutDependency({dependency: '#hasUniqueCoverArt', mode: 'falsy'}),
 
-      exposeUpdateValueOrContinue(),
+      exposeUpdateValueOrContinue({
+        validate: input.value(isDate),
+      }),
 
       withPropertyFromAlbum({property: 'trackArtDate'}),
-      exposeDependency({
-        dependency: '#album.trackArtDate',
-        update: {validate: isDate},
-      }),
+      exposeDependency({dependency: '#album.trackArtDate'}),
     ],
 
     commentary: commentary(),
@@ -175,7 +172,7 @@ export class Track extends Thing {
       inheritFromOriginalRelease({property: 'artistContribs'}),
 
       withResolvedContribs({
-        from: input.updateValue(),
+        from: input.updateValue({validate: isContributionList}),
       }).outputs({
         '#resolvedContribs': '#artistContribs',
       }),
@@ -183,10 +180,7 @@ export class Track extends Thing {
       exposeDependencyOrContinue({dependency: '#artistContribs'}),
 
       withPropertyFromAlbum({property: 'artistContribs'}),
-      exposeDependency({
-        dependency: '#album.artistContribs',
-        update: {validate: isContributionList},
-      }),
+      exposeDependency({dependency: '#album.artistContribs'}),
     ],
 
     contributorContribs: [
@@ -201,7 +195,7 @@ export class Track extends Thing {
       exitWithoutUniqueCoverArt(),
 
       withResolvedContribs({
-        from: input.updateValue(),
+        from: input.updateValue({validate: isContributionList}),
       }).outputs({
         '#resolvedContribs': '#coverArtistContribs',
       }),
@@ -209,10 +203,7 @@ export class Track extends Thing {
       exposeDependencyOrContinue({dependency: '#coverArtistContribs'}),
 
       withPropertyFromAlbum({property: 'trackCoverArtistContribs'}),
-      exposeDependency({
-        dependency: '#album.trackCoverArtistContribs',
-        update: {validate: isContributionList},
-      }),
+      exposeDependency({dependency: '#album.trackCoverArtistContribs'}),
     ],
 
     referencedTracks: [