Skip to content

Instantly share code, notes, and snippets.

@cmacrander
Last active August 26, 2015 20:17
Show Gist options
  • Save cmacrander/3c772e27d600d7dfaa48 to your computer and use it in GitHub Desktop.
Save cmacrander/3c772e27d600d7dfaa48 to your computer and use it in GitHub Desktop.
KeywordArguments for javascript
// 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