1
0
Fork 0

Merge branch 'master' into visibility/master

closure/master
Mike Gerwitz 2011-03-04 00:26:02 -05:00
commit dca7653adf
4 changed files with 223 additions and 53 deletions

View File

@ -53,6 +53,7 @@ var class_instance = {};
* use the class's extend() method. If unavailable (or extending a non-ease.js * use the class's extend() method. If unavailable (or extending a non-ease.js
* class/object), use the module's extend() method. * class/object), use the module's extend() method.
* *
* @param {string=} name optional name
* @param {Object} def class definition * @param {Object} def class definition
* *
* @return {Class} new class * @return {Class} new class
@ -60,16 +61,19 @@ var class_instance = {};
module.exports = function() module.exports = function()
{ {
var def = {}, var def = {},
name = ''; name = '',
type = typeof arguments[ 0 ]
;
// anonymous class switch ( type )
if ( typeof arguments[ 0 ] === 'object' )
{ {
// anonymous class
case 'object':
def = arguments[ 0 ]; def = arguments[ 0 ];
// ensure we have the proper number of arguments (if they passed in too // ensure we have the proper number of arguments (if they passed in
// many, it may signify that they don't know what they're doing, and likely // too many, it may signify that they don't know what they're doing,
// they're not getting the result they're looking for) // and likely they're not getting the result they're looking for)
if ( arguments.length > 1 ) if ( arguments.length > 1 )
{ {
throw Error( throw Error(
@ -77,10 +81,11 @@ module.exports = function()
arguments.length + " given." arguments.length + " given."
); );
} }
}
break;
// named class // named class
else if ( typeof arguments[ 0 ] === 'string' ) case 'string':
{
name = arguments[ 0 ]; name = arguments[ 0 ];
def = arguments[ 1 ]; def = arguments[ 1 ];
@ -90,11 +95,14 @@ module.exports = function()
// the definition must be an object // the definition must be an object
if ( typeof def !== 'object' ) if ( typeof def !== 'object' )
{ {
throw TypeError( "Unexpected value for named class definition" ); throw TypeError(
"Unexpected value for named class definition"
);
} }
}
else break;
{
default:
// we don't know what to do! // we don't know what to do!
throw TypeError( throw TypeError(
"Expecting anonymous class definition or named class definition" "Expecting anonymous class definition or named class definition"

View File

@ -35,23 +35,27 @@ var util = require( './util' ),
* extended. To extend an existing interface, call its extend() method, or use * extended. To extend an existing interface, call its extend() method, or use
* the extend() method of this module. * the extend() method of this module.
* *
* @param {string=} name optional name
* @param {Object} def interface definition * @param {Object} def interface definition
* *
* @return {Interface} new interface * @return {Interface} new interface
*/ */
module.exports = function( def ) module.exports = function()
{ {
// if the first argument is an object, then we are declaring an interface var def = {},
if ( typeof def !== 'object' ) name = '',
{ type = typeof arguments[ 0 ]
throw TypeError( ;
"Must provide interface definition when declaring interface"
);
}
// ensure we have the proper number of arguments (if they passed in too switch ( type )
// 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) // anonymous interface
case 'object':
def = arguments[ 0 ];
// 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 ) if ( arguments.length > 1 )
{ {
throw Error( throw Error(
@ -60,6 +64,26 @@ module.exports = function( def )
); );
} }
break;
// named class
case 'string':
name = arguments[ 0 ];
def = arguments[ 1 ];
// add the name to the definition
def.__name = name;
break;
default:
// we don't know what to do!
throw TypeError(
"Expecting anonymous interface definition or named " +
"interface definition"
);
}
return extend( def ); return extend( def );
}; };
@ -75,6 +99,26 @@ module.exports.extend = function()
}; };
/**
* Determines whether the provided object is an interface created through
* ease.js
*
* @param {Object} obj object to test
*
* @return {boolean} true if interface (created through ease.js), otherwise
* false
*/
module.exports.isInterface = function( obj )
{
obj = obj || {};
return ( obj.prototype instanceof Interface )
? true
: false
;
};
/** /**
* Default interface implementation * Default interface implementation
* *
@ -94,12 +138,20 @@ var extend = ( function( extending )
props = args.pop() || {}, props = args.pop() || {},
base = args.pop() || Interface, base = args.pop() || Interface,
prototype = new base(), prototype = new base(),
iname = '',
members = member_builder.initMembers( members = member_builder.initMembers(
prototype, prototype, prototype prototype, prototype, prototype
) )
; ;
// grab the name, if one was provided
if ( iname = props.__name )
{
// we no longer need it
delete props.__name;
}
// sanity check // sanity check
inheritCheck( prototype ); inheritCheck( prototype );
@ -145,6 +197,8 @@ var extend = ( function( extending )
} ); } );
attachExtend( new_interface ); attachExtend( new_interface );
attachStringMethod( new_interface, iname );
new_interface.prototype = prototype; new_interface.prototype = prototype;
new_interface.constructor = new_interface; new_interface.constructor = new_interface;
@ -238,3 +292,20 @@ function attachExtend( func )
}); });
} }
/**
* Provides more sane/useful output when interface is converted to a string
*
* @param {Object} func interface
* @param {string=} iname interface name
*
* @return {undefined}
*/
function attachStringMethod( func, iname )
{
func.toString = ( iname )
? function() { return '[object Interface <' + iname + '>]'; }
: function() { return '[object Interface]'; }
;
}

View File

@ -105,6 +105,12 @@ for ( var i = 0; i < base_types.length; i++ )
"Interface contains defined abstract methods" "Interface contains defined abstract methods"
); );
assert.equal(
Interface.isInterface( BaseType ),
true,
"Interface is considered to be an interface"
);
var SubType = Interface.extend( BaseType, var SubType = Interface.extend( BaseType,
{ {

View File

@ -0,0 +1,85 @@
/**
* Tests interface 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' ),
Interface = common.require( 'interface' )
;
/**
* Interfaces may be named by passing the name as the first argument to the
* module
*/
( function testInterfaceAcceptsName()
{
assert.doesNotThrow( function()
{
var iface = Interface( 'Foo', {} );
assert.equal(
Interface.isInterface( iface ),
true,
"Interface defined with name is returned as a valid interface"
);
}, Error, "Interface accepts name" );
// the second argument must be an object
assert.throws( function()
{
Interface( 'Foo', 'Bar' );
}, TypeError, "Second argument to named interface must be the definition" );
} )();
/**
* By default, anonymous interfacees should simply state that they are a
* interface when they are converted to a string
*/
( function testConvertingAnonymousInterfaceToStringYieldsInterfaceString()
{
assert.equal(
Interface( {} ).toString(),
'[object Interface]',
"Converting anonymous interface to string yields interface string"
);
} )();
/**
* If the interface is named, then the name should be presented when it is
* converted to a string
*/
( function testConvertingNamedInterfaceToStringYieldsInterfaceStringContainingName()
{
var name = 'Foo';
assert.equal(
Interface( name, {} ).toString(),
'[object Interface <' + name + '>]',
"Converting named interface to string yields string with name of " +
"interface"
);
} )();