Added support for named abstract subclasses
parent
db9de2712e
commit
a67d704837
|
@ -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 );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 )
|
||||||
{
|
{
|
||||||
|
|
|
@ -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"
|
||||||
|
);
|
||||||
|
} )();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue