« 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/cacheable-object.js69
-rw-r--r--src/data/things/homepage-layout.js6
-rw-r--r--src/data/things/index.js8
3 files changed, 45 insertions, 38 deletions
diff --git a/src/data/cacheable-object.js b/src/data/cacheable-object.js
index 3f70af30..e0703259 100644
--- a/src/data/cacheable-object.js
+++ b/src/data/cacheable-object.js
@@ -16,25 +16,11 @@ export default class CacheableObject {
   static updateValue = Symbol.for('CacheableObject.updateValues');
 
   constructor({seal = true} = {}) {
-    this[CacheableObject.updateValue] = Object.create(null);
     this[CacheableObject.cachedValue] = Object.create(null);
     this[CacheableObject.cacheValid] = Object.create(null);
 
-    const propertyDescriptors = this.constructor[CacheableObject.propertyDescriptors];
-    for (const property of Reflect.ownKeys(propertyDescriptors)) {
-      const {flags, update} = propertyDescriptors[property];
-      if (!flags.update) continue;
-
-      if (
-        typeof update === 'object' &&
-        update !== null &&
-        'default' in update
-      ) {
-        this[property] = update?.default;
-      } else {
-        this[property] = null;
-      }
-    }
+    this[CacheableObject.updateValue] =
+      Object.create(this[CacheableObject.updateValue]);
 
     if (seal) {
       Object.seal(this);
@@ -50,9 +36,31 @@ export default class CacheableObject {
       throw new Error(`Expected constructor ${this.name} to provide CacheableObject.propertyDescriptors`);
     }
 
+    const propertyDescriptors = this[CacheableObject.propertyDescriptors];
+
+    // Finalize prototype update value
+
+    this.prototype[CacheableObject.updateValue] =
+      Object.create(
+        Object.getPrototypeOf(this.prototype)[CacheableObject.updateValue] ??
+        null);
+
+    for (const property of Reflect.ownKeys(propertyDescriptors)) {
+      const {flags, update} = propertyDescriptors[property];
+      if (!flags.update) continue;
+
+      if (typeof update === 'object' && update !== null && 'default' in update) {
+        validatePropertyValue(property, null, update.default, update);
+        this.prototype[CacheableObject.updateValue][property] = update.default;
+      } else {
+        this.prototype[CacheableObject.updateValue][property] = null;
+      }
+    }
+
+    // Finalize prototype property descriptors
+
     this[CacheableObject.propertyDependants] = Object.create(null);
 
-    const propertyDescriptors = this[CacheableObject.propertyDescriptors];
     for (const property of Reflect.ownKeys(propertyDescriptors)) {
       const {flags, update, expose} = propertyDescriptors[property];
 
@@ -74,17 +82,7 @@ export default class CacheableObject {
           }
 
           if (newValue !== null && update?.validate) {
-            try {
-              const result = update.validate(newValue);
-              if (result === undefined) {
-                throw new TypeError(`Validate function returned undefined`);
-              } else if (result !== true) {
-                throw new TypeError(`Validation failed for value ${newValue}`);
-              }
-            } catch (caughtError) {
-              throw new CacheableObjectPropertyValueError(
-                property, oldValue, newValue, {cause: caughtError});
-            }
+            validatePropertyValue(property, oldValue, newValue, update);
           }
 
           this[CacheableObject.updateValue][property] = newValue;
@@ -261,3 +259,18 @@ export class CacheableObjectPropertyValueError extends Error {
     this.property = property;
   }
 }
+
+// good ol' module-scope utility functions
+function validatePropertyValue(property, oldValue, newValue, update) {
+  try {
+    const result = update.validate(newValue);
+    if (result === undefined) {
+      throw new TypeError(`Validate function returned undefined`);
+    } else if (result !== true) {
+      throw new TypeError(`Validation failed for value ${newValue}`);
+    }
+  } catch (caughtError) {
+    throw new CacheableObjectPropertyValueError(
+      property, oldValue, newValue, {cause: caughtError});
+  }
+}
diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js
index 2456ca95..d00a2f4b 100644
--- a/src/data/things/homepage-layout.js
+++ b/src/data/things/homepage-layout.js
@@ -244,8 +244,6 @@ export class HomepageLayoutActionsRow extends HomepageLayoutRow {
   static [Thing.friendlyName] = `Homepage Actions Row`;
 
   static [Thing.getPropertyDescriptors] = (opts) => ({
-    ...HomepageLayoutRow[Thing.getPropertyDescriptors](opts),
-
     // Update & expose
 
     actionLinks: {
@@ -278,8 +276,6 @@ export class HomepageLayoutAlbumCarouselRow extends HomepageLayoutRow {
   static [Thing.friendlyName] = `Homepage Album Carousel Row`;
 
   static [Thing.getPropertyDescriptors] = (opts, {Album} = opts) => ({
-    ...HomepageLayoutRow[Thing.getPropertyDescriptors](opts),
-
     // Update & expose
 
     albums: referenceList({
@@ -312,8 +308,6 @@ export class HomepageLayoutAlbumGridRow extends HomepageLayoutRow {
   static [Thing.friendlyName] = `Homepage Album Grid Row`;
 
   static [Thing.getPropertyDescriptors] = (opts, {Album, Group} = opts) => ({
-    ...HomepageLayoutRow[Thing.getPropertyDescriptors](opts),
-
     // Update & expose
 
     sourceGroup: [
diff --git a/src/data/things/index.js b/src/data/things/index.js
index 41301575..676453ce 100644
--- a/src/data/things/index.js
+++ b/src/data/things/index.js
@@ -181,10 +181,10 @@ function evaluatePropertyDescriptors() {
         }
       }
 
-      constructor[CacheableObject.propertyDescriptors] = {
-        ...constructor[CacheableObject.propertyDescriptors] ?? {},
-        ...results,
-      };
+      constructor[CacheableObject.propertyDescriptors] =
+        Object.create(constructor[CacheableObject.propertyDescriptors] ?? null);
+
+      Object.assign(constructor[CacheableObject.propertyDescriptors], results);
     },
 
     showFailedClasses(failedClasses) {