1
0
Fork 0

Classes now contain implement() method

closure/master
Mike Gerwitz 2011-01-10 19:56:09 -05:00
parent ec9f24a926
commit 7fb0f6a820
2 changed files with 68 additions and 11 deletions

View File

@ -60,7 +60,7 @@ exports.implement = function()
var args = Array.prototype.slice.call( arguments ); var args = Array.prototype.slice.call( arguments );
// apply to an empty (new) object // apply to an empty (new) object
args.unshift( {} ); args.unshift( exports.extend() );
return implement.apply( this, args ); return implement.apply( this, args );
}; };
@ -347,10 +347,10 @@ var extend = ( function( extending )
/** /**
* Implements interface(s) into an object * Implements interface(s) into an object
* *
* This will copy all of the abstract methods from the interface into the * This will copy all of the abstract methods from the interface and merge it
* destination object. * into the given object.
* *
* @param {Object} dest destination object * @param {Object} base base object
* @param {...Interface} interfaces interfaces to implement into dest * @param {...Interface} interfaces interfaces to implement into dest
* *
* @return {Object} destination object with interfaces implemented * @return {Object} destination object with interfaces implemented
@ -358,7 +358,8 @@ var extend = ( function( extending )
var implement = function() var implement = function()
{ {
var args = Array.prototype.slice.call( arguments ), var args = Array.prototype.slice.call( arguments ),
dest = args.shift(), dest = {},
base = args.shift(),
len = args.length, len = args.length,
arg = null, arg = null,
@ -375,7 +376,8 @@ var implement = function()
implemented.push( arg ); 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; getMeta( class_new.__cid ).implemented = implemented;
return class_new; return class_new;
@ -395,6 +397,7 @@ function setupProps( func, abstract_methods, class_id )
{ {
attachAbstract( func, abstract_methods ); attachAbstract( func, abstract_methods );
attachExtend( func ); attachExtend( func );
attachImplement( func );
attachId( func, class_id ); 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 * Attaches the unique id to the class and its prototype
* *

View File

@ -42,23 +42,47 @@ assert.ok(
"Class provides method to implement interfaces" "Class provides method to implement interfaces"
); );
var Foo = {}, var Foo = {},
PlainFoo = Class.extend(); PlainFoo = Class.extend(),
PlainFoo2 = {};
assert.ok(
( PlainFoo.implement instanceof Function ),
"Classes contain an implement() method"
);
assert.doesNotThrow( function() assert.doesNotThrow( function()
{ {
Foo = Class.implement( Type, Type2 ); Foo = Class.implement( Type, Type2 );
}, Error, "Class can implement interfaces" ); }, 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( assert.ok(
( ( Foo.prototype.foo instanceof Function ) ( ( Foo.prototype.foo instanceof Function )
&& ( Foo.prototype.foo2 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( assert.equal(
Foo.isAbstract(), ( Foo.isAbstract() && PlainFoo2.isAbstract() ),
true, true,
"Classes that implements interface(s) are considered abstract if the " + "Classes that implements interface(s) are considered abstract if the " +
"implemented methods have no concrete implementations" "implemented methods have no concrete implementations"
@ -67,12 +91,23 @@ assert.equal(
assert.equal( assert.equal(
Foo.abstractMethods.length, Foo.abstractMethods.length,
2, 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( assert.ok(
( ( Foo.abstractMethods[ 0 ] == 'foo' ) ( ( Foo.abstractMethods[ 0 ] == 'foo' )
&& ( Foo.abstractMethods[ 1 ] == 'foo2' ) && ( Foo.abstractMethods[ 1 ] == 'foo2' )
)
&& ( ( PlainFoo2.abstractMethods[ 0 ] == 'foo' )
&& ( PlainFoo2.abstractMethods[ 1 ] == 'foo2' )
), ),
"Abstract methods list contains names of implemented methods" "Abstract methods list contains names of implemented methods"
); );