Trait named staging object
This implements Trait("Foo"), analogous to Class("Foo"). This was intended to have existed in the 0.2.0 release, but was overlooked.newmaster
commit
f257a73e1d
72
lib/Trait.js
72
lib/Trait.js
|
@ -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 );
|
||||
};
|
||||
|
||||
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
|
||||
return Trait.extend.apply(
|
||||
return Trait.extend.call(
|
||||
{ __$$meta: { ifaces: ifaces } },
|
||||
arguments
|
||||
dfn
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
@ -25,6 +25,7 @@ require( 'common' ).testCase(
|
|||
{
|
||||
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 ) );
|
||||
},
|
||||
} );
|
||||
|
|
Loading…
Reference in New Issue