interface() no longer returns a usable class
- Number of reasons. First and foremost, it doesn't make sense to return a usable class with no body/definition - Secondly, to make the following commit(s) possible and consistentclosure/master
parent
ace9f4c1ea
commit
0f9454b79b
52
lib/class.js
52
lib/class.js
|
@ -100,12 +100,11 @@ module.exports.extend = function( base )
|
|||
*/
|
||||
module.exports.implement = function()
|
||||
{
|
||||
var args = Array.prototype.slice.call( arguments );
|
||||
|
||||
// apply to an empty (new) object
|
||||
args.unshift( module.exports.extend() );
|
||||
|
||||
return implement.apply( this, args );
|
||||
// implement on empty base
|
||||
return createImplement(
|
||||
module.exports.extend(),
|
||||
Array.prototype.slice.call( arguments )
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
@ -313,6 +312,37 @@ function createStaging( cname )
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an intermediate object to permit implementing interfaces
|
||||
*
|
||||
* This object defers processing until extend() is called. This intermediate
|
||||
* object ensures that a usable class is not generated until after extend() is
|
||||
* called, as it does not make sense to create a class without any
|
||||
* body/definition.
|
||||
*
|
||||
* @param {Object} base base class to implement atop of
|
||||
* @param {Array} ifaces interfaces to implement
|
||||
*
|
||||
* @return {Object} intermediate implementation object
|
||||
*/
|
||||
function createImplement( base, ifaces )
|
||||
{
|
||||
ifaces.unshift( base );
|
||||
|
||||
// Defer processing until after extend(). This also ensures that implement()
|
||||
// returns nothing usable.
|
||||
return {
|
||||
extend: function( def )
|
||||
{
|
||||
return extend.apply( null, [
|
||||
implement.apply( this, ifaces ),
|
||||
def
|
||||
] );
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates extend function
|
||||
*
|
||||
|
@ -714,6 +744,8 @@ function attachExtend( func )
|
|||
/**
|
||||
* Attaches implement method to the given function (class)
|
||||
*
|
||||
* Please see the implement() export of this module for more information.
|
||||
*
|
||||
* @param {function()} func function (class) to attach method to
|
||||
*
|
||||
* @return {undefined}
|
||||
|
@ -722,10 +754,10 @@ function attachImplement( func )
|
|||
{
|
||||
util.defineSecureProp( func, 'implement', function()
|
||||
{
|
||||
var args = Array.prototype.slice.call( arguments );
|
||||
args.unshift( func );
|
||||
|
||||
return implement.apply( this, args );
|
||||
return createImplement(
|
||||
func,
|
||||
Array.prototype.slice.call( arguments )
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -64,19 +64,41 @@ var Type = Interface.extend( {
|
|||
{
|
||||
assert.doesNotThrow( function()
|
||||
{
|
||||
Foo = Class.implement( Type, Type2 );
|
||||
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"
|
||||
|
||||
/**
|
||||
* Initially, the implement() method returned an abstract class. However, it
|
||||
* doesn't make sense to create a class without any actual definition (and
|
||||
* there's other implementation considerations that caused this route to be
|
||||
* taken). One wouldn't do "class Foo implements Type", and not provide any
|
||||
* body.
|
||||
*
|
||||
* Therefore, implement() should return nothing useful until extend() is called
|
||||
* on it.
|
||||
*/
|
||||
( function testResultOfImplementIsNotUsableAsAClass()
|
||||
{
|
||||
var result = Class.implement( Type );
|
||||
|
||||
assert.equal(
|
||||
( Class.isClass( result ) ),
|
||||
false,
|
||||
"Result of implement operation on class is not usable as a Class"
|
||||
);
|
||||
} )();
|
||||
|
||||
|
||||
/**
|
||||
* As a consequence of the above, we must extend with an empty definition
|
||||
* (base) in order to get our abstract class.
|
||||
*/
|
||||
( function testAbstractMethodsCopiedIntoNewClassUsingEmptyBase()
|
||||
{
|
||||
Foo = Class.implement( Type, Type2 ).extend( {} );
|
||||
|
||||
assert.ok(
|
||||
( ( Foo.prototype.foo instanceof Function )
|
||||
&& ( Foo.prototype.foo2 instanceof Function )
|
||||
|
@ -90,19 +112,32 @@ var Type = Interface.extend( {
|
|||
{
|
||||
assert.doesNotThrow( function()
|
||||
{
|
||||
PlainFoo2 = PlainFoo.implement( Type, Type2 );
|
||||
PlainFoo.implement( Type, Type2 );
|
||||
}, Error, "Classes can implement interfaces" );
|
||||
} )();
|
||||
|
||||
assert.ok(
|
||||
( Class.isClass( PlainFoo2 ) ),
|
||||
"Class returned from implementing interfaces on an existing base is a " +
|
||||
"valid class"
|
||||
|
||||
/**
|
||||
* Ensure the same system mentioned above also applies to the extend() method on
|
||||
* existing classes
|
||||
*/
|
||||
( function testImplementingInterfaceAtopExistingClassIsNotUsableByDefault()
|
||||
{
|
||||
var result = PlainFoo.implement( Type );
|
||||
|
||||
assert.equal(
|
||||
( Class.isClass( result ) ),
|
||||
false,
|
||||
"Result of implementing interfaces on an existing base is not " +
|
||||
"usable as a Class"
|
||||
);
|
||||
} )();
|
||||
|
||||
|
||||
( function testAbstractMethodsCopiedIntoNewClassUsingExistingBase()
|
||||
{
|
||||
PlainFoo2 = PlainFoo.implement( Type, Type2 ).extend( {} );
|
||||
|
||||
assert.ok(
|
||||
( ( PlainFoo2.prototype.foo instanceof Function )
|
||||
&& ( PlainFoo2.prototype.foo2 instanceof Function )
|
||||
|
|
Loading…
Reference in New Issue