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()
|
module.exports.implement = function()
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments );
|
// implement on empty base
|
||||||
|
return createImplement(
|
||||||
// apply to an empty (new) object
|
module.exports.extend(),
|
||||||
args.unshift( module.exports.extend() );
|
Array.prototype.slice.call( arguments )
|
||||||
|
);
|
||||||
return implement.apply( this, args );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
* Creates extend function
|
||||||
*
|
*
|
||||||
|
@ -714,6 +744,8 @@ function attachExtend( func )
|
||||||
/**
|
/**
|
||||||
* Attaches implement method to the given function (class)
|
* 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
|
* @param {function()} func function (class) to attach method to
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
|
@ -722,10 +754,10 @@ function attachImplement( func )
|
||||||
{
|
{
|
||||||
util.defineSecureProp( func, 'implement', function()
|
util.defineSecureProp( func, 'implement', function()
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments );
|
return createImplement(
|
||||||
args.unshift( func );
|
func,
|
||||||
|
Array.prototype.slice.call( arguments )
|
||||||
return implement.apply( this, args );
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,19 +64,41 @@ var Type = Interface.extend( {
|
||||||
{
|
{
|
||||||
assert.doesNotThrow( function()
|
assert.doesNotThrow( function()
|
||||||
{
|
{
|
||||||
Foo = Class.implement( Type, Type2 );
|
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 " +
|
* Initially, the implement() method returned an abstract class. However, it
|
||||||
"valid class"
|
* 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()
|
( function testAbstractMethodsCopiedIntoNewClassUsingEmptyBase()
|
||||||
{
|
{
|
||||||
|
Foo = Class.implement( Type, Type2 ).extend( {} );
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
( ( Foo.prototype.foo instanceof Function )
|
( ( Foo.prototype.foo instanceof Function )
|
||||||
&& ( Foo.prototype.foo2 instanceof Function )
|
&& ( Foo.prototype.foo2 instanceof Function )
|
||||||
|
@ -90,19 +112,32 @@ var Type = Interface.extend( {
|
||||||
{
|
{
|
||||||
assert.doesNotThrow( function()
|
assert.doesNotThrow( function()
|
||||||
{
|
{
|
||||||
PlainFoo2 = PlainFoo.implement( Type, Type2 );
|
PlainFoo.implement( Type, Type2 );
|
||||||
}, Error, "Classes can implement interfaces" );
|
}, Error, "Classes can implement interfaces" );
|
||||||
|
} )();
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
( Class.isClass( PlainFoo2 ) ),
|
/**
|
||||||
"Class returned from implementing interfaces on an existing base is a " +
|
* Ensure the same system mentioned above also applies to the extend() method on
|
||||||
"valid class"
|
* 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()
|
( function testAbstractMethodsCopiedIntoNewClassUsingExistingBase()
|
||||||
{
|
{
|
||||||
|
PlainFoo2 = PlainFoo.implement( Type, Type2 ).extend( {} );
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
( ( PlainFoo2.prototype.foo instanceof Function )
|
( ( PlainFoo2.prototype.foo instanceof Function )
|
||||||
&& ( PlainFoo2.prototype.foo2 instanceof Function )
|
&& ( PlainFoo2.prototype.foo2 instanceof Function )
|
||||||
|
|
Loading…
Reference in New Issue