1
0
Fork 0

Named trait staging object

The syntax Trait( "Name" ) now works as Class( "Name" ), permitting
implementing interfaces and extending from the staged object.
newmaster
Mike Gerwitz 2014-04-26 00:42:28 -04:00
parent bb98c9afb3
commit fd404fc69f
2 changed files with 136 additions and 23 deletions

View File

@ -36,17 +36,22 @@ function Trait()
{
switch ( arguments.length )
{
case 0:
throw Error( "Missing trait name or definition" );
case 1:
return Trait.extend.apply( this, arguments );
break;
return ( typeof arguments[ 0 ] === 'string' )
? _createStaging.apply( this, arguments )
: Trait.extend.apply( this, arguments );
case 2:
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 )
{
if ( arguments.length > 2 )
{
throw Error(
"Expecting at most two arguments for definition of named " +
"Trait " + name + "'; " + arguments.length + " given"
);
}
if ( typeof name !== 'string' )
{
throw Error(
@ -76,11 +73,26 @@ function createNamedTrait( name, dfn )
}
dfn.__name = name;
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 )
{
// we may have been passed some additional metadata
@ -244,19 +256,37 @@ function _parseGetSet( name, value, keywords, h )
*/
Trait.implement = function()
{
var ifaces = arguments;
return createImplement( arguments );
};
/**
* 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()
extend: function( dfn )
{
if ( name )
{
dfn.__name = name;
}
// pass our interface metadata as the invocation context
return Trait.extend.apply(
return Trait.extend.call(
{ __$$meta: { ifaces: ifaces } },
arguments
dfn
);
},
};
};
}
/**

View File

@ -1,7 +1,7 @@
/**
* Tests named trait definitions
*
* Copyright (C) 2014 Free Software Foundation, Inc.
* Copyright (C) 2014 Mike Gerwitz
*
* This file is part of GNU ease.js.
*
@ -23,8 +23,9 @@ require( 'common' ).testCase(
{
caseSetUp: function()
{
this.Sut = this.require( 'Trait' );
this.Class = this.require( 'class' );
this.Sut = this.require( 'Trait' );
this.Class = this.require( 'class' );
this.Interface = this.require( 'interface' );
},
@ -84,4 +85,86 @@ require( 'common' ).testCase(
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 ) );
},
} );