diff --git a/lib/util.js b/lib/util.js index ad42769..fc01f60 100644 --- a/lib/util.js +++ b/lib/util.js @@ -149,13 +149,21 @@ exports.clone = function( data ) exports.propParse = function( data, options ) { var fvoid = function() {}, - callback_each = options.each || undefined, - callback_prop = options.property || fvoid, - callback_method = options.method || fvoid, - callback_getter = options.getter || fvoid, - callback_setter = options.setter || fvoid, + callback_each = options.each || undefined, + callback_prop = options.property || fvoid, + callback_method = options.method || fvoid, + callback_getter = options.getter || fvoid, + callback_setter = options.setter || fvoid, + keyword_parser = options.keywordParser || propKeywordStringParser, - hasOwn = Object.prototype.hasOwnProperty; + hasOwn = Object.prototype.hasOwnProperty, + + parse_data = {}, + name = '', + keywords = {}, + value = null, + getter = false, + setter = false; // for each of the given properties, determine what type of property we're // dealing with (in the classic OO sense) @@ -167,36 +175,43 @@ exports.propParse = function( data, options ) continue; } - var value = data[ prop ], - getter = ( ( getset ) ? data.__lookupGetter__( prop ) : null ), - setter = ( ( getset ) ? data.__lookupSetter__( prop ) : null ); + value = data[ prop ]; + getter = ( ( getset ) ? data.__lookupGetter__( prop ) : null ); + setter = ( ( getset ) ? data.__lookupSetter__( prop ) : null ); + + parse_data = keyword_parser( prop ) || {}; + name = parse_data.name || prop; + keywords = parse_data.keywords || {}; // if an 'each' callback was provided, pass the data before parsing it if ( callback_each ) { - callback_each.call( callback_each, prop, value ); + callback_each.call( callback_each, name, value ); } // getter/setter if ( getter || setter ) { - callback_getter.call( callback_getter, prop, getter ); - callback_setter.call( callback_setter, prop, setter ); + callback_getter.call( callback_getter, name, getter ); + callback_setter.call( callback_setter, name, setter ); } // method - else if ( value instanceof Function ) + else if ( ( value instanceof Function ) || ( keywords['abstract'] ) ) { callback_method.call( callback_method, - prop, + name, value, - exports.isAbstractMethod( value ) + ( ( keywords['abstract'] ) + ? true + : exports.isAbstractMethod( value ) + ) ); } // simple property else { - callback_prop.call( callback_prop, prop, value ); + callback_prop.call( callback_prop, name, value ); } } } @@ -457,6 +472,45 @@ exports.arrayShrink = function( items ) } +/** + * Parses property keywords + * + * @param {string} prop property string, which may contain keywords + * + * @return {{name: string, keywords: Object.}} + */ +function propKeywordStringParser( prop ) +{ + var name = prop, + keywords = [], + keyword_obj = {}; + + prop = ''+( prop ); + + if ( prop.length > 8 ) + { + if ( prop[ 8 ] === ' ' ) + { + // the keywords are all words, except for the last, which is the + // property name + keywords = prop.split( ' ' ); + name = keywords.pop(); + + var i = keywords.length; + while ( i-- ) + { + keyword_obj[ keywords[ i ] ] = true; + } + } + } + + return { + name: name, + keywords: keyword_obj, + }; +} + + /** * Appropriately returns defineSecureProp implementation to avoid check on each * invocation diff --git a/test/test-util-prop-parse-keywords.js b/test/test-util-prop-parse-keywords.js new file mode 100644 index 0000000..49ff95d --- /dev/null +++ b/test/test-util-prop-parse-keywords.js @@ -0,0 +1,115 @@ +/** + * Tests util.propParse keyword parsing + * + * Copyright (C) 2010 Mike Gerwitz + * + * This file is part of ease.js. + * + * ease.js is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * @author Mike Gerwitz + * @package test + */ + +var common = require( './common' ), + assert = require( 'assert' ), + util = common.require( 'util' ); + +var data = { + 'abstract foo': null, +} + +var abstract_methods = [], + parse_data = {}; + + +util.propParse( data, { + method: function ( name, func, is_abstract ) + { + if ( is_abstract ) + { + abstract_methods.push( name ); + parse_data[ name ] = func; + } + }, +} ); + +assert.ok( + ( ( abstract_methods.length === 1 ) + && ( abstract_methods[ 0 ] === 'foo' ) + ), + "Methods with 'abstract' keyword recognized as abstract" +); + + +// +// custom parser +var data2 = { + foo: 'bar', + }, + map = { + foo: { 'abstract': true }, + }, + + suffix = 'poo', + + abstract_methods_2 = []; + +util.propParse( data2, { + keywordParser: function ( prop ) + { + return { + name: ( prop + suffix ), + keywords: map[ prop ], + }; + }, + + + method: function ( name, func, is_abstract ) + { + if ( is_abstract ) + { + abstract_methods_2.push( name ); + } + }, +} ); + +assert.ok( + ( abstract_methods_2[ 0 ] === ( 'foo' + suffix ) ), + "Can provide custom property keyword parser" +); + + +// +// integrity test +assert.doesNotThrow( function() +{ + var junk = { foo: 'bar' }; + + util.propParse( junk, { + keywordParser: function ( prop ) + { + // return nothing + } + } ); + + util.propParse( junk, { + keywordParser: function ( prop ) + { + // return bogus name and keywords + return { name: [], keywords: 'slefwef' }; + } + } ); +}, Error, "Custom keyword parser tolerates bogus response" ); +