diff --git a/lib/Trait.js b/lib/Trait.js index aa60424..209decf 100644 --- a/lib/Trait.js +++ b/lib/Trait.js @@ -38,7 +38,7 @@ Trait.extend = function( dfn ) // we need at least one abstract member in order to declare a class as // abstract (in this case, our trait class), so let's create a dummy one // just in case DFN does not contain any abstract members itself - dfn[ 'abstract protected __$$trait$$' ] = []; + dfn[ 'abstract protected ___$$trait$$' ] = []; function TraitType() { @@ -88,7 +88,7 @@ function createConcrete( acls ) { // start by providing a concrete implementation for our dummy method var dfn = { - 'protected __$$trait$$': function() {}, + 'protected ___$$trait$$': function() {}, }; // TODO: everything else @@ -111,44 +111,59 @@ function mixin( trait, dfn ) { // the abstract class hidden within the trait var acls = trait.__acls, - methods = acls.___$$methods$$, - pub = methods['public']; + methods = acls.___$$methods$$; // retrieve the private member name that will contain this trait object var iname = addTraitInst( trait.__ccls, dfn ); - // TODO: protected; ignore abstract - for ( var f in pub ) + mixMethods( methods['public'], dfn, 'public', iname ); + mixMethods( methods['protected'], dfn, 'protected', iname ); + + return dfn; +} + + +/** + * Mix methods from SRC into DEST using proxies + * + * @param {Object} src visibility object to scavenge from + * @param {Object} dest destination definition object + * @param {string} vis visibility modifier + * @param {string} ianem proxy destination (trait instance) + * + * @return {undefined} + */ +function mixMethods( src, dest, vis, iname ) +{ + // TODO: ignore abstract + for ( var f in src ) { - if ( !( Object.hasOwnProperty.call( pub, f ) ) ) + if ( !( Object.hasOwnProperty.call( src, f ) ) ) { continue; } // TODO: this is a kluge; we'll use proper reflection eventually, - // but for now, this is how we determine if this is an actual public - // method vs. something that just happens to be on the public - // visibility object - if ( !( pub[ f ].___$$keywords$$ ) ) + // but for now, this is how we determine if this is an actual method + // vs. something that just happens to be on the visibility object + if ( !( src[ f ].___$$keywords$$ ) || f === '___$$trait$$' ) { continue; } - var pname = 'public proxy ' + f; + var pname = vis + ' proxy ' + f; // if we have already set up a proxy for a field of this name, then // multiple traits have defined the same concrete member - if ( dfn[ pname ] !== undefined ) + if ( dest[ pname ] !== undefined ) { // TODO: between what traits? throw Error( "Trait member conflict: `" + f + "'" ); } // proxy this method to what will be the encapsulated trait object - dfn[ pname ] = iname; + dest[ pname ] = iname; } - - return dfn; } diff --git a/test/Trait/DefinitionTest.js b/test/Trait/DefinitionTest.js index 40ba6f0..ad2897d 100644 --- a/test/Trait/DefinitionTest.js +++ b/test/Trait/DefinitionTest.js @@ -45,18 +45,17 @@ require( 'common' ).testCase( ], // should (at least for the time being) be picked up by existing - // class error checks + // class error checks; TODO: but let's provide trait-specific + // error messages to avoid frustration and infuriation [ 'foo', "varying keywords; same visibility", { 'virtual public foo': function() {} }, { 'public virtual foo': function() {} }, ], - /* TODO [ 'foo', "different visibility", { 'public foo': function() {} }, { 'protected foo': function() {} }, ], - */ ]; }, @@ -234,7 +233,7 @@ require( 'common' ).testCase( // caused the error this.assertOk( e.message.match( '\\b' + fname + '\\b' ), - "Missing field name" + "Error message missing field name: " + e.message ); // TODO: we can also make less people hate us if we include the