[ { v1: '2', id: '50ad55e3cc97e21194000001' } ] [ { v110: '3', id: '50ad55e40e82c11194000001' } ] Coverage

Coverage

89%
414
370
44

manikin.js

89%
414
370
44
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() {
1321 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;
1421 if (process.env.NODE_ENV !== 'production') {
1521 for (key in require.cache) {
162199 delete require.cache[key];
17 }
18 }
1921 mongoose = require('mongoose');
2021 Schema = mongoose.Schema;
2121 Mixed = mongoose.Schema.Types.Mixed;
2221 ObjectID = mongoose.mongo.ObjectID;
2321 ObjectId = mongoose.Schema.ObjectId;
2421 db = null;
2521 connection = null;
2621 api = {};
2721 models = {};
2821 specmodels = {};
2921 meta = {};
3021 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 };
4121 makeModel = function(name, schema) {
4229 var ss;
4329 ss = new Schema(schema, {
44 strict: true
45 });
4629 ss.set('versionKey', false);
4729 return connection.model(name, ss, name);
48 };
4921 api.isValidId = function(id) {
502 try {
512 ObjectID(id);
521 return true;
53 } catch (ex) {
541 return false;
55 }
56 };
5721 preprocFilter = function(filter) {
5843 var x;
5943 x = _.extend({}, filter);
6043 if (x.id) {
6110 x._id = x.id;
62 }
6343 delete x.id;
6443 return x;
65 };
6621 massageOne = function(x) {
6773 if (!(x != null)) {
680 return x;
69 }
7073 x.id = x._id;
7173 delete x._id;
7273 return x;
73 };
7421 massageCore = function(r2) {
7578 if (Array.isArray(r2)) {
7630 return r2.map(massageOne);
77 } else {
7848 return massageOne(r2);
79 }
80 };
8121 massage = function(r2) {
8278 return massageCore(JSON.parse(JSON.stringify(r2)));
83 };
8421 massaged = function(f) {
8554 return function(err, data) {
8654 if (err) {
872 return f(err);
88 } else {
8952 return f(null, massage(data));
90 }
91 };
92 };
9321 api.connect = function(databaseUrl, callback) {
9416 var db2;
9516 connection = mongoose.createConnection(databaseUrl);
9616 toDef.forEach(function(_arg) {
9729 var name, v;
9829 name = _arg[0], v = _arg[1];
9929 return defModel(name, v);
100 });
10116 db2 = mongojs.connect(databaseUrl, Object.keys(api.getModels()));
10216 return db2[Object.keys(models)[0]].find(function(err) {
10316 return callback(err);
104 });
105 };
10621 api.close = function(callback) {
10716 connection.close();
10816 return callback();
109 };
11021 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 };
11921 api.getOne = function(model, config, callback) {
12014 var filter;
12114 filter = preprocFilter(config.filter || {});
12214 return models[model].findOne(filter, function(err, data) {
12314 if (err) {
1241 if (err.toString() === 'Error: Invalid ObjectId') {
1251 callback(new Error('No such id'));
126 } else {
1270 callback(err);
128 }
12913 } else if (!(data != null)) {
1301 return callback(new Error('No match'));
131 } else {
13212 return callback(null, massage(data));
133 }
134 });
135 };
13621 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 };
15421 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 };
17421 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 };
19921 api.post = function(model, indata, callback) {
20034 var owners, ownersOwners, ownersRaw, saveFunc;
20134 saveFunc = function(data) {
20234 return new models[model](data).save(function(err) {
20334 var fieldMatch, valueMatch;
20434 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 {
21334 return massaged(callback).apply(this, arguments);
214 }
215 });
216 };
21734 ownersRaw = getOwners(model);
21834 owners = _(ownersRaw).pluck('plur');
21934 ownersOwners = _(owners.map(function(x) {
2208 return getOwners(x);
221 })).flatten();
22234 if (ownersOwners.length === 0) {
22331 return saveFunc(indata);
224 } else {
2253 return api.getOne(owners[0], {
226 id: indata[ownersRaw[0].sing]
227 }, function(err, ownerdata) {
2283 var metaFields, paths;
2293 paths = models[owners[0]].schema.paths;
2303 metaFields = Object.keys(paths).filter(function(key) {
23114 return !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner'];
232 });
2333 metaFields.forEach(function(key) {
2344 return indata[key] = ownerdata[key];
235 });
2363 return saveFunc(indata);
237 });
238 }
239 };
24021 internalListSub = function(model, outer, id, filter, callback) {
2414 var finalFilter;
2424 if (!(callback != null)) {
2434 callback = filter;
2444 filter = {};
245 }
2464 if ((filter[outer] != null) && filter[outer].toString() !== id.toString()) {
2470 callback(new Error('No such id'));
2480 return;
249 }
2504 filter = preprocFilter(filter);
2514 finalFilter = _.extend({}, filter, _.object([[outer, id]]));
2524 return models[model].find(finalFilter, callback);
253 };
25421 api.delMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) {
2551 var inverseName, mm, secondaryModel;
2561 mm = getManyToMany(primaryModel).filter(function(x) {
2571 return x.name === propertyName;
258 })[0];
2591 if (mm === null) {
2600 callback(new Error('Invalid manyToMany-property'));
2610 return;
262 }
2631 secondaryModel = mm.ref;
2641 inverseName = mm.inverseName;
2651 return async.forEach([
266 {
267 model: primaryModel,
268 id: primaryId,
269 property: propertyName,
270 secondaryId: secondaryId
271 }, {
272 model: secondaryModel,
273 id: secondaryId,
274 property: inverseName,
275 secondaryId: primaryId
276 }
277 ], function(item, callback) {
2782 return models[item.model].findById(item.id, propagate(callback, function(data) {
2792 var conditions, options, update;
2802 conditions = {
281 _id: item.id
282 };
2832 update = {
284 $pull: _.object([[item.property, item.secondaryId]])
285 };
2862 options = {};
2872 return models[item.model].update(conditions, update, options, function(err, numAffected) {
2882 return callback(err);
289 });
290 }));
291 }, callback);
292 };
29321 insertOps = [];
29421 api.postMany = function(primaryModel, primaryId, propertyName, secondaryId, callback) {
2959 var hasAlready, insertOpMatch, insertOpNow, inverseName, mm, secondaryModel;
2969 mm = getManyToMany(primaryModel).filter(function(x) {
2979 return x.name === propertyName;
298 })[0];
2999 if (mm === null) {
3000 callback(new Error('Invalid manyToMany-property'));
3010 return;
302 }
3039 secondaryModel = mm.ref;
3049 inverseName = mm.inverseName;
3059 insertOpNow = [
306 {
307 primaryModel: primaryModel,
308 primaryId: primaryId,
309 propertyName: propertyName,
310 secondaryId: secondaryId
311 }, {
312 primaryModel: secondaryModel,
313 primaryId: secondaryId,
314 propertyName: inverseName,
315 secondaryId: primaryId
316 }
317 ];
3189 insertOpMatch = function(x1, x2) {
3194 return x1.primaryModel === x2.primaryModel && x1.primaryId === x2.primaryId && x1.propertyName === x2.propertyName && x1.secondaryId === x2.secondaryId;
320 };
3219 hasAlready = insertOps.some(function(x) {
3223 return insertOpNow.some(function(y) {
3234 return insertOpMatch(x, y);
324 });
325 });
3269 if (hasAlready) {
3273 callback(null, {
328 status: 'insert already in progress'
329 });
3303 return;
331 }
3326 insertOpNow.forEach(function(op) {
33312 return insertOps.push(op);
334 });
3356 return async.map(insertOpNow, function(item, callback) {
33612 return models[item.primaryModel].findById(item.primaryId, callback);
337 }, propagate(callback, function(datas) {
3386 var updated;
3396 updated = [false, false];
3406 insertOpNow.forEach(function(conf, i) {
34112 if (-1 === datas[i][conf.propertyName].indexOf(conf.secondaryId)) {
34212 datas[i][conf.propertyName].push(conf.secondaryId);
34312 return updated[i] = true;
344 }
345 });
3466 return async.forEach([0, 1], function(index, callback) {
34712 if (updated[index]) {
34812 return datas[index].save(callback);
349 } else {
3500 return callback();
351 }
352 }, function(err) {
3536 insertOps = insertOps.filter(function(x) {
35412 return !_(insertOpNow).contains(x);
355 });
3566 return callback(err, {
357 status: (updated.some(function(x) {
3586 return x;
359 }) ? 'inserted' : 'already inserted')
360 });
361 });
362 }));
363 };
36421 api.getMany = function(primaryModel, primaryId, propertyName, callback) {
36510 return models[primaryModel].findOne({
366 _id: primaryId
367 }).populate(propertyName).exec(function(err, story) {
36810 return callback(err, massage(story[propertyName]));
369 });
370 };
37121 desugarModel = function(modelName, tgt, src, keys) {
37256 return keys.forEach(function(key) {
37376 var obj;
37476 if (typeof src[key] === 'string') {
37523 obj = {};
37623 obj[key] = {
377 type: src[key]
378 };
37923 return desugarModel(modelName, tgt, obj, [key]);
38053 } else if (!(src[key].type != null)) {
3810 throw new Error("must assign a type: " + JSON.stringify(keys));
38253 } else if (src[key].type === 'mixed') {
3832 return tgt[key] = {
384 type: 'mixed'
385 };
38651 } else if (src[key].type === 'nested') {
3871 tgt[key] = {
388 type: 'nested'
389 };
3901 return desugarModel(modelName, tgt[key], src[key], _.without(Object.keys(src[key]), 'type'));
39150 } else if (_(['string', 'number', 'date', 'boolean']).contains(src[key].type)) {
39243 tgt[key] = {
393 type: src[key].type,
394 required: !!src[key].required,
395 index: !!src[key].index,
396 unique: !!src[key].unique
397 };
39843 if (src[key]["default"] != null) {
39915 tgt[key]["default"] = src[key]["default"];
400 }
40143 if (src[key].validate != null) {
4021 return tgt[key].validate = src[key].validate;
403 }
4047 } else if (src[key].type === 'hasOne') {
4050 return tgt[key] = {
406 ref: src[key].model,
407 validation: src[key].validation
408 };
4097 } else if (src[key].type === 'hasMany') {
4106 tgt[key] = src[key];
4116 return tgt[key].inverseName = src[key].inverseName || key;
412 } else {
4131 throw new Error("Invalid type: " + src[key].type);
414 }
415 });
416 };
41721 specTransform = function(allspec, modelName, tgt, src, keys) {
41832 return keys.forEach(function(key) {
41952 if (src[key].type === 'mixed') {
4202 return tgt[key] = {
421 type: Mixed
422 };
42350 } else if (src[key].type === 'nested') {
4241 tgt[key] = {};
4251 return specTransform(allspec, modelName, tgt[key], src[key], _.without(Object.keys(src[key]), 'type'));
42649 } else if (src[key].type === 'string') {
42736 tgt[key] = _.extend({}, src[key], {
428 type: String
429 });
43036 if (src[key].validate != null) {
4311 return tgt[key].validate = function(value, callback) {
4322 return src[key].validate(api, value, callback);
433 };
434 }
43513 } else if (src[key].type === 'number') {
4364 return tgt[key] = _.extend({}, src[key], {
437 type: Number
438 });
4399 } else if (src[key].type === 'date') {
4402 return tgt[key] = _.extend({}, src[key], {
441 type: Date
442 });
4437 } else if (src[key].type === 'boolean') {
4441 return tgt[key] = _.extend({}, src[key], {
445 type: Boolean
446 });
4476 } else if (src[key].type === 'hasOne') {
4480 return tgt[key] = {
449 ref: src[key].model,
450 'x-validation': src[key].validation
451 };
4526 } else if (src[key].type === 'hasMany') {
4536 tgt[key] = [
454 {
455 type: ObjectId,
456 ref: src[key].model,
457 inverseName: src[key].inverseName
458 }
459 ];
4606 return allspec[src[key].model][src[key].inverseName] = [
461 {
462 type: ObjectId,
463 ref: modelName,
464 inverseName: key
465 }
466 ];
467 }
468 });
469 };
47021 api.defModels = function(models) {
47118 var allspec, newrest, rest;
47218 rest = {};
47318 Object.keys(models).forEach(function(modelName) {
47432 var inspec, spec;
47532 spec = {};
47632 inspec = models[modelName].fields || {};
47732 desugarModel(modelName, spec, inspec, Object.keys(inspec));
47831 rest[modelName] = _.extend({}, models[modelName], {
479 fields: spec
480 });
48131 if (!rest[modelName].owners) {
48213 return rest[modelName].owners = {};
483 }
484 });
48517 specmodels = rest;
48617 newrest = {};
48717 allspec = {};
48817 Object.keys(rest).forEach(function(modelName) {
48931 return allspec[modelName] = {};
490 });
49117 Object.keys(rest).forEach(function(modelName) {
49231 var inspec, owners, spec;
49331 spec = allspec[modelName];
49431 owners = rest[modelName].owners || {};
49531 inspec = rest[modelName].fields || {};
49631 specTransform(allspec, modelName, spec, inspec, Object.keys(inspec));
49731 return newrest[modelName] = _.extend({}, rest[modelName], {
498 fields: spec
499 });
500 });
50117 return Object.keys(newrest).forEach(function(modelName) {
50231 if (connection === null) {
50331 return toDef.push([modelName, newrest[modelName]]);
504 } else {
5050 return defModel(modelName, newrest[modelName]);
506 }
507 });
508 };
50921 toDef = [];
51021 defModel = function(name, conf) {
51129 var owners, spec;
51229 spec = conf.fields;
51329 owners = conf.owners;
51429 Object.keys(owners).forEach(function(ownerName) {
5157 return spec[ownerName] = {
516 type: ObjectId,
517 ref: owners[ownerName],
518 required: true,
519 'x-owner': true
520 };
521 });
52229 Object.keys(owners).forEach(function(ownerName) {
5237 var paths;
5247 paths = models[owners[ownerName]].schema.paths;
5257 return Object.keys(paths).filter(function(p) {
52622 return paths[p].options['x-owner'] || paths[p].options['x-indirect-owner'];
527 }).forEach(function(p) {
5284 return spec[p] = {
529 type: ObjectId,
530 ref: paths[p].options.ref,
531 required: true,
532 'x-indirect-owner': true
533 };
534 });
535 });
53629 Object.keys(spec).forEach(function(fieldName) {
53764 if (spec[fieldName].ref != null) {
53811 return spec[fieldName].type = ObjectId;
539 }
540 });
54129 meta[name] = {
542 defaultSort: conf.defaultSort
543 };
54429 models[name] = makeModel(name, spec);
54529 models[name].schema.pre('save', nullablesValidation(models[name].schema));
54629 models[name].schema.pre('remove', function(next) {
5476 return preRemoveCascadeNonNullable(models[name], this._id.toString(), next);
548 });
54929 return models[name].schema.pre('remove', function(next) {
5506 return preRemoveCascadeNullable(models[name], this._id.toString(), next);
551 });
552 };
55321 getMetaFields = function(modelName) {
5540 var metaFields, paths, typeFunc, typeMap;
5550 typeMap = {
556 ObjectID: 'string',
557 String: 'string',
558 Number: 'number',
559 Boolean: 'boolean',
560 Date: 'date'
561 };
5620 paths = models[modelName].schema.paths;
5630 typeFunc = function(x) {
5640 if (x === Boolean) {
5650 return 'boolean';
566 }
5670 if (x === Date) {
5680 return 'date';
569 }
570 };
5710 metaFields = Object.keys(paths).filter(function(key) {
5720 return !Array.isArray(paths[key].options.type);
573 }).map(function(key) {
5740 return {
575 name: (key === '_id' ? 'id' : key),
576 readonly: key === '_id' || !!paths[key].options['x-owner'] || !!paths[key].options['x-indirect-owner'],
577 required: !!paths[key].options.required,
578 type: typeMap[paths[key].instance] || typeFunc(paths[key].options.type) || 'unknown'
579 };
580 });
5810 return _.sortBy(metaFields, 'name');
582 };
58321 getOwners = function(modelName) {
58442 var outers, paths;
58542 paths = models[modelName].schema.paths;
58642 outers = Object.keys(paths).filter(function(x) {
587134 return paths[x].options['x-owner'];
588 }).map(function(x) {
58911 return {
590 plur: paths[x].options.ref,
591 sing: x
592 };
593 });
59442 return outers;
595 };
59621 getOwnedModels = function(ownerModelName) {
5976 return _.flatten(Object.keys(models).map(function(modelName) {
59820 var paths;
59920 paths = models[modelName].schema.paths;
60020 return Object.keys(paths).filter(function(x) {
60172 return paths[x].options.type === ObjectId && paths[x].options.ref === ownerModelName && paths[x].options['x-owner'];
602 }).map(function(x) {
6033 return {
604 name: modelName,
605 field: x
606 };
607 });
608 }));
609 };
61021 getManyToMany = function(modelName) {
61116 var manyToMany, paths;
61216 paths = models[modelName].schema.paths;
61316 manyToMany = Object.keys(paths).filter(function(x) {
61453 return Array.isArray(paths[x].options.type);
615 }).map(function(x) {
61612 return {
617 inverseName: paths[x].options.type[0].inverseName,
618 ref: paths[x].options.type[0].ref,
619 name: x
620 };
621 });
62216 return manyToMany;
623 };
62421 api.getMeta = function(modelName) {
6250 return {
626 fields: getMetaFields(modelName),
627 owns: getOwnedModels(modelName),
628 owners: getOwners(modelName),
629 manyToMany: getManyToMany(modelName)
630 };
631 };
63221 api.getModels = function() {
63317 return specmodels;
634 };
63521 nullablesValidation = function(schema) {
63629 return function(next) {
63745 var nonNullOuters, outers, paths, self;
63845 self = this;
63945 paths = schema.paths;
64045 outers = Object.keys(paths).filter(function(x) {
641148 return paths[x].options.type === ObjectId && typeof paths[x].options.ref === 'string' && !paths[x].options['x-owner'];
642 }).map(function(x) {
6434 return {
644 plur: paths[x].options.ref,
645 sing: x,
646 validation: paths[x].options['x-validation']
647 };
648 });
64945 nonNullOuters = outers.filter(function(x) {
6504 return self[x.sing] != null;
651 });
65245 return async.forEach(nonNullOuters, function(o, callback) {
6534 return api.getOne(o.plur, {
654 id: self[o.sing]
655 }, function(err, data) {
6564 if (err || !data) {
6570 return callback(new Error("Invalid pointer"));
6584 } else if (o.validation) {
6590 return o.validation(self, data, function(err) {
6600 return callback(err ? new Error(err) : void 0);
661 });
662 } else {
6634 return callback();
664 }
665 });
666 }, next);
667 };
668 };
66921 preRemoveCascadeNonNullable = function(owner, id, next) {
6706 var manys;
6716 manys = getManyToMany(owner.modelName);
6726 return async.forEach(manys, function(many, callback) {
6732 var obj;
6742 obj = _.object([[many.inverseName, id]]);
6752 return models[many.ref].update(obj, {
676 $pull: obj
677 }, callback);
678 }, function(err) {
6796 var flattenedModels;
6806 flattenedModels = getOwnedModels(owner.modelName);
6816 return async.forEach(flattenedModels, function(mod, callback) {
6823 return internalListSub(mod.name, mod.field, id, function(err, data) {
6833 return async.forEach(data, function(item, callback) {
6843 return item.remove(callback);
685 }, callback);
686 });
687 }, next);
688 });
689 };
69021 preRemoveCascadeNullable = function(owner, id, next) {
6916 var flattenedModels, ownedModels;
6926 ownedModels = Object.keys(models).map(function(modelName) {
69320 var paths;
69420 paths = models[modelName].schema.paths;
69520 return Object.keys(paths).filter(function(x) {
69672 return paths[x].options.type === ObjectId && paths[x].options.ref === owner.modelName && !paths[x].options['x-owner'];
697 }).map(function(x) {
6981 return {
699 name: modelName,
700 field: x
701 };
702 });
703 });
7046 flattenedModels = _.flatten(ownedModels);
7056 return async.forEach(flattenedModels, function(mod, callback) {
7061 return internalListSub(mod.name, mod.field, id, function(err, data) {
7071 return async.forEach(data, function(item, callback) {
7080 item[mod.field] = null;
7090 item.save();
7100 return callback();
711 }, callback);
712 });
713 }, next);
714 };
71521 return api;
716 };
717
718}).call(this);