Permalink
Browse files

Switch to TypeScript

Allows us to easily ensure the validity of any code in the library and
gives users the option of IntelliSense with supporting IDEs.

Also added the ability to chain methods onto validators, with an
orUndefined() one provided by default. Added tests to ensure that it
functions as expected, and with minimal modifications to the tests (only
those necessary to adapt to the new chainable Validator object's API)
everything functions as expected. External API remains unchanged, except
for the addition of an `enforce.Validator` object to ease construction
of custom validators.

I have also added a translator between legacy calls to
`Enforce.add(property: string, validator: function(value: any, next:
(errorMessage?: string) => void, contexts?: ContextMap) => boolean);`
and the new Validator objects to help ensure a smooth transition between
any software using Enforce already and the new version.
  • Loading branch information...
1 parent 404d265 commit 2d53dfce73a7cb8051a0186d54ac24937feaac8b @SPARTAN563 SPARTAN563 committed Aug 14, 2013
View
@@ -51,8 +51,10 @@ checks.check({
## Validators
All validators accept a `msg` argument at the end. These argument is the error message returned if the
-validation fails. All validators return a `function` that is called by `Enforce` with the value of the property
-in question and a `next` callback.
+validation fails. All validators return a `Validator` object that is used by `Enforce` to support chaining
+and other functionality. `Validator` objects have a `validate` method which is called by `Enforce` with the
+value of the property in question, a `next` callback and an optional global context table which may be used to
+pass information between validators.
### Required
@@ -157,3 +159,33 @@ Checks if a property matches a predefined `RegExp` object accepting valid e-mail
`enforce.patterns.ipv4([ msg ])`
Checks if a property matches a predefined `RegExp` object accepting valid IPv4 address.
+
+## Chaining
+`Enforce` supports chaining operations on all `Validator` objects, these allow you to add additional common
+conditions to each validation step. All chain operations return `Validator` objects, allowing you to chain
+multiple commands together with ease.
+
+#### ifDefined
+
+`validation.ifDefined()`
+
+Only proceedes to check the `validation` if the property's value is not `null` or `undefined`, passing validation
+if it is.
+
+#### ifNotEmptyString
+
+`validation.ifNotEmptyString()`
+
+Only proceedes to check the `validation` if the property's value is a `string` with a length greater than 0.
+
+#### ifType
+
+`validation.ifType(type)`
+
+Only proceedes to check the `validation` if the property's value is of the specified type. Checked with a `typeof value == type` operation.
+
+#### ifNotType
+
+`validation.ifNotType(type)`
+
+Only proceedes to check the `validation` if the property's value is not of the specified type. Checked with a `typeof value != type` operation.
View
@@ -1,12 +1,33 @@
+/// <reference path="lib/node.d.ts" />
+/// <reference path="lib/enforce.d.ts" />
+/// <reference path="lib/enforcements/common.ts" />
+/// <reference path="lib/enforcements/lists.ts" />
+/// <reference path="lib/enforcements/ranges.ts" />
+/// <reference path="lib/enforcements/patterns.ts" />
+/// <reference path="lib/enforcements/security.ts" />
var common = require("./lib/enforcements/common");
+var Enforce = require("./lib/enforce");
+exports.Enforce = Enforce;
+var Validator = require("./lib/validator");
+exports.Validator = Validator;
+var lists = require("./lib/enforcements/lists");
+exports.lists = lists;
+var ranges = require("./lib/enforcements/ranges");
+exports.ranges = ranges;
+var patterns = require("./lib/enforcements/patterns");
+exports.patterns = patterns;
+var security = require("./lib/enforcements/security");
+exports.security = security;
-exports.security = require("./lib/enforcements/security");
-exports.patterns = require("./lib/enforcements/patterns");
-exports.ranges = require("./lib/enforcements/ranges");
-exports.lists = require("./lib/enforcements/lists");
+//Force TypeScript compiler to output our inputs (otherwise it optimizes them away)
+exports.Enforce.hasOwnProperty('x');
+exports.Validator.hasOwnProperty('x');
+exports.lists.hasOwnProperty('x');
+exports.ranges.hasOwnProperty('x');
+exports.patterns.hasOwnProperty('x');
+exports.security.hasOwnProperty('x');
for (var k in common) {
- exports[k] = common[k];
+ exports[k] = common[k];
}
-exports.Enforce = require("./lib/Enforce").Enforce;
View
@@ -0,0 +1,27 @@
+/// <reference path="lib/node.d.ts" />
+/// <reference path="lib/enforce.d.ts" />
+/// <reference path="lib/enforcements/common.ts" />
+/// <reference path="lib/enforcements/lists.ts" />
+/// <reference path="lib/enforcements/ranges.ts" />
+/// <reference path="lib/enforcements/patterns.ts" />
+/// <reference path="lib/enforcements/security.ts" />
+
+import common = require('lib/enforcements/common');
+export import Enforce = require('lib/enforce');
+export import Validator = require('lib/validator');
+export import lists = require('lib/enforcements/lists');
+export import ranges = require('lib/enforcements/ranges');
+export import patterns = require('lib/enforcements/patterns');
+export import security = require('lib/enforcements/security');
+
+//Force TypeScript compiler to output our inputs (otherwise it optimizes them away)
+Enforce.hasOwnProperty('x');
+Validator.hasOwnProperty('x');
+lists.hasOwnProperty('x');
+ranges.hasOwnProperty('x');
+patterns.hasOwnProperty('x');
+security.hasOwnProperty('x');
+
+for (var k in common) {
+ exports[k] = common[k];
+}
View
@@ -1,84 +1,91 @@
-exports.Enforce = Enforce;
-
-function Enforce(opts) {
- this.validations = {};
- this.ctx = {};
- this.options = {
- returnAllErrors : (opts && !!opts.returnAllErrors)
- };
-}
-
-Enforce.prototype.add = function (property, validator) {
- if (typeof validator != "function") {
- throw new Error("Missing validator (function) in Enforce.add(property, validator)");
- }
-
- if (!this.validations.hasOwnProperty(property)) {
- this.validations[property] = [];
- }
- this.validations[property].push(validator);
-
- return this;
-};
-
-Enforce.prototype.context = function (name, value) {
- if (arguments.length === 0) {
- return this.ctx;
- }
- if (arguments.length == 1) {
- return this.ctx[name];
- }
- this.ctx[name] = value;
- return this;
-};
-
-Enforce.prototype.clear = function () {
- this.validations = {};
-};
-
-Enforce.prototype.check = function (data, cb) {
- var validations = [];
- var errors = [];
- var next = function () {
- if (validations.length === 0) {
- return cb(errors.length > 0 ? errors : null);
- }
-
- var validation = validations.shift();
-
- this.ctx.property = validation.property;
-
- validation.validator.apply(data, [
- data[validation.property], function (msg) {
- if (msg) {
- var err = new Error(msg);
-
- err.property = validation.property;
- err.value = data[validation.property];
- err.msg = msg;
- err.type = "validation";
-
- if (!this.options.returnAllErrors) {
- return cb(err);
- }
-
- errors.push(err);
- }
-
- return next();
- }.bind(this),
- this.ctx
- ]);
- }.bind(this);
-
- for (var k in this.validations) {
- for (var i = 0; i < this.validations[k].length; i++) {
- validations.push({
- property : k,
- validator : this.validations[k][i]
- });
- }
- }
-
- return next();
-};
+/// <reference path="enforce.d.ts" />
+/// <reference path="validator.ts" />
+var Validator = require("./validator");
+
+var Enforce = (function () {
+ function Enforce(options) {
+ this.validations = {};
+ this.contexts = {};
+ this.options = {
+ returnAllErrors: options && !!options.returnAllErrors
+ };
+
+ return this;
+ }
+ Enforce.prototype.add = function (property, validator) {
+ if (typeof validator === 'function' && validator.length >= 2) {
+ validator = new Validator(validator);
+ }
+
+ if (validator.validate === undefined) {
+ throw new Error('Missing validator (function) in Enforce.add(property, validator)');
+ }
+
+ if (!this.validations.hasOwnProperty(property))
+ this.validations[property] = [];
+
+ this.validations[property].push(validator);
+ return this;
+ };
+
+ Enforce.prototype.context = function (name, value) {
+ if (name && value) {
+ this.contexts[name] = value;
+ return this;
+ } else if (name)
+ return this.contexts[name];
+else
+ return this.contexts;
+ };
+
+ Enforce.prototype.clear = function () {
+ this.validations = {};
+ };
+
+ Enforce.prototype.check = function (data, cb) {
+ var _this = this;
+ var validations = [];
+
+ var errors = [];
+ var next = function () {
+ if (validations.length === 0) {
+ return cb(errors.length > 0 ? errors : null);
+ }
+
+ var validation = validations.shift();
+ _this.contexts.property = validation.property;
+
+ validation.validator.validate(data[validation.property], function (message) {
+ if (message) {
+ var err = new Error(message);
+ err.property = validation.property;
+ err.value = data[validation.property];
+ err.msg = message;
+ err.type = "validation";
+
+ if (!this.options.returnAllErrors)
+ return cb([err]);
+ errors.push(err);
+ }
+
+ return next();
+ }.bind(_this), data, _this.contexts);
+ };
+
+ for (var k in this.validations) {
+ for (var i = 0; i < this.validations[k].length; i++) {
+ validations.push({
+ property: k,
+ validator: this.validations[k][i]
+ });
+ }
+ }
+
+ return next();
+ };
+ return Enforce;
+})();
+
+
+module.exports = Enforce;
+
View
@@ -0,0 +1,44 @@
+declare var exports: any
+
+declare module enforce {
+ export interface IEnforce {
+ add(property: string, validator: IValidator): IEnforce;
+ context(): any;
+ context(name: string): any;
+ context(name: string, value: any): IEnforce;
+ clear();
+ check(data: any, cb: (errors: Error[]) => void);
+ }
+
+ export interface EnforceStatic {
+ Enforce(options?: Options): IEnforce;
+ }
+
+ export interface Options {
+ returnAllErrors: boolean;
+ }
+
+ interface ContextMap {
+ property?: string;
+ [name: string]: any;
+ }
+
+ interface IValidator {
+ validate(data: any, next: (message?: string) => void, thisArg?: any, contexts?: enforce.ContextMap)
+ }
+
+ interface ValidationCallback {
+ (value: any, next: (errorMessage?: string) => boolean, contexts: ContextMap);
+ }
+
+ interface ValidatorMap {
+ [property: string]: IValidator[];
+ }
+
+ export interface ValidationError extends Error {
+ property?: string;
+ value?: any;
+ msg?: string;
+ type?: string;
+ }
+}
Oops, something went wrong.

0 comments on commit 2d53dfc

Please sign in to comment.