From 7fb0f6a8204c7ede099b4b19035b87fa2deb5aa4 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 10 Jan 2011 19:56:09 -0500 Subject: [PATCH] Classes now contain implement() method --- lib/class.js | 34 ++++++++++++++++++++++----- test/test-class-implement.js | 45 ++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/lib/class.js b/lib/class.js index 38781b2..75075c9 100644 --- a/lib/class.js +++ b/lib/class.js @@ -60,7 +60,7 @@ exports.implement = function() var args = Array.prototype.slice.call( arguments ); // apply to an empty (new) object - args.unshift( {} ); + args.unshift( exports.extend() ); return implement.apply( this, args ); }; @@ -347,10 +347,10 @@ var extend = ( function( extending ) /** * Implements interface(s) into an object * - * This will copy all of the abstract methods from the interface into the - * destination object. + * This will copy all of the abstract methods from the interface and merge it + * into the given object. * - * @param {Object} dest destination object + * @param {Object} base base object * @param {...Interface} interfaces interfaces to implement into dest * * @return {Object} destination object with interfaces implemented @@ -358,7 +358,8 @@ var extend = ( function( extending ) var implement = function() { var args = Array.prototype.slice.call( arguments ), - dest = args.shift(), + dest = {}, + base = args.shift(), len = args.length, arg = null, @@ -375,7 +376,8 @@ var implement = function() implemented.push( arg ); } - var class_new = exports.extend( dest ); + // create a new class with the implemented abstract methods + var class_new = exports.extend( base, dest ); getMeta( class_new.__cid ).implemented = implemented; return class_new; @@ -395,6 +397,7 @@ function setupProps( func, abstract_methods, class_id ) { attachAbstract( func, abstract_methods ); attachExtend( func ); + attachImplement( func ); attachId( func, class_id ); } @@ -495,6 +498,25 @@ function attachExtend( func ) } +/** + * Attaches implement method to the given function (class) + * + * @param {function()} func function (class) to attach method to + * + * @return {undefined} + */ +function attachImplement( func ) +{ + util.defineSecureProp( func, 'implement', function() + { + var args = Array.prototype.slice.call( arguments ); + args.unshift( func ); + + return implement.apply( this, args ); + }); +} + + /** * Attaches the unique id to the class and its prototype * diff --git a/test/test-class-implement.js b/test/test-class-implement.js index 8487619..b21e8cb 100644 --- a/test/test-class-implement.js +++ b/test/test-class-implement.js @@ -42,23 +42,47 @@ assert.ok( "Class provides method to implement interfaces" ); -var Foo = {}, - PlainFoo = Class.extend(); +var Foo = {}, + PlainFoo = Class.extend(), + PlainFoo2 = {}; + +assert.ok( + ( PlainFoo.implement instanceof Function ), + "Classes contain an implement() method" +); assert.doesNotThrow( function() { Foo = Class.implement( Type, Type2 ); }, Error, "Class can implement interfaces" ); +assert.ok( + ( Class.isClass( Foo ) ), + "Class returned from implementing interfaces on an empty base is a " + + "valid class" +); + assert.ok( ( ( Foo.prototype.foo instanceof Function ) && ( Foo.prototype.foo2 instanceof Function ) ), - "Abstract methods are copied into the new class prototype" + "Abstract methods are copied into the new class prototype (empty base)" +); + +assert.doesNotThrow( function() +{ + PlainFoo2 = PlainFoo.implement( Type, Type2 ); +}, Error, "Concrete classes can implement interfaces" ); + +assert.ok( + ( ( PlainFoo2.prototype.foo instanceof Function ) + && ( PlainFoo2.prototype.foo2 instanceof Function ) + ), + "Abstract methods are copied into the new class prototype (concrete base)" ); assert.equal( - Foo.isAbstract(), + ( Foo.isAbstract() && PlainFoo2.isAbstract() ), true, "Classes that implements interface(s) are considered abstract if the " + "implemented methods have no concrete implementations" @@ -67,12 +91,23 @@ assert.equal( assert.equal( Foo.abstractMethods.length, 2, - "Abstract methods list is updated when interface is implemented" + "Abstract methods list is updated when interface is implemented " + + "(empty base)" +); + +assert.equal( + PlainFoo2.abstractMethods.length, + 2, + "Abstract methods list is updated when interface is implemented " + + "(concrete base)" ); assert.ok( ( ( Foo.abstractMethods[ 0 ] == 'foo' ) && ( Foo.abstractMethods[ 1 ] == 'foo2' ) + ) + && ( ( PlainFoo2.abstractMethods[ 0 ] == 'foo' ) + && ( PlainFoo2.abstractMethods[ 1 ] == 'foo2' ) ), "Abstract methods list contains names of implemented methods" );