Fail intelligently if provided class in place of definition object
This is intended mainly to handle cases where the user forgets the second argument when extending a class: Class.extend( Base ) // missing second argument (definition object) * lib/ClassBuilder.js (exports.isClass): Move from lib/class.js. (exports.prototype.build): Throw error if definition object is a class. * lib/class.js (module.exports.isClass): Reference `ClassBuilder.isClass'. * test/Class/ExtendTest.js: Add respective test case.master
parent
55f47a3c5a
commit
60920f18a6
|
@ -305,6 +305,30 @@ exports.isInstanceOf = function( type, instance )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the provided object is a class created through ease.js
|
||||||
|
*
|
||||||
|
* @param {Object} obj object to test
|
||||||
|
*
|
||||||
|
* @return {boolean} true if class (created through ease.js), otherwise
|
||||||
|
* false
|
||||||
|
*/
|
||||||
|
module.exports.isClass = function( obj )
|
||||||
|
{
|
||||||
|
obj = obj || _dummyclass;
|
||||||
|
|
||||||
|
var meta = module.exports.getMeta( obj );
|
||||||
|
|
||||||
|
// TODO: we're checking a random field on the meta object; do something
|
||||||
|
// proper
|
||||||
|
return ( ( ( meta !== null ) && meta.implemented )
|
||||||
|
|| ( obj.prototype instanceof module.exports.ClassBase ) )
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper around ECMAScript instanceof check
|
* Wrapper around ECMAScript instanceof check
|
||||||
*
|
*
|
||||||
|
@ -383,6 +407,15 @@ exports.prototype.build = function extend( _, __ )
|
||||||
|| {}
|
|| {}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// respond intelligently if the definition object is mistakenly a class
|
||||||
|
if ( module.exports.isClass( props ) )
|
||||||
|
{
|
||||||
|
throw TypeError( ( an > 1 )
|
||||||
|
? "Expected class definition, but found class " + props.toString()
|
||||||
|
: "Missing second argument to extend class " + props.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// prevent extending final classes (TODO: abstract this check)
|
// prevent extending final classes (TODO: abstract this check)
|
||||||
if ( base.___$$final$$ === true )
|
if ( base.___$$final$$ === true )
|
||||||
{
|
{
|
||||||
|
|
17
lib/class.js
17
lib/class.js
|
@ -171,26 +171,11 @@ var _dummyinst = { constructor: { prototype: {} } };
|
||||||
/**
|
/**
|
||||||
* Determines whether the provided object is a class created through ease.js
|
* Determines whether the provided object is a class created through ease.js
|
||||||
*
|
*
|
||||||
* TODO: delegate to ClassBuilder
|
|
||||||
*
|
|
||||||
* @param {Object} obj object to test
|
* @param {Object} obj object to test
|
||||||
*
|
*
|
||||||
* @return {boolean} true if class (created through ease.js), otherwise false
|
* @return {boolean} true if class (created through ease.js), otherwise false
|
||||||
*/
|
*/
|
||||||
module.exports.isClass = function( obj )
|
module.exports.isClass = ClassBuilder.isClass;
|
||||||
{
|
|
||||||
obj = obj || _dummyclass;
|
|
||||||
|
|
||||||
var meta = ClassBuilder.getMeta( obj );
|
|
||||||
|
|
||||||
// TODO: we're checking a random field on the meta object; do something
|
|
||||||
// proper
|
|
||||||
return ( ( ( meta !== null ) && meta.implemented )
|
|
||||||
|| ( obj.prototype instanceof ClassBuilder.ClassBase ) )
|
|
||||||
? true
|
|
||||||
: false
|
|
||||||
;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -468,4 +468,36 @@ require( 'common' ).testCase(
|
||||||
// protected foo was recognized
|
// protected foo was recognized
|
||||||
this.assertOk( called );
|
this.assertOk( called );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a single argument is provided to `#extend' in the form
|
||||||
|
* `C.extend(o)', it is assumed that `o' is the definition
|
||||||
|
* object. However, it's also possible that the user provided the class
|
||||||
|
* to extend but _forgot_ the definition object.
|
||||||
|
*
|
||||||
|
* In this case, prior to this change, the supertype was treated as the
|
||||||
|
* definition object, which yielded some pretty confusing results. It's
|
||||||
|
* better to help the user out and provide a clear indication of what
|
||||||
|
* went wrong.
|
||||||
|
*/
|
||||||
|
'Extending with one argument that is a class yields an error': function()
|
||||||
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
|
// missing second argument
|
||||||
|
this.assertThrows( function()
|
||||||
|
{
|
||||||
|
_self.Sut.extend( _self.Sut( 'Whoops', {} ) );
|
||||||
|
}, TypeError );
|
||||||
|
|
||||||
|
// class provided as second argument (>_>)
|
||||||
|
this.assertThrows( function()
|
||||||
|
{
|
||||||
|
_self.Sut.extend(
|
||||||
|
_self.Sut( 'Base', {} ),
|
||||||
|
_self.Sut( 'Wth', {} )
|
||||||
|
);
|
||||||
|
}, TypeError );
|
||||||
|
}
|
||||||
} );
|
} );
|
||||||
|
|
Loading…
Reference in New Issue