1
0
Fork 0

[#25] Now injecting MemberBuilderValidator into MemberBuilder

closure/master
Mike Gerwitz 2011-11-02 23:28:23 -04:00
parent 3912f2d369
commit 4e2af2333d
9 changed files with 107 additions and 61 deletions

View File

@ -30,27 +30,24 @@
var util = require( __dirname + '/util' ), var util = require( __dirname + '/util' ),
Warning = require( __dirname + '/warn' ).Warning, Warning = require( __dirname + '/warn' ).Warning,
visibility = [ 'public', 'protected', 'private' ], visibility = [ 'public', 'protected', 'private' ]
validate = require( __dirname + '/MemberBuilderValidator' )()
; ;
/** /**
* Responsible for building class members * Responsible for building class members
*/ */
module.exports = function MemberBuilder( wrap_method, wrap_override ) module.exports = function MemberBuilder( wrap_method, wrap_override, validate )
{ {
// permit omitting 'new' keyword // permit omitting 'new' keyword
if ( !( this instanceof module.exports ) ) if ( !( this instanceof module.exports ) )
{ {
return new module.exports( wrap_method, wrap_override ); return new module.exports( wrap_method, wrap_override, validate );
} }
this._wrapMethod = wrap_method; this._wrapMethod = wrap_method;
this._wrapOverride = wrap_override; this._wrapOverride = wrap_override;
// XXX: temporarily tightly coupled
this._validate = validate; this._validate = validate;
}; };

View File

@ -31,7 +31,8 @@ var util = require( __dirname + '/util' ),
class_builder = ClassBuilder( class_builder = ClassBuilder(
require( __dirname + '/MemberBuilder' )( require( __dirname + '/MemberBuilder' )(
MethodWrapperFactory( wrappers.wrapNew ), MethodWrapperFactory( wrappers.wrapNew ),
MethodWrapperFactory( wrappers.wrapOverride ) MethodWrapperFactory( wrappers.wrapOverride ),
require( __dirname + '/MemberBuilderValidator' )()
), ),
require( __dirname + '/VisibilityObjectFactoryFactory' ) require( __dirname + '/VisibilityObjectFactoryFactory' )
.fromEnvironment() .fromEnvironment()

View File

@ -23,8 +23,18 @@
*/ */
var util = require( __dirname + '/util' ), var util = require( __dirname + '/util' ),
member_builder = require( __dirname + '/MemberBuilder' )(),
Class = require( __dirname + '/class' ); MethodWrapperFactory = require( __dirname + '/MethodWrapperFactory' ),
wrappers = require( __dirname + '/MethodWrappers' ).standard,
member_builder = require( __dirname + '/MemberBuilder' )(
MethodWrapperFactory( wrappers.wrapNew ),
MethodWrapperFactory( wrappers.wrapOverride ),
require( __dirname + '/MemberBuilderValidator' )()
),
Class = require( __dirname + '/class' )
;
/** /**

View File

@ -214,7 +214,8 @@ require( 'common' ).testCase(
); );
this.sut = this.require( 'MemberBuilder' )( this.sut = this.require( 'MemberBuilder' )(
stubFactory, stubFactory stubFactory, stubFactory,
this.getMock( 'MemberBuilderValidator' )
); );
this.members = this.sut.initMembers(); this.members = this.sut.initMembers();

View File

@ -2,7 +2,10 @@
var assert = require( 'assert' ), var assert = require( 'assert' ),
assert_wrapped = {}, assert_wrapped = {},
acount = 0; acount = 0,
common_require = require( __dirname + '/common' ).require
;
// wrap each of the assertions so that we can keep track of the number of times // wrap each of the assertions so that we can keep track of the number of times
@ -114,6 +117,30 @@ module.exports = function( test_case )
}; };
function getMock( proto )
{
var P = common_require( proto ),
Mock = function() {},
proto = Mock.prototype = new P()
;
for ( i in proto )
{
// only mock out methods
if ( typeof proto[ i ] !== 'function' )
{
continue;
}
// clear the method
proto[ i ] = function() {};
}
return new Mock();
}
/** /**
* Prepare assertion methods on context * Prepare assertion methods on context
* *
@ -122,7 +149,7 @@ module.exports = function( test_case )
function prepareCaseContext() function prepareCaseContext()
{ {
return { return {
require: require( __dirname + '/common' ).require, require: common_require,
fail: assert_wrapped.fail, fail: assert_wrapped.fail,
assertOk: assert_wrapped.ok, assertOk: assert_wrapped.ok,
@ -135,6 +162,8 @@ function prepareCaseContext()
assertDoesNotThrow: assert_wrapped.doesNotThrow, assertDoesNotThrow: assert_wrapped.doesNotThrow,
assertIfError: assert_wrapped.ifError, assertIfError: assert_wrapped.ifError,
incAssertCount: incAssertCount, incAssertCount: incAssertCount,
getMock: getMock,
}; };
} }

View File

@ -38,16 +38,16 @@ require( 'common' ).testCase(
this.builder = ClassBuilder( this.builder = ClassBuilder(
this.require( '/MemberBuilder' )( this.require( '/MemberBuilder' )(
MethodWrapperFactory( wrappers.wrapNew ), MethodWrapperFactory( wrappers.wrapNew ),
MethodWrapperFactory( wrappers.wrapOverride ) MethodWrapperFactory( wrappers.wrapOverride ),
this.getMock( 'MemberBuilderValidator' )
), ),
this.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment() this.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment()
) )
}, },
/** /** The const keyword should result in a static property. The rationale for
* The const keyword should result in a static property. The rationale for this * this is that, if a value is constant, then instances do not make sense.
* is that, if a value is constant, then instances do not make sense.
*/ */
'const keyword declares properties as static': function() 'const keyword declares properties as static': function()
{ {
@ -113,8 +113,8 @@ require( 'common' ).testCase(
}, },
} ), } ),
// be sure to override each method to ensure we're checking references // be sure to override each method to ensure we're checking
// on the subtype, *not* the parent type // references on the subtype, *not* the parent type
SubFoo = this.builder.build( Foo, SubFoo = this.builder.build( Foo,
{ {
'public static getProt': function() 'public static getProt': function()

View File

@ -29,20 +29,25 @@ var common = require( './common' ),
// dependencies should not be necessary for testing // dependencies should not be necessary for testing
ClassBuilder = common.require( '/ClassBuilder' ), ClassBuilder = common.require( '/ClassBuilder' ),
MethodWrapperFactory = common.require( '/MethodWrapperFactory' ), MethodWrapperFactory = common.require( '/MethodWrapperFactory' ),
wrappers = common.require( '/MethodWrappers' ).standard, wrappers = common.require( '/MethodWrappers' ).standard
builder = ClassBuilder(
common.require( '/MemberBuilder' )(
MethodWrapperFactory( wrappers.wrapNew ),
MethodWrapperFactory( wrappers.wrapOverride )
),
common.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment()
)
; ;
require( 'common' ).testCase( require( 'common' ).testCase(
{ {
setUp: function()
{
this.builder = ClassBuilder(
common.require( '/MemberBuilder' )(
MethodWrapperFactory( wrappers.wrapNew ),
MethodWrapperFactory( wrappers.wrapOverride ),
this.getMock( 'MemberBuilderValidator' )
),
common.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment()
);
},
/** /**
* To provide access to static members, this.__self is made available inside * To provide access to static members, this.__self is made available inside
* of instances. * of instances.
@ -50,7 +55,7 @@ require( 'common' ).testCase(
'Self property references class definition': function() 'Self property references class definition': function()
{ {
var val = [ 'baz' ], var val = [ 'baz' ],
Foo = builder.build( Foo = this.builder.build(
{ {
'public test': function() 'public test': function()
{ {
@ -77,7 +82,7 @@ require( 'common' ).testCase(
*/ */
'Static property lookup returns undefined if not found': function() 'Static property lookup returns undefined if not found': function()
{ {
var result = builder.build( {} ).$( 'foo' ); var result = this.builder.build( {} ).$( 'foo' );
this.assertEqual( result, undefined, this.assertEqual( result, undefined,
"Static property getter should return undefined if not found" "Static property getter should return undefined if not found"
@ -93,7 +98,7 @@ require( 'common' ).testCase(
'Static property accessor is not enumerable': function() 'Static property accessor is not enumerable': function()
{ {
var get = Object.getOwnPropertyDescriptor, var get = Object.getOwnPropertyDescriptor,
Foo = builder.build( {} ); Foo = this.builder.build( {} );
// don't perform the test if unsupported // don't perform the test if unsupported
if ( fallback ) if ( fallback )
@ -117,7 +122,7 @@ require( 'common' ).testCase(
{ {
var val = 'foo', var val = 'foo',
val2 = 'bar', val2 = 'bar',
Foo = builder.build( Foo = this.builder.build(
{ {
'public static foo': val, 'public static foo': val,
@ -198,7 +203,7 @@ require( 'common' ).testCase(
} ); } );
// define the class // define the class
var Foo = builder.build( def ); var Foo = this.builder.build( def );
this.assertEqual( Foo.foo, val, this.assertEqual( Foo.foo, val,
"Public static getters are accessible via class definition" "Public static getters are accessible via class definition"
@ -240,7 +245,7 @@ require( 'common' ).testCase(
'Static methods not bound to instance': function() 'Static methods not bound to instance': function()
{ {
var result = null, var result = null,
Foo = builder.build( Foo = this.builder.build(
{ {
'public static foo': function() 'public static foo': function()
{ {
@ -285,15 +290,15 @@ require( 'common' ).testCase(
} }
var baz = 'foobar', var baz = 'foobar',
Foo = builder.build( def ), Foo = this.builder.build( def ),
// extends from the parent and adds an additional // extends from the parent and adds an additional
SubFoo = builder.build( Foo, { 'public static baz': baz } ), SubFoo = this.builder.build( Foo, { 'public static baz': baz } ),
// simply extends from the parent (also serves as a check to ensure // simply extends from the parent (also serves as a check to ensure
// that static members of *all* parents are inherited, not just the // that static members of *all* parents are inherited, not just the
// immediate) // immediate)
SubSubFoo = builder.build( SubFoo, {} ) SubSubFoo = this.builder.build( SubFoo, {} )
; ;
// properties // properties
@ -358,11 +363,11 @@ require( 'common' ).testCase(
var val = [ 1, 2, 3 ], var val = [ 1, 2, 3 ],
val2 = [ 'a', 'b', 'c' ], val2 = [ 'a', 'b', 'c' ],
Foo = builder.build( Foo = this.builder.build(
{ {
'public static bar': val, 'public static bar': val,
} ), } ),
SubFoo = builder.build( Foo, {} ) SubFoo = this.builder.build( Foo, {} )
; ;
// the properties should reference the same object // the properties should reference the same object
@ -403,7 +408,7 @@ require( 'common' ).testCase(
'Setting static props to undefined will not corrupt lookup': function() 'Setting static props to undefined will not corrupt lookup': function()
{ {
var val = 'baz', var val = 'baz',
Foo = builder.build( Foo = this.builder.build(
{ {
'public static foo': '', 'public static foo': '',
} ) } )
@ -437,12 +442,12 @@ require( 'common' ).testCase(
*/ */
'Static property setters return proper context': function() 'Static property setters return proper context': function()
{ {
var Foo = builder.build( var Foo = this.builder.build(
{ {
'public static foo': '', 'public static foo': '',
} ), } ),
SubFoo = builder.build( Foo, {} ) SubFoo = this.builder.build( Foo, {} )
; ;
this.assertOk( Foo.$( 'foo', 'val' ) === Foo, this.assertOk( Foo.$( 'foo', 'val' ) === Foo,
@ -462,12 +467,14 @@ require( 'common' ).testCase(
*/ */
'Attempting to set undeclared static prop results in exception': function() 'Attempting to set undeclared static prop results in exception': function()
{ {
var _self = this;
this.assertThrows( this.assertThrows(
function() function()
{ {
// should throw an exception since property 'foo' has not been // should throw an exception since property 'foo' has not been
// declared // declared
builder.build( {} ).$( 'foo', 'val' ); _self.builder.build( {} ).$( 'foo', 'val' );
}, },
ReferenceError, ReferenceError,
"Attempting to set an undeclaraed static property results in an " + "Attempting to set an undeclaraed static property results in an " +
@ -483,7 +490,7 @@ require( 'common' ).testCase(
'Protected static members are available inside class only': function() 'Protected static members are available inside class only': function()
{ {
var val = 'foo', var val = 'foo',
Foo = builder.build( Foo = this.builder.build(
{ {
'protected static prop': val, 'protected static prop': val,
@ -577,7 +584,7 @@ require( 'common' ).testCase(
} ); } );
// define the class // define the class
var Foo = builder.build( def ); var Foo = this.builder.build( def );
this.assertEqual( Foo.getProp(), val, this.assertEqual( Foo.getProp(), val,
"Protected static getters are accessible from within the class" "Protected static getters are accessible from within the class"
@ -630,9 +637,9 @@ require( 'common' ).testCase(
}; };
} }
var Foo = builder.build( def ), var Foo = this.builder.build( def ),
SubFoo = builder.build( Foo, SubFoo = this.builder.build( Foo,
{ {
'public static bar': function() 'public static bar': function()
{ {
@ -655,7 +662,7 @@ require( 'common' ).testCase(
}, },
} ), } ),
SubSubFoo = builder.build( SubFoo, {} ) SubSubFoo = this.builder.build( SubFoo, {} )
; ;
this.assertEqual( SubFoo.bar(), val, this.assertEqual( SubFoo.bar(), val,
@ -715,7 +722,7 @@ require( 'common' ).testCase(
'Private static members are available inside class only': function() 'Private static members are available inside class only': function()
{ {
var val = 'foo', var val = 'foo',
Foo = builder.build( Foo = this.builder.build(
{ {
'private static prop': val, 'private static prop': val,
@ -792,9 +799,9 @@ require( 'common' ).testCase(
} ); } );
} }
var Foo = builder.build( def ), var Foo = this.builder.build( def ),
SubFoo = builder.build( Foo, SubFoo = this.builder.build( Foo,
{ {
'public static getPriv': function() 'public static getPriv': function()
{ {
@ -877,7 +884,7 @@ require( 'common' ).testCase(
} ); } );
// define the class // define the class
var Foo = builder.build( def ); var Foo = this.builder.build( def );
this.assertEqual( Foo.getProp(), val, this.assertEqual( Foo.getProp(), val,
"Private static getters are accessible from within the class" "Private static getters are accessible from within the class"
@ -901,13 +908,13 @@ require( 'common' ).testCase(
'Static methods can be overridden by subtypes': function() 'Static methods can be overridden by subtypes': function()
{ {
var val = 'bar', var val = 'bar',
Foo = builder.build( Foo = this.builder.build(
{ {
'public static foo': function() {}, 'public static foo': function() {},
'protected static bar': function() {}, 'protected static bar': function() {},
} ), } ),
SubFoo = builder.build( Foo, SubFoo = this.builder.build( Foo,
{ {
'public static foo': function() 'public static foo': function()
{ {
@ -945,12 +952,12 @@ require( 'common' ).testCase(
'Cannot exploit accessor method to gain access to parent private props': 'Cannot exploit accessor method to gain access to parent private props':
function() function()
{ {
var Foo = builder.build( var Foo = this.builder.build(
{ {
'private static foo': 'bar', 'private static foo': 'bar',
} ), } ),
SubFoo = builder.build( Foo, SubFoo = this.builder.build( Foo,
{ {
'public static getParentPrivate': function() 'public static getParentPrivate': function()
{ {
@ -974,7 +981,7 @@ require( 'common' ).testCase(
var val_orig = 'foobaz', var val_orig = 'foobaz',
val = 'foobar', val = 'foobar',
Foo = builder.build( Foo = this.builder.build(
{ {
'public static prop': val_orig, 'public static prop': val_orig,
@ -994,7 +1001,7 @@ require( 'common' ).testCase(
}, },
} ), } ),
SubFoo = builder.build( Foo, SubFoo = this.builder.build( Foo,
{ {
'public static prop': val, 'public static prop': val,
@ -1037,7 +1044,7 @@ require( 'common' ).testCase(
'Calls to parent static methods retain private member access': function() 'Calls to parent static methods retain private member access': function()
{ {
var val = 'foobar', var val = 'foobar',
Foo = builder.build( Foo = this.builder.build(
{ {
'private static _priv': val, 'private static _priv': val,
@ -1047,7 +1054,7 @@ require( 'common' ).testCase(
}, },
} ), } ),
SubFoo = builder.build( Foo, SubFoo = this.builder.build( Foo,
{ {
'public static getPriv2': function() 'public static getPriv2': function()
{ {

View File

@ -43,7 +43,8 @@ require( 'common' ).testCase(
this.builder = ClassBuilder( this.builder = ClassBuilder(
this.require( '/MemberBuilder' )( this.require( '/MemberBuilder' )(
MethodWrapperFactory( wrappers.wrapNew ), MethodWrapperFactory( wrappers.wrapNew ),
MethodWrapperFactory( wrappers.wrapOverride ) MethodWrapperFactory( wrappers.wrapOverride ),
this.getMock( 'MemberBuilderValidator' )
), ),
this.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment() this.require( '/VisibilityObjectFactoryFactory' ).fromEnvironment()
); );