Created
February 7, 2013 22:09
-
-
Save slawrence/4734699 to your computer and use it in GitHub Desktop.
A basic "schema" generator/comaparator. Requires underscore for deep compare.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global _.isEqual, console, schemanator: true */ | |
/*jslint browser: true */ | |
(function () { | |
'use strict'; | |
if (window.schemanator) { | |
console.error("schemanator already defined"); | |
return; | |
} | |
/** | |
An object that can generate a schema and compare objects. The compare method will output | |
the differences in properties. 'Schemas' can be compared to determine if json service | |
'contracts' have changed. | |
Dependencies: console, _.isEqual | |
*/ | |
window.schemanator = {}; | |
/** | |
* Is an object enumerable? | |
*/ | |
function isEnumerable(object) { | |
var prop; | |
if (typeof object === 'object') { | |
if (object) { | |
for (prop in object) { | |
if (object.hasOwnProperty(prop)) { | |
return true; | |
} | |
} | |
} | |
} | |
return false; | |
} | |
function isEmpty(obj) { | |
var prop; | |
for (prop in obj) { | |
if (obj.hasOwnProperty(prop)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Is an object an array? | |
*/ | |
function isArray(val) { | |
if (typeof val === 'object') { | |
if (val) { | |
if (Object.prototype.toString.call(val) === '[object Array]') { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
function isBothArray(a1, a2) { | |
return (isArray(a1) && isArray(a2)); | |
} | |
function isAlreadyDefinedInArray(schema, schema_list) { | |
var i; | |
for (i = 0; i < schema_list.length; i += 1) { | |
if (_.isEqual(schema_list[i], schema)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* Add a property to a object schema | |
*/ | |
function addObjectProperty(property, object, schema) { | |
schema.properties = schema.properties || {}; | |
//add this property to the object of properties | |
schema.properties[property] = {}; | |
schema.properties[property].id = property; | |
if (isArray(object)) { | |
schema.properties[property].type = 'array'; | |
} else { | |
schema.properties[property].type = typeof object; | |
} | |
} | |
function enumerate(object, schema) { | |
if (isArray(object)) { | |
if (object.length) { | |
enumerateArray(object, schema); | |
} | |
} else if (isEnumerable(object)) { | |
enumerateObject(object, schema); | |
} else { | |
schema.type = typeof object; | |
} | |
return schema; | |
} | |
/** | |
* Enumerate over an object and create a schema for each property | |
*/ | |
function enumerateObject(object, schema) { | |
var prop, prop_schema, type; | |
if (!object || !schema) { | |
console.error('Object or Schema is undefined/null'); | |
} | |
//enumerate over properties | |
for (prop in object) { | |
if (object.hasOwnProperty(prop)) { | |
addObjectProperty(prop, object[prop], schema); | |
enumerate(object[prop], schema.properties[prop]); | |
} | |
} | |
return schema; | |
} | |
// enumerate and add property to a array schema | |
function enumerateArray(array, schema) { | |
var i, temp_schema; | |
if (!array) { | |
console.error('Array or Schema is undefined/null'); | |
} | |
//enumerate array properties | |
schema.items = []; | |
for (i = 0; i < array.length; i += 1) { | |
temp_schema = enumerate(array[i], {}); | |
//check to see if other item schemas the same | |
if (!isAlreadyDefinedInArray(temp_schema, schema.items)) { | |
temp_schema.id = schema.items.length + ""; | |
if (isArray(array[i])) { | |
temp_schema.type = 'array'; | |
} else { | |
temp_schema.type = typeof array[i]; | |
} | |
schema.items.push(temp_schema); | |
} | |
} | |
} | |
/** | |
* Do a deep comparision and return differences between two objects | |
*/ | |
function compare(o1, o2) { | |
var prop, diff, ret = {}; | |
if (typeof o1 === 'object' && typeof o2 === 'object') { | |
for (prop in o2) { | |
//don't check against o2's prototype properties | |
if (o2.hasOwnProperty(prop)) { | |
//see if the property exists in o1 | |
if (o1.hasOwnProperty(prop)) { | |
//it does, compare values | |
if (typeof o2[prop] === 'object' && !isArray(o2[prop])) { | |
diff = compare(o1[prop], o2[prop]); | |
if (!isEmpty(diff)) { | |
ret[prop] = diff; | |
} | |
} else if (isArray(o1[prop]) && isArray(o2[prop])) { | |
if (_.isEqual(o1[prop], o2[prop])) { | |
//TODO: Improve the way arrays are 'compared' | |
//ret[prop] = "ARRAYS are different"; | |
ret[prop] = o2[prop]; | |
} | |
} else if (_.isEqual(o1[prop], o2[prop])) { | |
ret[prop] = o2[prop]; | |
} | |
} else { | |
ret[prop] = o2[prop]; | |
} | |
} | |
} | |
} | |
return ret; | |
} | |
/** | |
* Generate a "schema" from a json object. For now, not checking the prototype | |
*/ | |
schemanator.generate = function (object) { | |
var schema = { id: '#' }; | |
return enumerate(object, schema); | |
}; | |
/** | |
* Compare two objects, two schemas, or an object and a schema. Objects will generate | |
* a scheme which is then compared. Since a 'schema' is generated first, this method only | |
* checks for differences in properties, not in their values. | |
*/ | |
schemanator.schemaDiff = function (arg1, arg2) { | |
var schema1, schema2; | |
//helper function to convert to schema if not already a schema | |
function prepArgs(arg) { | |
return (arg.id === '#') ? arg : schemanator.generate(arg); | |
} | |
if (arg1 && arg2 && (typeof arg1 === 'object' && typeof arg2 === 'object')) { | |
return compare(prepArgs(arg1), prepArgs(arg2)); | |
} else { | |
console.error('Arguments were undefined or were not objects'); | |
} | |
}; | |
/** | |
* Output what's different in arg2 compared to arg1 | |
*/ | |
schemanator.diff = function (arg1, arg2) { | |
return compare(arg1, arg2); | |
}; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment