functionality
Advanced tools
Comparing version 1.3.2 to 2.0.0
@@ -11,3 +11,3 @@ module.exports = Context; | ||
this.handlers = []; | ||
this.overloads = []; | ||
this.matched = false; | ||
@@ -18,5 +18,7 @@ this.result = Context.noResult; | ||
Context.prototype.retry = function() { | ||
if(arguments.length) | ||
this.args = Array.prototype.slice.call(arguments); | ||
this.matched = false; | ||
this.result = Context.noResult; | ||
this.handlers = this.overload.handlers.on.slice(0); | ||
this.overloads = this.overload.handlers.on.slice(); | ||
return Context.noResult; | ||
@@ -28,3 +30,3 @@ }; | ||
if(this.result !== undefined) return; | ||
if(this.matched || !this.handlers.length) this.result = value; | ||
if(this.matched || !this.overloads.length) this.result = value; | ||
}; |
@@ -22,23 +22,23 @@ var Masquerade = require('./Masquerade.js'), | ||
Functionality.prototype.any = function() { | ||
return new Masquerade(function(value) { | ||
return value !== undefined && value !== null; | ||
}); | ||
Functionality.prototype.mask = function(name, handler) { | ||
return new Masquerade(name, handler); | ||
}; | ||
Functionality.prototype.mask = function(handler) { | ||
return new Masquerade(handler); | ||
}; | ||
Functionality.prototype.any = function(masks) { | ||
var args = Array.prototype.slice.call(arguments); | ||
return new Masquerade(function(value) { | ||
Functionality.prototype.any = function() { | ||
var args = Array.prototype.slice.call(arguments).map(Masquerade.type); | ||
return new Masquerade("Any", function(value) { | ||
if(args.length === 0) return value !== undefined && value !== null; | ||
return _.any(args, function(mask) { return Masquerade.match(mask, value) }); | ||
}).withToString(function() { | ||
if(args.length) return "Any " + args.join(' or '); | ||
return "Any"; | ||
}); | ||
}; | ||
Functionality.prototype.not = function(masks) { | ||
var args = Array.prototype.slice.call(arguments); | ||
return new Masquerade(function(value) { | ||
return value !== undefined && !_.any(args, function(mask) { return Masquerade.match(mask, value) }); | ||
Functionality.prototype.not = function() { | ||
var args = Array.prototype.slice.call(arguments).map(Masquerade.type); | ||
return new Masquerade("Not", function(value) { | ||
return value !== undefined && !_.any(args, function(mask) { return Masquerade.match([mask], [value]); }); | ||
}).withToString(function() { | ||
return "Not " + args.join(' or '); | ||
}); | ||
@@ -48,12 +48,15 @@ }; | ||
Functionality.prototype.opt = function(mask) { | ||
return new Masquerade(function(value, next) { | ||
return new Masquerade("Optional", function(value, next) { | ||
if(value === undefined || value === null) return next(); | ||
return Masquerade.match([mask], [value]) && next(); | ||
if(Masquerade.match([mask], [value])) return next(); | ||
return next(true); | ||
}).withToString(function() { | ||
return "Optional " + Masquerade.type(mask); | ||
}); | ||
}; | ||
Functionality.prototype.gobble = function() { | ||
return new Masquerade(function(value, next) { | ||
return true; | ||
Functionality.prototype.gobble = function(type) { | ||
return new Masquerade("Gobble", function(value, next) { | ||
if(type === undefined || Masquerade.match([type], [value])) return next(true); | ||
}); | ||
}; |
@@ -5,79 +5,96 @@ var _ = require('lodash'); | ||
function Masquerade(handler) { | ||
function Masquerade(name, handler) { | ||
this.name = name; | ||
this.handler = handler; | ||
} | ||
Masquerade.prototype.match = function(value, next) { | ||
if(this.handler.length <= 1) return this.handler(value) && next(); | ||
return this.handler(value, next); | ||
Masquerade.prototype.match = function(state) { | ||
if(this.handler.length <= 1) { | ||
var result = this.handler(state.value); | ||
state.next(); | ||
return result; | ||
} else | ||
return this.handler(state.value, state.next); | ||
}; | ||
Masquerade.match = function(mask, args) { | ||
if(!Array.isArray(mask)) { | ||
mask = [mask]; | ||
Masquerade.prototype.toString = function() { | ||
return this.name; | ||
}; | ||
Masquerade.prototype.withToString = function(formatter) { | ||
this.toString = formatter.bind(this); | ||
return this; | ||
}; | ||
Masquerade.match = function(masks, args, defaultFreeze) { | ||
if(!Array.isArray(masks)) { | ||
masks = [masks]; | ||
args = [args]; | ||
} else { | ||
mask = mask.slice(0); | ||
args = args.slice(0); | ||
} | ||
// Normalize masks | ||
mask = _.map(mask, function(m) { | ||
if(m instanceof Masquerade) return m; | ||
masks = _.map(masks, function(m) { | ||
return Masquerade.type(m); | ||
}); | ||
var next = function() { | ||
if(!mask.length) return !args.length; | ||
var m = mask.shift(), arg = args.shift(); | ||
return m.match(arg, next); | ||
}; | ||
var maskIndex = 0; | ||
var argIndex = 0; | ||
var state = { | ||
mask: masks[0], | ||
value: args[0], | ||
next: function(freeze) { | ||
if(freeze === undefined) freeze = defaultFreeze; | ||
if(!freeze) maskIndex++; | ||
argIndex++; | ||
return next(); | ||
}; | ||
state.mask = masks.length > maskIndex ? masks[maskIndex] : false; | ||
state.value = args.length > argIndex ? args[argIndex] : undefined; | ||
Masquerade.type = function(mask) { | ||
return new Masquerade(function(value) { | ||
// /regex/ => "a regex string" | ||
if(mask instanceof RegExp) return mask.test(value || ''); | ||
if(freeze && argIndex >= args.length) state.mask = false; | ||
// String => "string" | ||
if(mask === String) return typeof value == 'string'; | ||
// Boolean => true | ||
if(mask === Boolean) return typeof value == 'boolean'; | ||
// Number => 1 | ||
if(mask === Number) return typeof value == 'number'; | ||
return true; | ||
}.bind(state) | ||
}; | ||
// Function => function() { } | ||
if(mask === Function) return _.isFunction(value); | ||
while(state.mask) { | ||
var mask = state.mask; | ||
if(!mask.match(state)) | ||
return false; | ||
} | ||
return true; | ||
}; | ||
// Object => {}, new Object(), new String(), "" ... | ||
if(mask === Object) return _.isPlainObject(value); | ||
Masquerade.types = [ | ||
[String, new Masquerade("String", function(value) { return typeof value == 'string'; })], | ||
[Boolean, new Masquerade("Boolean", function(value) { return typeof value == 'boolean'; })], | ||
[Number, new Masquerade("Number", function(value) { return typeof value == 'number'; })], | ||
[Function, new Masquerade("Function", function(value) { return _.isFunction(value); })], | ||
[Object, new Masquerade("Object", function(value) { return _.isPlainObject(value); })], | ||
[RegExp, new Masquerade("RegExp", function(value) { return _.isRegExp(value); })], | ||
[Date, new Masquerade("Date", function(value) { return _.isDate(value); })], | ||
[Array, new Masquerade("Array", function(value) { return _.isArray(value); })] | ||
]; | ||
// RegExp => /regex/ | ||
if(mask === RegExp) return _.isRegExp(value); | ||
Masquerade.type = function(mask) { | ||
if(mask instanceof Masquerade) return mask; | ||
if(mask === null) return new Masquerade("null", function(value) { return value === null; }); | ||
if(mask === undefined) return new Masquerade("undefined", function(value) { return value === undefined; }); | ||
if(mask instanceof RegExp) return new Masquerade(mask.toString(), mask.test.bind(mask)); | ||
// Date => new Date() | ||
if(mask === Date) return _.isDate(value); | ||
for(var i = 0; i < Masquerade.types.length; i++) | ||
if(mask === Masquerade.types[i][0]) return Masquerade.types[i][1]; | ||
// null => null | ||
if(mask === null) return value === null; | ||
if(Array.isArray(mask) && mask.length === 1) return new Masquerade("[" + mask[0].name + "]", function(value) { | ||
if(!Array.isArray(value)) return false; | ||
if(!(mask[0] instanceof Masquerade)) mask = Masquerade.type(mask[0]); | ||
return Masquerade.match([mask], value, true); | ||
}); | ||
// undefined => undefined | ||
if(mask === undefined) return value === undefined; | ||
// Array => [...] | ||
if(mask === Array) return _.isArray(value); | ||
// [Number] => [1, 2, 3, 4] | ||
if(Array.isArray(mask) && mask.length == 1) | ||
return Array.isArray(value) && Masquerade.match(_.map(value, function() { return mask[0]; }), value); | ||
if(_.isPlainObject(mask)) return new Masquerade("Deep Object", function(value) { | ||
// { a: Number } => { a: 1 } | ||
if(_.isObject(mask)) | ||
return _.isObject(value) && _.every(mask, function(m, key) { return Masquerade.match(m, value[key]); }); | ||
return _.isObject(value) && _.every(mask, function(m, key) { return Masquerade.match(m, value[key]); }); | ||
}); | ||
return new Masquerade("InstanceOf", function(value) { | ||
// Masquerade => new Masquerade() | ||
@@ -84,0 +101,0 @@ return value instanceof mask; |
@@ -9,7 +9,11 @@ var Context = require('./Context.js'), | ||
this.__frozen = false; | ||
this.handlers = { | ||
var handlers = this.handlers = { | ||
first: null, | ||
on: [], | ||
or: function() { | ||
throw new Error("No overload for this method accepts the arguments " + JSON.stringify(this.args)); | ||
var error = "No overload for this method accepts the arguments (" + this.args.map(JSON.stringify).join(',') + ")\n"; | ||
error += "Available Overloads:\n"; | ||
for(var i = 0; i < handlers.on.length; i++) | ||
error += '(' + handlers.on[i].masks.join(', ') + ')\n'; | ||
throw new Error(error); | ||
}, | ||
@@ -51,8 +55,7 @@ then: function() { | ||
handler = masks.pop(); | ||
masks = masks.map(Masquerade.type); | ||
this.handlers.on.unshift(function() { | ||
if(Masquerade.match(masks, this.args)) { | ||
this.matched = true; | ||
return handler.apply(this, this.args); | ||
} | ||
this.handlers.on.unshift({ | ||
masks: masks, | ||
handler: handler | ||
}); | ||
@@ -76,11 +79,13 @@ return this; | ||
var ctx = new Context(context, this, args); | ||
ctx.handlers = this.handlers.on.slice(0); | ||
ctx.overloads = this.handlers.on.slice(); | ||
if(this.handlers.first) this.handlers.first.call(ctx); | ||
while(ctx.handlers.length && !ctx.matched) { | ||
var handler = ctx.handlers.shift(); | ||
while(ctx.overloads.length && !ctx.matched) { | ||
var overload = ctx.overloads.shift(); | ||
if(typeof handler == 'function') | ||
ctx.return(handler.call(ctx)); | ||
if(Masquerade.match(overload.masks, ctx.args)) { | ||
ctx.matched = true; | ||
ctx.return(overload.handler.apply(ctx, ctx.args)); | ||
} | ||
} | ||
@@ -87,0 +92,0 @@ |
{ | ||
"name": "functionality", | ||
"version": "1.3.2", | ||
"version": "2.0.0", | ||
"description": "Simple function overloading for JavaScript", | ||
@@ -5,0 +5,0 @@ "author": "Sierra Softworks", |
@@ -178,3 +178,3 @@ # Functionality | ||
### fn.mask(handler) | ||
### fn.mask(name, handler) | ||
**Creates a new mask option for use in your functions** | ||
@@ -187,11 +187,11 @@ | ||
```javascript | ||
var between1and10 = fn.mask(function(value) { | ||
var between1and10 = fn.mask("Between1And10", function(value) { | ||
return typeof value == 'number' && value > 1 && value < 10; | ||
}); | ||
var isEmpty = fn.mask(function(value, next) { | ||
var isEmpty = fn.mask("isEmpty", function(value, next) { | ||
return typeof value == 'string' && !value.length && next(); | ||
}) | ||
var startsWith1 = fn.mask(function(value, next) { | ||
var startsWith1 = fn.mask("Starts With", function(value, next) { | ||
return value === 1; | ||
@@ -198,0 +198,0 @@ }); |
@@ -18,4 +18,3 @@ var fn = require('../'); | ||
var f = fn.on(fn.not(String), function(o) { | ||
this.args[0] = JSON.stringify(o); | ||
return this.retry(); | ||
return this.retry(JSON.stringify(o)); | ||
}).on(String, function(s) { | ||
@@ -27,2 +26,3 @@ return s; | ||
f(1).should.equal('1'); | ||
f({ a: 1 }).should.equal('{"a":1}'); | ||
}); | ||
@@ -29,0 +29,0 @@ |
@@ -30,3 +30,3 @@ var Masquerade = require('../lib/Masquerade.js'); | ||
var optional = function(type) { | ||
return new Masquerade(function(value) { | ||
return new Masquerade("optional", function(value) { | ||
return value === undefined || Masquerade.match([type], [value]); | ||
@@ -132,3 +132,3 @@ }); | ||
it('should work correctly with basic validators', function() { | ||
var validator = new Masquerade(function(value) { | ||
var validator = new Masquerade("between 10 and 100", function(value) { | ||
return typeof value == 'number' && value >= 10 && value <= 100; | ||
@@ -144,4 +144,4 @@ }); | ||
it('should work correctly with greedy validators', function() { | ||
var gobble = new Masquerade(function(value, next) { | ||
return true; | ||
var gobble = new Masquerade("gobble", function(value, next) { | ||
return next(true); | ||
}); | ||
@@ -148,0 +148,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
30085
14
544