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 ); markAbstract( arguments );
// forward everything to Class // 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() exports.implement = function()
{ {
var impl = Class.implement.apply( this, arguments ), 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 );
};
abstractOverride( impl );
return 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 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) // constructor (function)
if ( instance instanceof type ) 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', assert.ok( typeof AbstractClass.extend === 'function',
"AbstractClass contains extend method" "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() ( function testAbstractClassContainsImplementMethod()
{ {
assert.ok( typeof AbstractClass.implement === 'function', 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" ); }, 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"
);
} )();