Last active
August 26, 2015 20:17
-
-
Save cmacrander/3c772e27d600d7dfaa48 to your computer and use it in GitHub Desktop.
KeywordArguments for javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See http://ejohn.org/blog/partial-functions-in-javascript/ | |
Function.prototype.partial = function () { | |
'use strict'; | |
var fn = this, args = Array.prototype.slice.call(arguments), i; | |
return function () { | |
var arg = 0; | |
for (i = 0; i < args.length && arg < arguments.length; i += 1) { | |
if (args[i] === undefined) { | |
args[i] = arguments[arg]; | |
arg += 1; | |
} | |
} | |
return fn.apply(this, args); | |
}; | |
}; | |
function forEach(o, f, thisObj) { | |
'use strict'; | |
var p, results = [], returnValue; | |
if (typeof f !== 'function') { | |
throw new TypeError(); | |
} | |
if (o instanceof Array) { | |
return o.forEach(f, thisObj); | |
} | |
for (p in o) { | |
if (Object.prototype.hasOwnProperty.call(o, p)) { | |
returnValue = f.call(thisObj, p, o[p], o); | |
if (returnValue !== undefined) { | |
results.push(returnValue); | |
} | |
} | |
} | |
return results; | |
} | |
function KeywordArguments(args) { | |
var forbiddenArguments = ['parse']; | |
forEach(args, function (k, v) { | |
if (forbiddenArguments.contains(k)) { | |
throw new Error("KeywordArguments can't accept argument " + k + "."); | |
} | |
this[k] = v; | |
}, this); | |
} | |
/* | |
*coequal.js v 1.0.0 Beta | |
*Author: Sudhanshu Yadav | |
*s-yadav.github.com | |
*Copyright (c) 2013 Sudhanshu Yadav. | |
*Dual licensed under the MIT and GPL licenses | |
*/ | |
// MODIFIED by CAM to remove dependence on jQuery | |
// Original: https://github.com/s-yadav/coequal.js | |
function coequal(data1, data2, option) { | |
var getType = function(data) { | |
var type = Object.prototype.toString.call(data), | |
domIntrfc = ['object HTML', 'object Node', 'object SVG', 'object Canvas'], | |
re = new RegExp(domIntrfc.join("|")); | |
//if (data instanceof jQuery) { | |
// type = 'jquery'; | |
//} else | |
if (type.search(re) != -1) { | |
type = 'dom'; | |
} | |
return type; | |
} | |
var check = function(data1, data2, option) { | |
var data1Type = getType(data1), | |
data2Type = getType(data2), | |
option = option || {}; | |
var funCheck = option.functionCheck || 'constructor', | |
orderCheck = option.orderCheck == null ? true : option.orderCheck, | |
collCheck = option.collectionCheck || false; | |
if (data1Type != data2Type) return false; | |
if (data1Type == '[object Function]') { | |
if (funCheck == 'code') { | |
return data1.toString() == data2.toString(); | |
} else if (data1 !== data2) return false; | |
} else if (data1Type == '[object Object]') { | |
for (var k in data1) { | |
if (!data1.hasOwnProperty(k)) continue; | |
if (!data2.hasOwnProperty(k)) return false; | |
if (!check(data1[k], data2[k], option)) return false; | |
} | |
for (k in data2) { | |
if (data2.hasOwnProperty(k) && !data1.hasOwnProperty(k)) return false; | |
} | |
} else if (data1Type == '[object Array]') { | |
if (data1.length != data2.length) return false; | |
var cnt1 = 0, | |
cnt2 = 0, | |
checked = []; | |
for (var i = 0; i < data1.length; i++) { | |
//if array element order matters | |
if (orderCheck) { | |
if (!check(data1[i], data2[i], option)) return false; | |
} | |
//if array element order not matters else { | |
else { | |
cnt1++; | |
for (var j = 0; j < data2.length; j++) { | |
if (checked.indexOf(j) != -1) continue; | |
if (check(data1[i], data2[j])) { | |
cnt2++; | |
checked.push(j); | |
break; | |
} | |
} | |
if (cnt1 != cnt2) return false; | |
} | |
} | |
} else if (data1Type == '[object Date]') { | |
return data1.getTime() == data2.getTime(); | |
} else if (data1Type == '[object RegExp]') { | |
return data1.toString() == data2.toString(); | |
} else if (data1Type == 'jquery') { | |
return check(data1.get(), data2.get(), option); | |
} else if (data1Type == 'dom') { | |
var collToArry = function(data) { | |
var ary = []; | |
if (!data.length) { | |
ary.push(data); | |
} else { | |
for (var i = 0; i < data.length; i++) { | |
ary.push(data[i]); | |
} | |
} | |
return ary; | |
} | |
if (collCheck) { | |
option.collectionCheck = false; | |
return check(collToArry(data1), collToArry(data2), option) | |
} else { | |
return data1 === data2; | |
} | |
} else { | |
return data1 === data2 | |
} | |
return true; | |
} | |
//to initialize | |
return check(data1, data2, option); | |
} | |
KeywordArguments.prototype.parse = function (p) { | |
var allArgs = this; | |
var parsedArg = {}; | |
var required = function (p) { | |
if (allArgs[p] === undefined) { | |
throw new Error("Keyword argument " + p + " is required."); | |
} | |
return parsedArg; | |
}; | |
var type = function (p, type) { | |
var pType = allArgs[p] === null ? | |
'null' : typeof allArgs[p]; // fix typeof null | |
if (pType !== type) { | |
throw new Error("Keyword argument " + p + " must be of type " + type + | |
". Got type " + pType + "."); | |
} | |
return parsedArg; | |
}; | |
var optional = function (p, defaultValue) { | |
if (allArgs[p] === undefined) { | |
allArgs[p] = defaultValue; | |
} | |
return parsedArg; | |
}; | |
var not = function (p, badValue) { | |
if (coequal(allArgs[p] == badValue)) { | |
throw new Error("Keyword argument " + p + " cannot have value " | |
+ badValue + "."); | |
} | |
return parsedArg; | |
}; | |
parsedArg.required = required.partial(p); | |
parsedArg.type = type.partial(p, undefined) | |
parsedArg.optional = optional.partial(p, undefined); | |
parsedArg.not = not.partial(p, undefined); | |
return parsedArg; | |
}; | |
// Example: | |
function f(args) { | |
args = new KeywordArguments(args); | |
args.parse('foo').required().type('number'); | |
args.parse('bar').optional([]).not('bar'); | |
console.log(args.foo, args.bar); | |
} | |
f(); // throws an error, foo required | |
f({foo: 'foo', bar: 5}); // throws an error, foo not a number | |
f({foo: 6, bar: 'bar'}); // throws an error, bar is 'bar' | |
f({foo: 6}); // logs 6, [] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment