« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data
diff options
context:
space:
mode:
Diffstat (limited to 'src/data')
-rw-r--r--src/data/cacheable-object.js5
-rw-r--r--src/data/checks.js7
-rw-r--r--src/data/composite.js42
-rw-r--r--src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js29
-rw-r--r--src/data/composite/things/contribution/withContainingReverseContributionList.js2
-rw-r--r--src/data/things/MusicVideo.js1
-rw-r--r--src/data/things/content/ContentEntry.js53
7 files changed, 101 insertions, 38 deletions
diff --git a/src/data/cacheable-object.js b/src/data/cacheable-object.js
index 0071c60d..f63fe4bd 100644
--- a/src/data/cacheable-object.js
+++ b/src/data/cacheable-object.js
@@ -50,7 +50,10 @@ export default class CacheableObject {
       if (!flags.update) continue;
 
       if (typeof update === 'object' && update !== null && 'default' in update) {
-        validatePropertyValue(property, null, update.default, update);
+        if (update.validate) {
+          validatePropertyValue(property, null, update.default, update);
+        }
+
         this.prototype[CacheableObject.updateValue][property] = update.default;
       } else {
         this.prototype[CacheableObject.updateValue][property] = null;
diff --git a/src/data/checks.js b/src/data/checks.js
index 0a0e7f52..01b5cf9e 100644
--- a/src/data/checks.js
+++ b/src/data/checks.js
@@ -352,6 +352,9 @@ export function filterReferenceErrors(wikiData, {
 
             switch (findFnKey) {
               case '_content':
+                // note: quoted artists ("Quoted Artists" field)`) currently
+                // not handled here at all. limitation of current code! oops!
+
                 if (value) {
                   value =
                     value.map(entry =>
@@ -721,13 +724,13 @@ export function reportContentTextErrors(wikiData, {
 
   const commentaryShape = {
     body: 'commentary body',
-    artistText: 'commentary artist text',
+    headingArtistText: 'commentary artist text',
     annotation: 'commentary annotation',
   };
 
   const lyricsShape = {
     body: 'lyrics body',
-    artistText: 'lyrics artist text',
+    headingArtistText: 'lyrics artist text',
     annotation: 'lyrics annotation',
   };
 
diff --git a/src/data/composite.js b/src/data/composite.js
index 8ac906c7..a1b6548b 100644
--- a/src/data/composite.js
+++ b/src/data/composite.js
@@ -10,17 +10,27 @@ import {TupleMap} from '#wiki-data';
 
 const globalCompositeCache = {};
 
-const _valueIntoToken = shape =>
-  (value = null) =>
-    (value === null
-      ? Symbol.for(`hsmusic.composite.${shape}`)
-   : typeof value === 'string'
-      ? Symbol.for(`hsmusic.composite.${shape}:${value}`)
-      : {
-          symbol: Symbol.for(`hsmusic.composite.${shape.split('.')[0]}`),
-          shape,
-          value,
-        });
+const _valueIntoToken = shape => (value = null) => {
+  if (value === null) {
+    return Symbol.for(`hsmusic.composite.${shape}`);
+  }
+
+  if (typeof value === 'string') {
+    return Symbol.for(`hsmusic.composite.${shape}:${value}`);
+  }
+
+  if (typeof value === 'object') {
+    if (Object.values(value).some(isInputToken)) {
+      throw new TypeError(`Don't nest input tokens inside ${shape}()`);
+    }
+  }
+
+  return {
+    symbol: Symbol.for(`hsmusic.composite.${shape.split('.')[0]}`),
+    shape,
+    value,
+  };
+};
 
 export const input = _valueIntoToken('input');
 input.symbol = Symbol.for('hsmusic.composite.input');
@@ -397,9 +407,9 @@ export function templateCompositeFrom(description) {
         }
       }
 
-      const inputAppearance = name =>
-        (isInputToken(preparedInputs[name])
-          ? `${getInputTokenShape(preparedInputs[name])}() call`
+      const inputAppearance = token =>
+        (isInputToken(token)
+          ? `${getInputTokenShape(token)}() call`
           : `dependency name`);
 
       if (!empty(misplacedInputNames)) {
@@ -415,8 +425,8 @@ export function templateCompositeFrom(description) {
       }
 
       for (const index of namedAndPositionalConflictInputPositions) {
-        const conflictingName = positionalInputNames[index];
-        push(new Error(`${name}: Provided as both named and positional (i = ${index}) input`));
+        const conflictingName = positionalInputs[index];
+        push(new Error(`${conflictingName}: Provided as both named and positional (i = ${index}) input`));
       }
 
       for (const {skipped, before} of skippedInputNames) {
diff --git a/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
index 69da8c75..a6200ee8 100644
--- a/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
+++ b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
@@ -8,16 +8,19 @@ export default templateCompositeFrom({
   annotation: `withExpressedOrImplicitArtistReferences`,
 
   inputs: {
-    from: input({type: 'array', acceptsNull: true}),
+    fromExpressed: input({type: 'array', acceptsNull: true}),
+    fromContent: input({type: 'string', acceptsNull: true}),
+
+    filterArtistTags: input({type: 'function', defaultValue: () => true}),
   },
 
   outputs: ['#artistReferences'],
 
   steps: () => [
     {
-      dependencies: [input('from')],
+      dependencies: [input('fromExpressed')],
       compute: (continuation, {
-        [input('from')]: expressedArtistReferences,
+        [input('fromExpressed')]: expressedArtistReferences,
       }) =>
         (expressedArtistReferences
           ? continuation.raiseOutput({'#artistReferences': expressedArtistReferences})
@@ -25,12 +28,12 @@ export default templateCompositeFrom({
     },
 
     raiseOutputWithoutDependency({
-      dependency: 'artistText',
-      output: input.value({'#artistReferences': null}),
+      dependency: input('fromContent'),
+      output: input.value({'#artistReferences': []}),
     }),
 
     withContentNodes({
-      from: 'artistText',
+      from: input('fromContent'),
     }),
 
     withMappedList({
@@ -42,13 +45,19 @@ export default templateCompositeFrom({
       '#mappedList': '#artistTagFilter',
     }),
 
-    withFilteredList({
-      list: '#contentNodes',
-      filter: '#artistTagFilter',
+    withFilteredList('#contentNodes', '#artistTagFilter')
+      .outputs({'#filteredList': '#artistTags'}),
+
+    withMappedList({
+      list: '#artistTags',
+      map: input('filterArtistTags'),
     }).outputs({
-      '#filteredList': '#artistTags',
+      '#mappedList': '#customFilter',
     }),
 
+    withFilteredList({list: '#artistTags', filter: '#customFilter'})
+      .outputs({'#filteredList': '#artistTags'}),
+
     withMappedList({
       list: '#artistTags',
       map: input.value(node =>
diff --git a/src/data/composite/things/contribution/withContainingReverseContributionList.js b/src/data/composite/things/contribution/withContainingReverseContributionList.js
index d8288b17..c600707c 100644
--- a/src/data/composite/things/contribution/withContainingReverseContributionList.js
+++ b/src/data/composite/things/contribution/withContainingReverseContributionList.js
@@ -3,7 +3,7 @@
 // current contribution is dateless, the list is filtered to only include
 // dateless contributions from the same immediately nearby context.
 
-import {input, templateCompositeFrom} from '#composite';
+import {templateCompositeFrom} from '#composite';
 
 import {raiseOutputWithoutDependency, withResultOfAvailabilityCheck}
   from '#composite/control-flow';
diff --git a/src/data/things/MusicVideo.js b/src/data/things/MusicVideo.js
index a7eba04c..acdfaa2b 100644
--- a/src/data/things/MusicVideo.js
+++ b/src/data/things/MusicVideo.js
@@ -10,7 +10,6 @@ import {constituteFrom} from '#composite/wiki-data';
 
 import {
   exposeConstant,
-  exposeDependency,
   exposeUpdateValueOrContinue,
   exposeWhetherDependencyAvailable,
   exitWithoutDependency,
diff --git a/src/data/things/content/ContentEntry.js b/src/data/things/content/ContentEntry.js
index 04df303f..47f86622 100644
--- a/src/data/things/content/ContentEntry.js
+++ b/src/data/things/content/ContentEntry.js
@@ -1,5 +1,5 @@
 import {input, V} from '#composite';
-import {transposeArrays} from '#sugar';
+import {transposeArrays, unique} from '#sugar';
 import Thing from '#thing';
 import {is, isDate, validateReferenceList} from '#validators';
 import {parseDate} from '#yaml';
@@ -33,14 +33,43 @@ export class ContentEntry extends Thing {
 
     thing: thing(),
 
-    artists: [
+    headingArtists: [
       withExpressedOrImplicitArtistReferences({
-        from: input.updateValue({
+        fromExpressed: input.updateValue({
           validate: validateReferenceList('artist'),
         }),
+
+        fromContent: 'headingArtistText',
+      }),
+
+      withResolvedReferenceList({
+        list: '#artistReferences',
+        find: soupyFind.input('artist'),
       }),
 
-      exitWithoutDependency('#artistReferences', V([])),
+      exposeDependency('#resolvedReferenceList'),
+    ],
+
+    quotedArtists: [
+      exitWithoutDependency('body', V([])),
+
+      {
+        dependencies: ['body'],
+        compute: (continuation, {body}) => continuation({
+          ['#filterArtistTags']: node =>
+            /(\n|^)> <i>$/.test(body.slice(0, node.i)) &&
+            /^:<\/i>/.test(body.slice(node.iEnd)),
+        }),
+      },
+
+      withExpressedOrImplicitArtistReferences({
+        fromExpressed: input.updateValue({
+          validate: validateReferenceList('artist'),
+        }),
+
+        fromContent: 'body',
+        filterArtistTags: '#filterArtistTags',
+      }),
 
       withResolvedReferenceList({
         list: '#artistReferences',
@@ -50,7 +79,7 @@ export class ContentEntry extends Thing {
       exposeDependency('#resolvedReferenceList'),
     ],
 
-    artistText: contentString(),
+    headingArtistText: contentString(),
 
     annotation: contentString(),
 
@@ -119,6 +148,14 @@ export class ContentEntry extends Thing {
 
     isContentEntry: exposeConstant(V(true)),
 
+    artists: [
+      {
+        dependencies: ['headingArtists', 'quotedArtists'],
+        compute: ({headingArtists, quotedArtists}) =>
+          unique([...headingArtists, ...quotedArtists]),
+      },
+    ],
+
     annotationParts: [
       withAnnotationPartNodeLists(),
 
@@ -230,8 +267,10 @@ export class ContentEntry extends Thing {
 
   static [Thing.yamlDocumentSpec] = {
     fields: {
-      'Artists': {property: 'artists'},
-      'Artist Text': {property: 'artistText'},
+      'Artists': {property: 'headingArtists'},
+      'Artist Text': {property: 'headingArtistText'},
+
+      'Quoted Artists': {property: 'quotedArtists'},
 
       'Annotation': {property: 'annotation'},