guards
JavaScript library for data type & data structure validations providing a
runtime analog of types. Check out docs for more details.
Install
npm install guards
Example
var guards = require("guards");
var Point = guards.Schema({
x: guards.Number(0),
y: guards.Number(0)
});
function color(value) {
if (typeof value === "number" && value <= 255 && value >= 0)
return value
throw new TypeError("Color is a number between 0 and 255");
}
var RGB = guards.Tuple([ color, color, color ]);
var Segment = guards.Schema({
start: Point,
end: Point,
color: RGB,
});
var segment = Segment({ end: { y: 23 }, color: [17, 255, 0] })
// { start: { x: 0, y: 0 }, end: { x: 0, y: 23 }, color: [ 17, 255, 0 ] }
Prior art
| |
|
vim:ts=2:sts=2:sw=2:
BEGIN LICENSE BLOCK
## Version MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is Jetpack.
The Initial Developer of the Original Code is
the Mozilla Foundation.
Portions created by the Initial Developer are Copyright (C) 2010
the Initial Developer. All Rights Reserved.
Contributor(s):
Irakli Gozalishvili gozala@mozilla.com (Original Author)
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
END LICENSE BLOCK
|
"use strict";
var isArray, isFunction, isObject, isUndefined, isNumber, isString, utils;
utils = require("./type");
isArray = utils.isArray;
isFunction = utils.isFunction;
isUndefined = utils.isUndefined;
isNumber = utils.isNumber;
isString = utils.isString;
isObject = utils.isObject;
|
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 = function String(defaultValue, message) {
message = message || "String expected instead of {{type}} `{{value}}`";
return function GString(value, key) {
if (isUndefined(value))
value = defaultValue;
else if (!isString(value))
throw new TypeError(message.replace("{{key}}", key)
.replace("{{value}}", value)
.replace("{{type}}", typeof value));
return 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 gLength = guards.Number(0);
var length1 = gLength();
// 0
var length2 = gLength(17);
// 17
var length3 = gLength("7");
// TypeError: Number expected instead of string `7`
var gCount = guards.Number(0, "number expected not a {{type}}");
var count = gCount({ value: 4 });
// TypeError: number expected not a object
|
exports.Number = function Number(defaultValue, message) {
message = message || "Number expected instead of {{type}} `{{value}}`";
return function GNumber(value, key) {
if (isUndefined(value))
value = defaultValue;
else if (!isNumber(value))
throw new TypeError(message.replace("{{key}}", key)
.replace("{{value}}", value)
.replace("{{type}}", typeof value));
return 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 Schemas, 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({
x: guards.Number(0),
y: guards.Number(0)
});
var p1 = Point();
// { x: 0, y: 0 }
var p2 = Point({ x: 17, z: 50 });
// { x: 17, y: 0 }
var p3 = Point({ x: "5" });
// TypeError: Number expected instead of string `5`
var p4 = Point("{ y: 6 }");
// TypeError: Object expected instead of string `{ y: 6 }`
var Segment = guards.Schema({
start: Point,
end: Point,
opacity: guards.Number(1)
});
var s1 = Segment();
// { start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, opacity: 1 }
var s2 = Segment({ end: { x: 17 }, opacity: 0.5 });
// { start: { x: 0, y: 0 }, end: { x: 17, y: 0 }, opacity: 0.5 }
var s3 = Segment({ start: 17 });
// TypeError: Object expected instead of number `17`
|
var Schema = exports.Schema = function Schema(descriptor, message) {
message = message || "Object expected instead of {{type}} `{{value}}`";
return function GSchema(value, key) {
var data;
if (isUndefined(value))
value = {};
if (!isObject(value))
throw new TypeError(message.replace("{{key}}", key)
.replace("{{value}}", value)
.replace("{{type}}", typeof value));
data = {};
Object.keys(descriptor).forEach(function(key) {
var guard = descriptor[key];
data[key] = guard(value[key], key);
}, this);
return data;
};
};
|
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(guards.String(""));
var ww1 = Words([ "foo", "bar" ]);
// [ 'foo', 'bar' ]
var ww2 = Words([ "foo", 9 ]);
// TypeError: String expected instead of number `9`
var Point = guards.Schema({
x: guards.Number(0),
y: guards.Number(0)
});
var Points = guards.Array(Point);
var pp1 = Points([{}, { x: 2, y: 8 }]);
// [ { x: 0, y: 0 }, { x: 2, y: 8 } ]
var pp2 = Points({ x: 2, y: 8 });
// TypeError: Array expected instead of object `[object Object]`
var Graph = guards.Array(Points);
var g1 = Graph([]);
// []
var g2 = Graph([
[{ x: 17, foo: "bar" }, { x: 16 }],
[{ y: 4 }],
[]
]);
// [ [ { x: 17, y: 0 }, { x: 16, y: 0 } ], [ { x: 0, y: 4 } ], [] ]
|
exports.Array = function Array(guard, message) {
message = message || "Array expected instead of {{type}} `{{value}}`";
return function GArray(value, key) {
if (isUndefined(value))
value = [];
if (!isArray(value))
throw new TypeError(message.replace("{{key}}", key)
.replace("{{value}}", value)
.replace("{{type}}", typeof value));
return value.map(function(value, index) {
return guard(value, index);
}, this);
};
};
|
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 } ]
|
exports.Tuple = function Tuple(guards, message) {
message = message || "Array expected instead of {{type}} `{{value}}`";
return function GTuple(value, key) {
if (isUndefined(value))
value = [];
if (!isArray(value))
throw new TypeError(message.replace("{{key}}", key)
.replace("{{value}}", value)
.replace("{{type}}", typeof value));
return guards.map(function(guard, index) {
return guard(value[index], index);
}, this);
};
};
|