parent
b96aaa35d9
commit
840a495017
88
lib/class.js
88
lib/class.js
|
@ -47,24 +47,47 @@ var class_meta = {};
|
||||||
*
|
*
|
||||||
* @return {Class} new class
|
* @return {Class} new class
|
||||||
*/
|
*/
|
||||||
module.exports = function( def )
|
module.exports = function()
|
||||||
{
|
{
|
||||||
// the class definition should be an object
|
var def = {},
|
||||||
if ( typeof def !== 'object' )
|
name = '';
|
||||||
{
|
|
||||||
throw TypeError(
|
|
||||||
"Must provide class definition when declaring a new class"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we have the proper number of arguments (if they passed in too
|
// anonymous class
|
||||||
// many, it may signify that they don't know what they're doing, and likely
|
if ( typeof arguments[ 0 ] === 'object' )
|
||||||
// they're not getting the result they're looking for)
|
|
||||||
if ( arguments.length > 1 )
|
|
||||||
{
|
{
|
||||||
throw Error(
|
def = arguments[ 0 ];
|
||||||
"Expecting one argument for Class definition; " +
|
|
||||||
arguments.length + " given."
|
// ensure we have the proper number of arguments (if they passed in too
|
||||||
|
// many, it may signify that they don't know what they're doing, and likely
|
||||||
|
// they're not getting the result they're looking for)
|
||||||
|
if ( arguments.length > 1 )
|
||||||
|
{
|
||||||
|
throw Error(
|
||||||
|
"Expecting one argument for Class definition; " +
|
||||||
|
arguments.length + " given."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// named class
|
||||||
|
else if ( typeof arguments[ 0 ] === 'string' )
|
||||||
|
{
|
||||||
|
name = arguments[ 0 ];
|
||||||
|
def = arguments[ 1 ];
|
||||||
|
|
||||||
|
// add the name to the definition
|
||||||
|
def.__name = name;
|
||||||
|
|
||||||
|
// the definition must be an object
|
||||||
|
if ( typeof def !== 'object' )
|
||||||
|
{
|
||||||
|
throw TypeError( "Unexpected value for named class definition" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we don't know what to do!
|
||||||
|
throw TypeError(
|
||||||
|
"Expecting anonymous class definition or named class definition"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +271,7 @@ var extend = ( function( extending )
|
||||||
props = args.pop() || {},
|
props = args.pop() || {},
|
||||||
base = args.pop() || Class,
|
base = args.pop() || Class,
|
||||||
prototype = new base(),
|
prototype = new base(),
|
||||||
|
cname = '',
|
||||||
|
|
||||||
hasOwn = Array.prototype.hasOwnProperty;
|
hasOwn = Array.prototype.hasOwnProperty;
|
||||||
|
|
||||||
|
@ -261,6 +285,13 @@ var extend = ( function( extending )
|
||||||
|| { __length: 0 }
|
|| { __length: 0 }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
// grab the name, if one was provided
|
||||||
|
if ( cname = props.__name )
|
||||||
|
{
|
||||||
|
// we no longer need it
|
||||||
|
delete props.__name;
|
||||||
|
}
|
||||||
|
|
||||||
util.propParse( props, {
|
util.propParse( props, {
|
||||||
each: function( name, value, keywords )
|
each: function( name, value, keywords )
|
||||||
{
|
{
|
||||||
|
@ -328,7 +359,7 @@ var extend = ( function( extending )
|
||||||
prototype.parent = base.prototype;
|
prototype.parent = base.prototype;
|
||||||
|
|
||||||
// set up the new class
|
// set up the new class
|
||||||
var new_class = createCtor( abstract_methods );
|
var new_class = createCtor( cname, abstract_methods );
|
||||||
|
|
||||||
attachPropInit( prototype, properties );
|
attachPropInit( prototype, properties );
|
||||||
|
|
||||||
|
@ -345,6 +376,7 @@ var extend = ( function( extending )
|
||||||
// create internal metadata for the new class
|
// create internal metadata for the new class
|
||||||
var meta = createMeta( new_class, base.prototype.__cid );
|
var meta = createMeta( new_class, base.prototype.__cid );
|
||||||
meta.abstractMethods = abstract_methods;
|
meta.abstractMethods = abstract_methods;
|
||||||
|
meta.name = cname;
|
||||||
|
|
||||||
// we're done with the extension process
|
// we're done with the extension process
|
||||||
extending = false;
|
extending = false;
|
||||||
|
@ -359,11 +391,12 @@ var extend = ( function( extending )
|
||||||
* This constructor will call the __constructor method for concrete classes
|
* This constructor will call the __constructor method for concrete classes
|
||||||
* and throw an exception for abstract classes (to prevent instantiation).
|
* and throw an exception for abstract classes (to prevent instantiation).
|
||||||
*
|
*
|
||||||
|
* @param {string} cname class name (may be empty)
|
||||||
* @param {Array.<string>} abstract_methods list of abstract methods
|
* @param {Array.<string>} abstract_methods list of abstract methods
|
||||||
*
|
*
|
||||||
* @return {Function} constructor
|
* @return {Function} constructor
|
||||||
*/
|
*/
|
||||||
function createCtor( abstract_methods )
|
function createCtor( cname, abstract_methods )
|
||||||
{
|
{
|
||||||
// concrete class
|
// concrete class
|
||||||
if ( abstract_methods.__length === 0 )
|
if ( abstract_methods.__length === 0 )
|
||||||
|
@ -398,10 +431,10 @@ var extend = ( function( extending )
|
||||||
};
|
};
|
||||||
|
|
||||||
// provide a more intuitive string representation
|
// provide a more intuitive string representation
|
||||||
__self.toString = function()
|
__self.toString = ( cname )
|
||||||
{
|
? function() { return '<class ' + cname + '>'; }
|
||||||
return '<Class>';
|
: function() { return '<Class>'; }
|
||||||
};
|
;
|
||||||
|
|
||||||
return __self;
|
return __self;
|
||||||
}
|
}
|
||||||
|
@ -412,15 +445,14 @@ var extend = ( function( extending )
|
||||||
{
|
{
|
||||||
if ( !extending )
|
if ( !extending )
|
||||||
{
|
{
|
||||||
throw new Error( "Abstract classes cannot be instantiated" );
|
throw Error( "Abstract classes cannot be instantiated" );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// provide a more intuitive string representation
|
__abstract_self.toString = ( cname )
|
||||||
__abstract_self.toString = function()
|
? function() { return '<abstract class ' + cname + '>'; }
|
||||||
{
|
: function() { return '<AbstractClass>'; }
|
||||||
return '<Abstract Class>';
|
;
|
||||||
};
|
|
||||||
|
|
||||||
return __abstract_self;
|
return __abstract_self;
|
||||||
}
|
}
|
||||||
|
@ -476,7 +508,7 @@ var implement = function()
|
||||||
/**
|
/**
|
||||||
* Sets up common properties for the provided function (class)
|
* Sets up common properties for the provided function (class)
|
||||||
*
|
*
|
||||||
* @param {Function} func function (class) to set up
|
* @param {function()} func function (class) to set up
|
||||||
* @param {Array.<string>} abstract_methods list of abstract method names
|
* @param {Array.<string>} abstract_methods list of abstract method names
|
||||||
* @param {number} class_id unique id to assign to class
|
* @param {number} class_id unique id to assign to class
|
||||||
*
|
*
|
||||||
|
|
|
@ -181,18 +181,6 @@ assert.throws( function()
|
||||||
}, TypeError, "Abstract methods must be declared as arrays" );
|
}, TypeError, "Abstract methods must be declared as arrays" );
|
||||||
|
|
||||||
|
|
||||||
// otherwise it'll output the internal constructor code, which is especially
|
|
||||||
// confusing since the user does not write it
|
|
||||||
( function testConvertingAbstractClassToStringYieldsClassString()
|
|
||||||
{
|
|
||||||
assert.equal(
|
|
||||||
Class.extend( { 'abstract foo': [] } ).toString(),
|
|
||||||
'<Abstract Class>',
|
|
||||||
"Converting abstract class to string yields class string"
|
|
||||||
);
|
|
||||||
} )();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There was an issue where the object holding the abstract methods list was not
|
* There was an issue where the object holding the abstract methods list was not
|
||||||
* checking for methods by using hasOwnProperty(). Therefore, if a method such
|
* checking for methods by using hasOwnProperty(). Therefore, if a method such
|
||||||
|
|
|
@ -244,18 +244,6 @@ for ( var i = 0; i < class_count; i++ )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// otherwise it'll output the internal constructor code, which is especially
|
|
||||||
// confusing since the user does not write it
|
|
||||||
( function testConvertingClassToStringYieldsClassString()
|
|
||||||
{
|
|
||||||
assert.equal(
|
|
||||||
Class.extend( {} ).toString(),
|
|
||||||
'<Class>',
|
|
||||||
"Converting class to string yields class string"
|
|
||||||
);
|
|
||||||
} )();
|
|
||||||
|
|
||||||
|
|
||||||
( function testInvokingClassModuleRequiresObjectAsArgumentIfCreating()
|
( function testInvokingClassModuleRequiresObjectAsArgumentIfCreating()
|
||||||
{
|
{
|
||||||
assert.throws( function()
|
assert.throws( function()
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* Tests class naming
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Mike Gerwitz
|
||||||
|
*
|
||||||
|
* This file is part of ease.js.
|
||||||
|
*
|
||||||
|
* ease.js is free software: you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Mike Gerwitz
|
||||||
|
* @package test
|
||||||
|
*/
|
||||||
|
|
||||||
|
var common = require( './common' ),
|
||||||
|
assert = require( 'assert' ),
|
||||||
|
Class = common.require( 'class' )
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classes may be named by passing the name as the first argument to the module
|
||||||
|
*/
|
||||||
|
( function testClassAcceptsName()
|
||||||
|
{
|
||||||
|
assert.doesNotThrow( function()
|
||||||
|
{
|
||||||
|
var cls = Class( 'Foo', {} );
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
Class.isClass( cls ),
|
||||||
|
true,
|
||||||
|
"Class defined with name is returned as a valid class"
|
||||||
|
);
|
||||||
|
}, Error, "Class accepts name" );
|
||||||
|
|
||||||
|
// the second argument must be an object
|
||||||
|
assert.throws( function()
|
||||||
|
{
|
||||||
|
Class( 'Foo', 'Bar' );
|
||||||
|
}, TypeError, "Second argument to named class must be the definition" );
|
||||||
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, anonymous classes should just state that they are a class when
|
||||||
|
* they are converted to a string
|
||||||
|
*/
|
||||||
|
( function testConvertingAnonymousClassToStringYieldsClassString()
|
||||||
|
{
|
||||||
|
// concrete
|
||||||
|
assert.equal(
|
||||||
|
Class( {} ).toString(),
|
||||||
|
'<Class>',
|
||||||
|
"Converting anonymous class to string yields class string"
|
||||||
|
);
|
||||||
|
|
||||||
|
// abstract
|
||||||
|
assert.equal(
|
||||||
|
Class( { 'abstract foo': [] } ).toString(),
|
||||||
|
'<AbstractClass>',
|
||||||
|
"Converting abstract anonymous class to string yields class string"
|
||||||
|
);
|
||||||
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the class is named, then the name should be presented when it is converted
|
||||||
|
* to a string
|
||||||
|
*/
|
||||||
|
( function testConvertingNamedClassToStringYieldsClassStringContainingName()
|
||||||
|
{
|
||||||
|
var name = 'Foo';
|
||||||
|
|
||||||
|
// concrete
|
||||||
|
assert.equal(
|
||||||
|
Class( name, {} ).toString(),
|
||||||
|
'<class ' + name + '>',
|
||||||
|
"Converting named class to string yields string with name of class"
|
||||||
|
);
|
||||||
|
|
||||||
|
// abstract
|
||||||
|
assert.equal(
|
||||||
|
Class( name, { 'abstract foo': [] } ).toString(),
|
||||||
|
'<abstract class ' + name + '>',
|
||||||
|
"Converting abstract named class to string yields string with name " +
|
||||||
|
"of class"
|
||||||
|
);
|
||||||
|
} )();
|
||||||
|
|
Loading…
Reference in New Issue