1
0
Fork 0

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
Mike Gerwitz 2014-04-26 00:52:48 -04:00
commit f257a73e1d
No known key found for this signature in database
GPG Key ID: F22BB8158EE30EAB
2 changed files with 136 additions and 23 deletions

View File

@ -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
); );
}, },
}; };
}; }
/** /**

View File

@ -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 ) );
},
} ); } );