Browse files

Initial commit

  • Loading branch information...
0 parents commit 0e056cd9b2246479589e6ee3246fb01605d80cc2 @Baggz committed Nov 14, 2011
Showing with 774 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +255 −0 README.md
  3. +51 −0 package.json
  4. +412 −0 src/amanda.js
  5. +53 −0 tests/test.js
3 .gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+profile
+node_modules
255 README.md
@@ -0,0 +1,255 @@
+# Amanda
+
+[Amanda](https://github.com/Baggz/Amanda) validates data against JSON Schema.
+
+#### Features
+
+* You can create **your own validators**
+* Fully **asynchronous**
+* Can be used with **Node.js** and **in the browser**
+* Amanda has **no dependencies**
+* **AMD compatible**, you can load it via [RequireJS](https://github.com/jrburke/requirejs)
+* Lightweight
+* Fully **documented**
+* Tested
+
+## Download
+
+To install **Amanda**, use [NPM](http://npmjs.org/).
+
+```
+$ npm install amanda
+```
+
+Releases are available for download from GitHub.
+
+| **Version** | **Description** | **Size** | **Action** |
+|:------------|:----------------|:---------|:-----------|
+| `amanda.js` | *uncompressed, with comments* | 1 KB | [Download](https://raw.github.com/Baggz/Amanda/master/src/amanda.js) |
+| `amanda.min.js` | *compressed, without comments* | 1 KB | [Download](https://raw.github.com/Baggz/Amanda/master/dist/amanda.min.js) |
+
+# Documentation
+
+**Methods**
+
+* [validate](#validate)
+* [addValidator](#addValidator)
+
+**Objects**
+
+* [schema](#schema)
+
+<a name="validate"></a>
+## Validate
+
+### validate(instance, schema, callback)
+
+**Example**
+
+```javascript
+/**
+ * Schema
+ */
+var schema = {
+ type: 'object',
+ properties: {
+ user: {
+ name: {
+ type: 'string',
+ length: [2, 45],
+ required: true
+ },
+ surname: {
+ type: 'string',
+ length: [2, 45],
+ required: true
+ }
+ }
+ }
+};
+
+/**
+ * Body
+ */
+var body = {
+ user: {
+ name: 'František',
+ surname: 'Hába'
+ }
+};
+
+// Validate
+amanda.validate(body, schema, function(error) {
+ if (error) {
+ // Do something...
+ }
+});
+```
+
+<a name="addValidator"></a>
+## AddValidator
+
+### addValidator(name, fn)
+
+**Example**
+
+```javascript
+var evenValidator = function(paramName, paramValue, validator, validators, callback) {
+
+ // If ‘even: true’
+ if ( validator ) {
+
+ if (typeof paramValue === 'number' && (paramValue % 2) === 0) {
+ // No problem, the number is event
+ callback(null);
+ } else {
+ // Ou, ou, the number is not even
+ callback('Sorry, but ‘' + paramName + '’ is not even.');
+ }
+
+ }
+
+};
+
+// Add a new validator
+amanda.addValidator('even', evenValidator);
+
+var schema = {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ length: [2, 45],
+ even: true // <= That's your validator
+ }
+ }
+}
+
+```
+
+<a name="schema"></a>
+## Schema
+
+**Validators**
+
+* [required](#required)
+* [length](#length)
+* [type](#type)
+* [values](#values)
+* [except](#except)
+
+### Required
+
+```javascript
+var schema = {
+ type: 'object',
+ properties: {
+ username: {
+ required: true
+ }
+ }
+};
+```
+
+### Length
+
+**Example**
+
+```javascript
+var schema = {
+ type: 'object',
+ properties: {
+ username: {
+ type: 'string',
+ length: [2, 45]
+ }
+ }
+};
+```
+
+### Type
+
+**Values**
+
+* string
+* object
+* array
+* function
+* url
+* email
+* alphanumeric
+* alpha
+* number
+...TODO
+
+**Example**
+
+```javascript
+var schema = {
+ type: 'object',
+ properties: {
+ username: {
+ type: 'string'
+ },
+ email: {
+ type: 'email'
+ },
+ contacts: {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ }
+ }
+};
+```
+
+### Values
+
+**Example**
+
+```javascript
+var schema = {
+ type: 'object',
+ properties: {
+ sex: {
+ type: 'string',
+ values: ['female', 'male']
+ }
+ }
+};
+```
+
+### Except
+
+**Example**
+
+```javascript
+var schema = {
+ type: 'object',
+ properties: {
+ username: {
+ type: 'string',
+ except: ['admin', 'administrator']
+ }
+ }
+};
+```
+
+# Running Tests
+
+```
+$ npm tests/
+```
+
+# License
+
+(The MIT License)
+
+Copyright (c) 2011 František Hába &lt;hello@frantisekhaba.com&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51 package.json
@@ -0,0 +1,51 @@
+{
+
+ "name": "amanda",
+ "description": "JSON Schema validator",
+ "version": "0.1.0",
+ "author": "František Hába <hello@frantisekhaba.com>",
+
+ "devDependencies": {
+ "nodeunit": "0.5.1"
+ },
+
+ "keywords": [
+ "JSON",
+ "JSON Schema",
+ "schema",
+ "validator",
+ "validate",
+ "JSON validator",
+ "schema validator",
+ "async",
+ "browser"
+ ],
+
+ "homepage": "https://github.com/Baggz/Amanda",
+
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/Baggz/Amanda.git"
+ },
+
+ "engines": {
+ "node": ">= 0.3.0"
+ },
+
+ "bugs": {
+ "url": "https://github.com/Baggz/Amanda/issues",
+ "email": "hello@frantisekhaba.com"
+ },
+
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://github.com/Baggz/Amanda/blob/master/README.md"
+ }
+ ],
+
+ "scripts": {
+ "test" : "nodeunit tests/"
+ }
+
+}
412 src/amanda.js
@@ -0,0 +1,412 @@
+(function() {
+
+ /**
+ * Error
+ *
+ */
+ var Error = function(paramName, paramValue, validatorName, validatorValue, message) {
+
+ var properties = [
+ 'paramName',
+ 'paramValue',
+ 'validatorName',
+ 'validatorValue',
+ 'message'
+ ];
+
+ for (var i = 0, len = arguments.length; i < len; i++) {
+ this[properties[i]] = arguments[i];
+ }
+
+ return this;
+
+ };
+
+ /**
+ * Each
+ *
+ * @param {object} list
+ * @param {function} iterator
+ * @param {function} callback
+ */
+ var each = function(list, iterator, callback) {
+
+ // Fronta jednotlivých požadavků
+ var queue = [];
+
+ /**
+ * AddToQueue
+ *
+ * @param {string} key
+ * @param {string|object} value
+ */
+ var addToQueue = function(key, value) {
+ var index = queue.length + 1;
+ queue.push(function() {
+
+ var next = function(error) {
+ var fn = queue[index];
+ if ( !error && fn ) {
+ return fn();
+ } else if ( !error && !fn ) {
+ return callback(null);
+ } else {
+ return callback(error);
+ }
+ };
+
+ return ( key && value ) ? iterator(key, value, next) : iterator(value, next);
+
+ });
+ };
+
+ // If list is an array
+ if ( Object.prototype.toString.call(list) === '[object Array]' ) {
+ for (var i = 0, len = list.length; i < len; i++) {
+ addToQueue(undefined, list[i]);
+ }
+ }
+
+ // If list is an object
+ if ( Object.prototype.toString.call(list) === '[object Object]' ) {
+ for (var key in list) {
+ if (list.hasOwnProperty(key)) {
+ addToQueue(key, list[key]);
+ }
+ }
+ }
+
+ // And goo!
+ return queue[0]();
+
+ };
+
+ /**
+ * Validators
+ *
+ * List of validators.
+ */
+ var validators = {};
+
+ /**
+ * ValidatorsList
+ *
+ * List of validators names, the order is important.
+ */
+ var validatorsList = [];
+
+ /**
+ * ValidateParam
+ *
+ * @param {string} paramName
+ * @param {object} paramValidators
+ * @param {string|object} paramValue
+ * @param {function} callback
+ */
+ var validateParam = function(paramName, paramValidators, paramValue, callback) {
+
+ /**
+ * Iterator
+ *
+ * @param {string} validatorName
+ * @param {function} callback
+ */
+ var iterator = function(validatorName, callback) {
+ if ( paramValidators[validatorName] ) {
+ var fn = validators[validatorName];
+ fn(paramName, paramValue, paramValidators[validatorName], paramValidators, callback);
+ } else {
+ return callback(null);
+ }
+ };
+
+ // Run forEach
+ return each(validatorsList, iterator, callback);
+
+ };
+
+ /**
+ * ValidateSchema
+ *
+ * @param {object} instance
+ * @param {object} schema
+ * @param {function} callback
+ */
+ var validateSchema = function(instance, schema, callback) {
+
+ // Pokud se jedná o objekt
+ if (schema.type === 'object' && schema.properties) {
+ return each(schema.properties, function(paramName, paramValidators, callback) {
+ if (paramValidators.type === 'object' && paramValidators.properties) {
+ return validateSchema(instance[paramName], schema.properties[paramName], callback);
+ } else {
+ return validateParam(paramName, paramValidators, instance[paramName], callback);
+ }
+ }, callback);
+ } else if (schema.type === 'array' && schema.items) {
+ return each(instance, function(item, callback) {
+ return validateParam(undefined, schema.items, item, callback);
+ }, callback);
+ } else {
+ return validateParam(undefined, schema, instance, callback);
+ }
+
+ };
+
+ /**
+ * Amanda
+ *
+ */
+ var Amanda = {
+
+ /**
+ * Validate
+ *
+ * @param {object} structure
+ */
+ validate: function() {
+ return validateSchema.apply(this, arguments);
+ },
+
+ /**
+ * AddValidator
+ *
+ * @param {string} validatorName
+ * @param {function} validatorFn
+ */
+ addValidator: function(validatorName, validatorFn) {
+ validatorsList.push(validatorName);
+ validators[validatorName] = validatorFn;
+ }
+
+ };
+
+ /**
+ * Required
+ */
+ Amanda.addValidator('required', function(paramName, paramValue, validator, validators, callback) {
+
+ // Pokud je parametr povinný a není vyplněn
+ if (validator && !paramValue) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'required',
+ validator,
+ null
+ ));
+ }
+
+ // Vše je v pořádku, jedeme dál
+ return callback(null);
+
+ });
+
+ /**
+ * Length
+ */
+ Amanda.addValidator('length', function(paramName, paramValue, validator, validators, callback) {
+
+ // Validátor spustíme jen pokud se jedná string, jinak přeskočíme
+ if (typeof paramValue === 'string') {
+
+ // Pokud je validátor zapsán ve tvaru “[2, 45]”
+ if (Array.isArray(validator) && (paramValue.length < validator[0] || paramValue.length > validator[1])) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'length',
+ validator,
+ null
+ ));
+ }
+
+ // Pokud je validátor zapsán ve tvaru “12”
+ if (typeof validator === 'number' && paramValue.length !== validator) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'length',
+ validator,
+ null
+ ));
+ }
+
+ // Vše je v pořádku, jedeme dál
+ return callback(null);
+
+ }
+
+ // Vše je v pořádku, jedeme dál
+ return callback(null);
+
+ });
+
+ /**
+ * Values
+ */
+ Amanda.addValidator('values', function(paramName, paramValue, validator, validators, callback) {
+
+ // Pokud hodnota parametru neodpovídá výčtu
+ if (validator.indexOf( paramValue ) === -1) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'values',
+ validator,
+ null
+ ));
+ }
+
+ // Vše je v pořádku, jedeme dál
+ return callback(null);
+
+ });
+
+ /**
+ * Except
+ */
+ Amanda.addValidator('except', function(paramName, paramValue, validator, validators, callback) {
+
+ // Pokud hodnota parametru odpovídá výčtu nepovolených hodnot
+ if (validator.indexOf( paramValue ) !== -1) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'except',
+ validator,
+ null
+ ));
+ }
+
+ // Vše je v pořádku, jedeme dál
+ return callback(null);
+
+ });
+
+ /**
+ * Min
+ */
+ Amanda.addValidator('min', function(paramName, paramValue, validator, validators, callback) {
+ if (typeof paramValue === 'number' && paramValue <= validator) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'min',
+ validator,
+ null
+ ));
+ } else {
+ return callback(null);
+ }
+ });
+
+ /**
+ * Max
+ */
+ Amanda.addValidator('max', function(paramName, paramValue, validator, validators, callback) {
+ if (typeof paramValue === 'number' && paramValue >= validator) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'max',
+ validator,
+ null
+ ));
+ } else {
+ return callback(null);
+ }
+ });
+
+ /**
+ * Pattern
+ */
+ Amanda.addValidator('pattern', function(paramName, paramValue, validator, validators, callback) {
+ if (typeof paramValue === 'string' && !paramValue.match(validator)) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'pattern',
+ validator,
+ null
+ ));
+ } else {
+ return callback(null);
+ }
+ });
+
+ /**
+ * Type
+ */
+ Amanda.addValidator('type', (function() {
+
+ /**
+ * Types
+ */
+ var types = {
+ array: function(input) {
+ return Array.isArray(input);
+ },
+ alpha: function(input) {
+ return (typeof input === 'string' && input.match(/^[a-zA-Z]+$/));
+ },
+ alphanumeric: function(input) {
+ return ((typeof input === 'string' && input.match(/^[a-zA-Z0-9]+$/)) || typeof input === 'number');
+ },
+ ipv4: function(input) {
+ return (typeof input === 'string' && input.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/));
+ },
+ ipv6: function(input) {
+ return (typeof input === 'string' && input.match(/(?:(?:[a-f\d]{1,4}:)*(?:[a-f\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-f\d]{1,4}:)*[a-f\d]{1,4})?::(?:(?:[a-f\d]{1,4}:)*(?:[a-f\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)/));
+ },
+ ip: function(input) {
+ return types.ipv4(input) || types.ipv6;
+ },
+ email: function(input) {
+ return (typeof input === 'string' && input.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/));
+ },
+ url: function(input) {
+ return (typeof input === 'string' && input.match(/^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|cat|coop|int|pro|tel|xxx|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2})?)|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/));
+ }
+ };
+
+ // Generate the rest of type checkers
+ [
+ 'string',
+ 'number',
+ 'function',
+ 'object'
+ ].forEach(function(type) {
+ types[type] = function() {
+ return ( typeof arguments[0] === type ) ? true : false;
+ };
+ });
+
+ return function(paramName, paramValue, validator, validators, callback) {
+ if ( !types[validator](paramValue) ) {
+ return callback(new Error(
+ paramName,
+ paramValue,
+ 'pattern',
+ validator,
+ null
+ ));
+ } else {
+ return callback(null);
+ }
+ };
+
+ }()));
+
+ // Export
+ if ( typeof module !== 'undefined' && module.exports ) {
+ module.exports = Amanda;
+ } else if ( typeof define !== 'undefined' ) {
+ define(function() {
+ return Amanda;
+ });
+ } else {
+ this.Amanda = Amanda;
+ }
+
+}());
53 tests/test.js
@@ -0,0 +1,53 @@
+// Load
+var amanda = require('../src/amanda.js');
+
+/**
+ * Test route matching
+ */
+exports['Test #1'] = function(test) {
+
+ var schema = {
+ type: 'object',
+ properties: {
+ username: {
+ type: 'string',
+ length: [2, 45]
+ },
+ name: {
+ type: 'string',
+ length: [2, 45]
+ }
+ }
+ };
+
+ amanda.validate({
+ username: 'John',
+ name: 'John Dee'
+ }, schema, function(error) {
+ test.equal(error, undefined);
+ });
+
+ amanda.validate({
+ username: '',
+ name: 'John Dee'
+ }, schema, function(error) {
+ test.ok(error);
+ test.done();
+ });
+
+};
+
+
+
+/*
+amanda.validate([
+ 'a', 'b'
+], {
+ type: 'array',
+ items: {
+ type: 'number'
+ }
+}, function() {
+ console.log(arguments);
+});
+*/

0 comments on commit 0e056cd

Please sign in to comment.