Coverage

79%
381
302
79

manikin.js

79%
381
302
79
LineHitsSource
1// Generated by CoffeeScript 1.4.0
21(function() {
31 var ObjectId, async, mongojs, mongoose, _,
4 __slice = [].slice;
5
61 async = require('async');
7
81 _ = require('underscore');
9
101 mongoose = require('mongoose');
11
121 mongojs = require('mongojs');
13
141 ObjectId = mongoose.Schema.ObjectId;
15
161 exports.create = function() {
1717 var api, db, defModel, desugarModel, getKeys, getManyToMany, getMetaFields, getOwnedModels, getOwners, internalListSub, massage, massageCore, massageOne, massaged, meta, model, models, nullablesValidation, preRemoveCascadeNonNullable, preRemoveCascadeNullable, preprocFilter, propagate, specTransform, specmodels;
1817 db = null;
1917 api = {};
2017 models = {};
2117 specmodels = {};
2217 meta = {};
2317 propagate = function(callback, f) {
244 return function() {
254 var args, err;
264 err = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
274 if (err) {
280 return callback(err);
29 } else {
304 return f.apply(this, args);
31 }
32 };
33 };
3417 model = function(name, schema) {
3525 var ss;
3625 ss = new mongoose.Schema(schema, {
37 strict: true
38 });
3925 ss.set('versionKey', false);
4025 return mongoose.model(name, ss);
41 };
4217 api.isValidId = function(id) {
432 try {
442 mongoose.mongo.ObjectID(id);
451 return true;
46 } catch (ex) {
471 return false;
48 }
49 };
5017 preprocFilter = function(filter) {
5115 var x;
5215 x = _.extend({}, filter);
5315 if (x.id) {
548 x._id = x.id;
55 }
5615 delete x.id;
5715 return x;
58 };
5917 massageOne = function(x) {
6028 if (!(x != null)) {
610 return x;
62 }
6328 x.id = x._id;
6428 delete x._id;
6528 return x;
66 };
6717 massageCore = function(r2) {
6825 if (Array.isArray(r2)) {
694 return r2.map(massageOne);
70 } else {
7121 return massageOne(r2);
72 }
73 };
7417 massage = function(r2) {
7525 return massageCore(JSON.parse(JSON.stringify(r2)));
76 };
7717 massaged = function(f) {
7816 return function(err, data) {
7916 if (err) {
800 return f(err);
81 } else {
8216 return f(null, massage(data));
83 }
84 };
85 };
8617 api.connect = function(databaseUrl, callback) {
8711 mongoose.connect(databaseUrl);
887 db = mongojs.connect(databaseUrl, Object.keys(api.getModels()));
897 return db[Object.keys(models)[0]].find(function(err) {
907 return callback(err);
91 });
92 };
9317 api.close = function(callback) {
946 mongoose.connection.close();
956 return callback();
96 };
9717 api.list = function(model, filter, callback) {
983 var rr;
993 filter = preprocFilter(filter);
1003 rr = models[model].find(filter);
1013 if (meta[model].defaultSort != null) {
1021 rr = rr.sort(_.object([[meta[model].defaultSort, 'asc']]));
103 }
1043 return rr.exec(massaged(callback));
105 };
10617 api.getOne = function(model, config, callback) {
1079 var filter;
1089 filter = preprocFilter(config.filter || {});
1099 return models[model].findOne(filter, function(err, data) {
1109 if (err) {
1111 if (err.toString() === 'Error: Invalid ObjectId') {
1121 callback(new Error('No such id'));
113 } else {
1140 callback(err);
115 }
1168 } else if (!(data != null)) {
1171 return callback(new Error('No match'));
118 } else {
1197 return callback(null, massage(data));
120 }
121 });
122 };
12317 api.delOne = function(model, filter, callback) {
1242 filter = preprocFilter(filter);
1252 return models[model].findOne(filter, function(err, d) {
1262 if (err) {
1271 if (err.toString() === 'Error: Invalid ObjectId') {
1281 return callback(new Error('No such id'));
129 } else {
1300 return callback(err);
131 }
1321 } else if (!(d != null)) {
1330 return callback(new Error('No such id'));
134 } else {
1351 return d.remove(function(err) {
1360 return callback(err, !err ? massage(d) : void 0);
137 });
138 }
139 });
140 };
14117 getKeys = function(data, target, prefix) {
1422 var valids;
1432 if (target == null) {
1441 target = [];
145 }
1462 if (prefix == null) {
1471 prefix = '';
148 }
1492 valids = ['Array', 'String', 'Boolean', 'Date', 'Number', 'Null'];
1502 Object.keys(data).forEach(function(key) {
1516 if (valids.some(function(x) {
15220 return _(data[key])['is' + x]();
153 })) {
1545 return target.push(prefix + key);
155 } else {
1561 return getKeys(data[key], target, prefix + key + '.');
157 }
158 });
1592 return target;
160 };
16117 api.putOne = function(modelName, data, filter, callback) {
1621 var inputFields, inputFieldsValid, invalidFields, validField;
1631 filter = preprocFilter(filter);
1641 model = models[modelName];
1651 inputFieldsValid = getKeys(data);
1661 inputFields = Object.keys(data);
1671 validField = Object.keys(model.schema.paths);
1681 invalidFields = _.difference(inputFieldsValid, validField);
1691 if (invalidFields.length > 0) {
1700 callback(new Error("Invalid fields: " + invalidFields.join(', ')));
1710 return;
172 }
1731 return model.findOne(filter, propagate(callback, function(d) {
1741 if (!(d != null)) {
1750 callback(new Error("No such id"));
1760 return;
177 }
1781 inputFields.forEach(function(key) {
1794 return d[key] = data[key];
180 });
1811 return d.save(function(err) {
1821 return callback(err, err ? null : massage(d));
183 });
184 }));
185 };
18617 api.post = function(model, indata, callback) {
18713 var owners, ownersOwners, ownersRaw, saveFunc;
18813 saveFunc = function(data) {
18913 return new models[model](data).save(function(err) {
19013 var fieldMatch, valueMatch;
19113 if (err && err.code === 11000) {
1920 fieldMatch = err.err.match(/\$([a-zA-Z]+)_1/);
1930 valueMatch = err.err.match(/"([a-zA-Z]+)"/);
1940 if (fieldMatch && valueMatch) {
1950 return callback(new Error("Duplicate value '" + valueMatch[1] + "' for " + fieldMatch[1]));
196 } else {
1970 return callback(new Error("Unique constraint violated"));
198 }
199 } else {
20013 return massaged(callback).apply(this, arguments);
201 }
202 });
203 };
20413 ownersRaw = getOwners(model);
20513 owners = _(ownersRaw).pluck('plur');
20613 ownersOwners = _(owners.map(function(x) {
2073 return getOwners(x);
208 })).flatten();
20913 if (ownersOwners.length === 0) {
21012 return saveFunc(indata);
211 } else {
2121 return api.getOne(owners[0], {
213 id: indata[ownersRaw[0].sing]
214 }, function(err, ownerdata) {
2151 var metaFields, paths;
2161 paths = models[owners[0]].schema.paths;
2171 metaFields = Object.keys(paths).filter(function(key) {
2185 return !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner'];
219 });
2201 metaFields.forEach(function(key) {
2211 return indata[key] = ownerdata[key];
222 });
2231 return saveFunc(indata);
224 });
225 }
226 };
22717 internalListSub = function(model, outer, id, filter, callback) {
2280 var finalFilter;
2290 if (!(callback != null)) {
2300 callback = filter;
2310 filter = {};
232 }
2330 if ((filter[outer] != null) && filter[outer].toString() !== id.toString()) {
2340 callback(new Error('No such id'));
2350 return;
236 }
2370 filter = preprocFilter(filter);
2380 finalFilter = _.extend({}, filter, _.object([[outer, id]]));
2390 return models[model].find(finalFilter, callback);
240 };
24117 api.delMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) {
2420 var inverseName, mm, secondaryModel;
2430 mm = getManyToMany(primaryModel).filter(function(x) {
2440 return x.name === propertyName;
245 })[0];
2460 if (mm === null) {
2470 callback(new Error('Invalid manyToMany-property'));
2480 return;
249 }
2500 secondaryModel = mm.ref;
2510 inverseName = mm.inverseName;
2520 return async.forEach([
253 {
254 model: primaryModel,
255 id: primaryId,
256 property: propertyName,
257 secondaryId: secondaryId
258 }, {
259 model: secondaryModel,
260 id: secondaryId,
261 property: inverseName,
262 secondaryId: primaryId
263 }
264 ], function(item, callback) {
2650 return models[item.model].findById(item.id, propagate(callback, function(data) {
2660 var conditions, options, update;
2670 conditions = {
268 _id: item.id
269 };
2700 update = {
271 $pull: _.object([[item.property, item.secondaryId]])
272 };
2730 options = {};
2740 return models[item.model].update(conditions, update, options, function(err, numAffected) {
2750 return callback(err);
276 });
277 }));
278 }, callback);
279 };
28017 api.postMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) {
2811 var inverseName, mm, secondaryModel;
2821 mm = getManyToMany(primaryModel).filter(function(x) {
2831 return x.name === propertyName;
284 })[0];
2851 if (mm === null) {
2860 callback(new Error('Invalid manyToMany-property'));
2870 return;
288 }
2891 secondaryModel = mm.ref;
2901 inverseName = mm.inverseName;
2911 return models[primaryModel].findById(primaryId, propagate(callback, function(data) {
2921 return models[secondaryModel].findById(secondaryId, propagate(callback, function(data2) {
2931 if (-1 === data[propertyName].indexOf(secondaryId)) {
2941 data[propertyName].push(secondaryId);
295 }
2961 if (-1 === data2[inverseName].indexOf(primaryId)) {
2971 data2[inverseName].push(primaryId);
298 }
2991 return data.save(propagate(callback, function() {
3001 return data2.save(function(err) {
3011 return callback(err, {});
302 });
303 }));
304 }));
305 }));
306 };
30717 api.getMany = function(primaryModel, primaryId, propertyName, callback) {
3081 return models[primaryModel].findOne({
309 _id: primaryId
310 }).populate(propertyName).exec(function(err, story) {
3111 return callback(err, massage(story[propertyName]));
312 });
313 };
31417 desugarModel = function(modelName, tgt, src, keys) {
31541 return keys.forEach(function(key) {
31660 var obj;
31760 if (typeof src[key] === 'string') {
31814 obj = {};
31914 obj[key] = {
320 type: src[key]
321 };
32214 return desugarModel(modelName, tgt, obj, [key]);
32346 } else if (!(src[key].type != null)) {
3240 throw new Error("must assign a type: " + JSON.stringify(keys));
32546 } else if (src[key].type === 'mixed') {
3262 return tgt[key] = {
327 type: 'mixed'
328 };
32944 } else if (src[key].type === 'nested') {
3301 tgt[key] = {
331 type: 'nested'
332 };
3331 return desugarModel(modelName, tgt[key], src[key], _.without(Object.keys(src[key]), 'type'));
33443 } else if (_(['string', 'number', 'date', 'boolean']).contains(src[key].type)) {
33538 tgt[key] = {
336 type: src[key].type,
337 required: !!src[key].required,
338 index: !!src[key].index,
339 unique: !!src[key].unique
340 };
34138 if (src[key]["default"] != null) {
34218 tgt[key]["default"] = src[key]["default"];
343 }
34438 if (src[key].validate != null) {
3451 return tgt[key].validate = src[key].validate;
346 }
3475 } else if (src[key].type === 'hasOne') {
3480 return tgt[key] = {
349 ref: src[key].model,
350 validation: src[key].validation
351 };
3525 } else if (src[key].type === 'hasMany') {
3534 tgt[key] = src[key];
3544 return tgt[key].inverseName = src[key].inverseName || key;
355 } else {
3561 throw new Error("Invalid type: " + src[key].type);
357 }
358 });
359 };
36017 specTransform = function(allspec, modelName, tgt, src, keys) {
36126 return keys.forEach(function(key) {
36245 if (src[key].type === 'mixed') {
3632 return tgt[key] = {
364 type: mongoose.Schema.Types.Mixed
365 };
36643 } else if (src[key].type === 'nested') {
3671 tgt[key] = {};
3681 return specTransform(allspec, modelName, tgt[key], src[key], _.without(Object.keys(src[key]), 'type'));
36942 } else if (src[key].type === 'string') {
37031 tgt[key] = _.extend({}, src[key], {
371 type: String
372 });
37331 if (src[key].validate != null) {
3741 return tgt[key].validate = function(value, callback) {
3750 return src[key].validate(api, value, callback);
376 };
377 }
37811 } else if (src[key].type === 'number') {
3794 return tgt[key] = _.extend({}, src[key], {
380 type: Number
381 });
3827 } else if (src[key].type === 'date') {
3832 return tgt[key] = _.extend({}, src[key], {
384 type: Date
385 });
3865 } else if (src[key].type === 'boolean') {
3871 return tgt[key] = _.extend({}, src[key], {
388 type: Boolean
389 });
3904 } else if (src[key].type === 'hasOne') {
3910 return tgt[key] = {
392 ref: src[key].model,
393 'x-validation': src[key].validation
394 };
3954 } else if (src[key].type === 'hasMany') {
3964 tgt[key] = [
397 {
398 type: ObjectId,
399 ref: src[key].model,
400 inverseName: src[key].inverseName
401 }
402 ];
4034 return allspec[src[key].model][src[key].inverseName] = [
404 {
405 type: ObjectId,
406 ref: modelName,
407 inverseName: key
408 }
409 ];
410 }
411 });
412 };
41317 api.defModels = function(models) {
41414 var allspec, newrest, rest;
41514 rest = {};
41614 Object.keys(models).forEach(function(modelName) {
41726 var inspec, spec;
41826 spec = {};
41926 inspec = models[modelName].fields || {};
42026 desugarModel(modelName, spec, inspec, Object.keys(inspec));
42125 rest[modelName] = _.extend({}, models[modelName], {
422 fields: spec
423 });
42425 if (!rest[modelName].owners) {
4257 return rest[modelName].owners = {};
426 }
427 });
42813 specmodels = rest;
42913 newrest = {};
43013 allspec = {};
43113 Object.keys(rest).forEach(function(modelName) {
43225 return allspec[modelName] = {};
433 });
43413 Object.keys(rest).forEach(function(modelName) {
43525 var inspec, owners, spec;
43625 spec = allspec[modelName];
43725 owners = rest[modelName].owners || {};
43825 inspec = rest[modelName].fields || {};
43925 specTransform(allspec, modelName, spec, inspec, Object.keys(inspec));
44025 return newrest[modelName] = _.extend({}, rest[modelName], {
441 fields: spec
442 });
443 });
44413 return Object.keys(newrest).forEach(function(modelName) {
44525 return defModel(modelName, newrest[modelName]);
446 });
447 };
44817 defModel = function(name, conf) {
44925 var owners, spec;
45025 spec = conf.fields;
45125 owners = conf.owners;
45225 Object.keys(owners).forEach(function(ownerName) {
4538 return spec[ownerName] = {
454 type: ObjectId,
455 ref: owners[ownerName],
456 required: true,
457 'x-owner': true
458 };
459 });
46025 Object.keys(owners).forEach(function(ownerName) {
4618 var paths;
4628 paths = models[owners[ownerName]].schema.paths;
4638 return Object.keys(paths).filter(function(p) {
46425 return paths[p].options['x-owner'] || paths[p].options['x-indirect-owner'];
465 }).forEach(function(p) {
4664 return spec[p] = {
467 type: ObjectId,
468 ref: paths[p].options.ref,
469 required: true,
470 'x-indirect-owner': true
471 };
472 });
473 });
47425 Object.keys(spec).forEach(function(fieldName) {
47559 if (spec[fieldName].ref != null) {
47612 return spec[fieldName].type = ObjectId;
477 }
478 });
47925 meta[name] = {
480 defaultSort: conf.defaultSort
481 };
48225 models[name] = model(name, spec);
48325 models[name].schema.pre('save', nullablesValidation(models[name].schema));
48425 models[name].schema.pre('remove', function(next) {
4851 return preRemoveCascadeNonNullable(models[name], this._id.toString(), next);
486 });
48725 return models[name].schema.pre('remove', function(next) {
4880 return preRemoveCascadeNullable(models[name], this._id.toString(), next);
489 });
490 };
49117 getMetaFields = function(modelName) {
4923 var metaFields, paths, typeFunc, typeMap;
4933 typeMap = {
494 ObjectID: 'string',
495 String: 'string',
496 Number: 'number',
497 Boolean: 'boolean',
498 Date: 'date'
499 };
5003 paths = models[modelName].schema.paths;
5013 typeFunc = function(x) {
5020 if (x === Boolean) {
5030 return 'boolean';
504 }
5050 if (x === Date) {
5060 return 'date';
507 }
508 };
5093 metaFields = Object.keys(paths).filter(function(key) {
51010 return !Array.isArray(paths[key].options.type);
511 }).map(function(key) {
5128 return {
513 name: (key === '_id' ? 'id' : key),
514 readonly: key === '_id' || !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner'],
515 required: !!paths[key].options.required,
516 type: typeMap[paths[key].instance] || typeFunc(paths[key].options.type) || 'unknown'
517 };
518 });
5193 return _.sortBy(metaFields, 'name');
520 };
52117 getOwners = function(modelName) {
52219 var outers, paths;
52319 paths = models[modelName].schema.paths;
52419 outers = Object.keys(paths).filter(function(x) {
52566 return paths[x].options['x-owner'];
526 }).map(function(x) {
5275 return {
528 plur: paths[x].options.ref,
529 sing: x
530 };
531 });
53219 return outers;
533 };
53417 getOwnedModels = function(ownerModelName) {
5353 return _.flatten(Object.keys(models).map(function(modelName) {
5369 var paths;
5379 paths = models[modelName].schema.paths;
5389 return Object.keys(paths).filter(function(x) {
53930 return paths[x].options.type === ObjectId && paths[x].options.ref === ownerModelName && paths[x].options['x-owner'];
540 }).map(function(x) {
5411 return {
542 name: modelName,
543 field: x
544 };
545 });
546 }));
547 };
54817 getManyToMany = function(modelName) {
5495 var manyToMany, paths;
5505 paths = models[modelName].schema.paths;
5515 manyToMany = Object.keys(paths).filter(function(x) {
55216 return Array.isArray(paths[x].options.type);
553 }).map(function(x) {
5544 return {
555 inverseName: paths[x].options.type[0].inverseName,
556 ref: paths[x].options.type[0].ref,
557 name: x
558 };
559 });
5605 return manyToMany;
561 };
56217 api.getMeta = function(modelName) {
5633 return {
564 fields: getMetaFields(modelName),
565 owns: getOwnedModels(modelName),
566 owners: getOwners(modelName),
567 manyToMany: getManyToMany(modelName)
568 };
569 };
57017 api.getModels = function() {
5718 return specmodels;
572 };
57317 nullablesValidation = function(schema) {
57425 return function(next) {
57520 var nonNullOuters, outers, paths, self;
57620 self = this;
57720 paths = schema.paths;
57820 outers = Object.keys(paths).filter(function(x) {
57974 return paths[x].options.type === ObjectId && typeof paths[x].options.ref === 'string' && !paths[x].options['x-owner'];
580 }).map(function(x) {
5811 return {
582 plur: paths[x].options.ref,
583 sing: x,
584 validation: paths[x].options['x-validation']
585 };
586 });
58720 nonNullOuters = outers.filter(function(x) {
5881 return self[x.sing] != null;
589 });
59020 return async.forEach(nonNullOuters, function(o, callback) {
5911 return api.getOne(o.plur, {
592 id: self[o.sing]
593 }, function(err, data) {
5941 if (err || !data) {
5950 return callback(new Error("Invalid pointer"));
5961 } else if (o.validation) {
5970 return o.validation(self, data, function(err) {
5980 return callback(err ? new Error(err) : void 0);
599 });
600 } else {
6011 return callback();
602 }
603 });
604 }, next);
605 };
606 };
60717 preRemoveCascadeNonNullable = function(owner, id, next) {
6081 var manys;
6091 manys = getManyToMany(owner.modelName);
6101 return manys.forEach(function(many) {
6111 return models[many.ref].find(_.object([[many.inverseName, id]]).exec(function(err, val) {
6120 var flattenedModels;
6130 console.log("eec");
6140 console.log(val);
6150 flattenedModels = getOwnedModels(owner.modelName);
6160 return async.forEach(flattenedModels, function(mod, callback) {
6170 return internalListSub(mod.name, mod.field, id, function(err, data) {
6180 return async.forEach(data, function(item, callback) {
6190 console.log(item);
6200 return item.remove(callback);
621 }, callback);
622 });
623 }, next);
624 }));
625 });
626 };
62717 preRemoveCascadeNullable = function(owner, id, next) {
6280 var flattenedModels, ownedModels;
6290 ownedModels = Object.keys(models).map(function(modelName) {
6300 var paths;
6310 paths = models[modelName].schema.paths;
6320 return Object.keys(paths).filter(function(x) {
6330 return paths[x].options.type === ObjectId && paths[x].options.ref === owner.modelName && !paths[x].options['x-owner'];
634 }).map(function(x) {
6350 return {
636 name: modelName,
637 field: x
638 };
639 });
640 });
6410 flattenedModels = _.flatten(ownedModels);
6420 return async.forEach(flattenedModels, function(mod, callback) {
6430 return internalListSub(mod.name, mod.field, id, function(err, data) {
6440 return async.forEach(data, function(item, callback) {
6450 item[mod.field] = null;
6460 item.save();
6470 return callback();
648 }, callback);
649 });
650 }, next);
651 };
65217 return api;
653 };
654
655}).call(this);