Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@efeminella
Created March 11, 2012 06:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save efeminella/2015278 to your computer and use it in GitHub Desktop.
Save efeminella/2015278 to your computer and use it in GitHub Desktop.
Simple Comparator API for JavaScript
var Comparator=Comparator||(function(){var c=function(g,f){return this[g]===f};var a=function(f){if(!this.hasOwnProperty(f)){this[f]=undefined}};var b=function(g,m,f){var k=g.prototype,o=(f?f.length:0),j=0,l,h;for(j;j<o;j++){l=f[j];h="is"+l.replace(/^./,l.match(/^./)[0].toUpperCase());if(k[h]===undefined){k[h]=(function(){return c}(m,l))}}};var d=function(g,k,f){var i=g.prototype,h,j;for(h in f){j=f[h];if(i[h]===undefined){i[h]=(function(){return c}(k,j))}}};var e=function(g,h,f){if(g.constructor!=Object){a.call(g.prototype,h);if(Object.prototype.toString.call(f)==="[object Array]"){return b(g,h,f)}return d(g,h,f)}else{throw new Error("Attempting to augment Object prototype")}};return{each:e}}());
/*
Copyright (c) 2011 Eric Feminella <efeminella [at] gmail.com>
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/*jslint vars: true, white: true, plusplus: true, sloppy: true */
/**
*
* <code>Comparator</code> provides an all <code>static</code> API
* which allows for augmenting existing objects with comparison
* methods based on a given <code>property</code> and the constants
* against which the <code>property</code> can be evaluated.
*
* @module commons
* @class Comparator
* @static
*
*
*/
var Comparator = Comparator || ( function()
{
/**
*
* Determines if the specific property for the given instance is
* equal to that of the provided value.
*
* @private
* @method compare
* @returns {Boolean} true if property equals value, otherwise false.
*
*/
var compare = function( property, value )
{
return this[property] === value;
};
/**
*
* Determines if the specific property exists on the prototype of
* the given instance. If not, it will be added.
*
* @private
* @method validateProperty
*
*/
var validateProperty = function( property )
{
if ( !this.hasOwnProperty( property ) )
{
this[property] = undefined;
}
};
/**
*
* Augments the specified Type with comparison methods for each
* <code>constant</code> provided in the <code>values</code>
* argument <code>Array</code>. Each new method is created in
* the format "is" + <code>constant</code> value.
*
* @private
* @method fromArray
*
*/
var fromArray = function( Type, property, values )
{
var fn = Type.prototype,
n = ( values ? values.length : 0 ),
i = 0,
value,
name;
for ( i; i < n; i ++ )
{
value = values[i];
name = "is" + value.replace( /^./, value.match(/^./)[0].toUpperCase() );
if ( fn[ name ] === undefined )
{
fn[ name ] = (function() {
return compare
}(property, value) );
}
}
};
/**
*
* Augments the specified <code>Type</code> arguments prototype
* with comparison methods for each <code>name</code> provided
* in the <code>values</code> <code>Object</code>.
*
* @private
* @method fromObject
*
*/
var fromObject = function( Type, property, values )
{
var fn = Type.prototype,
name,
value;
for ( name in values )
{
value = values[name];
if ( fn[ name ] === undefined )
{
fn[ name ] = ( function() {
return compare
}(property, value) );
}
}
};
/**
*
* Determines if the specified <code>values</code> argument is an
* <code>Array</code> or an <code>Object</code>, and invokes the
* appropriate factory method.
*
* @private
* @method forEach
*
*/
var forEach = function( Type, property, values )
{
if ( Type.constructor != Object )
{
validateProperty.call( Type.prototype, property );
if ( Object.prototype.toString.call( values ) === "[object Array]" )
{
return fromArray( Type, property, values );
}
return fromObject( Type, property, values );
}
else {
throw new Error( "Attempting to augment Object prototype");
}
};
return {
/**
*
* <code>Comparator</code> defines a single <code>static</code> method which
* can be invoked to allow for an existing <code>Type</code> to be augmented
* with comparison methods based on an <code>Array</code> of constants to which
* a given <code>property</code> can be evaluated to.
*
* @method each
*
* @param Type The Class or Object to which the comparison methods are to be
* added.If the specified <code>type</code> is an <code>Object</code> instance,
* an <code>Error</code> will be thrown.
*
* @param property The property against which the comparisons are to be made.
* If the property has not been defined it, too, will be added.
*
* @param {Array} values An Array of constants where each value will be used
* to create a new comparison method (prefixed with 'is'). If the constants
* specified are <code>Strings</code>, an <code>Array</code> containing each
* constant will typically suffice. For example, passing <code>[Foo.BAR]</code>
* (where BAR equals 'Bar') would result in an <code>isBar()</code> method
* being created.
*
* To specify custom comparison method names, an <code>Object</code> of name/value
* pairs can be used where each name defines the <code>name</code> of the method
* which is to be added and the <code>value</code> is the constant evaluated by
* the method. This is useful for constants which are not <code>Strings</code>.
* For example, a type <code>DeviceVersion</code> a key <code>isIOS421</code>
* with an associated value <code>DeviceVersion.IOS_4_2_1</code> which equals
* <code>4.2.1</code> would result in an <code>isIOS421();</code> method being
* created on the <code>DeviceVersion</code> <code>prototype</code>.
*
*/
each: forEach
}
}());
/**
*
* ComparatorTest Defines the Comparator Test Suite implementation.
*
*/
var ComparatorTest =
{
run: function()
{
module( "Comparator API" );
test( "Test 'each' creates comparison functions from Array.", function()
{
var Compass = function(direction){
this.direction = direction;
}
Compass.NORTH = "North";
Compass.EAST = "East";
Compass.SOUTH = "South";
Compass.WEST = "West";
var compass = new Compass();
expect( 8 );
Comparator.each( Compass, "direction", [ "North", "East", "South", "West" ] );
var compass = new Compass()
ok( compass.isNorth, "expecting function 'isNorth' to have been created." );
ok( compass.isEast, "expecting function 'isEast' to have been created." );
ok( compass.isSouth, "expecting function 'isSouth' to have been created." );
ok( compass.isWest, "expecting function 'isWest' to have been created." );
ok( new Compass( Compass.NORTH ).isNorth(), "expecting function 'isNorth' to evaluate to true." );
ok( new Compass( Compass.EAST ).isEast, "expecting function 'isEast' to evaluate to true." );
ok( new Compass( Compass.SOUTH ).isSouth, "expecting function 'isSouth' to evaluate to true." );
ok( new Compass( Compass.WEST ).isWest, "expecting function 'isWest' to evaluate to true." );
});
test( "Test 'each' creates comparison functions from object.", function()
{
var RGB = function(value){
this.value = value;
}
RGB.RED = "#FF0000";
RGB.GREEN = "#00FF00";
RGB.BLUE = "#0000FF";
var rgb = new RGB();
expect( 6 );
Comparator.each( RGB, "value", { isRed: RGB.RED,
isGreen: RGB.GREEN,
isBlue: RGB.BLUE } );
ok( rgb.isRed, "expecting function 'isRed' to have been created." );
ok( rgb.isGreen, "expecting function 'isGreen' to have been created." );
ok( rgb.isBlue, "expecting function 'isBlue' to have been created." );
ok( new RGB( RGB.RED ).isRed(), "expecting function 'isRed' to evaluate to true." );
ok( new RGB( RGB.GREEN ).isGreen(), "expecting function 'isGreen' to evaluate to true." );
ok( new RGB( RGB.BLUE ).isBlue(), "expecting function 'isBlue' to evaluate to true." );
});
test( "Test 'each' creates property if undefined.", function()
{
var RGB = function(){}
RGB.RED = "#FF0000";
RGB.GREEN = "#00FF00";
RGB.BLUE = "#0000FF";
expect( 6 );
Comparator.each( RGB, "value", { isRed: RGB.RED,
isGreen: RGB.GREEN,
isBlue: RGB.BLUE } );
var rgb = new RGB();
rgb.value = RGB.RED;
ok( rgb.isRed, "expecting function 'isRed' to have been created." );
ok( rgb.isGreen, "expecting function 'isGreen' to have been created." );
ok( rgb.isBlue, "expecting function 'isBlue' to have been created." );
rgb.value = RGB.RED;
ok( rgb.isRed(), "expecting function 'isRed' to evaluate to true." );
rgb.value = RGB.GREEN;
ok( rgb.isGreen(), "expecting function 'isGreen' to evaluate to true." );
rgb.value = RGB.BLUE;
ok( rgb.isBlue(), "expecting function 'isBlue' to evaluate to true." );
});
test( "Test 'each' throws exception if Object", function()
{
var o = {test:"test"};
o.TEST = "test";
raises( function() {
Comparator.each( o, "test", [o.TEST] )
})
})
}
};
$(document).ready( ComparatorTest.run );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment