-
Notifications
You must be signed in to change notification settings - Fork 0
/
qualify.js
238 lines (209 loc) · 5.98 KB
/
qualify.js
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* Utilities for function handling */
"use strict";
/* TODO: Support for checking argument types. This could even read the source code comment tags and build checks based on documented features. */
/* for node-lint */
/*global Buffer: false, clearInterval: false, clearTimeout: false, console: false, global: false, module: false, process: false, querystring: false, require: false, setInterval: false, setTimeout: false, util: false, __filename: false, __dirname: false */
var mod = module.exports = {},
foreach = require('snippets').foreach,
JSONSchema = require('json-schema');
/** Returns true if the argument is a function */
mod.isFunction = function(f) {
return f && (typeof f === 'function');
};
/** Returns true if the argument is an object */
mod.isObject = function(f) {
return f && (typeof f === 'object');
};
/** Validate `f` based on `schema`. */
mod.validate = function(f, schema) {
if(schema === undefined) {
return {valid:true, errors:[]};
}
if(!mod.isObject(schema)) {
return {valid:false, errors:[{'message':'Schema was invalid'}]};
}
if(schema.type === 'function') {
if( (!schema.required) && (!f) ) {
return;
}
if(!mod.isFunction(f)) {
return {valid:false, errors:[{'message':'Object was not a function.'}]};
}
return {valid:true, errors:[]};
}
return JSONSchema.validate(f, schema);
};
/** */
function debug_stringify (what) {
var cases = {
'numger': function(w) { return ''+ w; },
'string': function(w) { return '"' + w + '"'; },
'undefined': function(w) { return 'undefined'; },
'function': function(w) {
return ''+w;
},
'object': function(w) {
var ret = [], type;
if(w instanceof Array) {
type = 'Array';
foreach(w).each(function(item) {
ret.push(debug_stringify(item));
});
return '[' + ret.join(', ') + ']';
} else {
type = 'Object';
foreach(w).each(function(item, key) {
ret.push(key + ':' + debug_stringify(item));
});
return '{' + ret.join(', ') + '}';
}
}
}, c;
c = cases[typeof what];
if(c === undefined) {
return typeof what + '('+JSON.stringify(what) +')';
}
return c(what);
}
/* Conforms argument list in reverse order and verifies that first (which was original last defined argument) is an function.
* @param args Argument list as array.
* @returns Returns argument list in reverse order. First element is the callback function.
*/
function do_conform(args, opts) {
function prettify(e) {
var msg = ""+e;
if(e.fileName) {
msg += " [" + e.fileName;
if(e.lineNumber) {
msg += ":" + e.lineNumber;
}
msg += "]";
}
if (e.stack) {
msg += '\n====== stack ======\n';
msg += e.stack + "\n";
}
return msg;
}
var arg, fn, validate, opts_max_length, defaults_direction;
try {
args = Array.prototype.slice.call(args);
opts = opts || {};
validate = opts.validate || [];
opts_max_length = opts.max || args.length;
defaults_direction = opts.defaults || 'left';
opts.type = opts.type || 'basic';
if(opts.length) {
opts.min = opts.length;
opts.max = opts.length;
}
if(!opts.min) {
opts.min = 0;
}
// First priority to search for the callback function
if(opts.type === 'async') {
(function(){
if(args.length < 1) {
return;
}
var i = args.length - 1;
for(; i >= 0; i -= 1) {
if(!mod.isFunction(args[i])) {
//throw new TypeError("Last argument is not valid function!");
return;
}
fn = args[i];
return;
}
}());
}
// Pad args from left (with undefined) if the direction to set defaults is from right
if( opts.max && (defaults_direction === 'right') ) {
while(args.length < opts.max) {
args.unshift(undefined);
}
}
// Create default error handler if fn is undefined
if( (opts.type === 'async') && (!mod.isFunction(fn)) ) {
throw new TypeError("No callback function");
}
// Check states
/*
if(args.length < 1) {
throw new TypeError("No callback function found!");
}
*/
if(opts.min && (args.length < opts.min) ) {
throw new TypeError("Not enough arguments!");
}
if(opts.max && (args.length > opts.max) ) {
throw new TypeError("Too many arguments!");
}
// Check validate
if(!(validate instanceof Array)) {
throw new TypeError("opts.validate must be an Array!");
} else if(validate.length !== 0) {
(function(){
var i = 0;
function do_loop(){
var result, msgs = [];
result = mod.validate(args[i], validate[i]);
if(!result.valid) {
foreach(result.errors).each(function(p) {
msgs.push( ((p.property !== undefined) ? 'property="' + p.property : '') + '", ' + p.message );
});
throw new TypeError("Argument #" + i + " was invalid: " + msgs.join('; ') + " (schema was " + debug_stringify(validate[i]) + ")");
}
}
// Check args with each schema from validate
for(; i < validate.length; i += 1) {
do_loop();
}
}());
}
return args;
} catch(e) {
if(mod.isFunction(fn)) {
fn(prettify(e));
} else {
throw e;
}
}
}
/* Conformed function builder */
mod.conform = function(opts, fn) {
if(!mod.isFunction(fn)) {
throw new TypeError("Second argument for conform must be an function.");
}
var retfn = function() {
var obj = this,
max_length = opts.max || arguments.length,
args = do_conform(arguments, opts),
ret;
if(!args) {
return;
}
//args.reverse();
//while(args.length < max_length) {
// args.unshift(undefined);
//}
ret = fn.apply(obj, args);
// FIXME: Check return value based on opts.returns
if(opts.returns) {
(function() {
var result, msgs = [];
result = mod.validate(ret, opts.returns);
if(!result.valid) {
foreach(result.errors).each(function(p) {
msgs.push( ((p.property !== undefined) ? 'property="' + p.property : '') + '", ' + p.message );
});
throw new TypeError("Returned value was invalid: " + msgs.join('; ') + " (schema was " + debug_stringify(opts.returns) + ")");
}
}());
}
return ret;
};
return retfn;
};
mod.create = mod.conform;
/* EOF */