1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| (function(){
function isFunction(o){
return typeof o === "function";
}
function isTyped(def){
return isFunction(def) || Array.isArray(def) || def instanceof ArrayModel;
}
function wrapObject(obj) {
return obj instanceof Object ? obj : Object.create(null);
}
function validateType(obj, _def, path){
var def = _def;
if(!Array.isArray(_def)) {
def = [_def];
} else if(def.length < 2){
def = _def.concat(undefined);
}
if (!def.some(function(type){
if(obj == null){ return obj == type; }
if(type instanceof ArrayModel){ return type.validate(obj); }
return obj == type || (type instanceof Function && obj instanceof type) || obj.constructor === type;
})) {
var authorizedTypes = def.map(function(t){ return isFunction(t) ? t.name || t.toString() : String(t) });
throw new Error("expecting " + path + " to be " + authorizedTypes.join(" or ") + ", got " + (typeof obj));
}
}
function objToString(obj, ndeep){
if(ndeep === undefined){ ndeep = 1; }
if(obj == null){ return String(obj); }
if(typeof obj == "string"){ return '"'+obj+'"'; }
if(typeof obj == "function"){ return obj.name || obj.toString(ndeep); }
if(Array.isArray(obj)){
return '[' + obj.map(function(item) {
return objToString(item, ndeep+1);
}).join(', ') + ']';
}
if(obj && typeof obj == "object"){
var indent = Array(ndeep).join('\t');
return '{' + Object.keys(obj).map(function(key){
return '\n\t' + indent + key + ': ' + objToString(obj[key], ndeep+1);
}).join(',') + '\n' + indent + '}';
}
return String(obj);
}
Object.defineModel = function(modelDefinition) {
function getProxy(obj, def, path) {
var wrapper = wrapObject(obj);
if(isTyped(def)){
validateType(obj, def, path);
return obj;
} else {
var proxy = Object.create(Object.getPrototypeOf(wrapper));
Object.keys(def).forEach(function(key) {
var newPath = (path ? [path,key].join('.') : key);
Object.defineProperty(proxy, key, {
get: function () {
//console.log("get",key, def[key]);
return getProxy(wrapper[key], def[key], newPath);
},
set: function (val) {
//console.log("set",key, def[key], val);
wrapper[key] = getProxy(val, def[key], key, newPath);
}
});
});
Object.defineProperty(proxy,"constructor",{ //TODO: trouver une solution moins intrusive pour tester isType(obj, Model)
configurable: false,
writable: false,
value: constructor
});
return proxy;
}
}
var constructor = function(obj) {
return getProxy(obj, modelDefinition);
};
constructor.toString = objToString.bind(this, modelDefinition);
return constructor;
};
function ArrayModel(def){
this.def = def;
this._min = 0;
this._max = Infinity;
}
ArrayModel.prototype.min = function(val){ this._min = val; return this; };
ArrayModel.prototype.max = function(val){ this._max = val; return this; };
ArrayModel.prototype.validate = function(obj){
var type = this.def;
return obj.length >= this._min && obj.length <= this._max
&& obj.every(function(o,i){ validateType(o, type, 'Array['+i+']'); return true; });
};
ArrayModel.prototype.toString = function(ndeep){
return 'Array.of('+objToString(this.def, ndeep)+') (between '+this._min+' and '+this._max+' items)';
};
Array.defineModel = function(def){
return new ArrayModel(def);
};
})(); |
Partager