From 897a4afab4c605289a06e54e2542a84dacd00e22 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 10 Feb 2014 23:11:34 -0500 Subject: [PATCH] Added support for mixing in traits using use method on a base class --- lib/class.js | 30 +++++++++++++++++++++++---- test/Trait/MixedExtendTest.js | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/lib/class.js b/lib/class.js index af2d51a..d74608a 100644 --- a/lib/class.js +++ b/lib/class.js @@ -374,9 +374,9 @@ function createUse( base, traits ) return { extend: function() { - var args = Array.prototype.slice.call( arguments ), - dfn = args.pop(), - base = args.pop(); + var args = Array.prototype.slice.call( arguments ), + dfn = args.pop(), + ext_base = args.pop(); // "mix" each trait into the provided definition object for ( var i = 0, n = traits.length; i < n; i++ ) @@ -384,7 +384,7 @@ function createUse( base, traits ) traits[ i ].__mixin( dfn ); } - var C = extend.call( null, base, dfn ), + var C = extend.call( null, ( base || ext_base ), dfn ), meta = ClassBuilder.getMeta( C ); // add each trait to the list of implemented types so that the @@ -495,6 +495,7 @@ function setupProps( func ) { attachExtend( func ); attachImplement( func ); + attachUse( func ); } @@ -544,3 +545,24 @@ function attachImplement( func ) }); } + +/** + * Attaches use method to the given function (class) + * + * Please see the `use' export of this module for more information. + * + * @param {function()} func function (class) to attach method to + * + * @return {undefined} + */ +function attachUse( func ) +{ + util.defineSecureProp( func, 'use', function() + { + return createUse( + func, + Array.prototype.slice.call( arguments ) + ); + } ); +} + diff --git a/test/Trait/MixedExtendTest.js b/test/Trait/MixedExtendTest.js index 4a39646..9d77e96 100644 --- a/test/Trait/MixedExtendTest.js +++ b/test/Trait/MixedExtendTest.js @@ -136,4 +136,43 @@ require( 'common' ).testCase( // o mixes in Tb this.assertOk( this.Class.isA( Tb, o ) ); }, + + + /** + * This alternative syntax mixes a trait directly into a base class and + * then omits the base class as an argument to the extend method; this + * syntax is most familiar with named classes, but we are not testing + * named classes here. + */ + 'Can mix in traits directly atop of existing class': function() + { + var called_foo = false, + called_bar = false, + called_baz = false; + + var C = this.Class( + { + 'public foo': function() { called_foo = true; }, + } ); + + var T = this.Sut( + { + 'public bar': function() { called_bar = true; }, + } ); + + // we must ensure not only that we have mixed in the trait, but that + // we have also maintained C's interface and can further extend it + var inst = C.use( T ).extend( + { + 'public baz': function() { called_baz = true; }, + } )(); + + inst.foo(); + inst.bar(); + inst.baz(); + + this.assertOk( called_foo ); + this.assertOk( called_bar ); + this.assertOk( called_baz ); + }, } );