Dont forget to use --title to specify me! | |
| lib/guards.js |
vim:set ts=2 sw=2 sts=2 expandtab
|
|
jshint newcap: true undef: true es5: true node: true devel: true
forin: true
|
|
global define: true
|
(typeof define !== "function" ? function($){ $(require, exports, module); } : define)(function(require, exports, module, undefined) {
var utils = require("./type");
var Extendable = require("./extendables").Extendable;
var isArray = utils.isArray;
var isFunction = utils.isFunction;
var isUndefined = utils.isUndefined;
var isNull = utils.isNull;
var isNumber = utils.isNumber;
var isString = utils.isString;
var isObject = utils.isObject;
var isBoolean = utils.isBoolean;
var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
function getOwnPropertyDescriptors(object) {
var descriptors = {};
Object.getOwnPropertyNames(object).forEach(function(name) {
descriptors[name] = Object.getOwnPropertyDescriptor(object, name);
});
return descriptors;
}
var NO_DEFAULT = { valueOf: function() { return "Guard without default"; } };
exports.version = "0.2.0";
|
Guard
Function takes isValid function and message as an argument and returns
guard generator function. Returned function may be used to generate value
guards that are validated with given isValid function. Please see
exports.Number or exports.String for more details.
##
param: Function isValid Function that will be used by guard to validate values. It will be called
with value argument that needs to be validated. If function returns
true value is valid if false it's not. param: String [message="Unexpected value: `{{value}}`"] Optional message argument may be passed that will be used as a template
for a TypeError message that is thrown by generated guards when called
with invalid values.
|
|
Guard
Guard function that may be called with a value to be set to a guarded
variable. If value is invalid TypeError is thrown. Optionally second
name argument may be passed, which is useful when guards are used for
object properties. In such case name argument just name of object
property and will be used to give a better error messages.
##
|
var Guard = Extendable.extend({
constructor: function Guard(value, name) {
if (this.defaults !== NO_DEFAULT && isUndefined(value))
value = this.defaults;
if (!this.isValid(value))
throw new TypeError(this.message.replace("{{name}}", name).
replace("{{value}}", value).
replace("{{type}}", typeof value));
return this.validate(value, name);
},
defaults: NO_DEFAULT,
|
Default error message used to by TypeError thrown when invalid value
is used.
|
message: "Unexpected value: `{{value}}`",
isValid: function isValid(value) {
return true;
},
|
|
validate: function validate(value, name) {
return value;
}
});
Guard.isGuard = true;
exports.Guard = Guard;
|
Function
Function creates a function guard - function that may be used to perform run
time type checks on the values. Function takes an optional defaultValue
that will be returned by a created guard if it's invoked without (or with
undefined ) value argument. The defaultValue will fall back to
undefined if not provided. Function also takes another optional argument
message that represents a template of a message of the TypeError that
will be thrown by guard if it's invoked with invalid value .
param: Object | String | Number | function [defaultValue] Value that returned guard is going to fall back to if invoked without
a value or if it's undefined . param: String [message] Optional error message template that will be a message of a TypeError
that is will be thrown if returned guard is invoked with a wrong value
type. If message contains "{{value}}" and "{{type}}" strings they
are going to be replaced with an actual value and it's type.
Examples
var guards = require("guards");
var Callee = guards.Function("Anonymous");
Callee(Object) == Object
// true
Callee(function() { return "hello world" })
Callee(function() { return "hello world" })
Callee(7);
// TypeError: Function expected instead of number `7`
guards.Function("Hi", "{{type}} is not a function.");
Callee({})
// TypeError: object is not a function
|
exports.Function = Guard.extend({
isValid: isFunction,
message: "Function expected instead of {{type}} `{{value}}`"
});
|
String
Function creates a string guard - function that may be used to perform run
time type checks on the values. Function takes an optional defaultValue
that will be returned by a created guard if it's invoked without (or with
undefined ) value argument. The defaultValue will fallback to
undefined if not provided. Function also takes another optional argument
message that represents a template of a message of the TypeError that
will be thrown by guard if it's invoked with incorrect value.
param: Object | String | Number | function [defaultValue] Value that returned guard is going to fall back to if invoked without
a value or if it's undefined . param: String [message] Optional error message template that will be a message of a TypeError
that is will be thrown if returned guard is invoked with a wrong value
type. If message contains "{{value}}" and "{{type}}" strings they
are going to be replaced with an actual value and it's type.
Examples
var guards = require("guards");
var gUser = guards.String("Anonymous");
var user1 = gUser("Jack");
// "Jack"
var user2 = gUser();
// "Anonymous"
var user3 = gUser(7);
// TypeError: String expected instead of number `7`
var gHi = guards.String("Hi", "string expected not a {{type}}");
var msg = gHi(function() {});
// TypeError: string expected not a function
|
exports.String = Guard.extend({
isValid: isString,
message: "String expected instead of {{type}} `{{value}}`"
});
|
Number
Function creates a number guard - function that may be used to perform run
time type checks on the values. Function takes an optional defaultValue
that will be returned by a created guard if it's invoked without (or with
undefined ) value argument. The defaultValue will fallback to
undefined if not provided. Function also takes another optional argument
message that represents a template of a message of the TypeError that
will be thrown by guard if it's invoked with incorrect value.
param: Object | String | Number | function [defaultValue] Value that returned guard is going to fall back to if invoked without
a value or if it's undefined . param: String [message] Optional error message template that will be a message of a TypeError
that is will be thrown if returned guard is invoked with a wrong value
type. If message contains "{{value}}" and "{{type}}" strings they
are going to be replaced with an actual value and it's type.
examples
var guards = require("guards")
var Length = guards.Number.extend({ defaults: 0 })
Length()
// 0
Length(17)
// 17
Length("7")
// TypeError: Number expected instead of string `7`
var Count = guards.Number.extend({
defaults: 0,
message: "number expected not a {{type}}"
});
Count({ value: 4 });
// TypeError: number expected not a object
|
exports.Number = Guard.extend({
isValid: isNumber,
message: "Number expected instead of {{type}} `{{value}}`"
});
exports.Boolean = Guard.extend({
isValid: isBoolean,
message: "Boolean expected instead of {{type}} `{{value}}`"
});
exports.Null = Guard.extend({
isValid: isNull,
message: "Boolean expected instead of {{type}} `{{value}}`"
});
|
Schema
Schema is useful for defining guards for data objects that have particular
structure. Function takes descriptor argument that is a map of guards
guarding same named properties of the value being validated. Scheme may
contain guards for a primitive values like String and Number and also
guards for more complex data structures defined by other Schema s, or to
put it other way Schema may contain guards that were created by Schema
itself which allows defining deeply nested data structures.
Generated guard will accept only object values as an argument. All the
non-guarded properties (that are not present in the descriptor ) of the
value will be stripped out. All the missing properties of the value
will be replaced / assembled from the defaults if associated guards provide
fallback mechanism to default value.
param: Object descriptor Object containing guards for the associated (same named properties) of
the guarded object value . param: String [message] Optional error message template that will be a message of a TypeError
that is will be thrown if returned guard is invoked with a wrong value
type (other then "object" or "undefined"). If message contains
"{{value}}" and "{{type}}" strings they are going to be replaced with
an actual value and it's type.
Examples
var guards = require("guards");
var Point = guards.Schema.extend({
x: guards.Number.extend({ defaults: 0 }),
y: guards.Number.extend({ defaults: 0 })
})
new Point
// { x: 0, y: 0 }
Point({ x: 17, z: 50 })
// { x: 17, y: 0 }
Point({ x: "5" })
// TypeError: Number expected instead of string `5`
Point("{ y: 6 }")
// TypeError: Object expected instead of string `{ y: 6 }`
var Segment = guards.Schema.extend({
start: Point,
end: Point,
opacity: guards.Number.extend({ defaults: 1 })
})
new Segment
// { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 }
Segment({ end: { x: 17 }, opacity: 0.5 })
// { start: { x: 0, y: 0 }, end: { x: 17, y: 0 }, opacity: 0.5 }
Segment({ start: 17 });
// TypeError: Object expected instead of number `17`
|
var Schema = Guard.extend({
message: "Object expected instead of {{type}} `{{value}}`",
isValid: function isValid(value, name) {
return isUndefined(value) || (isObject(value) && !isArray(value));
},
validate: function validate(value, name) {
value = value || {};
var data = {};
for (var key in this) {
if (this[key].isGuard)
data[key] = this[key](value[key], name);
}
return data;
}
});
exports.Schema = Schema;
|
Array
Array can be used to define guards for an arrays containing elements of some
type or schema. Function takes guard as an argument that will guard all the
elements of the value array that is passed to the returned guard.
param: Function guard Guard that is going to be used to verify elements of the array. It can
be any guard created by String , Schema , Array or any custom guard
as well. param: String [message] Optional error message template that will be a message of a TypeError
that is will be thrown if returned guard is invoked with a wrong value
type (other then "array" or "undefined"). If message contains
"{{value}}" and "{{type}}" strings they are going to be replaced with
an actual value and it's type.
Examples
var guards = require("guards")
var Words = guards.Array.extend([
guards.String.extend({ defaults: "" })
])
Words([ "foo", "bar" ])
// [ 'foo', 'bar' ]
Words([ "foo", 9 ])
// TypeError: String expected instead of number `9`
var Point = guards.Schema.extend({
x: guards.Number.extend({ defaults: 0 }),
y: guards.Number.extend({ defaults: 0 })
})
var Points = guards.Array.extend([ Point ])
new Points([{}, { x: 2, y: 8 }])
// [ { x: 0, y: 0 }, { x: 2, y: 8 } ]
Points({ x: 2, y: 8 })
// TypeError: Array expected instead of object `[object Object]`
var Graph = guards.Array.extend([ Points ])
new Graph
// []
Graph([
[{ x: 17, foo: "bar" }, { x: 16 }],
[{ y: 4 }],
[]
])
// [ [ { x: 17, y: 0 }, { x: 16, y: 0 } ], [ { x: 0, y: 4 } ], [] ]
|
var Array = Guard.extend({
message: "Array expected instead of {{type}} `{{value}}`",
isValid: function isValid(value) {
return isArray(value) || isUndefined(value);
},
validate: function validate(value, name) {
value = value || [];
return value.map(function(value, index) {
return this[0](value, index);
}, this);
}
});
exports.Array = Array;
|
Tuple
Tuple can be used to define guards for an arrays containing predefined
amount of elements guarded by specific guards. Tuple guards are something
in between Array and Schema guards. Function takes array of guards as an
argument that will be used to validate same indexed elements of the value
array that is passed to the returned guard.
param: Function[] guards Guards that are going to be used to verify elements of the array. It can
be any guard created by String , Schema , Array or any custom guard
as well. param: String [message] Optional error message template that will be a message of a TypeError
that is will be thrown if returned guard is invoked with a wrong value
type (other then "array" or "undefined"). If message contains
"{{value}}" and "{{type}}" strings they are going to be replaced with
an actual value and it's type.
Examples
var guards = require("guards");
var Point = guards.Schema({
x: guards.Number(0),
y: guards.Number(0)
});
var Segment = guards.Schema({
start: Point,
end: Point,
opacity: guards.Number(1)
});
var Triangle = guards.Tuple([ Segment, Segment, Segment ]);
var t1 = Triangle();
// [ { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 },
// { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 },
// { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 }
// ]
var t2 = Triangle([
{ opacity: 0, foo: "bar" },
{ start: { x: 2 } }
]);
// [ { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 0 },
// { start: { x: 2, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 },
// { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 }
// ]
var t2 = Triangle("foo");
// TypeError: Array expected instead of string `foo`
var t3 = Triangle([{ start: { x: '3' } } ]);
// TypeError: Number expected instead of string `3`
var Pointer = guards.Tuple([ Point, Segment ]);
var p1 = Pointer();
// [ { x: 0, y: 0 }, { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 } ]
var p2 = Pointer([ { x: 17 }, { opacity: 0 } ]);
// [ { x: 17, y: 0 }, { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 0 } ]
var p3 = Pointer([ { foo: "bar" }, { baz: "bla" }, "foo" ]);
// [ { x: 0, y: 0 }, { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 } ]
|
var Tuple = Guard.extend({
message: "Array expected instead of {{type}} `{{value}}`",
isValid: function isValid(value) {
return isArray(value) || isUndefined(value);
},
validate: function validate(value, name) {
value = value || [];
var data = [];
for (var key in this) {
if (this[key].isGuard)
data[key] = this[key](value[key], name);
}
return data;
}
});
exports.Tuple = Tuple;
|
AnyOf
AnyOf can be used to define guards that validates value s that must
satisfy just one of many guards. This is handy in specific specific
scenarios were valid value may have different types or schemas.
Function takes any number guards as an arguments and returns composed
guard, which when called will try to validate a given value with a given
guards in an order they were passed, the first validate value is returned
as result, unless non will validate in which case TypeError is thrown.
Examples
var guards = require("guards")
var ObjectPoint = guards.Schema.extend({
x: guards.Number.extend({ defaults: 0 }),
y: guards.Number.extend({ defaults: 0 })
})
var ArrayPoint = guards.Tuple.extend([
guards.Number.extend({ defaults: 0 }),
guards.Number.extend({ defaults: 0 })
])
var Point = guards.AnyOf.extend([ ObjectPoint, ArrayPoint ]);
Point([ 1 ])
// [ 1, 0 ]
Point({ y: 15 })
// { x: 0, y: 15 }
Point(1, 2)
// TypeError: Passed value: `1` has invalid type or structure
|
var AnyOf = Guard.extend({
message: "Passed value: `{{value}}` has invalid type or structure",
validate: function validate(value, name) {
for (var key in this) {
if (this[key].isGuard)
try { return this[key](value, name); } catch(error) {}
}
throw new TypeError(this.message.replace("{{value}}", value));
}
});
exports.AnyOf = AnyOf;
});
|