Named trait staging object
The syntax Trait( "Name" ) now works as Class( "Name" ), permitting implementing interfaces and extending from the staged object.newmaster
parent
bb98c9afb3
commit
fd404fc69f
72
lib/Trait.js
72
lib/Trait.js
|
@ -36,17 +36,22 @@ function Trait()
|
||||||
{
|
{
|
||||||
switch ( arguments.length )
|
switch ( arguments.length )
|
||||||
{
|
{
|
||||||
|
case 0:
|
||||||
|
throw Error( "Missing trait name or definition" );
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return Trait.extend.apply( this, arguments );
|
return ( typeof arguments[ 0 ] === 'string' )
|
||||||
break;
|
? _createStaging.apply( this, arguments )
|
||||||
|
: Trait.extend.apply( this, arguments );
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
return createNamedTrait.apply( this, arguments );
|
return createNamedTrait.apply( this, arguments );
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw Error( "Missing trait name or definition" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw Error(
|
||||||
|
"Expecting at most two arguments for definition of named " +
|
||||||
|
"Trait " + name + "'; " + arguments.length + " given"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,14 +65,6 @@ function Trait()
|
||||||
*/
|
*/
|
||||||
function createNamedTrait( name, dfn )
|
function createNamedTrait( name, dfn )
|
||||||
{
|
{
|
||||||
if ( arguments.length > 2 )
|
|
||||||
{
|
|
||||||
throw Error(
|
|
||||||
"Expecting at most two arguments for definition of named " +
|
|
||||||
"Trait " + name + "'; " + arguments.length + " given"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( typeof name !== 'string' )
|
if ( typeof name !== 'string' )
|
||||||
{
|
{
|
||||||
throw Error(
|
throw Error(
|
||||||
|
@ -76,11 +73,26 @@ function createNamedTrait( name, dfn )
|
||||||
}
|
}
|
||||||
|
|
||||||
dfn.__name = name;
|
dfn.__name = name;
|
||||||
|
|
||||||
return Trait.extend( dfn );
|
return Trait.extend( dfn );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _createStaging( name )
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
extend: function( dfn )
|
||||||
|
{
|
||||||
|
return createNamedTrait( name, dfn );
|
||||||
|
},
|
||||||
|
|
||||||
|
implement: function()
|
||||||
|
{
|
||||||
|
return createImplement( arguments, name );
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Trait.extend = function( dfn )
|
Trait.extend = function( dfn )
|
||||||
{
|
{
|
||||||
// we may have been passed some additional metadata
|
// we may have been passed some additional metadata
|
||||||
|
@ -244,19 +256,37 @@ function _parseGetSet( name, value, keywords, h )
|
||||||
*/
|
*/
|
||||||
Trait.implement = function()
|
Trait.implement = function()
|
||||||
{
|
{
|
||||||
var ifaces = arguments;
|
return createImplement( arguments );
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
|
||||||
extend: function()
|
/**
|
||||||
|
* Create a staging object from which a trait implementing a set of
|
||||||
|
* interfaces may be defined
|
||||||
|
*
|
||||||
|
* @param {...Function} interfaces interfaces to implement
|
||||||
|
* @param {string=} name optional trait name
|
||||||
|
*
|
||||||
|
* @return {Object} staged trait object
|
||||||
|
*/
|
||||||
|
function createImplement( ifaces, name )
|
||||||
{
|
{
|
||||||
|
return {
|
||||||
|
extend: function( dfn )
|
||||||
|
{
|
||||||
|
if ( name )
|
||||||
|
{
|
||||||
|
dfn.__name = name;
|
||||||
|
}
|
||||||
|
|
||||||
// pass our interface metadata as the invocation context
|
// pass our interface metadata as the invocation context
|
||||||
return Trait.extend.apply(
|
return Trait.extend.call(
|
||||||
{ __$$meta: { ifaces: ifaces } },
|
{ __$$meta: { ifaces: ifaces } },
|
||||||
arguments
|
dfn
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Tests named trait definitions
|
* Tests named trait definitions
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Free Software Foundation, Inc.
|
* Copyright (C) 2014 Mike Gerwitz
|
||||||
*
|
*
|
||||||
* This file is part of GNU ease.js.
|
* This file is part of GNU ease.js.
|
||||||
*
|
*
|
||||||
|
@ -25,6 +25,7 @@ require( 'common' ).testCase(
|
||||||
{
|
{
|
||||||
this.Sut = this.require( 'Trait' );
|
this.Sut = this.require( 'Trait' );
|
||||||
this.Class = this.require( 'class' );
|
this.Class = this.require( 'class' );
|
||||||
|
this.Interface = this.require( 'interface' );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,4 +85,86 @@ require( 'common' ).testCase(
|
||||||
Sut( {}, {} );
|
Sut( {}, {} );
|
||||||
} );
|
} );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just as is the case with classes, providing only a name for the trait
|
||||||
|
* should create a staging object with which subsequent calls may be
|
||||||
|
* chained, just as if those calls were made on Trait directly. The
|
||||||
|
* difference is that the name shall propagate.
|
||||||
|
*/
|
||||||
|
'Providing only trait name creates staging object': function()
|
||||||
|
{
|
||||||
|
var Sut = this.Sut;
|
||||||
|
this.assertDoesNotThrow( function()
|
||||||
|
{
|
||||||
|
// this does not create a trait, but it should be acceptable
|
||||||
|
// just as Class( "Foo" ) is
|
||||||
|
Sut( "Foo" );
|
||||||
|
} );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The named trait staging object should permit direct extension using
|
||||||
|
* an extend method, which should do the same thing as Trait.extend.
|
||||||
|
*/
|
||||||
|
'Can extend named trait staging object': function()
|
||||||
|
{
|
||||||
|
var Sut = this.Sut,
|
||||||
|
expected = {},
|
||||||
|
name = "Foo",
|
||||||
|
T = null;
|
||||||
|
|
||||||
|
this.assertDoesNotThrow( function()
|
||||||
|
{
|
||||||
|
// this does not create a trait, but it should be acceptable
|
||||||
|
// just as Class( "Foo" ) is
|
||||||
|
T = Sut( name )
|
||||||
|
.extend( { foo: function() { return expected; } } );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ensure that extending worked as expected
|
||||||
|
this.assertStrictEqual(
|
||||||
|
this.Class( {} ).use( T )().foo(),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
|
||||||
|
// ensure that trait was properly named
|
||||||
|
this.assertOk( T.toString().match( name ) );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implement method on the named staging object should work just as
|
||||||
|
* Trait.implement.
|
||||||
|
*/
|
||||||
|
'Can implement interface using named trait staging object':
|
||||||
|
function()
|
||||||
|
{
|
||||||
|
|
||||||
|
var Sut = this.Sut,
|
||||||
|
expected = {},
|
||||||
|
name = "Foo",
|
||||||
|
I = this.Interface( {} ),
|
||||||
|
I2 = this.Interface( {} ),
|
||||||
|
T = null;
|
||||||
|
|
||||||
|
this.assertDoesNotThrow( function()
|
||||||
|
{
|
||||||
|
// this does not create a trait, but it should be acceptable
|
||||||
|
// just as Class( "Foo" ) is
|
||||||
|
T = Sut( "Foo" )
|
||||||
|
.implement( I, I2 )
|
||||||
|
.extend( {} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// ensure that implement worked as intended
|
||||||
|
var inst = this.Class( {} ).use( T )();
|
||||||
|
this.assertOk( this.Class.isA( I, inst ) );
|
||||||
|
this.assertOk( this.Class.isA( I2, inst ) );
|
||||||
|
|
||||||
|
// ensure that trait was properly named
|
||||||
|
this.assertOk( T.toString().match( name ) );
|
||||||
|
},
|
||||||
} );
|
} );
|
||||||
|
|
Loading…
Reference in New Issue