1
0
Fork 0

Added support for static proxy methods

When the static keyword is provided, the proxy will use the static accessor
method to look up the requested member.
perfodd
Mike Gerwitz 2012-05-03 14:13:47 -04:00
parent d84b86b21b
commit e67c14e8c3
No known key found for this signature in database
GPG Key ID: F22BB8158EE30EAB
6 changed files with 76 additions and 16 deletions

View File

@ -133,7 +133,12 @@ exports.buildMethod = function(
// we might be overriding an existing method // we might be overriding an existing method
if ( keywords[ 'proxy' ] ) if ( keywords[ 'proxy' ] )
{ {
dest[ name ] = this._createProxy( value, instCallback, cid, name ); // TODO: Note that this is not compatible with method hiding, due to its
// positioning (see hideMethod() below); address once method hiding is
// implemented (the validators currently handle everything else)
dest[ name ] = this._createProxy(
value, instCallback, cid, name, keywords
);
} }
else if ( prev ) else if ( prev )
{ {
@ -373,15 +378,16 @@ function hideMethod( super_method, new_method, instCallback, cid )
* @param {Function} instCallback function to call in order to retrieve * @param {Function} instCallback function to call in order to retrieve
* object to bind 'this' keyword to * object to bind 'this' keyword to
* *
* @param {number} cid class id * @param {number} cid class id
* @param {string} mname name of method to invoke on destination object * @param {string} mname name of method to invoke on destination object
* @param {Object} keywords method keywords
* *
* @return {Function} proxy method * @return {Function} proxy method
*/ */
exports._createProxy = function( proxy_to, instCallback, cid, mname ) exports._createProxy = function( proxy_to, instCallback, cid, mname, keywords )
{ {
return this._wrapProxy.wrapMethod( return this._wrapProxy.wrapMethod(
proxy_to, null, cid, instCallback, mname proxy_to, null, cid, instCallback, mname, keywords
); );
}; };

View File

@ -56,11 +56,12 @@ module.exports = exports = function MethodWrapperFactory( factory )
* @param {function()} getInst function to determine instance and return * @param {function()} getInst function to determine instance and return
* associated visibility object * associated visibility object
* @param {string=} name name of method * @param {string=} name name of method
* @param {Object=} keywords method keywords
*/ */
exports.prototype.wrapMethod = function( exports.prototype.wrapMethod = function(
method, super_method, cid, getInst, name method, super_method, cid, getInst, name, keywords
) )
{ {
return this._factory( method, super_method, cid, getInst, name ); return this._factory( method, super_method, cid, getInst, name, keywords );
}; };

View File

@ -83,21 +83,29 @@ exports.standard = {
}, },
wrapProxy: function( proxy_to, _, cid, getInst, name ) wrapProxy: function( proxy_to, _, cid, getInst, name, keywords )
{ {
// it is important that we store only a boolean value as to whether or
// not this method is static *outside* of the returned closure, so as
// not to keep an unnecessary reference to the keywords object
var is_static = keywords && keywords[ 'static' ];
return function() return function()
{ {
var context = getInst( this, cid ) || this, var context = getInst( this, cid ) || this,
retval = undefined, retval = undefined,
dest = context[ proxy_to ] dest = ( ( is_static )
? context.$( proxy_to )
: context[ proxy_to ]
)
; ;
// rather than allowing a cryptic error to be thrown, attempt to // rather than allowing a cryptic error to be thrown, attempt to
// detect when the proxy call will fail and provide a useful error // detect when the proxy call will fail and provide a useful error
// message // message
if ( ( typeof dest !== 'object' ) if ( !( ( dest !== null ) && ( typeof dest === 'object' )
|| ( typeof dest[ name ] !== 'function' ) && ( typeof dest[ name ] === 'function' )
) ) )
{ {
throw TypeError( throw TypeError(
"Unable to proxy " + name + "() call to '" + proxy_to + "Unable to proxy " + name + "() call to '" + proxy_to +

View File

@ -170,7 +170,7 @@ require( 'common' ).testCase(
); );
this.assertDeepEqual( this.assertDeepEqual(
[ value, null, cid, instCallback, name ], [ value, null, cid, instCallback, name, keywords ],
this.proxyFactoryCall, this.proxyFactoryCall,
"Proxy factory should be called with proper arguments" "Proxy factory should be called with proper arguments"
); );

View File

@ -65,11 +65,13 @@ var common = require( './common' ),
cid = 55, cid = 55,
getInst = function() {}, getInst = function() {},
name = 'someMethod', name = 'someMethod',
keywords = { 'static': true, 'public': true },
retval = 'foobar'; retval = 'foobar';
var result = Sut( var result = Sut(
function( function(
given_method, given_super, given_cid, givenGetInst, givenName given_method, given_super, given_cid, givenGetInst, given_name,
given_keywords
) )
{ {
called = true; called = true;
@ -90,13 +92,17 @@ var common = require( './common' ),
"Factory method should be provided with proper inst function" "Factory method should be provided with proper inst function"
); );
assert.equal( givenName, name, assert.equal( given_name, name,
"Factory method should be provided with proper method name" "Factory method should be provided with proper method name"
); );
assert.equal( given_keywords, keywords,
"Factory method should be provided with proper keywords"
);
return retval; return retval;
} }
).wrapMethod( method, super_method, cid, getInst, name ); ).wrapMethod( method, super_method, cid, getInst, name, keywords );
// we'll include this in addition to the following assertion (which is // we'll include this in addition to the following assertion (which is
// redundant) to make debugging more clear // redundant) to make debugging more clear

View File

@ -342,3 +342,42 @@ function proxyErrorAssertCommon( e, prop, method )
); );
} )(); } )();
/**
* If the `static' keyword is provided, then the proxy mustn't operate on
* instance properties. Instead, the static accessor method $() must be used.
*/
( function testCanProxyToStaticMembers()
{
var getInst = function()
{
// pretend that we're a static class with a static accessor method
return {
$: function( name )
{
// implicitly tests that the argument is properly passed
// (would otherwise return `undefined`)
return s[ name ];
},
};
},
keywords = { 'static': true };
val = [ 'value' ],
s = {
// destination object
foo: {
method: function()
{
return val;
},
}
};
assert.strictEqual( val,
sut.standard.wrapProxy( 'foo', null, 0, getInst, 'method', keywords )(),
"Should properly proxy to static membesr via static accessor method"
);
} )();