From 60920f18a61d7fdbf8782a9a9eae61bf8b068386 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Sat, 4 Nov 2017 01:07:06 -0400 Subject: [PATCH] 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. --- lib/ClassBuilder.js | 33 +++++++++++++++++++++++++++++++++ lib/class.js | 17 +---------------- test/Class/ExtendTest.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/lib/ClassBuilder.js b/lib/ClassBuilder.js index 439efc3..4d5430c 100644 --- a/lib/ClassBuilder.js +++ b/lib/ClassBuilder.js @@ -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 * @@ -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) if ( base.___$$final$$ === true ) { diff --git a/lib/class.js b/lib/class.js index 2f40caa..acec26d 100644 --- a/lib/class.js +++ b/lib/class.js @@ -171,26 +171,11 @@ var _dummyinst = { constructor: { prototype: {} } }; /** * Determines whether the provided object is a class created through ease.js * - * TODO: delegate to ClassBuilder - * * @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 = 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 - ; -}; +module.exports.isClass = ClassBuilder.isClass; /** diff --git a/test/Class/ExtendTest.js b/test/Class/ExtendTest.js index 1ffeac2..1b2cc16 100644 --- a/test/Class/ExtendTest.js +++ b/test/Class/ExtendTest.js @@ -468,4 +468,36 @@ require( 'common' ).testCase( // protected foo was recognized 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 ); + } } );