1
0
Fork 0

Added support for named abstract subclasses

closure/master
Mike Gerwitz 2011-05-22 21:05:46 -04:00
parent db9de2712e
commit a67d704837
3 changed files with 95 additions and 12 deletions

View File

@ -39,7 +39,16 @@ module.exports = exports = function()
markAbstract( arguments );
// forward everything to Class
return Class.apply( this, arguments );
var result = Class.apply( this, arguments );
// if we're using the temporary object, then override its methods to permit
// abstract classes
if ( !Class.isClass( result ) )
{
abstractOverride( result );
}
return result;
};
@ -64,16 +73,9 @@ exports.extend = function()
*/
exports.implement = function()
{
var impl = Class.implement.apply( this, arguments ),
extend = impl.extend;
// wrap extend, applying the abstract flag
impl.extend = function()
{
markAbstract( arguments );
return extend.apply( this, arguments );
};
var impl = Class.implement.apply( this, arguments );
abstractOverride( impl );
return impl;
};
@ -100,3 +102,23 @@ function markAbstract( args )
}
}
/**
* Overrides object members to permit abstract classes
*
* @param {Object} obj object to override
*
* @return {undefined}
*/
function abstractOverride( obj )
{
var extend = obj.extend;
// wrap extend, applying the abstract flag
obj.extend = function()
{
markAbstract( arguments );
return extend.apply( this, arguments );
};
}

View File

@ -1004,7 +1004,7 @@ exports.isInstanceOf = function( type, instance )
try
{
// check prototype chain (with throw an error if type is not a
// check prototype chain (will throw an error if type is not a
// constructor (function)
if ( instance instanceof type )
{

View File

@ -106,14 +106,27 @@ var common = require( './common' ),
} )();
( function testAbstractClassContainsExtendMethod()
/**
* Just as Class contains an extend method, so should AbstractClass.
*/
( function testAbstractClassExtendMethodReturnsNewClass()
{
assert.ok( typeof AbstractClass.extend === 'function',
"AbstractClass contains extend method"
);
assert.ok(
Class.isClass(
AbstractClass.extend( { 'abstract foo': [] } )
),
"Abstract class extend method returns class"
);
} )();
/**
* Just as Class contains an implement method, so should AbstractClass.
*/
( function testAbstractClassContainsImplementMethod()
{
assert.ok( typeof AbstractClass.implement === 'function',
@ -363,3 +376,51 @@ var ConcreteFoo = Class.extend( AbstractFoo,
}, Error, "Should not throw error if overriding a prototype method" );
} )();
/**
* Ensure we support named abstract class extending
*/
( function testCanCreateNamedAbstractSubtypes()
{
assert.doesNotThrow( function()
{
var cls = AbstractClass( 'NamedSubFoo' ).extend( AbstractFoo, {} );
}, Error, "Can create named abstract subtypes" );
} )();
/**
* Abstract classes, when extended, should yield a concrete class by default.
* Otherwise, the user should once again use AbstractClass to clearly state that
* the subtype is abstract.
*/
( function testExtendingAbstractClassIsNotAbstractByDefault()
{
var cls_named = AbstractClass( 'NamedSubFoo' ).extend( AbstractFoo, {} ),
anon_named = AbstractClass.extend( AbstractFoo, {} );
// named
assert.throws(
function()
{
// should throw an error, since we're not declaring it as abstract
// and we're not providing a concrete impl
Class.isAbstract( cls_named.extend( {} ) );
},
TypeError,
"Extending named abstract classes should be concrete by default"
);
// anonymous
assert.throws(
function()
{
// should throw an error, since we're not declaring it as abstract
// and we're not providing a concrete impl
Class.isAbstract( AbstractFoo.extend( {} ) );
},
TypeError,
"Extending anonymous abstract classes should be concrete by default"
);
} )();