diff --git a/src/putility/src/features/TraitsFeature.js b/src/putility/src/features/TraitsFeature.js index 68b4ac5f..2da996b7 100644 --- a/src/putility/src/features/TraitsFeature.js +++ b/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); + } + } + } + } }; diff --git a/src/putility/test/traits.test.js b/src/putility/test/traits.test.js new file mode 100644 index 00000000..7f88625d --- /dev/null +++ b/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'); + }) +}); \ No newline at end of file