UNPKG

node-fit-model

Version:

Checks whether an object is fits into a model.

307 lines (257 loc) 8.35 kB
"use strict"; /** * The model starts here * @param modelDir * @constructor */ // Read js files from modelDir var fs = require('fs'), fileMap;// private function Model(obj) { if(!obj) throw new Error("Paramters must be set"); var modelDir = obj.modelDir, postFix = obj.postFix, shouldThrow = obj.shouldThrow; if(!modelDir) throw new Error("modelDir must be defined in options"); if(!postFix) console.log('Warning : Not defining postFix will make you type more'); if(shouldThrow === undefined) shouldThrow = true; // Filemap to just read now and cache fileMap = {}; // Map all files data into cache var files = fs.readdirSync(modelDir); files.forEach(function(file){ var fileName = mapFileName(file, postFix); if(fileMap[fileName]) return; var data = require(modelDir + '/' + file); if(typeof data === 'string') data = JSON.parse(data); if(!isArray(data)) throw new Error("Models in file : " + file + " needs to be an array"); fileMap[fileName] = data; }); }; /** * Map file name so it can be cached * @param fileName * @param postFix * @returns {string} */ var mapFileName = function(fileName, postFix){ var postRegExp = new RegExp(postFix + '$', ''); return fileName.replace(postRegExp, '').toLowerCase(); }; /** * Is Array or not * @param someVar * @returns {boolean} */ var isArray = function(someVar) { return Object.prototype.toString.call( someVar ) === '[object Array]'; }; /** * * @param modelName * @param data */ Model.prototype.fitModel = function(modelName, data) { return checkModel(modelName, data); }; /** * Check Model * @param modelName * @param data * @param iteration Used so that cached data is used */ var checkModel = function(modelName, data) { if(!modelName) throw new Error("modelName needs to be defined"); var model = fileMap[modelName.toLowerCase()]; if(!model) throw new Error("Invalid model name provided => " + modelName); model.forEach(function(row) { var param = row.name, value = data[param], required = row.required, canBeNull = row.canBeNull, dataType = row.type, options = row.options, maxLength = row.maxLength, minLength = row.minLength, canBeEmpty = row.canBeEmpty; if(!validateRequired(value, required, canBeNull)) { throw new Error("Value of " + row.name + " is required. Actual => " + value); } // If you're here and value is undefined, // it is not required to be there // so don't worry about other features if(typeof value === 'undefined') return; if(!validateDataType(data, param, value, dataType, options, canBeNull, canBeEmpty)) { if(required) { if (dataType == 'enum') { throw new Error("Value of " + row.name + " must be enum and have a value in [ " + options.join(', ') + " ]. Actual => " + value); } else if(canBeEmpty) { if((typeof value === 'string' && value.length) || (Object.keys(value).length)) { throw new Error("Value of " + row.name + " must be " + dataType + ". Actual => " + value); } } else { throw new Error("Value of " + row.name + " must be " + dataType + ". Actual => " + value); } } } if(!validateMaxLength(value, maxLength)) { throw new Error("Value of " + value + " must have a length of at most " + maxLength); } if(!validateMinLength(value, minLength)) { throw new Error("Value of " + value + " must have a length of at least " + minLength); } }); return true; } /** * Is it required * @param value * @param required * @returns {*|boolean} */ var validateRequired = function(value, required, canBeNull) { return !required || // Not required (typeof value !== 'undefined' && value !== null) || // Value just exists. It's ok (canBeNull && value === null); // it's defined somehow and it can be null }; /** * Validate the data with model's data type * @param value * @param dataType * @param options */ var validateDataType = function(data, param, value, dataType, options, canBeNull, canBeEmpty) { if(!value && canBeNull) return true; var verified = false; switch(dataType) { case "int": case "integer": verified = isInt(value); break; case "timestamp": // Check if it can be parsed as a Date object var d = new Date(value); verified = !isNaN(d.getTime()); break; case "float": case "double": verified = isFloat(value) || isInt(value); // value can be 0.0 and be parsed as 0 break; case "number": // int or float verified = isNumber(value); break; case "string": verified = typeof value === 'string'; break; case "enum": // Options must be set in enum verified = options && options.indexOf(value) > -1; break; case "bool": verified = typeof value === 'boolean'; break; default: // Object type if(/object/i.test(dataType)) { // Object or an Array var matches = dataType.match(/\(.*\)/); if(!matches || !matches.length) verified = typeof value === 'object' && Object.prototype.toString.call( value ) !== '[object Object]'; else { var model = matches[0].replace(/\(|\)/g, ''); if (!model) throw new Error("Model in the object is undefined. Probably a syntax error."); verified = canBeEmpty || checkModel(model, data[param]); } } // Array type else if(/array/i.test(dataType)) { var matches = dataType.match(/\(.*\)/); if(!matches || !matches.length) verified = Object.prototype.toString.call( value ) === '[object Array]'; else { var model = matches[0].replace(/\(|\)/g, ''); if (!model) throw new Error("Model in the array is undefined. Probably a syntax error."); // loop through all objects for (var key in data[param]) { verified = checkModel(model, data[param][key]); if (!verified) break; } } } else throw new Error("Invalid data type: " + dataType + " is not in modelling data type list validateDataType"); } return verified; }; /** * Best performance for finding isInt * according to http://jsperf.com/tfm-is-integer * * @param value * @returns {boolean} */ var isInt = function(value) { var x; if (isNaN(value)) { return false; } x = parseFloat(value); return (x | 0) === x; }; /** * is Float * @param n * @returns {boolean} */ var isFloat = function(n) { n = parseFloat(n); return n === +n && n !== (n|0); }; /** * IsNumber * @param n * @returns {boolean} */ var isNumber = function(n) { return /^-?[\d.]+(?:e-?\d+)?$/.test(n); }; /** * * @param value * @param length * @throws * @returns {boolean} */ var validateMaxLength = function(value, length) { if(!length || typeof value === 'undefined') return true; if(typeof value !== 'string') value = value.toString(); if(value.length > length) throw new Error("Length of " + value + " must be less than " + length); return true; }; /** * * @param value * @param length * @returns {boolean} */ var validateMinLength = function(value, length) { if(!length || typeof value === 'undefined') return true; if(typeof value !== 'string') value = value.toString(); if(value.length < length) throw new Error("Length of " + value + " must be greater than " + length); return true; }; module.exports = Model;