Merge branch 'master' into visibility/master
commit
635395b303
1
TODO
1
TODO
|
@ -14,6 +14,7 @@ Misc
|
||||||
cannot be used (# is not a valid token)
|
cannot be used (# is not a valid token)
|
||||||
- Class/Interface naming
|
- Class/Interface naming
|
||||||
- Will be especially helpful for error messages
|
- Will be especially helpful for error messages
|
||||||
|
- Class module is becoming too large; refactor
|
||||||
|
|
||||||
Property Keywords
|
Property Keywords
|
||||||
- Restrictions; throw exceptions when unknown keywords are used
|
- Restrictions; throw exceptions when unknown keywords are used
|
||||||
|
|
204
lib/class.js
204
lib/class.js
|
@ -60,46 +60,20 @@ var class_instance = {};
|
||||||
*/
|
*/
|
||||||
module.exports = function()
|
module.exports = function()
|
||||||
{
|
{
|
||||||
var def = {},
|
var type = typeof arguments[ 0 ],
|
||||||
name = '',
|
result = null
|
||||||
type = typeof arguments[ 0 ]
|
|
||||||
;
|
;
|
||||||
|
|
||||||
switch ( type )
|
switch ( type )
|
||||||
{
|
{
|
||||||
// anonymous class
|
// anonymous class
|
||||||
case 'object':
|
case 'object':
|
||||||
def = arguments[ 0 ];
|
result = createAnonymousClass.apply( null, arguments );
|
||||||
|
|
||||||
// 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;
|
break;
|
||||||
|
|
||||||
// named class
|
// named class
|
||||||
case 'string':
|
case 'string':
|
||||||
name = arguments[ 0 ];
|
result = createNamedClass.apply( null, arguments );
|
||||||
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -109,7 +83,7 @@ module.exports = function()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return extend( def );
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,12 +110,11 @@ module.exports.extend = function( base )
|
||||||
*/
|
*/
|
||||||
module.exports.implement = function()
|
module.exports.implement = function()
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments );
|
// implement on empty base
|
||||||
|
return createImplement(
|
||||||
// apply to an empty (new) object
|
module.exports.extend(),
|
||||||
args.unshift( module.exports.extend() );
|
Array.prototype.slice.call( arguments )
|
||||||
|
);
|
||||||
return implement.apply( this, args );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -251,6 +224,151 @@ module.exports.isA = module.exports.isInstanceOf;
|
||||||
function Class() {};
|
function Class() {};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new anonymous Class from the given class definition
|
||||||
|
*
|
||||||
|
* @param {Object} def class definition
|
||||||
|
*
|
||||||
|
* @return {Class} new anonymous class
|
||||||
|
*/
|
||||||
|
function createAnonymousClass( def )
|
||||||
|
{
|
||||||
|
// 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 anonymous Class definition; " +
|
||||||
|
arguments.length + " given."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return extend( def );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new named Class from the given class definition
|
||||||
|
*
|
||||||
|
* @param {string} name class name
|
||||||
|
* @param {Object} def class definition
|
||||||
|
*
|
||||||
|
* @return {Class} new named class
|
||||||
|
*/
|
||||||
|
function createNamedClass( name, def )
|
||||||
|
{
|
||||||
|
// if too many arguments were provided, it's likely that they're
|
||||||
|
// expecting some result that they're not going to get
|
||||||
|
if ( arguments.length > 2 )
|
||||||
|
{
|
||||||
|
throw Error(
|
||||||
|
"Expecting two arguments for named Class definition; " +
|
||||||
|
arguments.length + " given."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no definition was given, return a staging object, to apply the name to
|
||||||
|
// the class once it is actually created
|
||||||
|
if ( def === undefined )
|
||||||
|
{
|
||||||
|
return createStaging( name );
|
||||||
|
}
|
||||||
|
// the definition must be an object
|
||||||
|
else if ( typeof def !== 'object' )
|
||||||
|
{
|
||||||
|
throw TypeError(
|
||||||
|
"Unexpected value for named class definition; object expected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the name to the definition
|
||||||
|
def.__name = name;
|
||||||
|
|
||||||
|
return extend( def );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a staging object to stage a class name
|
||||||
|
*
|
||||||
|
* The class name will be applied to the class generated by operations performed
|
||||||
|
* on the staging object. This allows applying names to classes that need to be
|
||||||
|
* extended or need to implement interfaces.
|
||||||
|
*
|
||||||
|
* @param {string} cname desired class name
|
||||||
|
*
|
||||||
|
* @return {Object} object staging the given class name
|
||||||
|
*/
|
||||||
|
function createStaging( cname )
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
extend: function()
|
||||||
|
{
|
||||||
|
var args = Array.prototype.slice.apply( arguments );
|
||||||
|
|
||||||
|
// extend() takes a maximum of two arguments. If only one
|
||||||
|
// argument is provided, then it is to be the class definition.
|
||||||
|
// Otherwise, the first argument is the supertype and the second
|
||||||
|
// argument is the class definition. Either way you look at it,
|
||||||
|
// the class definition is always the final argument.
|
||||||
|
//
|
||||||
|
// We want to add the name to the definition.
|
||||||
|
args[ args.length - 1 ].__name = cname;
|
||||||
|
|
||||||
|
return extend.apply( null, args );
|
||||||
|
},
|
||||||
|
|
||||||
|
implement: function()
|
||||||
|
{
|
||||||
|
// implement on empty base, providing the class name to be used once
|
||||||
|
// extended
|
||||||
|
return createImplement(
|
||||||
|
extend( {} ),
|
||||||
|
Array.prototype.slice.call( arguments ),
|
||||||
|
cname
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an intermediate object to permit implementing interfaces
|
||||||
|
*
|
||||||
|
* This object defers processing until extend() is called. This intermediate
|
||||||
|
* object ensures that a usable class is not generated until after extend() is
|
||||||
|
* called, as it does not make sense to create a class without any
|
||||||
|
* body/definition.
|
||||||
|
*
|
||||||
|
* @param {Object} base base class to implement atop of
|
||||||
|
* @param {Array} ifaces interfaces to implement
|
||||||
|
* @param {string=} cname optional class name once extended
|
||||||
|
*
|
||||||
|
* @return {Object} intermediate implementation object
|
||||||
|
*/
|
||||||
|
function createImplement( base, ifaces, cname )
|
||||||
|
{
|
||||||
|
ifaces.push( base );
|
||||||
|
|
||||||
|
// Defer processing until after extend(). This also ensures that implement()
|
||||||
|
// returns nothing usable.
|
||||||
|
return {
|
||||||
|
extend: function( def )
|
||||||
|
{
|
||||||
|
// if a name was provided, use it
|
||||||
|
if ( cname )
|
||||||
|
{
|
||||||
|
def.__name = cname;
|
||||||
|
}
|
||||||
|
|
||||||
|
return extend.apply( null, [
|
||||||
|
implement.apply( this, ifaces ),
|
||||||
|
def
|
||||||
|
] );
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -519,7 +637,7 @@ var implement = function()
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments ),
|
var args = Array.prototype.slice.call( arguments ),
|
||||||
dest = {},
|
dest = {},
|
||||||
base = args.shift(),
|
base = args.pop(),
|
||||||
len = args.length,
|
len = args.length,
|
||||||
arg = null,
|
arg = null,
|
||||||
|
|
||||||
|
@ -730,6 +848,8 @@ function attachExtend( func )
|
||||||
/**
|
/**
|
||||||
* Attaches implement method to the given function (class)
|
* Attaches implement method to the given function (class)
|
||||||
*
|
*
|
||||||
|
* Please see the implement() export of this module for more information.
|
||||||
|
*
|
||||||
* @param {function()} func function (class) to attach method to
|
* @param {function()} func function (class) to attach method to
|
||||||
*
|
*
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
|
@ -738,10 +858,10 @@ function attachImplement( func )
|
||||||
{
|
{
|
||||||
util.defineSecureProp( func, 'implement', function()
|
util.defineSecureProp( func, 'implement', function()
|
||||||
{
|
{
|
||||||
var args = Array.prototype.slice.call( arguments );
|
return createImplement(
|
||||||
args.unshift( func );
|
func,
|
||||||
|
Array.prototype.slice.call( arguments )
|
||||||
return implement.apply( this, args );
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,38 +42,20 @@ var util = require( './util' ),
|
||||||
*/
|
*/
|
||||||
module.exports = function()
|
module.exports = function()
|
||||||
{
|
{
|
||||||
var def = {},
|
var type = typeof arguments[ 0 ]
|
||||||
name = '',
|
result = null
|
||||||
type = typeof arguments[ 0 ]
|
|
||||||
;
|
;
|
||||||
|
|
||||||
switch ( type )
|
switch ( type )
|
||||||
{
|
{
|
||||||
// anonymous interface
|
// anonymous interface
|
||||||
case 'object':
|
case 'object':
|
||||||
def = arguments[ 0 ];
|
result = createAnonymousInterface.apply( null, arguments );
|
||||||
|
|
||||||
// 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;
|
break;
|
||||||
|
|
||||||
// named class
|
// named class
|
||||||
case 'string':
|
case 'string':
|
||||||
name = arguments[ 0 ];
|
result = createNamedInterface.apply( null, arguments );
|
||||||
def = arguments[ 1 ];
|
|
||||||
|
|
||||||
// add the name to the definition
|
|
||||||
def.__name = name;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -84,7 +66,7 @@ module.exports = function()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return extend( def );
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,6 +109,65 @@ module.exports.isInterface = function( obj )
|
||||||
function Interface() {}
|
function Interface() {}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new anonymous Interface from the given interface definition
|
||||||
|
*
|
||||||
|
* @param {Object} def interface definition
|
||||||
|
*
|
||||||
|
* @return {Interface} new anonymous interface
|
||||||
|
*/
|
||||||
|
function createAnonymousInterface( def )
|
||||||
|
{
|
||||||
|
// 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."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return extend( def );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new named interface from the given interface definition
|
||||||
|
*
|
||||||
|
* @param {string} name interface name
|
||||||
|
* @param {Object} def interface definition
|
||||||
|
*
|
||||||
|
* @return {Interface} new named interface
|
||||||
|
*/
|
||||||
|
function createNamedInterface( name, def )
|
||||||
|
{
|
||||||
|
// if too many arguments were provided, it's likely that they're
|
||||||
|
// expecting some result that they're not going to get
|
||||||
|
if ( arguments.length > 2 )
|
||||||
|
{
|
||||||
|
throw Error(
|
||||||
|
"Expecting two arguments for named Interface definition; " +
|
||||||
|
arguments.length + " given."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the definition must be an object
|
||||||
|
if ( typeof def !== 'object' )
|
||||||
|
{
|
||||||
|
throw TypeError(
|
||||||
|
"Unexpected value for named class definition; object expected"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the name to the definition
|
||||||
|
def.__name = name;
|
||||||
|
|
||||||
|
return extend( def );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var extend = ( function( extending )
|
var extend = ( function( extending )
|
||||||
{
|
{
|
||||||
return function extend()
|
return function extend()
|
||||||
|
|
|
@ -64,19 +64,41 @@ var Type = Interface.extend( {
|
||||||
{
|
{
|
||||||
assert.doesNotThrow( function()
|
assert.doesNotThrow( function()
|
||||||
{
|
{
|
||||||
Foo = Class.implement( Type, Type2 );
|
Class.implement( Type, Type2 );
|
||||||
}, Error, "Class can implement interfaces" );
|
}, Error, "Class can implement interfaces" );
|
||||||
|
} )();
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
( Class.isClass( Foo ) ),
|
/**
|
||||||
"Class returned from implementing interfaces on an empty base is a " +
|
* Initially, the implement() method returned an abstract class. However, it
|
||||||
"valid class"
|
* doesn't make sense to create a class without any actual definition (and
|
||||||
|
* there's other implementation considerations that caused this route to be
|
||||||
|
* taken). One wouldn't do "class Foo implements Type", and not provide any
|
||||||
|
* body.
|
||||||
|
*
|
||||||
|
* Therefore, implement() should return nothing useful until extend() is called
|
||||||
|
* on it.
|
||||||
|
*/
|
||||||
|
( function testResultOfImplementIsNotUsableAsAClass()
|
||||||
|
{
|
||||||
|
var result = Class.implement( Type );
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
( Class.isClass( result ) ),
|
||||||
|
false,
|
||||||
|
"Result of implement operation on class is not usable as a Class"
|
||||||
);
|
);
|
||||||
} )();
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As a consequence of the above, we must extend with an empty definition
|
||||||
|
* (base) in order to get our abstract class.
|
||||||
|
*/
|
||||||
( function testAbstractMethodsCopiedIntoNewClassUsingEmptyBase()
|
( function testAbstractMethodsCopiedIntoNewClassUsingEmptyBase()
|
||||||
{
|
{
|
||||||
|
Foo = Class.implement( Type, Type2 ).extend( {} );
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
( ( Foo.prototype.foo instanceof Function )
|
( ( Foo.prototype.foo instanceof Function )
|
||||||
&& ( Foo.prototype.foo2 instanceof Function )
|
&& ( Foo.prototype.foo2 instanceof Function )
|
||||||
|
@ -90,19 +112,32 @@ var Type = Interface.extend( {
|
||||||
{
|
{
|
||||||
assert.doesNotThrow( function()
|
assert.doesNotThrow( function()
|
||||||
{
|
{
|
||||||
PlainFoo2 = PlainFoo.implement( Type, Type2 );
|
PlainFoo.implement( Type, Type2 );
|
||||||
}, Error, "Classes can implement interfaces" );
|
}, Error, "Classes can implement interfaces" );
|
||||||
|
} )();
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
( Class.isClass( PlainFoo2 ) ),
|
/**
|
||||||
"Class returned from implementing interfaces on an existing base is a " +
|
* Ensure the same system mentioned above also applies to the extend() method on
|
||||||
"valid class"
|
* existing classes
|
||||||
|
*/
|
||||||
|
( function testImplementingInterfaceAtopExistingClassIsNotUsableByDefault()
|
||||||
|
{
|
||||||
|
var result = PlainFoo.implement( Type );
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
( Class.isClass( result ) ),
|
||||||
|
false,
|
||||||
|
"Result of implementing interfaces on an existing base is not " +
|
||||||
|
"usable as a Class"
|
||||||
);
|
);
|
||||||
} )();
|
} )();
|
||||||
|
|
||||||
|
|
||||||
( function testAbstractMethodsCopiedIntoNewClassUsingExistingBase()
|
( function testAbstractMethodsCopiedIntoNewClassUsingExistingBase()
|
||||||
{
|
{
|
||||||
|
PlainFoo2 = PlainFoo.implement( Type, Type2 ).extend( {} );
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
( ( PlainFoo2.prototype.foo instanceof Function )
|
( ( PlainFoo2.prototype.foo instanceof Function )
|
||||||
&& ( PlainFoo2.prototype.foo2 instanceof Function )
|
&& ( PlainFoo2.prototype.foo2 instanceof Function )
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
|
|
||||||
var common = require( './common' ),
|
var common = require( './common' ),
|
||||||
assert = require( 'assert' ),
|
assert = require( 'assert' ),
|
||||||
Class = common.require( 'class' )
|
|
||||||
|
Class = common.require( 'class' ),
|
||||||
|
Interface = common.require( 'interface' )
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,6 +51,27 @@ var common = require( './common' ),
|
||||||
{
|
{
|
||||||
Class( 'Foo', 'Bar' );
|
Class( 'Foo', 'Bar' );
|
||||||
}, TypeError, "Second argument to named class must be the definition" );
|
}, TypeError, "Second argument to named class must be the definition" );
|
||||||
|
|
||||||
|
// we should be permitted only two arguments
|
||||||
|
var args = [ 'Foo', {}, 'extra' ];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Class.apply( null, args );
|
||||||
|
|
||||||
|
// we should not get to this line (an exception should be thrown due to
|
||||||
|
// too many arguments)
|
||||||
|
assert.fail(
|
||||||
|
"Should accept only two arguments when creating named class"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch ( e )
|
||||||
|
{
|
||||||
|
assert.notEqual(
|
||||||
|
e.toString().match( args.length + ' given' ),
|
||||||
|
null,
|
||||||
|
"Named class error should provide number of given arguments"
|
||||||
|
);
|
||||||
|
}
|
||||||
} )();
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,3 +151,48 @@ var common = require( './common' ),
|
||||||
);
|
);
|
||||||
} )();
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In order to accommodate syntax such as extending classes, ease.js supports
|
||||||
|
* staging class names. This will return an object that operates exactly like
|
||||||
|
* the normal Class module, but will result in a named class once the class is
|
||||||
|
* created.
|
||||||
|
*/
|
||||||
|
( function testCanCreateNamedClassUsingStagingMethod()
|
||||||
|
{
|
||||||
|
var name = 'Foo',
|
||||||
|
named = Class( name ).extend( {} )
|
||||||
|
namedi = Class( name ).implement( Interface( {} ) ).extend( {} )
|
||||||
|
;
|
||||||
|
|
||||||
|
// ensure what was returned is a valid class
|
||||||
|
assert.equal(
|
||||||
|
Class.isClass( named ),
|
||||||
|
true,
|
||||||
|
"Named class generated via staging method is considered to be a " +
|
||||||
|
"valid class"
|
||||||
|
);
|
||||||
|
|
||||||
|
// was the name set?
|
||||||
|
assert.equal(
|
||||||
|
named.toString(),
|
||||||
|
'[object Class <' + name + '>]',
|
||||||
|
"Name is set on named clas via staging method"
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// we should also be able to implement interfaces
|
||||||
|
assert.equal(
|
||||||
|
Class.isClass( namedi ),
|
||||||
|
true,
|
||||||
|
"Named class generated via staging method, implementing an " +
|
||||||
|
"interface, is considered to be a valid class"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
namedi.toString(),
|
||||||
|
'[object Class <' + name + '>]',
|
||||||
|
"Name is set on named class via staging method when implementing"
|
||||||
|
);
|
||||||
|
} )();
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,27 @@ var common = require( './common' ),
|
||||||
{
|
{
|
||||||
Interface( 'Foo', 'Bar' );
|
Interface( 'Foo', 'Bar' );
|
||||||
}, TypeError, "Second argument to named interface must be the definition" );
|
}, TypeError, "Second argument to named interface must be the definition" );
|
||||||
|
|
||||||
|
// we should be permitted only two arguments
|
||||||
|
var args = [ 'Foo', {}, 'extra' ];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Interface.apply( null, args );
|
||||||
|
|
||||||
|
// we should not get to this line (an exception should be thrown due to
|
||||||
|
// too many arguments)
|
||||||
|
assert.fail(
|
||||||
|
"Should accept only two arguments when creating named interface"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch ( e )
|
||||||
|
{
|
||||||
|
assert.notEqual(
|
||||||
|
e.toString().match( args.length + ' given' ),
|
||||||
|
null,
|
||||||
|
"Named interface error should provide number of given arguments"
|
||||||
|
);
|
||||||
|
}
|
||||||
} )();
|
} )();
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue