[ { v1: '2', id: '50bcce0697a5be3ef5000001' } ] [ { v110: '3', id: '50bcce07b21d0f3ef5000001' } ] Coverage

Coverage

89%
414
372
42

manikin.js

89%
414
372
42
LineHitsSource
1// Generated by CoffeeScript 1.4.0
21(function() {
31 var async, mongojs, _,
4 __slice = [].slice;
5
61 async = require('async');
7
81 _ = require('underscore');
9
101 mongojs = require('mongojs');
11
121 exports.create = function() {
1322 var Mixed, ObjectID, ObjectId, Schema, api, connection, db, defModel, desugarModel, getKeys, getManyToMany, getMetaFields, getOwnedModels, getOwners, insertOps, internalListSub, key, makeModel, massage, massageCore, massageOne, massaged, meta, models, mongoose, nullablesValidation, preRemoveCascadeNonNullable, preRemoveCascadeNullable, preprocFilter, propagate, specTransform, specmodels, toDef;
1422 if (process.env.NODE_ENV !== 'production') {
1522 for (key in require.cache) {
162306 delete require.cache[key];
17 }
18 }
1922 mongoose = require('mongoose');
2022 Schema = mongoose.Schema;
2122 Mixed = mongoose.Schema.Types.Mixed;
2222 ObjectID = mongoose.mongo.ObjectID;
2322 ObjectId = mongoose.Schema.ObjectId;
2422 db = null;
2522 connection = null;
2622 api = {};
2722 models = {};
2822 specmodels = {};
2922 meta = {};
3022 propagate = function(callback, f) {
319 return function() {
329 var args, err;
339 err = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
349 if (err) {
350 return callback(err);
36 } else {
379 return f.apply(this, args);
38 }
39 };
40 };
4122 makeModel = function(name, schema) {
4233 var ss;
4333 ss = new Schema(schema, {
44 strict: true
45 });
4633 ss.set('versionKey', false);
4733 return connection.model(name, ss, name);
48 };
4922 api.isValidId = function(id) {
502 try {
512 ObjectID(id);
521 return true;
53 } catch (ex) {
541 return false;
55 }
56 };
5722 preprocFilter = function(filter) {
5851 var x;
5951 x = _.extend({}, filter);
6051 if (x.id) {
6116 x._id = x.id;
62 }
6351 delete x.id;
6451 return x;
65 };
6622 massageOne = function(x) {
6796 if (!(x != null)) {
680 return x;
69 }
7096 x.id = x._id;
7196 delete x._id;
7296 return x;
73 };
7422 massageCore = function(r2) {
7595 if (Array.isArray(r2)) {
7630 return r2.map(massageOne);
77 } else {
7865 return massageOne(r2);
79 }
80 };
8122 massage = function(r2) {
8295 return massageCore(JSON.parse(JSON.stringify(r2)));
83 };
8422 massaged = function(f) {
8563 return function(err, data) {
8663 if (err) {
872 return f(err);
88 } else {
8961 return f(null, massage(data));
90 }
91 };
92 };
9322 api.connect = function(databaseUrl, callback) {
9417 var db2;
9517 connection = mongoose.createConnection(databaseUrl);
9617 toDef.forEach(function(_arg) {
9733 var name, v;
9833 name = _arg[0], v = _arg[1];
9933 return defModel(name, v);
100 });
10117 db2 = mongojs.connect(databaseUrl, Object.keys(api.getModels()));
10217 return db2[Object.keys(models)[0]].find(function(err) {
10317 return callback(err);
104 });
105 };
10622 api.close = function(callback) {
10716 connection.close();
10816 return callback();
109 };
11022 api.list = function(model, filter, callback) {
11120 var rr;
11220 filter = preprocFilter(filter);
11320 rr = models[model].find(filter);
11420 if (meta[model].defaultSort != null) {
1151 rr = rr.sort(_.object([[meta[model].defaultSort, 'asc']]));
116 }
11720 return rr.exec(massaged(callback));
118 };
11922 api.getOne = function(model, config, callback) {
12022 var filter;
12122 filter = preprocFilter(config.filter || {});
12222 return models[model].findOne(filter, function(err, data) {
12322 if (err) {
1241 if (err.toString() === 'Error: Invalid ObjectId') {
1251 callback(new Error('No such id'));
126 } else {
1270 callback(err);
128 }
12921 } else if (!(data != null)) {
1301 return callback(new Error('No match'));
131 } else {
13220 return callback(null, massage(data));
133 }
134 });
135 };
13622 api.delOne = function(model, filter, callback) {
1374 filter = preprocFilter(filter);
1384 return models[model].findOne(filter, function(err, d) {
1394 if (err) {
1401 if (err.toString() === 'Error: Invalid ObjectId') {
1411 return callback(new Error('No such id'));
142 } else {
1430 return callback(err);
144 }
1453 } else if (!(d != null)) {
1460 return callback(new Error('No such id'));
147 } else {
1483 return d.remove(function(err) {
1493 return callback(err, !err ? massage(d) : void 0);
150 });
151 }
152 });
153 };
15422 getKeys = function(data, target, prefix) {
1552 var valids;
1562 if (target == null) {
1571 target = [];
158 }
1592 if (prefix == null) {
1601 prefix = '';
161 }
1622 valids = ['Array', 'String', 'Boolean', 'Date', 'Number', 'Null'];
1632 Object.keys(data).forEach(function(key) {
1646 if (valids.some(function(x) {
16520 return _(data[key])['is' + x]();
166 })) {
1675 return target.push(prefix + key);
168 } else {
1691 return getKeys(data[key], target, prefix + key + '.');
170 }
171 });
1722 return target;
173 };
17422 api.putOne = function(modelName, data, filter, callback) {
1751 var inputFields, inputFieldsValid, invalidFields, model, validField;
1761 filter = preprocFilter(filter);
1771 model = models[modelName];
1781 inputFieldsValid = getKeys(data);
1791 inputFields = Object.keys(data);
1801 validField = Object.keys(model.schema.paths);
1811 invalidFields = _.difference(inputFieldsValid, validField);
1821 if (invalidFields.length > 0) {
1830 callback(new Error("Invalid fields: " + invalidFields.join(', ')));
1840 return;
185 }
1861 return model.findOne(filter, propagate(callback, function(d) {
1871 if (!(d != null)) {
1880 callback(new Error("No such id"));
1890 return;
190 }
1911 inputFields.forEach(function(key) {
1924 return d[key] = data[key];
193 });
1941 return d.save(function(err) {
1951 return callback(err, err ? null : massage(d));
196 });
197 }));
198 };
19922 api.post = function(model, indata, callback) {
20043 var owners, ownersOwners, ownersRaw, saveFunc;
20143 saveFunc = function(data) {
20243 return new models[model](data).save(function(err) {
20343 var fieldMatch, valueMatch;
20443 if (err && err.code === 11000) {
2050 fieldMatch = err.err.match(/\$([a-zA-Z]+)_1/);
2060 valueMatch = err.err.match(/"([a-zA-Z]+)"/);
2070 if (fieldMatch && valueMatch) {
2080 return callback(new Error("Duplicate value '" + valueMatch[1] + "' for " + fieldMatch[1]));
209 } else {
2100 return callback(new Error("Unique constraint violated"));
211 }
212 } else {
21343 return massaged(callback).apply(this, arguments);
214 }
215 });
216 };
21743 ownersRaw = getOwners(model);
21843 owners = _(ownersRaw).pluck('plur');
21943 ownersOwners = _(owners.map(function(x) {
22016 return getOwners(x);
221 })).flatten();
22243 if (ownersOwners.length === 0) {
22337 return saveFunc(indata);
224 } else {
2256 return api.getOne(owners[0], {
226 filter: {
227 id: indata[ownersRaw[0].sing]
228 }
229 }, function(err, ownerdata) {
2306 var metaFields, paths;
2316 paths = models[owners[0]].schema.paths;
2326 metaFields = Object.keys(paths).filter(function(key) {
23326 return !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner'];
234 });
2356 metaFields.forEach(function(key) {
2368 return indata[key] = ownerdata[key];
237 });
2386 return saveFunc(indata);
239 });
240 }
241 };
24222 internalListSub = function(model, outer, id, filter, callback) {
2434 var finalFilter;
2444 if (!(callback != null)) {
2454 callback = filter;
2464 filter = {};
247 }
2484 if ((filter[outer] != null) && filter[outer].toString() !== id.toString()) {
2490 callback(new Error('No such id'));
2500 return;
251 }
2524 filter = preprocFilter(filter);
2534 finalFilter = _.extend({}, filter, _.object([[outer, id]]));
2544 return models[model].find(finalFilter, callback);
255 };
25622 api.delMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) {
2571 var inverseName, mm, secondaryModel;
2581 mm = getManyToMany(primaryModel).filter(function(x) {
2591 return x.name === propertyName;
260 })[0];
2611 if (mm === null) {
2620 callback(new Error('Invalid manyToMany-property'));
2630 return;
264 }
2651 secondaryModel = mm.ref;
2661 inverseName = mm.inverseName;
2671 return async.forEach([
268 {
269 model: primaryModel,
270 id: primaryId,
271 property: propertyName,
272 secondaryId: secondaryId
273 }, {
274 model: secondaryModel,
275 id: secondaryId,
276 property: inverseName,
277 secondaryId: primaryId
278 }
279 ], function(item, callback) {
2802 return models[item.model].findById(item.id, propagate(callback, function(data) {
2812 var conditions, options, update;
2822 conditions = {
283 _id: item.id
284 };
2852 update = {
286 $pull: _.object([[item.property, item.secondaryId]])
287 };
2882 options = {};
2892 return models[item.model].update(conditions, update, options, function(err, numAffected) {
2902 return callback(err);
291 });
292 }));
293 }, callback);
294 };
29522 insertOps = [];
29622 api.postMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) {
2979 var hasAlready, insertOpMatch, insertOpNow, inverseName, mm, secondaryModel;
2989 mm = getManyToMany(primaryModel).filter(function(x) {
2999 return x.name === propertyName;
300 })[0];
3019 if (mm === null) {
3020 callback(new Error('Invalid manyToMany-property'));
3030 return;
304 }
3059 secondaryModel = mm.ref;
3069 inverseName = mm.inverseName;
3079 insertOpNow = [
308 {
309 primaryModel: primaryModel,
310 primaryId: primaryId,
311 propertyName: propertyName,
312 secondaryId: secondaryId
313 }, {
314 primaryModel: secondaryModel,
315 primaryId: secondaryId,
316 propertyName: inverseName,
317 secondaryId: primaryId
318 }
319 ];
3209 insertOpMatch = function(x1, x2) {
3214 return x1.primaryModel === x2.primaryModel && x1.primaryId === x2.primaryId && x1.propertyName === x2.propertyName && x1.secondaryId === x2.secondaryId;
322 };
3239 hasAlready = insertOps.some(function(x) {
3243 return insertOpNow.some(function(y) {
3254 return insertOpMatch(x, y);
326 });
327 });
3289 if (hasAlready) {
3293 callback(null, {
330 status: 'insert already in progress'
331 });
3323 return;
333 }
3346 insertOpNow.forEach(function(op) {
33512 return insertOps.push(op);
336 });
3376 return async.map(insertOpNow, function(item, callback) {
33812 return models[item.primaryModel].findById(item.primaryId, callback);
339 }, propagate(callback, function(datas) {
3406 var updated;
3416 updated = [false, false];
3426 insertOpNow.forEach(function(conf, i) {
34312 if (-1 === datas[i][conf.propertyName].indexOf(conf.secondaryId)) {
34412 datas[i][conf.propertyName].push(conf.secondaryId);
34512 return updated[i] = true;
346 }
347 });
3486 return async.forEach([0, 1], function(index, callback) {
34912 if (updated[index]) {
35012 return datas[index].save(callback);
351 } else {
3520 return callback();
353 }
354 }, function(err) {
3556 insertOps = insertOps.filter(function(x) {
35612 return !_(insertOpNow).contains(x);
357 });
3586 return callback(err, {
359 status: (updated.some(function(x) {
3606 return x;
361 }) ? 'inserted' : 'already inserted')
362 });
363 });
364 }));
365 };
36622 api.getMany = function(primaryModel, primaryId, propertyName, callback) {
36710 return models[primaryModel].findOne({
368 _id: primaryId
369 }).populate(propertyName).exec(function(err, story) {
37010 return callback(err, massage(story[propertyName]));
371 });
372 };
37322 desugarModel = function(modelName, tgt, src, keys) {
37464 return keys.forEach(function(key) {
37585 var obj;
37685 if (typeof src[key] === 'string') {
37727 obj = {};
37827 obj[key] = {
379 type: src[key]
380 };
38127 return desugarModel(modelName, tgt, obj, [key]);
38258 } else if (!(src[key].type != null)) {
3830 throw new Error("must assign a type: " + JSON.stringify(keys));
38458 } else if (src[key].type === 'mixed') {
3852 return tgt[key] = {
386 type: 'mixed'
387 };
38856 } else if (src[key].type === 'nested') {
3891 tgt[key] = {
390 type: 'nested'
391 };
3921 return desugarModel(modelName, tgt[key], src[key], _.without(Object.keys(src[key]), 'type'));
39355 } else if (_(['string', 'number', 'date', 'boolean']).contains(src[key].type)) {
39447 tgt[key] = {
395 type: src[key].type,
396 required: !!src[key].required,
397 index: !!src[key].index,
398 unique: !!src[key].unique
399 };
40047 if (src[key]["default"] != null) {
40115 tgt[key]["default"] = src[key]["default"];
402 }
40347 if (src[key].validate != null) {
4041 return tgt[key].validate = src[key].validate;
405 }
4068 } else if (src[key].type === 'hasOne') {
4071 return tgt[key] = src[key];
4087 } else if (src[key].type === 'hasMany') {
4096 tgt[key] = src[key];
4106 return tgt[key].inverseName = src[key].inverseName || key;
411 } else {
4121 throw new Error("Invalid type: " + src[key].type);
413 }
414 });
415 };
41622 specTransform = function(allspec, modelName, tgt, src, keys) {
41736 return keys.forEach(function(key) {
41857 if (src[key].type === 'mixed') {
4192 return tgt[key] = {
420 type: Mixed
421 };
42255 } else if (src[key].type === 'nested') {
4231 tgt[key] = {};
4241 return specTransform(allspec, modelName, tgt[key], src[key], _.without(Object.keys(src[key]), 'type'));
42554 } else if (src[key].type === 'string') {
42639 tgt[key] = _.extend({}, src[key], {
427 type: String
428 });
42939 if (src[key].validate != null) {
4301 return tgt[key].validate = function(value, callback) {
4312 return src[key].validate(api, value, callback);
432 };
433 }
43415 } else if (src[key].type === 'number') {
4355 return tgt[key] = _.extend({}, src[key], {
436 type: Number
437 });
43810 } else if (src[key].type === 'date') {
4392 return tgt[key] = _.extend({}, src[key], {
440 type: Date
441 });
4428 } else if (src[key].type === 'boolean') {
4431 return tgt[key] = _.extend({}, src[key], {
444 type: Boolean
445 });
4467 } else if (src[key].type === 'hasOne') {
4471 return tgt[key] = {
448 ref: src[key].model,
449 'x-validation': src[key].validation
450 };
4516 } else if (src[key].type === 'hasMany') {
4526 tgt[key] = [
453 {
454 type: ObjectId,
455 ref: src[key].model,
456 inverseName: src[key].inverseName
457 }
458 ];
4596 return allspec[src[key].model][src[key].inverseName] = [
460 {
461 type: ObjectId,
462 ref: modelName,
463 inverseName: key
464 }
465 ];
466 }
467 });
468 };
46922 api.defModels = function(models) {
47019 var allspec, newrest, rest;
47119 rest = {};
47219 Object.keys(models).forEach(function(modelName) {
47336 var inspec, spec;
47436 spec = {};
47536 inspec = models[modelName].fields || {};
47636 desugarModel(modelName, spec, inspec, Object.keys(inspec));
47735 rest[modelName] = _.extend({}, models[modelName], {
478 fields: spec
479 });
48035 if (!rest[modelName].owners) {
48114 return rest[modelName].owners = {};
482 }
483 });
48418 specmodels = rest;
48518 newrest = {};
48618 allspec = {};
48718 Object.keys(rest).forEach(function(modelName) {
48835 return allspec[modelName] = {};
489 });
49018 Object.keys(rest).forEach(function(modelName) {
49135 var inspec, owners, spec;
49235 spec = allspec[modelName];
49335 owners = rest[modelName].owners || {};
49435 inspec = rest[modelName].fields || {};
49535 specTransform(allspec, modelName, spec, inspec, Object.keys(inspec));
49635 return newrest[modelName] = _.extend({}, rest[modelName], {
497 fields: spec
498 });
499 });
50018 return Object.keys(newrest).forEach(function(modelName) {
50135 if (connection === null) {
50235 return toDef.push([modelName, newrest[modelName]]);
503 } else {
5040 return defModel(modelName, newrest[modelName]);
505 }
506 });
507 };
50822 toDef = [];
50922 defModel = function(name, conf) {
51033 var owners, spec;
51133 spec = conf.fields;
51233 owners = conf.owners;
51333 Object.keys(owners).forEach(function(ownerName) {
51410 return spec[ownerName] = {
515 type: ObjectId,
516 ref: owners[ownerName],
517 required: true,
518 'x-owner': true
519 };
520 });
52133 Object.keys(owners).forEach(function(ownerName) {
52210 var paths;
52310 paths = models[owners[ownerName]].schema.paths;
52410 return Object.keys(paths).filter(function(p) {
52529 return paths[p].options['x-owner'] || paths[p].options['x-indirect-owner'];
526 }).forEach(function(p) {
5275 return spec[p] = {
528 type: ObjectId,
529 ref: paths[p].options.ref,
530 required: true,
531 'x-indirect-owner': true
532 };
533 });
534 });
53533 Object.keys(spec).forEach(function(fieldName) {
53673 if (spec[fieldName].ref != null) {
53716 return spec[fieldName].type = ObjectId;
538 }
539 });
54033 meta[name] = {
541 defaultSort: conf.defaultSort
542 };
54333 models[name] = makeModel(name, spec);
54433 models[name].schema.pre('save', nullablesValidation(models[name].schema));
54533 models[name].schema.pre('remove', function(next) {
5466 return preRemoveCascadeNonNullable(models[name], this._id.toString(), next);
547 });
54833 return models[name].schema.pre('remove', function(next) {
5496 return preRemoveCascadeNullable(models[name], this._id.toString(), next);
550 });
551 };
55222 getMetaFields = function(modelName) {
5530 var metaFields, paths, typeFunc, typeMap;
5540 typeMap = {
555 ObjectID: 'string',
556 String: 'string',
557 Number: 'number',
558 Boolean: 'boolean',
559 Date: 'date'
560 };
5610 paths = models[modelName].schema.paths;
5620 typeFunc = function(x) {
5630 if (x === Boolean) {
5640 return 'boolean';
565 }
5660 if (x === Date) {
5670 return 'date';
568 }
569 };
5700 metaFields = Object.keys(paths).filter(function(key) {
5710 return !Array.isArray(paths[key].options.type);
572 }).map(function(key) {
5730 return {
574 name: (key === '_id' ? 'id' : key),
575 readonly: key === '_id' || !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner'],
576 required: !!paths[key].options.required,
577 type: typeMap[paths[key].instance] || typeFunc(paths[key].options.type) || 'unknown'
578 };
579 });
5800 return _.sortBy(metaFields, 'name');
581 };
58222 getOwners = function(modelName) {
58359 var outers, paths;
58459 paths = models[modelName].schema.paths;
58559 outers = Object.keys(paths).filter(function(x) {
586189 return paths[x].options['x-owner'];
587 }).map(function(x) {
58822 return {
589 plur: paths[x].options.ref,
590 sing: x
591 };
592 });
59359 return outers;
594 };
59522 getOwnedModels = function(ownerModelName) {
5966 return _.flatten(Object.keys(models).map(function(modelName) {
59720 var paths;
59820 paths = models[modelName].schema.paths;
59920 return Object.keys(paths).filter(function(x) {
60072 return paths[x].options.type === ObjectId && paths[x].options.ref === ownerModelName && paths[x].options['x-owner'];
601 }).map(function(x) {
6023 return {
603 name: modelName,
604 field: x
605 };
606 });
607 }));
608 };
60922 getManyToMany = function(modelName) {
61016 var manyToMany, paths;
61116 paths = models[modelName].schema.paths;
61216 manyToMany = Object.keys(paths).filter(function(x) {
61353 return Array.isArray(paths[x].options.type);
614 }).map(function(x) {
61512 return {
616 inverseName: paths[x].options.type[0].inverseName,
617 ref: paths[x].options.type[0].ref,
618 name: x
619 };
620 });
62116 return manyToMany;
622 };
62322 api.getMeta = function(modelName) {
6240 return {
625 fields: getMetaFields(modelName),
626 owns: getOwnedModels(modelName),
627 owners: getOwners(modelName),
628 manyToMany: getManyToMany(modelName)
629 };
630 };
63122 api.getModels = function() {
63218 return specmodels;
633 };
63422 nullablesValidation = function(schema) {
63533 return function(next) {
63654 var nonNullOuters, outers, paths, self;
63754 self = this;
63854 paths = schema.paths;
63954 outers = Object.keys(paths).filter(function(x) {
640181 return paths[x].options.type === ObjectId && typeof paths[x].options.ref === 'string' && !paths[x].options['x-owner'];
641 }).map(function(x) {
6429 return {
643 plur: paths[x].options.ref,
644 sing: x,
645 validation: paths[x].options['x-validation']
646 };
647 });
64854 nonNullOuters = outers.filter(function(x) {
6499 return self[x.sing] != null;
650 });
65154 return async.forEach(nonNullOuters, function(o, callback) {
6529 return api.getOne(o.plur, {
653 id: self[o.sing]
654 }, function(err, data) {
6559 if (err || !data) {
6560 return callback(new Error("Invalid pointer"));
6579 } else if (o.validation) {
6580 return o.validation(self, data, function(err) {
6590 return callback(err ? new Error(err) : void 0);
660 });
661 } else {
6629 return callback();
663 }
664 });
665 }, next);
666 };
667 };
66822 preRemoveCascadeNonNullable = function(owner, id, next) {
6696 var manys;
6706 manys = getManyToMany(owner.modelName);
6716 return async.forEach(manys, function(many, callback) {
6722 var obj;
6732 obj = _.object([[many.inverseName, id]]);
6742 return models[many.ref].update(obj, {
675 $pull: obj
676 }, callback);
677 }, function(err) {
6786 var flattenedModels;
6796 flattenedModels = getOwnedModels(owner.modelName);
6806 return async.forEach(flattenedModels, function(mod, callback) {
6813 return internalListSub(mod.name, mod.field, id, function(err, data) {
6823 return async.forEach(data, function(item, callback) {
6833 return item.remove(callback);
684 }, callback);
685 });
686 }, next);
687 });
688 };
68922 preRemoveCascadeNullable = function(owner, id, next) {
6906 var flattenedModels, ownedModels;
6916 ownedModels = Object.keys(models).map(function(modelName) {
69220 var paths;
69320 paths = models[modelName].schema.paths;
69420 return Object.keys(paths).filter(function(x) {
69572 return paths[x].options.type === ObjectId && paths[x].options.ref === owner.modelName && !paths[x].options['x-owner'];
696 }).map(function(x) {
6971 return {
698 name: modelName,
699 field: x
700 };
701 });
702 });
7036 flattenedModels = _.flatten(ownedModels);
7046 return async.forEach(flattenedModels, function(mod, callback) {
7051 return internalListSub(mod.name, mod.field, id, function(err, data) {
7061 return async.forEach(data, function(item, callback) {
7070 item[mod.field] = null;
7080 item.save();
7090 return callback();
710 }, callback);
711 });
712 }, next);
713 };
71422 return api;
715 };
716
717}).call(this);