update string+ and test

This commit is contained in:
eleith 2012-09-10 04:26:31 -07:00
parent 81eb3f30db
commit 4e442e3ce5
5 changed files with 191 additions and 614 deletions

View File

@ -1,420 +0,0 @@
(function()
{
var Rules = function(param, rules)
{
this.param = param;
this.rules = rules;
};
Rules.prototype.Error = function(message, rule, value)
{
switch(typeof(this.rules.error))
{
case 'string':
message = this.rules.error;
break;
case 'object':
if(this.rules.error[rule])
{
message = this.rules.error[rule];
}
else if(this.rules.error['default'])
{
message = this.rules.error['default'];
}
break;
}
if(_.isString(value))
message = message.replace(/%v/, value.toString());
if(_.isString(rule))
message = message.replace(/%r/, rule);
return message.replace(/%p/, this.param).replace(/%v/, "value").replace(/%r/, "rule");
};
Rules.prototype.apply = function(value)
{
if(_.isEmpty(value) && !_.isUndefined(this.rules['default']))
{
value = this.rules['default'];
}
if(this.rules.required)
{
if(this.rules.required && _.isUndefined(value))
{
throw this.Error("%p is a required parameter", "required", value);
}
}
// if value is not required and is undefined, no more rules need to be run
else if(_.isUndefined(value))
return value;
if(this.rules.filters)
{
value = this.filter(value);
}
if(this.rules.type)
{
if(typeof(this.rules.type) == "string" && typeof(is[this.rules.type]) == "function")
{
if(!is[this.rules.type](value))
throw this.Error("%p is not a " + this.rules.type, "type", value);
}
else if(typeof(this.rules.type == "function"))
{
if(!this.rules.type(value))
throw this.Error("%p is not a valid type", "type", value);
}
else
{
throw this.Error("%p is not a valid type", "type", value);
}
}
if(this.rules.properties)
{
this.check(value);
}
return value;
};
Rules.prototype.filter = function(value)
{
switch(typeof(this.rules.filters))
{
case 'function':
value = this.rules.filters(value);
break;
case 'string':
if(typeof(filters[this.rules.filters]) === 'function')
{
value = filters[this.rules.filters](value);
}
break;
case 'object':
if(_.isArray(this.rules.filters))
{
this.rules.filters.forEach(function(filter) { value = filters[filter](value); });
}
break;
}
return value;
};
Rules.prototype.check = function(value)
{
switch(typeof(this.rules.properties))
{
case 'function':
if(!this.rules.properties(value))
throw this.Error("%p is not valid");
break;
case 'object':
var properties = _.keys(this.rules.properties);
for(var i = 0; i < properties.length; i++)
{
var property = properties[i];
if(typeof(checks[property]) === "function")
{
var args = this.rules.properties[property];
if(!checks[property].apply(null, [value, args]))
throw this.Error("%p failed %r with %v", property);
}
else if(typeof(this.rules.properties[property]) === "function")
{
if(!this.rules.properties[property].apply(null, [value]))
throw this.Error("%p failed on %r with %v", property);
}
}
break;
}
};
var checks =
{
'max': function(value, length)
{
if(_.isArray(value) || typeof(value) == "string")
{
return value.length <= length;
}
else if(typeof(value) == "number")
{
return value <= length;
}
else
{
return false;
}
},
'min': function(value, length)
{
if(_.isArray(value) || typeof(value) == "string")
{
return value.length >= length;
}
else if(typeof(value) == "number")
{
return value >= length;
}
else
{
return false;
}
},
'regex': function(value, regex)
{
return regex.test(value);
},
'in': function(value, list)
{
return list.indexOf(value) != -1;
}
};
var is =
{
'string+': function(value)
{
return typeof(value) == 'string' && value !== "";
},
'string': function(value)
{
return typeof(value) == 'string';
},
'alphanum': function(value)
{
return (/^[a-zA-Z0-9]+$/i).test(value);
},
'alpha': function(value)
{
return (/^[a-zA-Z]+$/i).test(value);
},
'object': function(value)
{
return typeof(value) == 'object' && !_.isArray(value);
},
'array': function(value)
{
return _.isArray(value);
},
'date': function(value)
{
// getTime() allows us to check if date is valid
return _.isDate(value) && !isNaN(value.getTime());
},
'number': function(value)
{
return typeof(value) == 'number' && !isNaN(value);
},
'int': function(value)
{
return typeof(value) == 'number' && value % 1 === 0 && !isNaN(value);
},
'boolean': function(value)
{
return _.isBoolean(value);
},
'float': function(value)
{
return typeof(value) == 'number' && !isNaN(value);
},
'email': function(value)
{
return (/[a-z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-z0-9!#$%&'*+\/=?\^_`{|}~\-]+)*@(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?/).test(value);
},
'url': function(value)
{
return (/^(http(s)?:\/\/)?([\w\-]+\.{1})*(([\w\-]*){1}(\.{1}[A-Za-z]{2,4}){1}){1}(\:{1}\d*)?([\?\/|\#]+[\@\~\;\+\'\#\,\.\%\-\/\&\?\=\w\$\s]*)*$/i).test(value);
},
'zipcode': function(value)
{
return (/\d{5}/).test(value);
}
};
var filters =
{
'toInt': function(value)
{
return parseInt(value, 10);
},
'toFloat': function(value)
{
return parseFloat(value, 10);
},
'toString': function(value)
{
return value.toString();
},
'toDate': function(value)
{
return new Date(value);
},
'toBoolean': function(value)
{
if(value === 1 || value === true || /true|on|yes|1/.test(value))
return true;
else if(value === 0 || value === false || /false|off|no|0/.test(value))
return false;
else
return value;
},
'trim': function(value)
{
return _.isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
},
'lowercase': function(value)
{
return _.isString(value) ? value.toLowerCase() : value;
},
'uppercase': function(value)
{
return _.isString(value) ? value.toUpperCase() : value;
}
};
var rules =
{
create: function(param, rules) { return new Rules(param, rules); },
types: is,
filters: filters,
properties: checks
};
var Schema = function(schema)
{
this.schema = schema;
};
Schema.prototype.validate = function(data)
{
var params = _.keys(this.schema);
var errors = {};
var values = {};
var value;
var get = typeof(data) == "function" ? data : function(p) { return data[p]; };
for(var i = 0; i < params.length; i++)
{
var schema = this.schema[params[i]];
try
{
// if undefined, don't store it.
value = rules.create(params[i], schema).apply(get(params[i]));
if(!_.isUndefined(value))
{
values[params[i]] = value;
}
// does this rule contain embedded schemas
if(typeof(schema.schema) == "object" && !_.isArray(schema.schema) && _.keys(schema.schema).length && !_.isUndefined(values[params[i]]))
{
if(schema.type == "object")
{
_.keys(schema.schema).forEach(function(param)
{
try
{
// if undefined, don't store it
value = rules.create(params[i] + "." + param, schema.schema[param]).apply(get(params[i])[param]);
if(!_.isUndefined(value))
{
values[params[i]][param] = value;
}
}
catch(error)
{
if(!errors[params[i]] || typeof(errors[params[i]]) != 'object')
errors[params[i]] = {};
errors[params[i]][param] = error;
}
});
}
else if(schema.type == "array")
{
values[params[i]].forEach(function(value, index)
{
try
{
// if not required and undefined, don't store in values!
values[params[i]][index] = rules.create(params[i] + "[" + index + "]", schema.schema).apply(value);
}
catch(error)
{
if(!_.isArray(errors[params[i]]))
errors[params[i]] = [];
errors[params[i]][index] = error;
}
});
}
}
}
catch(error)
{
errors[params[i]] = error;
}
}
return {data:values, errors:errors, valid:_.keys(errors).length === 0};
};
_.schema =
{
test: function(value, schema) { return (new Schema({input:schema})).validate({input:value}); },
create: function(schema) { return new Schema(schema);},
rules: rules
};
})();

View File

@ -2,31 +2,31 @@ var _ = require('underscore');
var Rules = function(param, rules) var Rules = function(param, rules)
{ {
this.param = param; this.param = param;
this.rules = rules; this.rules = rules;
}; };
Rules.prototype.Error = function(message, rule, value) Rules.prototype.Error = function(message, rule, value)
{ {
switch(typeof(this.rules.error)) switch(typeof(this.rules.error))
{ {
case 'string': case 'string':
message = this.rules.error; message = this.rules.error;
break; break;
case 'object': case 'object':
if(this.rules.error[rule]) if(this.rules.error[rule])
{ {
message = this.rules.error[rule]; message = this.rules.error[rule];
} }
else if(this.rules.error['default']) else if(this.rules.error['default'])
{ {
message = this.rules.error['default']; message = this.rules.error['default'];
} }
break; break;
} }
if(_.isString(value)) if(_.isString(value))
message = message.replace(/%v/, value.toString()); message = message.replace(/%v/, value.toString());
@ -34,34 +34,34 @@ Rules.prototype.Error = function(message, rule, value)
if(_.isString(rule)) if(_.isString(rule))
message = message.replace(/%r/, rule); message = message.replace(/%r/, rule);
return message.replace(/%p/, this.param).replace(/%v/, "value").replace(/%r/, "rule"); return message.replace(/%p/, this.param).replace(/%v/, "value").replace(/%r/, "rule");
}; };
Rules.prototype.apply = function(value) Rules.prototype.apply = function(value)
{ {
if(_.isEmpty(value) && !_.isUndefined(this.rules['default'])) if(_.isEmpty(value) && !_.isUndefined(this.rules['default']))
{ {
value = this.rules['default']; value = this.rules['default'];
} }
if(this.rules.required) if(this.rules.required)
{ {
if(this.rules.required && _.isUndefined(value)) if(this.rules.required && _.isUndefined(value))
{ {
throw this.Error("%p is a required parameter", "required", value); throw this.Error("%p is a required parameter", "required", value);
} }
} }
// if value is not required and is undefined, no more rules need to be run // if value is not required and is undefined, no more rules need to be run
else if(_.isUndefined(value)) else if(_.isUndefined(value))
return value; return value;
if(this.rules.filters) if(this.rules.filters)
{ {
value = this.filter(value); value = this.filter(value);
} }
if(this.rules.type) if(this.rules.type)
{ {
if(typeof(this.rules.type) == "string" && typeof(is[this.rules.type]) == "function") if(typeof(this.rules.type) == "string" && typeof(is[this.rules.type]) == "function")
{ {
if(!is[this.rules.type](value)) if(!is[this.rules.type](value))
@ -76,87 +76,87 @@ Rules.prototype.apply = function(value)
{ {
throw this.Error("%p is not a valid type", "type", value); throw this.Error("%p is not a valid type", "type", value);
} }
} }
if(this.rules.properties) if(this.rules.properties)
{ {
this.check(value); this.check(value);
} }
return value; return value;
}; };
Rules.prototype.filter = function(value) Rules.prototype.filter = function(value)
{ {
switch(typeof(this.rules.filters)) switch(typeof(this.rules.filters))
{ {
case 'function': case 'function':
value = this.rules.filters(value); value = this.rules.filters(value);
break; break;
case 'string': case 'string':
if(typeof(filters[this.rules.filters]) === 'function') if(typeof(filters[this.rules.filters]) === 'function')
{ {
value = filters[this.rules.filters](value); value = filters[this.rules.filters](value);
} }
break; break;
case 'object': case 'object':
if(_.isArray(this.rules.filters)) if(_.isArray(this.rules.filters))
{ {
this.rules.filters.forEach(function(filter) { value = filters[filter](value); }); this.rules.filters.forEach(function(filter) { value = filters[filter](value); });
} }
break; break;
} }
return value; return value;
}; };
Rules.prototype.check = function(value) Rules.prototype.check = function(value)
{ {
switch(typeof(this.rules.properties)) switch(typeof(this.rules.properties))
{ {
case 'function': case 'function':
if(!this.rules.properties(value)) if(!this.rules.properties(value))
throw this.Error("%p is not valid"); throw this.Error("%p is not valid");
break; break;
case 'object': case 'object':
var properties = _.keys(this.rules.properties); var properties = _.keys(this.rules.properties);
for(var i = 0; i < properties.length; i++) for(var i = 0; i < properties.length; i++)
{ {
var property = properties[i]; var property = properties[i];
if(typeof(checks[property]) === "function") if(typeof(checks[property]) === "function")
{ {
var args = this.rules.properties[property]; var args = this.rules.properties[property];
if(!checks[property].apply(null, [value, args])) if(!checks[property].apply(null, [value, args]))
throw this.Error("%p failed %r with %v", property); throw this.Error("%p failed %r with %v", property);
} }
else if(typeof(this.rules.properties[property]) === "function") else if(typeof(this.rules.properties[property]) === "function")
{ {
if(!this.rules.properties[property].apply(null, [value])) if(!this.rules.properties[property].apply(null, [value]))
throw this.Error("%p failed on %r with %v", property); throw this.Error("%p failed on %r with %v", property);
} }
} }
break; break;
} }
}; };
var checks = var checks =
{ {
'max': function(value, length) 'max': function(value, length)
{ {
if(_.isArray(value) || typeof(value) == "string") if(_.isArray(value) || typeof(value) == "string")
{ {
return value.length <= length; return value.length <= length;
@ -169,10 +169,10 @@ var checks =
{ {
return false; return false;
} }
}, },
'min': function(value, length) 'min': function(value, length)
{ {
if(_.isArray(value) || typeof(value) == "string") if(_.isArray(value) || typeof(value) == "string")
{ {
return value.length >= length; return value.length >= length;
@ -185,30 +185,30 @@ var checks =
{ {
return false; return false;
} }
}, },
'regex': function(value, regex) 'regex': function(value, regex)
{ {
return regex.test(value); return regex.test(value);
}, },
'in': function(value, list) 'in': function(value, list)
{ {
return list.indexOf(value) != -1; return list.indexOf(value) != -1;
} }
}; };
var is = var is =
{ {
'string+': function(value) 'string+': function(value)
{ {
return typeof(value) == 'string' && value !== ""; return typeof(value) == 'string' && value.length && !(/\s+/.test(value));
}, },
'string': function(value) 'string': function(value)
{ {
return typeof(value) == 'string'; return typeof(value) == 'string';
}, },
'alphanum': function(value) 'alphanum': function(value)
{ {
@ -220,104 +220,104 @@ var is =
return (/^[a-zA-Z]+$/i).test(value); return (/^[a-zA-Z]+$/i).test(value);
}, },
'object': function(value) 'object': function(value)
{ {
return typeof(value) == 'object' && !_.isArray(value); return typeof(value) == 'object' && !_.isArray(value);
}, },
'array': function(value) 'array': function(value)
{ {
return _.isArray(value); return _.isArray(value);
}, },
'date': function(value) 'date': function(value)
{ {
// getTime() allows us to check if date is valid // getTime() allows us to check if date is valid
return _.isDate(value) && !isNaN(value.getTime()); return _.isDate(value) && !isNaN(value.getTime());
}, },
'number': function(value) 'number': function(value)
{ {
return typeof(value) == 'number' && !isNaN(value); return typeof(value) == 'number' && !isNaN(value);
}, },
'int': function(value) 'int': function(value)
{ {
return typeof(value) == 'number' && value % 1 === 0 && !isNaN(value); return typeof(value) == 'number' && value % 1 === 0 && !isNaN(value);
}, },
'boolean': function(value) 'boolean': function(value)
{ {
return _.isBoolean(value); return _.isBoolean(value);
}, },
'float': function(value) 'float': function(value)
{ {
return typeof(value) == 'number' && !isNaN(value); return typeof(value) == 'number' && !isNaN(value);
}, },
'email': function(value) 'email': function(value)
{ {
return (/[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?/).test(value); return (/[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+\/=?\^_`{|}~\-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?/).test(value);
}, },
'url': function(value) 'url': function(value)
{ {
return (/^(http(s)?:\/\/)?([\w\-]+\.{1})*(([\w\-]*){1}(\.{1}[A-Za-z]{2,4}){1}){1}(\:{1}\d*)?([\?\/|\#]+[\@\~\;\+\'\#\,\.\%\-\/\&\?\=\w\$\s]*)*$/i).test(value); return (/^(http(s)?:\/\/)?([\w\-]+\.{1})*(([\w\-]*){1}(\.{1}[A-Za-z]{2,4}){1}){1}(\:{1}\d*)?([\?\/|\#]+[\@\~\;\+\'\#\,\.\%\-\/\&\?\=\w\$\s]*)*$/i).test(value);
}, },
'zipcode': function(value) 'zipcode': function(value)
{ {
return (/\d{5}/).test(value); return (/\d{5}/).test(value);
} }
}; };
var filters = var filters =
{ {
'toInt': function(value) 'toInt': function(value)
{ {
return parseInt(value, 10); return parseInt(value, 10);
}, },
'toFloat': function(value) 'toFloat': function(value)
{ {
return parseFloat(value, 10); return parseFloat(value, 10);
}, },
'toString': function(value) 'toString': function(value)
{ {
return value.toString(); return value.toString();
}, },
'toDate': function(value) 'toDate': function(value)
{ {
return new Date(value); return new Date(value);
}, },
'toBoolean': function(value) 'toBoolean': function(value)
{ {
if(value === 1 || value === true || /true|on|yes|1/.test(value)) if(value === 1 || value === true || /true|on|yes|1/.test(value))
return true; return true;
else if(value === 0 || value === false || /false|off|no|0/.test(value)) else if(value === 0 || value === false || /false|off|no|0/.test(value))
return false; return false;
else else
return value; return value;
}, },
'trim': function(value) 'trim': function(value)
{ {
return _.isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value; return _.isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
}, },
'lowercase': function(value) 'lowercase': function(value)
{ {
return _.isString(value) ? value.toLowerCase() : value; return _.isString(value) ? value.toLowerCase() : value;
}, },
'uppercase': function(value) 'uppercase': function(value)
{ {
return _.isString(value) ? value.toUpperCase() : value; return _.isString(value) ? value.toUpperCase() : value;
} }
}; };
exports.create = function(param, rules) { return new Rules(param, rules); }; exports.create = function(param, rules) { return new Rules(param, rules); };

View File

@ -1,6 +1 @@
var schema = require("./lib/schema.js"); module.exports = require("./lib/schema.js");
Object.keys(schema).forEach(function(one)
{
exports[one] = schema[one];
});

View File

@ -1,7 +1,7 @@
describe("default schemas", function() describe("default schemas", function()
{ {
var schemajs = require('../schema'); var schemajs = require('../schema');
var expect = require('chai').expect; var expect = require('chai').expect;
it("default values", function() it("default values", function()
{ {

View File

@ -29,12 +29,14 @@ describe("schema types", function()
var input1 = schema.validate({input: 'username'}); var input1 = schema.validate({input: 'username'});
var input2 = schema.validate({input: 112390123}); var input2 = schema.validate({input: 112390123});
var input3 = schema.validate({input: ''}); var input3 = schema.validate({input: ''});
var input4 = schema.validate({}); var input4 = schema.validate({input: " "});
var input5 = schema.validate({});
expect(input1.valid).to.be.ok; expect(input1.valid).to.be.ok;
expect(!input2.valid).to.be.ok; expect(!input2.valid).to.be.ok;
expect(!input3.valid).to.be.ok; expect(!input3.valid).to.be.ok;
expect(input4.valid).to.be.ok; expect(!input4.valid).to.be.ok;
expect(input5.valid).to.be.ok;
}); });
it("number", function() it("number", function()