From 85e687e29c0d22893af2ed587d4abe0d3dc1aa8e Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Thu, 3 Mar 2011 23:59:37 -0500 Subject: [PATCH 1/4] Added Interface.isInterface() --- lib/interface.js | 20 ++++++++++++++++++++ test/test-interface-extend.js | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/lib/interface.js b/lib/interface.js index f9098f4..8cba127 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -75,6 +75,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 * diff --git a/test/test-interface-extend.js b/test/test-interface-extend.js index 06d68e7..2e542c7 100644 --- a/test/test-interface-extend.js +++ b/test/test-interface-extend.js @@ -105,6 +105,12 @@ for ( var i = 0; i < base_types.length; i++ ) "Interface contains defined abstract methods" ); + assert.equal( + Interface.isInterface( BaseType ), + true, + "Interface is considered to be an interface" + ); + var SubType = Interface.extend( BaseType, { From ec7b4e2b6bd5a3282470e12e74f2f2bcf7189c67 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Fri, 4 Mar 2011 00:17:25 -0500 Subject: [PATCH 2/4] Updated class module docblock --- lib/class.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/class.js b/lib/class.js index 9da0d7d..5f7b947 100644 --- a/lib/class.js +++ b/lib/class.js @@ -43,7 +43,8 @@ var class_meta = {}; * use the class's extend() method. If unavailable (or extending a non-ease.js * class/object), use the module's extend() method. * - * @param {Object} def class definition + * @param {string=} name optional name + * @param {Object} def class definition * * @return {Class} new class */ From 009c4a93e969d83cb1e97aef5bac17897ac03992 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Fri, 4 Mar 2011 00:19:02 -0500 Subject: [PATCH 3/4] Interfaces now have sane/useful values when converted to strings --- lib/interface.js | 77 ++++++++++++++++++++++++++------- test/test-interface-name.js | 85 +++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 test/test-interface-name.js diff --git a/lib/interface.js b/lib/interface.js index 8cba127..6e41e8d 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -35,28 +35,46 @@ var util = require( './util' ), * extended. To extend an existing interface, call its extend() method, or use * the extend() method of this module. * - * @param {Object} def interface definition + * @param {string=} name optional name + * @param {Object} def interface definition * * @return {Interface} new interface */ -module.exports = function( def ) +module.exports = function() { - // if the first argument is an object, then we are declaring an interface - if ( typeof def !== 'object' ) - { - throw TypeError( - "Must provide interface definition when declaring interface" - ); - } + var def = {}, + name = ''; - // 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 ) + // anonymous interface + if ( typeof arguments[ 0 ] === 'object' ) { - throw Error( - "Expecting one argument for Interface definition; " + - arguments.length + " given." + 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 ) + { + throw Error( + "Expecting one argument for Interface 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; + } + else + { + // we don't know what to do! + throw TypeError( + "Expecting anonymous interface definition or named interface definition" ); } @@ -114,12 +132,20 @@ var extend = ( function( extending ) props = args.pop() || {}, base = args.pop() || Interface, prototype = new base(), + iname = '', members = member_builder.initMembers( prototype, prototype, prototype ) ; + // grab the name, if one was provided + if ( iname = props.__name ) + { + // we no longer need it + delete props.__name; + } + // sanity check inheritCheck( prototype ); @@ -165,6 +191,8 @@ var extend = ( function( extending ) } ); attachExtend( new_interface ); + attachStringMethod( new_interface, iname ); + new_interface.prototype = prototype; new_interface.constructor = new_interface; @@ -258,3 +286,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]'; } + ; +} + diff --git a/test/test-interface-name.js b/test/test-interface-name.js new file mode 100644 index 0000000..cfe5689 --- /dev/null +++ b/test/test-interface-name.js @@ -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 . + * + * @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" + ); +} )(); + From 7bb87e370f32b5d643b82b172955fee26ed4bd89 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Fri, 4 Mar 2011 00:24:42 -0500 Subject: [PATCH 4/4] No need to recheck the type each time --- lib/class.js | 77 ++++++++++++++++++++++++++---------------------- lib/interface.js | 64 ++++++++++++++++++++++------------------ 2 files changed, 77 insertions(+), 64 deletions(-) diff --git a/lib/class.js b/lib/class.js index 5f7b947..7ef56f6 100644 --- a/lib/class.js +++ b/lib/class.js @@ -51,45 +51,52 @@ var class_meta = {}; module.exports = function() { var def = {}, - name = ''; + name = '', + type = typeof arguments[ 0 ] + ; - // anonymous class - if ( typeof arguments[ 0 ] === 'object' ) + switch ( type ) { - def = arguments[ 0 ]; + // anonymous class + 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 ) - { - throw Error( - "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." + ); + } + + break; + + // named class + case '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" + ); + } + + break; + + default: + // we don't know what to do! + throw TypeError( + "Expecting anonymous class definition or named class definition" ); - } - } - // 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" - ); } return extend( def ); diff --git a/lib/interface.js b/lib/interface.js index 6e41e8d..1ae2ce3 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -43,39 +43,45 @@ var util = require( './util' ), module.exports = function() { var def = {}, - name = ''; + name = '', + type = typeof arguments[ 0 ] + ; - // anonymous interface - if ( typeof arguments[ 0 ] === 'object' ) + switch ( type ) { - def = arguments[ 0 ]; + // 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 ) - { - throw Error( - "Expecting one argument for Interface 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 Interface definition; " + + arguments.length + " given." + ); + } + + 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" ); - } - } - // named class - else if ( typeof arguments[ 0 ] === 'string' ) - { - name = arguments[ 0 ]; - def = arguments[ 1 ]; - - // add the name to the definition - def.__name = name; - } - else - { - // we don't know what to do! - throw TypeError( - "Expecting anonymous interface definition or named interface definition" - ); } return extend( def );