Преглед изворни кода

feat(putility): trait method override support

Support for trait method overrides will make it possible to use putility
traits with the decorator pattern while using a proxy class to avoid
redundant re-implementation of proxy methods.

Use of the pattern described above will occur in the implementation of
client-side filesystem caching.
KernelDeimos пре 7 месеци
родитељ
комит
43c5402b7c
2 измењених фајлова са 75 додато и 1 уклоњено
  1. 26 1
      src/putility/src/features/TraitsFeature.js
  2. 49 0
      src/putility/test/traits.test.js

+ 26 - 1
src/putility/src/features/TraitsFeature.js

@@ -1,5 +1,6 @@
 module.exports = {
-    install_in_instance: (instance, { parameters }) => {
+    // old implementation
+    install_in_instance_: (instance, { parameters }) => {
         const impls = instance._get_merged_static_object('IMPLEMENTS');
         
         instance._.impls = {};
@@ -17,4 +18,28 @@ module.exports = {
         instance.as = trait_name => instance._.impls[trait_name];
         instance.list_traits = () => Object.keys(instance._.impls);
     },
+
+    // new implementation
+    install_in_instance: (instance, { parameters }) => {
+        const chain = instance._get_inheritance_chain();
+        instance._.impls = {};
+        
+        instance.as = trait_name => instance._.impls[trait_name];
+        instance.list_traits = () => Object.keys(instance._.impls);
+
+        for ( const cls of chain ) {
+            const cls_traits = cls.IMPLEMENTS;
+            if ( ! cls_traits ) continue;
+            for ( const trait_name in cls_traits ) {
+                const impl = instance._.impls[trait_name] ??
+                    (instance._.impls[trait_name] = {});
+                const cls_impl = cls_traits[trait_name];
+
+                for ( const method_name in cls_impl ) {
+                    const fn = cls_impl[method_name];
+                    impl[method_name] = fn.bind(instance);
+                }
+            }
+        }
+    }
 };

+ 49 - 0
src/putility/test/traits.test.js

@@ -0,0 +1,49 @@
+const { expect } = require('chai');
+const { AdvancedBase } = require("../src/AdvancedBase");
+
+class TestClass extends AdvancedBase {
+    static IMPLEMENTS = {
+        test_trait: {
+            test_method: () => 'A'
+        },
+        override_trait: {
+            preserved_method: () => 'B',
+            override_method: () => 'C',
+        },
+    }
+}
+
+class TestSubClass extends TestClass {
+    static IMPLEMENTS = {
+        override_trait: {
+            override_method: () => 'D',
+        }
+    }
+}
+
+describe('traits', () => {
+    it('instance.as', () => {
+        const o = new TestClass();
+        expect(o.as).to.be.a('function');
+        const ot = o.as('test_trait');
+        expect(ot.test_method).to.be.a('function');
+        expect(ot.test_method()).to.equal('A');
+    });
+    it('traits of parent', () => {
+        const o = new TestSubClass();
+        console.log(o._get_merged_static_object('IMPLEMENTS'))
+        expect(o.as).to.be.a('function');
+        const ot = o.as('test_trait');
+        expect(ot.test_method).to.be.a('function');
+        expect(ot.test_method()).to.equal('A');
+    })
+    it('trait method overrides', () => {
+        const o = new TestSubClass();
+        expect(o.as).to.be.a('function');
+        const ot = o.as('override_trait');
+        expect(ot.preserved_method).to.be.a('function');
+        expect(ot.override_method).to.be.a('function');
+        expect (ot.preserved_method()).to.equal('B');
+        expect (ot.override_method()).to.equal('D');
+    })
+});