1 | 'use strict';
|
2 |
|
3 | var
|
4 | _ = require('lodash'),
|
5 | RandExp = require('randexp'),
|
6 | chance = require('chance').Chance(),
|
7 | djson = require('describe-json');
|
8 |
|
9 | var _schemas = [];
|
10 | var _customTypes = [
|
11 | {
|
12 | name: 'number',
|
13 | customType: function () {
|
14 | return chance.natural();
|
15 | }
|
16 | },
|
17 | {
|
18 | name: 'boolean',
|
19 | customType: function () {
|
20 | return chance.bool();
|
21 | }
|
22 | },
|
23 | {
|
24 | name: 'null',
|
25 | customType: function () {
|
26 | return null;
|
27 | }
|
28 | },
|
29 | {
|
30 | name: 'string',
|
31 | customType: function () {
|
32 | return chance.string();
|
33 | }
|
34 | },
|
35 | {
|
36 | name: 'array',
|
37 | customType: function () {
|
38 | return [];
|
39 | }
|
40 | },
|
41 | {
|
42 | name: 'object',
|
43 | customType: function () {
|
44 | return {};
|
45 | }
|
46 | },
|
47 | {
|
48 | name: 'function',
|
49 | customType: function () {
|
50 | return function(){};
|
51 | }
|
52 | }
|
53 | ];
|
54 |
|
55 | var _dreamHelper = {
|
56 | chance: chance,
|
57 | oneOf: function (collection) {
|
58 | return _.sample(collection);
|
59 | }
|
60 | };
|
61 |
|
62 | var _defaultOutput = {
|
63 | Dream: 'Hello World'
|
64 | };
|
65 |
|
66 | var _genericSchema = {
|
67 | name: 'generic',
|
68 | schema: {
|
69 | default: String
|
70 | }
|
71 | };
|
72 |
|
73 | function Dream() {
|
74 |
|
75 |
|
76 |
|
77 | this._dreamHelper = _dreamHelper;
|
78 |
|
79 |
|
80 | this.defaultSchema = function (schema) {
|
81 | _genericSchema = validateAndReturnSchema(schema);
|
82 | return this;
|
83 | }.bind(this);
|
84 |
|
85 | this.useSchema = function useSchema(schema) {
|
86 | var
|
87 | schemaToUse,
|
88 | dreamInstance;
|
89 |
|
90 | schemaToUse = validateAndReturnSchema(schema);
|
91 | dreamInstance = new Dream();
|
92 | dreamInstance.schema(schemaToUse);
|
93 | dreamInstance._selectedSchema = schemaToUse;
|
94 |
|
95 | return dreamInstance;
|
96 | }.bind(this);
|
97 |
|
98 | this.input = function input(input) {
|
99 | this._dreamHelper.input = input;
|
100 | return (this);
|
101 | }.bind(this);
|
102 |
|
103 | this.generateSchema = function () {
|
104 | var
|
105 | describedJson,
|
106 | schemaName = '',
|
107 | jsonInput = '',
|
108 | validatedJsonInput,
|
109 | guessProperties = false,
|
110 | newSchema,
|
111 | args = [];
|
112 |
|
113 | Array.prototype.push.apply(args, arguments);
|
114 |
|
115 | args.forEach(function (argument) {
|
116 | switch (typeof (argument)) {
|
117 | case 'string':
|
118 | schemaName = argument;
|
119 | break;
|
120 | case 'object':
|
121 | jsonInput = argument;
|
122 | break;
|
123 | case 'boolean':
|
124 | guessProperties = argument;
|
125 | break;
|
126 | }
|
127 | });
|
128 |
|
129 | validatedJsonInput = _.first(_.flatten([jsonInput]));
|
130 | describedJson = djson.describe(validatedJsonInput || {});
|
131 |
|
132 | newSchema = {
|
133 | name: schemaName || 'generic',
|
134 | schema: describedJson
|
135 | };
|
136 |
|
137 | if (guessProperties === true) {
|
138 | newSchema.schema = guessCustomTypes(describedJson);
|
139 | }
|
140 | addOrReplace(_schemas, newSchema);
|
141 | return this;
|
142 | }.bind(this);
|
143 |
|
144 | this.customType = function (typeName, customType) {
|
145 | var
|
146 | newCustomType = {},
|
147 | validTypeName;
|
148 |
|
149 | validTypeName = typeof (typeName) === 'string' ? typeName : 'generic';
|
150 |
|
151 | if (customType.constructor === RegExp) {
|
152 | newCustomType = {
|
153 | name: validTypeName,
|
154 | customType: function () {
|
155 | return new RandExp(customType).gen();
|
156 | }
|
157 | };
|
158 | } else if (typeof (customType) === 'function') {
|
159 | newCustomType = {
|
160 | name: validTypeName,
|
161 | customType: customType
|
162 | };
|
163 | } else {
|
164 | newCustomType = {
|
165 | name: validTypeName,
|
166 | customType: function () {
|
167 | return '[Invalid Custom Type]';
|
168 | }
|
169 | };
|
170 | }
|
171 |
|
172 | addOrReplace(_customTypes, newCustomType);
|
173 |
|
174 | return this;
|
175 | }.bind(this);
|
176 |
|
177 | this.cleanse = function () {
|
178 | this._output = null;
|
179 | this._selectedSchema = null;
|
180 | }.bind(this);
|
181 |
|
182 | this.output = function output(callback) {
|
183 | var output;
|
184 |
|
185 | output = this._output || generateOutput();
|
186 | this.cleanse();
|
187 |
|
188 | if (typeof (callback) === 'function') {
|
189 | callback(null, output);
|
190 | return this;
|
191 | } else {
|
192 | return output;
|
193 | }
|
194 |
|
195 | }.bind(this);
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | this.schema = function schema(schema) {
|
203 | var
|
204 | validatedSchema,
|
205 | newSchema,
|
206 | args = [];
|
207 |
|
208 | Array.prototype.push.apply(args, arguments);
|
209 |
|
210 | if (args.length > 1) {
|
211 | newSchema = {
|
212 | name: typeof (args[0]) === 'string' ? args.shift() : 'generic',
|
213 | schema: typeof (args[0]) === 'object' ? args.shift() : {}
|
214 | };
|
215 | } else {
|
216 | newSchema = schema;
|
217 | }
|
218 |
|
219 | validatedSchema = validateAndReturnSchema(newSchema);
|
220 |
|
221 | if (validatedSchema.name === 'generic') {
|
222 | this._selectedSchema = validatedSchema;
|
223 | } else {
|
224 |
|
225 | addOrReplace(_schemas, validatedSchema);
|
226 |
|
227 | if (_schemas.length === 1) {
|
228 | this._selectedSchema = validatedSchema;
|
229 | }
|
230 | }
|
231 |
|
232 | return this;
|
233 | }.bind(this);
|
234 |
|
235 | this.generate = function generate(amount, generateRandomData) {
|
236 | var
|
237 | outputItem,
|
238 | iterations = amount || 1,
|
239 | outputArray = [],
|
240 | i = 0;
|
241 |
|
242 | for (; i < iterations; i++) {
|
243 | outputItem = generateOutputFromSchema(selectAvailableSchema(), generateRandomData);
|
244 | outputArray.push(outputItem);
|
245 | }
|
246 |
|
247 | this._output = outputArray.length === 1 ? outputArray[0] : outputArray;
|
248 | return this;
|
249 | }.bind(this);
|
250 |
|
251 | this.generateRnd = function generateRnd(amount) {
|
252 | return this.generate(amount, true);
|
253 | };
|
254 |
|
255 | var addOrReplace = function addOrReplace(collection, item) {
|
256 | var
|
257 | index;
|
258 |
|
259 | index = _.indexOf(collection, _.find(collection, { name: item.name }));
|
260 | if (index >= 0) {
|
261 | collection.splice(index, 1, item);
|
262 | } else {
|
263 | collection.push(item);
|
264 | }
|
265 |
|
266 | return collection;
|
267 | };
|
268 |
|
269 | var guessCustomTypes = function guessCustomTypes(schemaObject) {
|
270 | var
|
271 | customTypeExists,
|
272 | temporaryList = [];
|
273 |
|
274 | _.forIn(schemaObject, function (value, key) {
|
275 | if (typeof (value) === 'object') {
|
276 | if (Array.isArray(value)) {
|
277 | value.forEach(function (item) {
|
278 | if (typeof (item) === 'object') {
|
279 | temporaryList.push(guessCustomTypes(item));
|
280 | } else {
|
281 | temporaryList.push(item.toString());
|
282 | }
|
283 | });
|
284 | schemaObject[key] = temporaryList;
|
285 | } else {
|
286 | schemaObject[key] = guessCustomTypes(value);
|
287 | }
|
288 | } else {
|
289 |
|
290 | customTypeExists = _.find(_customTypes, { name: key.toString() });
|
291 |
|
292 | if (typeof (chance[key.toString()]) === 'function' || customTypeExists !== undefined) {
|
293 | schemaObject[key] = key.toString();
|
294 | }
|
295 | }
|
296 | });
|
297 |
|
298 | return schemaObject;
|
299 | };
|
300 |
|
301 | var validateAndReturnSchema = function (schema) {
|
302 | if (isValidSchema(schema)) return schema;
|
303 |
|
304 | if (typeof (schema) === 'string') {
|
305 | var foundSchema = _.findWhere(_schemas, { name: schema });
|
306 | return isValidSchema(foundSchema) ? foundSchema : _genericSchema;
|
307 | }
|
308 |
|
309 | if (typeof (schema) === 'object') {
|
310 | return {
|
311 | name: 'generic',
|
312 | schema: schema
|
313 | };
|
314 | }
|
315 |
|
316 | return _genericSchema;
|
317 | }.bind(this);
|
318 |
|
319 | var selectAvailableSchema = function () {
|
320 | if (this._selectedSchema) {
|
321 | return this._selectedSchema;
|
322 | }
|
323 |
|
324 | if (thereIsSchema() && _schemas.length === 1) {
|
325 | return _schemas[0];
|
326 | }
|
327 |
|
328 | return _genericSchema;
|
329 | }.bind(this);
|
330 |
|
331 | var generateOutput = function () {
|
332 |
|
333 | if (this._selectedSchema) {
|
334 | return generateOutputFromSchema(this._selectedSchema);
|
335 | } else {
|
336 | return _defaultOutput;
|
337 | }
|
338 |
|
339 | }.bind(this);
|
340 |
|
341 | var generateOutputFromSchema = function (schema, generateValues) {
|
342 | var
|
343 | outputObject = {},
|
344 | schemaToUse = validateAndReturnSchema(schema);
|
345 | _.forIn(schemaToUse.schema, function (value, key) {
|
346 | outputObject[key] = getValueFromType(value, generateValues);
|
347 | });
|
348 | return outputObject;
|
349 | }.bind(this);
|
350 |
|
351 | var getValueFromType = function getValueFromType(propertyType, generateValues) {
|
352 | var
|
353 | value,
|
354 | temporaryList = [],
|
355 | temporaryObject = {},
|
356 | temporaryValue,
|
357 | customTypeIndex,
|
358 | customTypeNeedle,
|
359 | context = this,
|
360 | types = {
|
361 | 'number': Number,
|
362 | 'string': String,
|
363 | 'boolean': Boolean,
|
364 | 'array': Array,
|
365 | 'object': Object,
|
366 | 'function': Function,
|
367 | 'date': Date
|
368 | };
|
369 |
|
370 | if (propertyType.constructor === RegExp) {
|
371 | if (generateValues) {
|
372 | return new RandExp(propertyType).gen();
|
373 | } else {
|
374 | return types[typeof (new RandExp(propertyType).gen())]();
|
375 | }
|
376 | }
|
377 |
|
378 | switch (typeof (propertyType)) {
|
379 | case 'string':
|
380 | customTypeNeedle = _.find(_customTypes, { name: propertyType });
|
381 | customTypeIndex = _.indexOf(_customTypes, customTypeNeedle);
|
382 |
|
383 | if (customTypeIndex >= 0) {
|
384 | temporaryValue = customTypeNeedle.customType(this._dreamHelper);
|
385 | } else {
|
386 | if (propertyType === 'array') {
|
387 | temporaryValue = [];
|
388 | } else {
|
389 | temporaryValue = (typeof (chance[propertyType]) === 'function') ? chance[propertyType]() : '[Unknown Custom Type]';
|
390 | }
|
391 | }
|
392 |
|
393 | if (generateValues) {
|
394 | value = temporaryValue;
|
395 | } else {
|
396 | value = types[typeof (temporaryValue)]();
|
397 | }
|
398 |
|
399 | break;
|
400 | case 'function':
|
401 |
|
402 | temporaryValue = propertyType();
|
403 |
|
404 | if (propertyType === Date) {
|
405 | value = new Date(temporaryValue);
|
406 | } else {
|
407 |
|
408 | if (generateValues) {
|
409 | customTypeNeedle = _.find(_customTypes, { name: typeof (temporaryValue) });
|
410 | value = isNative(propertyType) ? customTypeNeedle() : temporaryValue;
|
411 | } else {
|
412 | value = Array.isArray(temporaryValue) ? types['array']() : types[typeof (temporaryValue)]();
|
413 | }
|
414 |
|
415 | }
|
416 |
|
417 | break;
|
418 | case 'object':
|
419 |
|
420 | if (Array.isArray(propertyType)) {
|
421 | propertyType.forEach(function (item) {
|
422 | temporaryList.push(getValueFromType.call(context, item, generateValues));
|
423 | });
|
424 |
|
425 | value = temporaryList;
|
426 | } else {
|
427 | _.forIn(propertyType, function (value, key) {
|
428 | temporaryObject[key] = getValueFromType.call(context, value, generateValues);
|
429 | });
|
430 |
|
431 | value = temporaryObject;
|
432 | }
|
433 |
|
434 | break;
|
435 | default:
|
436 | value = '[Invalid Property]';
|
437 | }
|
438 |
|
439 | return value;
|
440 | }.bind(this);
|
441 |
|
442 | var isValidSchema = function isValidSchema(schema) {
|
443 | return _.has(schema, 'name') && _.has(schema, 'schema');
|
444 | };
|
445 |
|
446 | var thereIsSchema = function thereIsSchema() {
|
447 | return _schemas.length > 0;
|
448 | };
|
449 |
|
450 | function isNative(fn) {
|
451 | return (/^function\s*[a-z0-9_\$]*\s*\([^)]*\)\s*\{\s*\[native code\]\s*\}/i).test('' + fn);
|
452 | };
|
453 |
|
454 | }
|
455 |
|
456 | module.exports = new Dream();
|