From b82e4cd6206646499c8577159a88455c480e76ff Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Mon, 10 Oct 2016 18:41:49 +0200 Subject: [PATCH 01/26] diff --- CHANGELOG.md | 17 + build/app.js | 484 ++++++++++++++++++++++++++- dest/temp/ActiveRecord.js | 54 ++- dest/temp/specs/angular-dao.specs.js | 49 ++- package.json | 6 +- src/ActiveRecord.js | 29 +- tst/specs/angular-dao.specs.js | 57 +++- 7 files changed, 637 insertions(+), 59 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0d3c281 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# CHANGELOG + +## 2.0.0 + +- Diff on save +From 2.0.0 on, the default behaviour is to keep a copy of the entire object, and make a diff comparison on all the model keys to try to determine which keys have to be saved +If the object doesn't have an _id, the copy will be empty, allowing the entire object to be saved. + +If you wish to disable this change, pass `{force: true}` as an option object to the save method + +If you rewrote beforeSave methods, be careful and pass the optional `opts` object to the parents beforeSave method + +- BREAKING CHANGES + - Save behaviour + If you wish to keep sending all your fields to the server each you save (which you shouldn't). Pass `{force: true}` as options in the `save` method. + - re-population after save. + Until 2.0.0 when you wanted to repopulate an object after saving it (ie making another query), you had to pass it as the first argument of the save method. Now, it has to be given as an options object containing `{populate: whateverYouNeedToPopulate}` diff --git a/build/app.js b/build/app.js index cbd2803..e19ec12 100644 --- a/build/app.js +++ b/build/app.js @@ -22,6 +22,8 @@ var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); +var deep = require('deep-diff').diff; + /** * * Can be inherited or used as is. Holds the model definition, @@ -52,6 +54,7 @@ function ActiveRecord(model, name) { var _this = this; + this.__old = {}; _.each(model, function (field, key) { if (options && (options[key] || options[key] === 0)) { var name = _.isArray(field) ? field[0].ref : field.ref; @@ -84,8 +87,12 @@ function ActiveRecord(model, name) { } else { _this[key] = _this.buildField(field, options[key]); } - } else if (_.isArray(field)) { - _this[key] = []; + } + /*else if (_.isArray(field)) { + this[ key ] = []; + } */ + if (options && options._id) { + _this.__old[key] = _.cloneDeep(_this[key]); } }); return this; @@ -227,8 +234,18 @@ function ActiveRecord(model, name) { }, { key: 'beforeSave', value: function beforeSave(obj) { + // istanbul ignore next + + var _this3 = this; + + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + obj = obj || _.cloneDeep(this); _.each(model, function (field, key) { + if (!opts.force && !deep(obj[key], _this3.__old[key])) { + delete obj[key]; + return; + } if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { if (_.isArray(obj[key])) { @@ -246,25 +263,36 @@ function ActiveRecord(model, name) { obj[key] = new Date(moment(obj[key])).toISOString(); } }); + delete obj.rootUrl; + delete obj.$injector; + delete obj._injector; + delete obj.__old; return obj; } }, { key: 'save', - value: function save(populate) { + value: function save() { // istanbul ignore next - var _this3 = this; + var _this4 = this; - var toSave = this.beforeSave(); + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(function (resolve) { + return resolve({ data: _this4 }); + }); + } var callback; - if (populate) { + if (opts.populate) { var dao = sl.getDao(name); callback = function () { - return dao.getById(_this3._id, dao.query().populate(populate)); + return dao.getById(_this4._id, dao.query().populate(opts.populate)); }; } else { callback = function (data) { - _this3.build(data.data); + _this4.build(data.data); return data; }; } @@ -280,28 +308,28 @@ function ActiveRecord(model, name) { value: function saveDeep(populate) { // istanbul ignore next - var _this4 = this; + var _this5 = this; var promises = []; /** Find ref properties that might need to be saved */ _.each(model, function (v, k) { - if (_this4[k]) { + if (_this5[k]) { if (_.isArray(v)) { if (v[0].ref) { /** Array of nested Objects. Check if need to save each */ - _this4[k].forEach(function (e) { + _this5[k].forEach(function (e) { if (!e._id && e.saveDeep) promises.push(e.saveDeep()); }); } } else if (v.ref) { /** Single nested object, save it if needed */ - if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); + if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); } } }); var $q = this._injector.get('$q'); return $q.all(promises).then(function () { - return _this4.save(populate); + return _this5.save(populate); }); } }, { @@ -378,7 +406,7 @@ function ActiveRecord(model, name) { } module.exports = exports['default']; -},{"./ServiceLocator":5}],2:[function(require,module,exports){ +},{"./ServiceLocator":5,"deep-diff":11}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1130,4 +1158,430 @@ var Model2 = (function (_AR) { exports['default'] = Model2; module.exports = exports['default']; -},{"../ActiveRecord":1}]},{},[6]); +},{"../ActiveRecord":1}],11:[function(require,module,exports){ +(function (global){ +/*! + * deep-diff. + * Licensed under the MIT License. + */ +;(function(root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], function() { + return factory(); + }); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.DeepDiff = factory(); + } +}(this, function(undefined) { + 'use strict'; + + var $scope, conflict, conflictResolution = []; + if (typeof global === 'object' && global) { + $scope = global; + } else if (typeof window !== 'undefined') { + $scope = window; + } else { + $scope = {}; + } + conflict = $scope.DeepDiff; + if (conflict) { + conflictResolution.push( + function() { + if ('undefined' !== typeof conflict && $scope.DeepDiff === accumulateDiff) { + $scope.DeepDiff = conflict; + conflict = undefined; + } + }); + } + + // nodejs compatible on server side and in the browser. + function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + } + + function Diff(kind, path) { + Object.defineProperty(this, 'kind', { + value: kind, + enumerable: true + }); + if (path && path.length) { + Object.defineProperty(this, 'path', { + value: path, + enumerable: true + }); + } + } + + function DiffEdit(path, origin, value) { + DiffEdit.super_.call(this, 'E', path); + Object.defineProperty(this, 'lhs', { + value: origin, + enumerable: true + }); + Object.defineProperty(this, 'rhs', { + value: value, + enumerable: true + }); + } + inherits(DiffEdit, Diff); + + function DiffNew(path, value) { + DiffNew.super_.call(this, 'N', path); + Object.defineProperty(this, 'rhs', { + value: value, + enumerable: true + }); + } + inherits(DiffNew, Diff); + + function DiffDeleted(path, value) { + DiffDeleted.super_.call(this, 'D', path); + Object.defineProperty(this, 'lhs', { + value: value, + enumerable: true + }); + } + inherits(DiffDeleted, Diff); + + function DiffArray(path, index, item) { + DiffArray.super_.call(this, 'A', path); + Object.defineProperty(this, 'index', { + value: index, + enumerable: true + }); + Object.defineProperty(this, 'item', { + value: item, + enumerable: true + }); + } + inherits(DiffArray, Diff); + + function arrayRemove(arr, from, to) { + var rest = arr.slice((to || from) + 1 || arr.length); + arr.length = from < 0 ? arr.length + from : from; + arr.push.apply(arr, rest); + return arr; + } + + function realTypeOf(subject) { + var type = typeof subject; + if (type !== 'object') { + return type; + } + + if (subject === Math) { + return 'math'; + } else if (subject === null) { + return 'null'; + } else if (Array.isArray(subject)) { + return 'array'; + } else if (Object.prototype.toString.call(subject) === '[object Date]') { + return 'date'; + } else if (typeof subject.toString !== 'undefined' && /^\/.*\//.test(subject.toString())) { + return 'regexp'; + } + return 'object'; + } + + function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) { + path = path || []; + var currentPath = path.slice(0); + if (typeof key !== 'undefined') { + if (prefilter) { + if (typeof(prefilter) === 'function' && prefilter(currentPath, key)) { return; } + else if (typeof(prefilter) === 'object') { + if (prefilter.prefilter && prefilter.prefilter(currentPath, key)) { return; } + if (prefilter.normalize) { + var alt = prefilter.normalize(currentPath, key, lhs, rhs); + if (alt) { + lhs = alt[0]; + rhs = alt[1]; + } + } + } + } + currentPath.push(key); + } + + // Use string comparison for regexes + if (realTypeOf(lhs) === 'regexp' && realTypeOf(rhs) === 'regexp') { + lhs = lhs.toString(); + rhs = rhs.toString(); + } + + var ltype = typeof lhs; + var rtype = typeof rhs; + if (ltype === 'undefined') { + if (rtype !== 'undefined') { + changes(new DiffNew(currentPath, rhs)); + } + } else if (rtype === 'undefined') { + changes(new DiffDeleted(currentPath, lhs)); + } else if (realTypeOf(lhs) !== realTypeOf(rhs)) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } else if (Object.prototype.toString.call(lhs) === '[object Date]' && Object.prototype.toString.call(rhs) === '[object Date]' && ((lhs - rhs) !== 0)) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } else if (ltype === 'object' && lhs !== null && rhs !== null) { + stack = stack || []; + if (stack.indexOf(lhs) < 0) { + stack.push(lhs); + if (Array.isArray(lhs)) { + var i, len = lhs.length; + for (i = 0; i < lhs.length; i++) { + if (i >= rhs.length) { + changes(new DiffArray(currentPath, i, new DiffDeleted(undefined, lhs[i]))); + } else { + deepDiff(lhs[i], rhs[i], changes, prefilter, currentPath, i, stack); + } + } + while (i < rhs.length) { + changes(new DiffArray(currentPath, i, new DiffNew(undefined, rhs[i++]))); + } + } else { + var akeys = Object.keys(lhs); + var pkeys = Object.keys(rhs); + akeys.forEach(function(k, i) { + var other = pkeys.indexOf(k); + if (other >= 0) { + deepDiff(lhs[k], rhs[k], changes, prefilter, currentPath, k, stack); + pkeys = arrayRemove(pkeys, other); + } else { + deepDiff(lhs[k], undefined, changes, prefilter, currentPath, k, stack); + } + }); + pkeys.forEach(function(k) { + deepDiff(undefined, rhs[k], changes, prefilter, currentPath, k, stack); + }); + } + stack.length = stack.length - 1; + } + } else if (lhs !== rhs) { + if (!(ltype === 'number' && isNaN(lhs) && isNaN(rhs))) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } + } + } + + function accumulateDiff(lhs, rhs, prefilter, accum) { + accum = accum || []; + deepDiff(lhs, rhs, + function(diff) { + if (diff) { + accum.push(diff); + } + }, + prefilter); + return (accum.length) ? accum : undefined; + } + + function applyArrayChange(arr, index, change) { + if (change.path && change.path.length) { + var it = arr[index], + i, u = change.path.length - 1; + for (i = 0; i < u; i++) { + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + applyArrayChange(it[change.path[i]], change.index, change.item); + break; + case 'D': + delete it[change.path[i]]; + break; + case 'E': + case 'N': + it[change.path[i]] = change.rhs; + break; + } + } else { + switch (change.kind) { + case 'A': + applyArrayChange(arr[index], change.index, change.item); + break; + case 'D': + arr = arrayRemove(arr, index); + break; + case 'E': + case 'N': + arr[index] = change.rhs; + break; + } + } + return arr; + } + + function applyChange(target, source, change) { + if (target && source && change && change.kind) { + var it = target, + i = -1, + last = change.path ? change.path.length - 1 : 0; + while (++i < last) { + if (typeof it[change.path[i]] === 'undefined') { + it[change.path[i]] = (typeof change.path[i] === 'number') ? [] : {}; + } + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + applyArrayChange(change.path ? it[change.path[i]] : it, change.index, change.item); + break; + case 'D': + delete it[change.path[i]]; + break; + case 'E': + case 'N': + it[change.path[i]] = change.rhs; + break; + } + } + } + + function revertArrayChange(arr, index, change) { + if (change.path && change.path.length) { + // the structure of the object at the index has changed... + var it = arr[index], + i, u = change.path.length - 1; + for (i = 0; i < u; i++) { + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + revertArrayChange(it[change.path[i]], change.index, change.item); + break; + case 'D': + it[change.path[i]] = change.lhs; + break; + case 'E': + it[change.path[i]] = change.lhs; + break; + case 'N': + delete it[change.path[i]]; + break; + } + } else { + // the array item is different... + switch (change.kind) { + case 'A': + revertArrayChange(arr[index], change.index, change.item); + break; + case 'D': + arr[index] = change.lhs; + break; + case 'E': + arr[index] = change.lhs; + break; + case 'N': + arr = arrayRemove(arr, index); + break; + } + } + return arr; + } + + function revertChange(target, source, change) { + if (target && source && change && change.kind) { + var it = target, + i, u; + u = change.path.length - 1; + for (i = 0; i < u; i++) { + if (typeof it[change.path[i]] === 'undefined') { + it[change.path[i]] = {}; + } + it = it[change.path[i]]; + } + switch (change.kind) { + case 'A': + // Array was modified... + // it will be an array... + revertArrayChange(it[change.path[i]], change.index, change.item); + break; + case 'D': + // Item was deleted... + it[change.path[i]] = change.lhs; + break; + case 'E': + // Item was edited... + it[change.path[i]] = change.lhs; + break; + case 'N': + // Item is new... + delete it[change.path[i]]; + break; + } + } + } + + function applyDiff(target, source, filter) { + if (target && source) { + var onChange = function(change) { + if (!filter || filter(target, source, change)) { + applyChange(target, source, change); + } + }; + deepDiff(target, source, onChange); + } + } + + Object.defineProperties(accumulateDiff, { + + diff: { + value: accumulateDiff, + enumerable: true + }, + observableDiff: { + value: deepDiff, + enumerable: true + }, + applyDiff: { + value: applyDiff, + enumerable: true + }, + applyChange: { + value: applyChange, + enumerable: true + }, + revertChange: { + value: revertChange, + enumerable: true + }, + isConflict: { + value: function() { + return 'undefined' !== typeof conflict; + }, + enumerable: true + }, + noConflict: { + value: function() { + if (conflictResolution) { + conflictResolution.forEach(function(it) { + it(); + }); + conflictResolution = null; + } + return accumulateDiff; + }, + enumerable: true + } + }); + + return accumulateDiff; +})); + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[6]); diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index c65ff56..46e17c0 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -21,6 +21,8 @@ var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); +var deep = require('deep-diff').diff; + /** * * Can be inherited or used as is. Holds the model definition, @@ -51,6 +53,7 @@ function ActiveRecord(model, name) { var _this = this; + this.__old = {}; _.each(model, function (field, key) { if (options && (options[key] || options[key] === 0)) { var name = _.isArray(field) ? field[0].ref : field.ref; @@ -83,8 +86,12 @@ function ActiveRecord(model, name) { } else { _this[key] = _this.buildField(field, options[key]); } - } else if (_.isArray(field)) { - _this[key] = []; + } + /*else if (_.isArray(field)) { + this[ key ] = []; + } */ + if (options && options._id) { + _this.__old[key] = _.cloneDeep(_this[key]); } }); return this; @@ -226,8 +233,18 @@ function ActiveRecord(model, name) { }, { key: 'beforeSave', value: function beforeSave(obj) { + // istanbul ignore next + + var _this3 = this; + + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + obj = obj || _.cloneDeep(this); _.each(model, function (field, key) { + if (!opts.force && !deep(obj[key], _this3.__old[key])) { + delete obj[key]; + return; + } if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { if (_.isArray(obj[key])) { @@ -245,25 +262,36 @@ function ActiveRecord(model, name) { obj[key] = new Date(moment(obj[key])).toISOString(); } }); + delete obj.rootUrl; + delete obj.$injector; + delete obj._injector; + delete obj.__old; return obj; } }, { key: 'save', - value: function save(populate) { + value: function save() { // istanbul ignore next - var _this3 = this; + var _this4 = this; + + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - var toSave = this.beforeSave(); + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(function (resolve) { + return resolve({ data: _this4 }); + }); + } var callback; - if (populate) { + if (opts.populate) { var dao = sl.getDao(name); callback = function () { - return dao.getById(_this3._id, dao.query().populate(populate)); + return dao.getById(_this4._id, dao.query().populate(opts.populate)); }; } else { callback = function (data) { - _this3.build(data.data); + _this4.build(data.data); return data; }; } @@ -279,28 +307,28 @@ function ActiveRecord(model, name) { value: function saveDeep(populate) { // istanbul ignore next - var _this4 = this; + var _this5 = this; var promises = []; /** Find ref properties that might need to be saved */ _.each(model, function (v, k) { - if (_this4[k]) { + if (_this5[k]) { if (_.isArray(v)) { if (v[0].ref) { /** Array of nested Objects. Check if need to save each */ - _this4[k].forEach(function (e) { + _this5[k].forEach(function (e) { if (!e._id && e.saveDeep) promises.push(e.saveDeep()); }); } } else if (v.ref) { /** Single nested object, save it if needed */ - if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); + if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); } } }); var $q = this._injector.get('$q'); return $q.all(promises).then(function () { - return _this4.save(populate); + return _this5.save(populate); }); } }, { diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js index 16c76f1..3fbc517 100644 --- a/dest/temp/specs/angular-dao.specs.js +++ b/dest/temp/specs/angular-dao.specs.js @@ -292,7 +292,7 @@ describe('Angular DAO', function () { model2: { _id: '888', name: 'tutu' }, models2: [{ _id: '999' }] }); - model.save(['model2', 'models2']).then(function (pop) { + model.save({ force: true, populate: ['model2', 'models2'] }).then(function (pop) { expect(pop.models2[0]._id).toEqual('999'); }); httpBackend.flush(); @@ -307,7 +307,7 @@ describe('Angular DAO', function () { _id: '1234656', model2: '888' }); - model.save().then(function () { + model.save({ force: true }).then(function () { expect(model.model2._id).toEqual('888'); }); httpBackend.flush(); @@ -322,7 +322,7 @@ describe('Angular DAO', function () { _id: '1234656', model2: '777' }); - model.save().then(function () { + model.save({ force: true }).then(function () { expect(model.model2).toEqual('777'); }); httpBackend.flush(); @@ -337,7 +337,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: ['888'] }); - model.save().then(function () { + model.save({ force: true }).then(function () { expect(model.models2[0]._id).toEqual('888'); }); httpBackend.flush(); @@ -352,7 +352,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: ['888', '999'] }); - model.save().then(function () { + model.save({ force: true }).then(function () { expect(model.models2.length).toEqual(2); expect(model.models2[0]._id).toEqual('888'); expect(model.models2[0].name).toEqual('tutu'); @@ -370,7 +370,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: ['999', '111'] }); - model.save().then(function () { + model.save({ force: true }).then(function () { expect(model.models2.length).toEqual(2); expect(model.models2[0]).toEqual('999'); expect(model.models2[1]._id).toEqual('111'); @@ -379,16 +379,47 @@ describe('Angular DAO', function () { httpBackend.flush(); }); - it('Should save objects', function () { + it('should save only modified values', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }); + model.label = 'tutu'; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('should save only modified values in arrays', function () { + var model = ModelManager.createModel({ + _id: '123456', + models2: ['7777'], + label: 'toto' + }); + model.models2.push('8888'); + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: ['7777', '8888'] }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should save new objects', function () { var model = ModelManager.createModel({ - _id: '1234656', model2: '77777' }); - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656').respond(); + httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); model.save(); httpBackend.flush(); }); + it('Should not save unedited objects', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '77777' + }); + model.save(); + }); + it('Should deep nested objects', function () { var model = ModelManager.createModel({ label: 'toto', diff --git a/package.json b/package.json index cf8bc32..f2ddcb0 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,12 @@ { "author": "flogou ", "name": "angular-orm", - "version": "1.1.5", + "version": "2.0.0-alpha-1", "description": "", "homepage": "", - "dependencies": {}, + "dependencies": { + "deep-diff": "~0.3.4" + }, "files": [ "src/**/*.js" ], diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 9e0956b..e121b9b 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -1,5 +1,6 @@ //_ = require('lodash'); import ServiceLocator from './ServiceLocator' +var deep = require('deep-diff').diff /** * @@ -22,6 +23,7 @@ export default function ActiveRecord (model, name) { } build (options) { + this.__old = {} _.each(model, (field, key)=> { if (options && (options[ key ] || options[ key ] === 0)) { var name = _.isArray(field) ? field[ 0 ].ref : field.ref; @@ -59,8 +61,12 @@ export default function ActiveRecord (model, name) { } else { this[ key ] = this.buildField(field, options[ key ]) } - } else if (_.isArray(field)) { - this[ key ] = []; + } + /*else if (_.isArray(field)) { + this[ key ] = []; + } */ + if (options && options._id) { + this.__old[ key ] = _.cloneDeep(this[ key ]) } }); return this; @@ -197,9 +203,13 @@ export default function ActiveRecord (model, name) { } - beforeSave (obj) { + beforeSave (obj, opts={}) { obj = obj || _.cloneDeep(this); _.each(model, (field, key)=> { + if (!opts.force && !deep(obj[ key ], this.__old[ key ])) { + delete obj[ key ] + return + } if (obj[ key ] && (field.ref || (_.isArray(field) && field[ 0 ].ref))) { if (_.isArray(obj[ key ])) { @@ -219,17 +229,22 @@ export default function ActiveRecord (model, name) { }); delete obj.rootUrl; delete obj.$injector; + delete obj._injector; + delete obj.__old; return obj; } - save (populate) { - var toSave = this.beforeSave(); + save (opts={}) { + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(resolve => resolve({data: this})) + } var callback; - if (populate) { + if (opts.populate) { var dao = sl.getDao(name); callback = ()=> { - return dao.getById(this._id, dao.query().populate(populate)); + return dao.getById(this._id, dao.query().populate(opts.populate)); } } else { callback = (data) => { diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js index 79bb4cc..64e07c9 100644 --- a/tst/specs/angular-dao.specs.js +++ b/tst/specs/angular-dao.specs.js @@ -214,7 +214,7 @@ describe('Angular DAO', function () { expect(clone._id).not.toBeDefined() }); - it('Should Clone Deep', function(){ + it('Should Clone Deep', function () { var model = ModelManager.create({ _id: '123456', model2: { @@ -298,7 +298,7 @@ describe('Angular DAO', function () { model2: { _id: '888', name: 'tutu' }, models2: [ { _id: '999' } ] }) - model.save([ 'model2', 'models2' ]).then(function (pop) { + model.save({force: true, populate: [ 'model2', 'models2' ]}).then(function (pop) { expect(pop.models2[ 0 ]._id).toEqual('999') }) httpBackend.flush() @@ -313,7 +313,7 @@ describe('Angular DAO', function () { _id: '1234656', model2: '888' }) - model.save().then(function () { + model.save({force:true}).then(function () { expect(model.model2._id).toEqual('888') }) httpBackend.flush() @@ -328,7 +328,7 @@ describe('Angular DAO', function () { _id: '1234656', model2: '777' }) - model.save().then(function () { + model.save({force: true}).then(function () { expect(model.model2).toEqual('777') }) httpBackend.flush() @@ -343,7 +343,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: [ '888' ] }); - model.save().then(function () { + model.save({force: true}).then(function () { expect(model.models2[ 0 ]._id).toEqual('888') }) httpBackend.flush() @@ -358,7 +358,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: [ '888', '999' ] }); - model.save().then(function () { + model.save({force:true}).then(function () { expect(model.models2.length).toEqual(2) expect(model.models2[ 0 ]._id).toEqual('888') expect(model.models2[ 0 ].name).toEqual('tutu') @@ -376,7 +376,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: [ '999', '111' ] }); - model.save().then(function () { + model.save({force: true}).then(function () { expect(model.models2.length).toEqual(2) expect(model.models2[ 0 ]).toEqual('999') expect(model.models2[ 1 ]._id).toEqual('111') @@ -385,23 +385,54 @@ describe('Angular DAO', function () { httpBackend.flush() }) - it('Should save objects', function () { + it('should save only modified values', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }) + model.label = 'tutu' + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() + model.save() + httpBackend.flush() + }) + + it('should save only modified values in arrays', function () { + var model = ModelManager.createModel({ + _id: '123456', + models2: ['7777'], + label: 'toto' + }) + model.models2.push('8888') + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: ['7777', '8888'] }).respond() + model.save() + httpBackend.flush() + }) + + it('Should save new objects', function () { var model = ModelManager.createModel({ - _id: '1234656', model2: '77777' }); - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656').respond(); + httpBackend.expectPOST('http://MOCKURL.com/model1', {model2: '77777'}).respond(); model.save(); httpBackend.flush(); }); + it('Should not save unedited objects', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '77777' + }); + model.save(); + }); + it('Should deep nested objects', function () { var model = ModelManager.createModel({ label: 'toto', - model2: { name: 'toto'} + model2: { name: 'toto' } }); - httpBackend.expectPOST('http://MOCKURL.com/model2').respond({name: 'toto', _id: '1111'}); - httpBackend.expectPOST('http://MOCKURL.com/model1').respond({label: 'toto'}); + httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); + httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); model.saveDeep(); httpBackend.flush(); expect(model.model2._id).toEqual('1111') From d28466519b590e11729810f7d7b6c9b4cd4fd54e Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 09:12:39 +0200 Subject: [PATCH 02/26] abstraction for caching objects for diff purpose --- CHANGELOG.md | 1 + build/app.js | 724 ++++++++++++++++++++---------------- dest/temp/ActiveRecord.js | 655 ++++++++++++++++---------------- dest/temp/SessionManager.js | 60 +++ package.json | 2 +- src/ActiveRecord.js | 24 +- src/SessionManager.js | 31 ++ 7 files changed, 834 insertions(+), 663 deletions(-) create mode 100644 dest/temp/SessionManager.js create mode 100644 src/SessionManager.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d3c281..be26369 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,3 +15,4 @@ If you rewrote beforeSave methods, be careful and pass the optional `opts` objec If you wish to keep sending all your fields to the server each you save (which you shouldn't). Pass `{force: true}` as options in the `save` method. - re-population after save. Until 2.0.0 when you wanted to repopulate an object after saving it (ie making another query), you had to pass it as the first argument of the save method. Now, it has to be given as an options object containing `{populate: whateverYouNeedToPopulate}` + - Empty arrays: when building an active record, undefined arrays will not be instantiated to empty arrays (to prevent overriding unselected fields). Check for the actual presence of the array before trying to push. diff --git a/build/app.js b/build/app.js index e19ec12..4df8cd6 100644 --- a/build/app.js +++ b/build/app.js @@ -22,6 +22,10 @@ var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + var deep = require('deep-diff').diff; /** @@ -36,377 +40,380 @@ var deep = require('deep-diff').diff; */ function ActiveRecord(model, name) { - var sl = _ServiceLocator2['default'].instance; - var ActiveRecord = (function () { - function ActiveRecord($injector, rootUrl, options) { - _classCallCheck(this, ActiveRecord); - - this.$injector = $injector; - // this.$http = $injector.get('$http'); - this.rootUrl = rootUrl; - this.build(options); - } + var SManager = arguments.length <= 2 || arguments[2] === undefined ? (0, _SessionManager2['default'])(model) : arguments[2]; + return (function () { + var sl = _ServiceLocator2['default'].instance; + var session = new SManager(); - _createClass(ActiveRecord, [{ - key: 'build', - value: function build(options) { - // istanbul ignore next + var ActiveRecord = (function () { + function ActiveRecord($injector, rootUrl, options) { + _classCallCheck(this, ActiveRecord); - var _this = this; + this.$injector = $injector; + this.rootUrl = rootUrl; + this.build(options); + } - this.__old = {}; - _.each(model, function (field, key) { - if (options && (options[key] || options[key] === 0)) { - var name = _.isArray(field) ? field[0].ref : field.ref; - var dao = sl.getDao(name); - if (dao) { - /** - * Deal with populated fields when building a pre-existing object - * - * Some fields might be populated in existing object, but not in the incoming one - * To prevent things from disappearing, determine when new data should overwrite exiting - * - * In the case of an array, look at each incoming value. - * If it is populated, keep it - * If it isn't, try to find the populated value in the existing array, and use it instead. - */ - if (Array.isArray(options[key])) { - var populated = options[key].map(function (value) { - if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { - var found = _this[key].find(function (element) { - return element && element._id === value; - }); - if (found) return found; - } - return value; - }); - _this[key] = dao.build(_.clone(populated)); - } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { - _this[key] = dao.build(_.clone(options[key])); + _createClass(ActiveRecord, [{ + key: 'build', + value: function build(options) { + // istanbul ignore next + + var _this = this; + + var sess = {}; + _.each(model, function (field, key) { + if (options && (options[key] || options[key] === 0)) { + var name = _.isArray(field) ? field[0].ref : field.ref; + var dao = sl.getDao(name); + if (dao) { + /** + * Deal with populated fields when building a pre-existing object + * + * Some fields might be populated in existing object, but not in the incoming one + * To prevent things from disappearing, determine when new data should overwrite exiting + * + * In the case of an array, look at each incoming value. + * If it is populated, keep it + * If it isn't, try to find the populated value in the existing array, and use it instead. + */ + if (Array.isArray(options[key])) { + var populated = options[key].map(function (value) { + if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { + var found = _this[key].find(function (element) { + return element && element._id === value; + }); + if (found) return found; + } + return value; + }); + _this[key] = dao.build(_.clone(populated)); + } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { + _this[key] = dao.build(_.clone(options[key])); + } + } else { + _this[key] = _this.buildField(field, options[key]); } - } else { - _this[key] = _this.buildField(field, options[key]); } - } - /*else if (_.isArray(field)) { - this[ key ] = []; - } */ - if (options && options._id) { - _this.__old[key] = _.cloneDeep(_this[key]); - } - }); - return this; - } - }, { - key: 'clone', - value: function clone() { - var m = sl.getModel(name); - var ob = new m(this.$injector, this.rootUrl, this); - delete ob._id; - return ob; - } + if (options && options._id) { + sess[key] = _.cloneDeep(_this[key]); + } + }); + session.save(sess); + return this; + } + }, { + key: 'clone', + value: function clone() { + var m = sl.getModel(name); + var ob = new m(this.$injector, this.rootUrl, this); + delete ob._id; + return ob; + } - /** - * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! - */ - }, { - key: 'cloneDeep', - value: function cloneDeep() { - var clone = this.clone(); - /** Find ref properties that need their _id to be removed */ - _.each(model, function (v, k) { - if (clone[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Need to clone each */ - clone[k] = clone[k].map(function (e) { - return e.cloneDeep ? e.cloneDeep() : e; - }); + /** + * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! + */ + }, { + key: 'cloneDeep', + value: function cloneDeep() { + var clone = this.clone(); + /** Find ref properties that need their _id to be removed */ + _.each(model, function (v, k) { + if (clone[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Need to clone each */ + clone[k] = clone[k].map(function (e) { + return e.cloneDeep ? e.cloneDeep() : e; + }); + } + } else if (v.ref) { + /** Single nested object, replace it */ + clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } else if (v.ref) { - /** Single nested object, replace it */ - clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } - }); - return clone; - } - }, { - key: 'buildField', - value: function buildField(model, value) { - return _.clone(value); - } - }, { - key: 'archive', - value: function archive() { - return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); - } - }, { - key: 'restore', - value: function restore() { - return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); - } - }, { - key: 'remove', - value: function remove() { - return this['delete'](); - } - }, { - key: 'delete', - value: function _delete() { - return this.$http['delete'](this.rootUrl + '/' + this._id); - } - }, { - key: 'populate', - value: function populate(field, query) { - // istanbul ignore next + }); + return clone; + } + }, { + key: 'buildField', + value: function buildField(model, value) { + return _.clone(value); + } + }, { + key: 'archive', + value: function archive() { + return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); + } + }, { + key: 'restore', + value: function restore() { + return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); + } + }, { + key: 'remove', + value: function remove() { + return this['delete'](); + } + }, { + key: 'delete', + value: function _delete() { + return this.$http['delete'](this.rootUrl + '/' + this._id); + } + }, { + key: 'populate', + value: function populate(field, query) { + // istanbul ignore next - var _this2 = this; + var _this2 = this; - var $q = this.$injector.get('$q'); + var $q = this.$injector.get('$q'); - if (Array.isArray(field)) { - return $q.all(field.map(function (f) { - return _this2.populate(f, query); - })).then(function () { - return _this2; - }); - } + if (Array.isArray(field)) { + return $q.all(field.map(function (f) { + return _this2.populate(f, query); + })).then(function () { + return _this2; + }); + } - var deferred = $q.defer(); - var self = this; - if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { - /** - * The field is an array. It may contain mixed populated and unpopulated data. - * first, sort it out (typeof unpopulated is string) - */ - var grouped = _.groupBy(this[field], function (i) { - return typeof i; - }); - /** No string = everything is populated (or array is empty), we're all set */ - if (!grouped.string) { + var deferred = $q.defer(); + var self = this; + if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { + /** + * The field is an array. It may contain mixed populated and unpopulated data. + * first, sort it out (typeof unpopulated is string) + */ + var grouped = _.groupBy(this[field], function (i) { + return typeof i; + }); + /** No string = everything is populated (or array is empty), we're all set */ + if (!grouped.string) { + deferred.resolve(this[field]); + } else { + /** Resolve the model name and the corresponding DAO */ + var name = model[field][0].ref; + if (!name) deferred.reject('Cannot Populate: unknown model');else { + var dao = sl.getDao(name); + if (!dao) { + deferred.reject('Cannot Populate: unknown DAO'); + } else { + return dao.get(dao.query(query).select(grouped.string)).then(function (d) { + /** To preserve order, we map the existing field, replacing only the populated values */ + self[field] = self[field].map(function (f) { + if (typeof f === 'string') { + return _.find(d.data, function (foo) { + return foo._id === f; + }); + } + return f; + }); + return self; + }); + } + } + } + } + + if (!this[field] || typeof this[field] === 'object') { + // The field is empty or already populated. return. deferred.resolve(this[field]); } else { - /** Resolve the model name and the corresponding DAO */ - var name = model[field][0].ref; + var name = model[field].ref; if (!name) deferred.reject('Cannot Populate: unknown model');else { var dao = sl.getDao(name); if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then(function (d) { - /** To preserve order, we map the existing field, replacing only the populated values */ - self[field] = self[field].map(function (f) { - if (typeof f === 'string') { - return _.find(d.data, function (foo) { - return foo._id === f; - }); - } - return f; - }); + return dao.getById(this[field]).then(function (sub) { + self[field] = sub; return self; }); } } } - } - if (!this[field] || typeof this[field] === 'object') { - // The field is empty or already populated. return. - deferred.resolve(this[field]); - } else { - var name = model[field].ref; - if (!name) deferred.reject('Cannot Populate: unknown model');else { - var dao = sl.getDao(name); - if (!dao) { - deferred.reject('Cannot Populate: unknown DAO'); - } else { - return dao.getById(this[field]).then(function (sub) { - self[field] = sub; - return self; - }); + return deferred.promise; + } + }, { + key: 'beforeSave', + value: function beforeSave(obj) { + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + obj = obj || _.cloneDeep(this); + var old = session.retrieve(this._id) || {}; + _.each(model, function (field, key) { + if (!opts.force && !deep(obj[key], old[key])) { + delete obj[key]; + return; } - } + if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { + + if (_.isArray(obj[key])) { + obj[key] = _.compact(obj[key].map(function (val) { + if (typeof val === 'object') { + return field.nested ? val : val._id; + } else if (typeof val === 'string') { + return val; + } + })); + } else { + obj[key] = obj[key]._id || obj[key]; + } + } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { + obj[key] = new Date(moment(obj[key])).toISOString(); + } + }); + delete obj.rootUrl; + delete obj.$injector; + delete obj._injector; + return obj; } + }, { + key: 'save', + value: function save() { + // istanbul ignore next - return deferred.promise; - } - }, { - key: 'beforeSave', - value: function beforeSave(obj) { - // istanbul ignore next + var _this3 = this; - var _this3 = this; + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(function (resolve) { + return resolve({ data: _this3 }); + }); + } + var callback; + if (opts.populate) { + var dao = sl.getDao(name); + callback = function () { + return dao.getById(_this3._id, dao.query().populate(opts.populate)); + }; + } else { + callback = function (data) { + _this3.build(data.data); + return data; + }; + } - obj = obj || _.cloneDeep(this); - _.each(model, function (field, key) { - if (!opts.force && !deep(obj[key], _this3.__old[key])) { - delete obj[key]; - return; + if (this._id) { + return this.$http.put(this.rootUrl + '/' + this._id, toSave).then(callback); + } else { + return this.$http.post(this.rootUrl, toSave).then(callback); } - if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { - - if (_.isArray(obj[key])) { - obj[key] = _.compact(obj[key].map(function (val) { - if (typeof val === 'object') { - return field.nested ? val : val._id; - } else if (typeof val === 'string') { - return val; + } + }, { + key: 'saveDeep', + value: function saveDeep(populate) { + // istanbul ignore next + + var _this4 = this; + + var promises = []; + /** Find ref properties that might need to be saved */ + _.each(model, function (v, k) { + if (_this4[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Check if need to save each */ + _this4[k].forEach(function (e) { + if (!e._id && e.saveDeep) promises.push(e.saveDeep()); + }); } - })); - } else { - obj[key] = obj[key]._id || obj[key]; + } else if (v.ref) { + /** Single nested object, save it if needed */ + if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); + } } - } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { - obj[key] = new Date(moment(obj[key])).toISOString(); - } - }); - delete obj.rootUrl; - delete obj.$injector; - delete obj._injector; - delete obj.__old; - return obj; - } - }, { - key: 'save', - value: function save() { - // istanbul ignore next - - var _this4 = this; - - var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - - var toSave = this.beforeSave(null, opts); - if (_.isEmpty(toSave)) { - return this.$injector.get('$q')(function (resolve) { - return resolve({ data: _this4 }); + }); + var $q = this._injector.get('$q'); + return $q.all(promises).then(function () { + return _this4.save(populate); }); } - var callback; - if (opts.populate) { - var dao = sl.getDao(name); - callback = function () { - return dao.getById(_this4._id, dao.query().populate(opts.populate)); - }; - } else { - callback = function (data) { - _this4.build(data.data); - return data; - }; + }, { + key: '$injector', + get: function get() { + return this._injector; + }, + set: function set($injector) { + this._injector = $injector; } - - if (this._id) { - return this.$http.put(this.rootUrl + '/' + this._id, toSave).then(callback); - } else { - return this.$http.post(this.rootUrl, toSave).then(callback); + }, { + key: '$http', + get: function get() { + return this.$injector.get('$http'); } - } - }, { - key: 'saveDeep', - value: function saveDeep(populate) { - // istanbul ignore next - - var _this5 = this; - - var promises = []; - /** Find ref properties that might need to be saved */ - _.each(model, function (v, k) { - if (_this5[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Check if need to save each */ - _this5[k].forEach(function (e) { - if (!e._id && e.saveDeep) promises.push(e.saveDeep()); - }); - } - } else if (v.ref) { - /** Single nested object, save it if needed */ - if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); + }], [{ + key: 'getName', + value: function getName() { + return name; + } + }, { + key: 'makePopObject', + value: function makePopObject(pop) { + return _.map(pop, function (p, k) { + p = Array.isArray(p) ? p[0] : p; + var ob = { path: k }; + var sub = ActiveRecord.findSubPopulates(p.ref); + if (sub && sub.length) { + ob.populate = sub; } + return ob; + }); + } + }, { + key: 'findSubPopulates', + value: function findSubPopulates(ref) { + var serviceLocator = _ServiceLocator2['default'].instance; + var linkedModel = serviceLocator.getModel(ref); + if (linkedModel && linkedModel !== model) { + return linkedModel.populateParams(); } - }); - var $q = this._injector.get('$q'); - return $q.all(promises).then(function () { - return _this5.save(populate); - }); - } - }, { - key: '$injector', - get: function get() { - return this._injector; - }, - set: function set($injector) { - this._injector = $injector; - } - }, { - key: '$http', - get: function get() { - return this.$injector.get('$http'); - } - }], [{ - key: 'getName', - value: function getName() { - return name; - } - }, { - key: 'makePopObject', - value: function makePopObject(pop) { - return _.map(pop, function (p, k) { - p = Array.isArray(p) ? p[0] : p; - var ob = { path: k }; - var sub = ActiveRecord.findSubPopulates(p.ref); - if (sub && sub.length) { - ob.populate = sub; - } - return ob; - }); - } - }, { - key: 'findSubPopulates', - value: function findSubPopulates(ref) { - var serviceLocator = _ServiceLocator2['default'].instance; - var linkedModel = serviceLocator.getModel(ref); - if (linkedModel && linkedModel !== model) { - return linkedModel.populateParams(); } - } - }, { - key: 'populateParams', - value: function populateParams(populateArray) { - - // Get from the model the fields that reference another object - var pop = _.pick(model, function (v) { - if (_.isArray(v)) return v[0].ref; - return v.ref; - }); + }, { + key: 'populateParams', + value: function populateParams(populateArray) { + + // Get from the model the fields that reference another object + var pop = _.pick(model, function (v) { + if (_.isArray(v)) return v[0].ref; + return v.ref; + }); - if (populateArray === 'all') { - return ActiveRecord.makePopObject(pop); + if (populateArray === 'all') { + return ActiveRecord.makePopObject(pop); + } + var toPopulate = populateArray ? _.pick(pop, function (p, k) { + return _.contains(populateArray, k); + }) : _.pick(pop, function (v) { + return v.populateDefault || v[0] && v[0].populateDefault; + }); + return ActiveRecord.makePopObject(toPopulate); } - var toPopulate = populateArray ? _.pick(pop, function (p, k) { - return _.contains(populateArray, k); - }) : _.pick(pop, function (v) { - return v.populateDefault || v[0] && v[0].populateDefault; - }); - return ActiveRecord.makePopObject(toPopulate); - } - }, { - key: 'getModel', - value: function getModel() { - return model; - } - }]); + }, { + key: 'getModel', + value: function getModel() { + return model; + } + }, { + key: 'getSession', + value: function getSession() { + return session; + } + }]); + + return ActiveRecord; + })(); return ActiveRecord; })(); - // sl.registerModel(name, ActiveRecord); - return ActiveRecord; } module.exports = exports['default']; -},{"./ServiceLocator":5,"deep-diff":11}],2:[function(require,module,exports){ +},{"./ServiceLocator":5,"./SessionManager":6,"deep-diff":12}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -918,6 +925,67 @@ module.exports = exports["default"]; },{}],6:[function(require,module,exports){ 'use strict'; +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +exports['default'] = SessionManager; +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +function SessionManager(model) { + + var SessionManager = (function () { + function SessionManager() { + _classCallCheck(this, SessionManager); + + this.storage = {}; + } + + _createClass(SessionManager, [{ + key: 'save', + value: function save(obj) { + if (!obj._id) return; + this.storage[obj._id] = obj; + } + }, { + key: 'retrieve', + value: function retrieve(id) { + return this.storage[id]; + } + }, { + key: 'clean', + value: function clean() { + this.storage = {}; + } + }, { + key: 'model', + get: function get() { + return model; + } + }]); + + return SessionManager; + })(); + return SessionManager; +} + +module.exports = exports['default']; +},{"./ServiceLocator":5}],7:[function(require,module,exports){ +'use strict'; + // istanbul ignore next function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } @@ -938,7 +1006,7 @@ var _module = angular.module('tstModule', []); _DaoHelper2['default'].registerService(_module, 'ModelManager', _managersTstManager12['default']); _DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); -},{"./DaoHelper":2,"./managers/tstManager1":7,"./managers/tstManager2":8}],7:[function(require,module,exports){ +},{"./DaoHelper":2,"./managers/tstManager1":8,"./managers/tstManager2":9}],8:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -989,7 +1057,7 @@ var ModelManager = (function (_DAO) { exports['default'] = ModelManager; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel1.js":9}],8:[function(require,module,exports){ +},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel1.js":10}],9:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1040,7 +1108,7 @@ var ModelManager2 = (function (_DAO) { exports['default'] = ModelManager2; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel2.js":10}],9:[function(require,module,exports){ +},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel2.js":11}],10:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1105,7 +1173,7 @@ var Model = (function (_AR) { exports['default'] = Model; module.exports = exports['default']; -},{"../ActiveRecord":1}],10:[function(require,module,exports){ +},{"../ActiveRecord":1}],11:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1158,7 +1226,7 @@ var Model2 = (function (_AR) { exports['default'] = Model2; module.exports = exports['default']; -},{"../ActiveRecord":1}],11:[function(require,module,exports){ +},{"../ActiveRecord":1}],12:[function(require,module,exports){ (function (global){ /*! * deep-diff. @@ -1584,4 +1652,4 @@ module.exports = exports['default']; })); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}]},{},[6]); +},{}]},{},[7]); diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 46e17c0..2191c20 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -21,6 +21,10 @@ var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + var deep = require('deep-diff').diff; /** @@ -35,373 +39,376 @@ var deep = require('deep-diff').diff; */ function ActiveRecord(model, name) { - var sl = _ServiceLocator2['default'].instance; - var ActiveRecord = (function () { - function ActiveRecord($injector, rootUrl, options) { - _classCallCheck(this, ActiveRecord); - - this.$injector = $injector; - // this.$http = $injector.get('$http'); - this.rootUrl = rootUrl; - this.build(options); - } - - _createClass(ActiveRecord, [{ - key: 'build', - value: function build(options) { - // istanbul ignore next - - var _this = this; - - this.__old = {}; - _.each(model, function (field, key) { - if (options && (options[key] || options[key] === 0)) { - var name = _.isArray(field) ? field[0].ref : field.ref; - var dao = sl.getDao(name); - if (dao) { - /** - * Deal with populated fields when building a pre-existing object - * - * Some fields might be populated in existing object, but not in the incoming one - * To prevent things from disappearing, determine when new data should overwrite exiting - * - * In the case of an array, look at each incoming value. - * If it is populated, keep it - * If it isn't, try to find the populated value in the existing array, and use it instead. - */ - if (Array.isArray(options[key])) { - var populated = options[key].map(function (value) { - if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { - var found = _this[key].find(function (element) { - return element && element._id === value; - }); - if (found) return found; - } - return value; - }); - _this[key] = dao.build(_.clone(populated)); - } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { - _this[key] = dao.build(_.clone(options[key])); + var SManager = arguments.length <= 2 || arguments[2] === undefined ? (0, _SessionManager2['default'])(model) : arguments[2]; + return (function () { + var sl = _ServiceLocator2['default'].instance; + var session = new SManager(); + + var ActiveRecord = (function () { + function ActiveRecord($injector, rootUrl, options) { + _classCallCheck(this, ActiveRecord); + + this.$injector = $injector; + this.rootUrl = rootUrl; + this.build(options); + } + + _createClass(ActiveRecord, [{ + key: 'build', + value: function build(options) { + // istanbul ignore next + + var _this = this; + + var sess = {}; + _.each(model, function (field, key) { + if (options && (options[key] || options[key] === 0)) { + var name = _.isArray(field) ? field[0].ref : field.ref; + var dao = sl.getDao(name); + if (dao) { + /** + * Deal with populated fields when building a pre-existing object + * + * Some fields might be populated in existing object, but not in the incoming one + * To prevent things from disappearing, determine when new data should overwrite exiting + * + * In the case of an array, look at each incoming value. + * If it is populated, keep it + * If it isn't, try to find the populated value in the existing array, and use it instead. + */ + if (Array.isArray(options[key])) { + var populated = options[key].map(function (value) { + if (typeof value === 'string' && _this[key] && Array.isArray(_this[key])) { + var found = _this[key].find(function (element) { + return element && element._id === value; + }); + if (found) return found; + } + return value; + }); + _this[key] = dao.build(_.clone(populated)); + } else if (!_this[key] || typeof _this[key] === 'string' || typeof options[key] === 'object' || _this[key]._id !== options[key]) { + _this[key] = dao.build(_.clone(options[key])); + } + } else { + _this[key] = _this.buildField(field, options[key]); } - } else { - _this[key] = _this.buildField(field, options[key]); } - } - /*else if (_.isArray(field)) { - this[ key ] = []; - } */ - if (options && options._id) { - _this.__old[key] = _.cloneDeep(_this[key]); - } - }); - return this; - } - }, { - key: 'clone', - value: function clone() { - var m = sl.getModel(name); - var ob = new m(this.$injector, this.rootUrl, this); - delete ob._id; - return ob; - } + if (options && options._id) { + sess[key] = _.cloneDeep(_this[key]); + } + }); + session.save(sess); + return this; + } + }, { + key: 'clone', + value: function clone() { + var m = sl.getModel(name); + var ob = new m(this.$injector, this.rootUrl, this); + delete ob._id; + return ob; + } - /** - * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! - */ - }, { - key: 'cloneDeep', - value: function cloneDeep() { - var clone = this.clone(); - /** Find ref properties that need their _id to be removed */ - _.each(model, function (v, k) { - if (clone[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Need to clone each */ - clone[k] = clone[k].map(function (e) { - return e.cloneDeep ? e.cloneDeep() : e; - }); + /** + * Be careful when using this method. It will ignore non-populated fields, keeping the _id entry ! + */ + }, { + key: 'cloneDeep', + value: function cloneDeep() { + var clone = this.clone(); + /** Find ref properties that need their _id to be removed */ + _.each(model, function (v, k) { + if (clone[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Need to clone each */ + clone[k] = clone[k].map(function (e) { + return e.cloneDeep ? e.cloneDeep() : e; + }); + } + } else if (v.ref) { + /** Single nested object, replace it */ + clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } else if (v.ref) { - /** Single nested object, replace it */ - clone[k] = clone[k].cloneDeep ? clone[k].cloneDeep() : clone[k]; } - } - }); - return clone; - } - }, { - key: 'buildField', - value: function buildField(model, value) { - return _.clone(value); - } - }, { - key: 'archive', - value: function archive() { - return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); - } - }, { - key: 'restore', - value: function restore() { - return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); - } - }, { - key: 'remove', - value: function remove() { - return this['delete'](); - } - }, { - key: 'delete', - value: function _delete() { - return this.$http['delete'](this.rootUrl + '/' + this._id); - } - }, { - key: 'populate', - value: function populate(field, query) { - // istanbul ignore next + }); + return clone; + } + }, { + key: 'buildField', + value: function buildField(model, value) { + return _.clone(value); + } + }, { + key: 'archive', + value: function archive() { + return this.$http.put(this.rootUrl + '/' + this._id + '/archive', null); + } + }, { + key: 'restore', + value: function restore() { + return this.$http['delete'](this.rootUrl + '/' + this._id + '/archive'); + } + }, { + key: 'remove', + value: function remove() { + return this['delete'](); + } + }, { + key: 'delete', + value: function _delete() { + return this.$http['delete'](this.rootUrl + '/' + this._id); + } + }, { + key: 'populate', + value: function populate(field, query) { + // istanbul ignore next - var _this2 = this; + var _this2 = this; - var $q = this.$injector.get('$q'); + var $q = this.$injector.get('$q'); - if (Array.isArray(field)) { - return $q.all(field.map(function (f) { - return _this2.populate(f, query); - })).then(function () { - return _this2; - }); - } + if (Array.isArray(field)) { + return $q.all(field.map(function (f) { + return _this2.populate(f, query); + })).then(function () { + return _this2; + }); + } - var deferred = $q.defer(); - var self = this; - if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { - /** - * The field is an array. It may contain mixed populated and unpopulated data. - * first, sort it out (typeof unpopulated is string) - */ - var grouped = _.groupBy(this[field], function (i) { - return typeof i; - }); - /** No string = everything is populated (or array is empty), we're all set */ - if (!grouped.string) { + var deferred = $q.defer(); + var self = this; + if (Array.isArray(model[field]) && Array.isArray(this[field]) && this[field].length) { + /** + * The field is an array. It may contain mixed populated and unpopulated data. + * first, sort it out (typeof unpopulated is string) + */ + var grouped = _.groupBy(this[field], function (i) { + return typeof i; + }); + /** No string = everything is populated (or array is empty), we're all set */ + if (!grouped.string) { + deferred.resolve(this[field]); + } else { + /** Resolve the model name and the corresponding DAO */ + var name = model[field][0].ref; + if (!name) deferred.reject('Cannot Populate: unknown model');else { + var dao = sl.getDao(name); + if (!dao) { + deferred.reject('Cannot Populate: unknown DAO'); + } else { + return dao.get(dao.query(query).select(grouped.string)).then(function (d) { + /** To preserve order, we map the existing field, replacing only the populated values */ + self[field] = self[field].map(function (f) { + if (typeof f === 'string') { + return _.find(d.data, function (foo) { + return foo._id === f; + }); + } + return f; + }); + return self; + }); + } + } + } + } + + if (!this[field] || typeof this[field] === 'object') { + // The field is empty or already populated. return. deferred.resolve(this[field]); } else { - /** Resolve the model name and the corresponding DAO */ - var name = model[field][0].ref; + var name = model[field].ref; if (!name) deferred.reject('Cannot Populate: unknown model');else { var dao = sl.getDao(name); if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then(function (d) { - /** To preserve order, we map the existing field, replacing only the populated values */ - self[field] = self[field].map(function (f) { - if (typeof f === 'string') { - return _.find(d.data, function (foo) { - return foo._id === f; - }); - } - return f; - }); + return dao.getById(this[field]).then(function (sub) { + self[field] = sub; return self; }); } } } - } - if (!this[field] || typeof this[field] === 'object') { - // The field is empty or already populated. return. - deferred.resolve(this[field]); - } else { - var name = model[field].ref; - if (!name) deferred.reject('Cannot Populate: unknown model');else { - var dao = sl.getDao(name); - if (!dao) { - deferred.reject('Cannot Populate: unknown DAO'); - } else { - return dao.getById(this[field]).then(function (sub) { - self[field] = sub; - return self; - }); + return deferred.promise; + } + }, { + key: 'beforeSave', + value: function beforeSave(obj) { + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + obj = obj || _.cloneDeep(this); + var old = session.retrieve(this._id) || {}; + _.each(model, function (field, key) { + if (!opts.force && !deep(obj[key], old[key])) { + delete obj[key]; + return; } - } + if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { + + if (_.isArray(obj[key])) { + obj[key] = _.compact(obj[key].map(function (val) { + if (typeof val === 'object') { + return field.nested ? val : val._id; + } else if (typeof val === 'string') { + return val; + } + })); + } else { + obj[key] = obj[key]._id || obj[key]; + } + } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { + obj[key] = new Date(moment(obj[key])).toISOString(); + } + }); + delete obj.rootUrl; + delete obj.$injector; + delete obj._injector; + return obj; } + }, { + key: 'save', + value: function save() { + // istanbul ignore next - return deferred.promise; - } - }, { - key: 'beforeSave', - value: function beforeSave(obj) { - // istanbul ignore next + var _this3 = this; - var _this3 = this; + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + var toSave = this.beforeSave(null, opts); + if (_.isEmpty(toSave)) { + return this.$injector.get('$q')(function (resolve) { + return resolve({ data: _this3 }); + }); + } + var callback; + if (opts.populate) { + var dao = sl.getDao(name); + callback = function () { + return dao.getById(_this3._id, dao.query().populate(opts.populate)); + }; + } else { + callback = function (data) { + _this3.build(data.data); + return data; + }; + } - obj = obj || _.cloneDeep(this); - _.each(model, function (field, key) { - if (!opts.force && !deep(obj[key], _this3.__old[key])) { - delete obj[key]; - return; + if (this._id) { + return this.$http.put(this.rootUrl + '/' + this._id, toSave).then(callback); + } else { + return this.$http.post(this.rootUrl, toSave).then(callback); } - if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { - - if (_.isArray(obj[key])) { - obj[key] = _.compact(obj[key].map(function (val) { - if (typeof val === 'object') { - return field.nested ? val : val._id; - } else if (typeof val === 'string') { - return val; + } + }, { + key: 'saveDeep', + value: function saveDeep(populate) { + // istanbul ignore next + + var _this4 = this; + + var promises = []; + /** Find ref properties that might need to be saved */ + _.each(model, function (v, k) { + if (_this4[k]) { + if (_.isArray(v)) { + if (v[0].ref) { + /** Array of nested Objects. Check if need to save each */ + _this4[k].forEach(function (e) { + if (!e._id && e.saveDeep) promises.push(e.saveDeep()); + }); } - })); - } else { - obj[key] = obj[key]._id || obj[key]; + } else if (v.ref) { + /** Single nested object, save it if needed */ + if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); + } } - } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { - obj[key] = new Date(moment(obj[key])).toISOString(); - } - }); - delete obj.rootUrl; - delete obj.$injector; - delete obj._injector; - delete obj.__old; - return obj; - } - }, { - key: 'save', - value: function save() { - // istanbul ignore next - - var _this4 = this; - - var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - - var toSave = this.beforeSave(null, opts); - if (_.isEmpty(toSave)) { - return this.$injector.get('$q')(function (resolve) { - return resolve({ data: _this4 }); + }); + var $q = this._injector.get('$q'); + return $q.all(promises).then(function () { + return _this4.save(populate); }); } - var callback; - if (opts.populate) { - var dao = sl.getDao(name); - callback = function () { - return dao.getById(_this4._id, dao.query().populate(opts.populate)); - }; - } else { - callback = function (data) { - _this4.build(data.data); - return data; - }; + }, { + key: '$injector', + get: function get() { + return this._injector; + }, + set: function set($injector) { + this._injector = $injector; } - - if (this._id) { - return this.$http.put(this.rootUrl + '/' + this._id, toSave).then(callback); - } else { - return this.$http.post(this.rootUrl, toSave).then(callback); + }, { + key: '$http', + get: function get() { + return this.$injector.get('$http'); } - } - }, { - key: 'saveDeep', - value: function saveDeep(populate) { - // istanbul ignore next - - var _this5 = this; - - var promises = []; - /** Find ref properties that might need to be saved */ - _.each(model, function (v, k) { - if (_this5[k]) { - if (_.isArray(v)) { - if (v[0].ref) { - /** Array of nested Objects. Check if need to save each */ - _this5[k].forEach(function (e) { - if (!e._id && e.saveDeep) promises.push(e.saveDeep()); - }); - } - } else if (v.ref) { - /** Single nested object, save it if needed */ - if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); + }], [{ + key: 'getName', + value: function getName() { + return name; + } + }, { + key: 'makePopObject', + value: function makePopObject(pop) { + return _.map(pop, function (p, k) { + p = Array.isArray(p) ? p[0] : p; + var ob = { path: k }; + var sub = ActiveRecord.findSubPopulates(p.ref); + if (sub && sub.length) { + ob.populate = sub; } + return ob; + }); + } + }, { + key: 'findSubPopulates', + value: function findSubPopulates(ref) { + var serviceLocator = _ServiceLocator2['default'].instance; + var linkedModel = serviceLocator.getModel(ref); + if (linkedModel && linkedModel !== model) { + return linkedModel.populateParams(); } - }); - var $q = this._injector.get('$q'); - return $q.all(promises).then(function () { - return _this5.save(populate); - }); - } - }, { - key: '$injector', - get: function get() { - return this._injector; - }, - set: function set($injector) { - this._injector = $injector; - } - }, { - key: '$http', - get: function get() { - return this.$injector.get('$http'); - } - }], [{ - key: 'getName', - value: function getName() { - return name; - } - }, { - key: 'makePopObject', - value: function makePopObject(pop) { - return _.map(pop, function (p, k) { - p = Array.isArray(p) ? p[0] : p; - var ob = { path: k }; - var sub = ActiveRecord.findSubPopulates(p.ref); - if (sub && sub.length) { - ob.populate = sub; + } + }, { + key: 'populateParams', + value: function populateParams(populateArray) { + + // Get from the model the fields that reference another object + var pop = _.pick(model, function (v) { + if (_.isArray(v)) return v[0].ref; + return v.ref; + }); + + if (populateArray === 'all') { + return ActiveRecord.makePopObject(pop); } - return ob; - }); - } - }, { - key: 'findSubPopulates', - value: function findSubPopulates(ref) { - var serviceLocator = _ServiceLocator2['default'].instance; - var linkedModel = serviceLocator.getModel(ref); - if (linkedModel && linkedModel !== model) { - return linkedModel.populateParams(); + var toPopulate = populateArray ? _.pick(pop, function (p, k) { + return _.contains(populateArray, k); + }) : _.pick(pop, function (v) { + return v.populateDefault || v[0] && v[0].populateDefault; + }); + return ActiveRecord.makePopObject(toPopulate); } - } - }, { - key: 'populateParams', - value: function populateParams(populateArray) { - - // Get from the model the fields that reference another object - var pop = _.pick(model, function (v) { - if (_.isArray(v)) return v[0].ref; - return v.ref; - }); - - if (populateArray === 'all') { - return ActiveRecord.makePopObject(pop); + }, { + key: 'getModel', + value: function getModel() { + return model; } - var toPopulate = populateArray ? _.pick(pop, function (p, k) { - return _.contains(populateArray, k); - }) : _.pick(pop, function (v) { - return v.populateDefault || v[0] && v[0].populateDefault; - }); - return ActiveRecord.makePopObject(toPopulate); - } - }, { - key: 'getModel', - value: function getModel() { - return model; - } - }]); + }, { + key: 'getSession', + value: function getSession() { + return session; + } + }]); + + return ActiveRecord; + })(); return ActiveRecord; })(); - // sl.registerModel(name, ActiveRecord); - return ActiveRecord; } module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/SessionManager.js b/dest/temp/SessionManager.js new file mode 100644 index 0000000..8ea8794 --- /dev/null +++ b/dest/temp/SessionManager.js @@ -0,0 +1,60 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +exports['default'] = SessionManager; +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +function SessionManager(model) { + + var SessionManager = (function () { + function SessionManager() { + _classCallCheck(this, SessionManager); + + this.storage = {}; + } + + _createClass(SessionManager, [{ + key: 'save', + value: function save(obj) { + if (!obj._id) return; + this.storage[obj._id] = obj; + } + }, { + key: 'retrieve', + value: function retrieve(id) { + return this.storage[id]; + } + }, { + key: 'clean', + value: function clean() { + this.storage = {}; + } + }, { + key: 'model', + get: function get() { + return model; + } + }]); + + return SessionManager; + })(); + return SessionManager; +} + +module.exports = exports['default']; \ No newline at end of file diff --git a/package.json b/package.json index f2ddcb0..ba7b50c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-1", + "version": "2.0.0-alpha-2", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index e121b9b..8bde4eb 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -1,5 +1,6 @@ //_ = require('lodash'); import ServiceLocator from './ServiceLocator' +import SessionManager from './SessionManager' var deep = require('deep-diff').diff /** @@ -12,18 +13,19 @@ var deep = require('deep-diff').diff * @param name String Name of the Model (must be the same as described in 'ref' of others models relations. * @returns {$ES6_CLASS$} */ -export default function ActiveRecord (model, name) { +export default function ActiveRecord (model, name, SManager=SessionManager(model)) { let sl = ServiceLocator.instance; + let session = new SManager() + let ActiveRecord = class { constructor ($injector, rootUrl, options) { this.$injector = $injector; - // this.$http = $injector.get('$http'); this.rootUrl = rootUrl; this.build(options); } build (options) { - this.__old = {} + let sess = {} _.each(model, (field, key)=> { if (options && (options[ key ] || options[ key ] === 0)) { var name = _.isArray(field) ? field[ 0 ].ref : field.ref; @@ -62,13 +64,11 @@ export default function ActiveRecord (model, name) { this[ key ] = this.buildField(field, options[ key ]) } } - /*else if (_.isArray(field)) { - this[ key ] = []; - } */ if (options && options._id) { - this.__old[ key ] = _.cloneDeep(this[ key ]) + sess[ key ] = _.cloneDeep(this[ key ]) } }); + session.save(sess) return this; } @@ -205,8 +205,9 @@ export default function ActiveRecord (model, name) { beforeSave (obj, opts={}) { obj = obj || _.cloneDeep(this); + let old = session.retrieve(this._id) || {} _.each(model, (field, key)=> { - if (!opts.force && !deep(obj[ key ], this.__old[ key ])) { + if (!opts.force && !deep(obj[ key ], old[ key ])) { delete obj[ key ] return } @@ -230,7 +231,6 @@ export default function ActiveRecord (model, name) { delete obj.rootUrl; delete obj.$injector; delete obj._injector; - delete obj.__old; return obj; } @@ -332,7 +332,11 @@ export default function ActiveRecord (model, name) { return model; } + static getSession(){ + return session; + } + }; - // sl.registerModel(name, ActiveRecord); + return ActiveRecord; } diff --git a/src/SessionManager.js b/src/SessionManager.js new file mode 100644 index 0000000..197b27f --- /dev/null +++ b/src/SessionManager.js @@ -0,0 +1,31 @@ +import ServiceLocator from './ServiceLocator' + + +export default function SessionManager(model){ + + let SessionManager = class { + + constructor(){ + this.storage = {} + } + + save(obj){ + if (!obj._id) return + this.storage[obj._id] = obj + } + + retrieve(id){ + return this.storage[id] + } + + clean(){ + this.storage = {} + } + + get model(){ + return model + } + + } + return SessionManager +} \ No newline at end of file From a371580ec37136e84b6756f5e9d08b0feb2cd7b3 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 09:46:12 +0200 Subject: [PATCH 03/26] correct handling of diff between date and nested objects --- build/app.js | 27 +++++++-- dest/temp/ActiveRecord.js | 25 ++++++-- dest/temp/models/tstModel1.js | 2 + dest/temp/specs/angular-dao.specs.js | 71 ++++++++++++++++++++++ package.json | 2 +- src/ActiveRecord.js | 33 +++++++---- tst/models/tstModel1.js | 2 + tst/specs/angular-dao.specs.js | 89 +++++++++++++++++++++++++--- 8 files changed, 218 insertions(+), 33 deletions(-) diff --git a/build/app.js b/build/app.js index 4df8cd6..80aa520 100644 --- a/build/app.js +++ b/build/app.js @@ -95,8 +95,22 @@ function ActiveRecord(model, name) { _this[key] = _this.buildField(field, options[key]); } } - if (options && options._id) { - sess[key] = _.cloneDeep(_this[key]); + /** Save state of the object for diff purpose */ + if (options && options._id && _this[key]) { + var toSave = undefined; + /** If the field is a ref, only save id or the ids array */ + if (_.isArray(field) && field[0].ref || field.ref) { + if (_.isArray(field)) { + toSave = _this[key].map(function (entry) { + return typeof entry === 'string' ? entry : entry._id; + }); + } else { + toSave = typeof _this[key] === 'string' ? _this[key] : _this[key]._id; + } + } else { + toSave = _this[key]; + } + sess[key] = _.cloneDeep(toSave); } }); session.save(sess); @@ -244,10 +258,6 @@ function ActiveRecord(model, name) { obj = obj || _.cloneDeep(this); var old = session.retrieve(this._id) || {}; _.each(model, function (field, key) { - if (!opts.force && !deep(obj[key], old[key])) { - delete obj[key]; - return; - } if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { if (_.isArray(obj[key])) { @@ -264,6 +274,9 @@ function ActiveRecord(model, name) { } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { obj[key] = new Date(moment(obj[key])).toISOString(); } + if (!opts.force && !deep(obj[key], old[key])) { + delete obj[key]; + } }); delete obj.rootUrl; delete obj.$injector; @@ -1142,6 +1155,8 @@ var model = { }, //private: true + when: Date, + label: String, num: Number, diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 2191c20..2a13d2c 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -94,8 +94,22 @@ function ActiveRecord(model, name) { _this[key] = _this.buildField(field, options[key]); } } - if (options && options._id) { - sess[key] = _.cloneDeep(_this[key]); + /** Save state of the object for diff purpose */ + if (options && options._id && _this[key]) { + var toSave = undefined; + /** If the field is a ref, only save id or the ids array */ + if (_.isArray(field) && field[0].ref || field.ref) { + if (_.isArray(field)) { + toSave = _this[key].map(function (entry) { + return typeof entry === 'string' ? entry : entry._id; + }); + } else { + toSave = typeof _this[key] === 'string' ? _this[key] : _this[key]._id; + } + } else { + toSave = _this[key]; + } + sess[key] = _.cloneDeep(toSave); } }); session.save(sess); @@ -243,10 +257,6 @@ function ActiveRecord(model, name) { obj = obj || _.cloneDeep(this); var old = session.retrieve(this._id) || {}; _.each(model, function (field, key) { - if (!opts.force && !deep(obj[key], old[key])) { - delete obj[key]; - return; - } if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { if (_.isArray(obj[key])) { @@ -263,6 +273,9 @@ function ActiveRecord(model, name) { } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { obj[key] = new Date(moment(obj[key])).toISOString(); } + if (!opts.force && !deep(obj[key], old[key])) { + delete obj[key]; + } }); delete obj.rootUrl; delete obj.$injector; diff --git a/dest/temp/models/tstModel1.js b/dest/temp/models/tstModel1.js index 297d139..9332e36 100644 --- a/dest/temp/models/tstModel1.js +++ b/dest/temp/models/tstModel1.js @@ -31,6 +31,8 @@ var model = { }, //private: true + when: Date, + label: String, num: Number, diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js index 3fbc517..2049528 100644 --- a/dest/temp/specs/angular-dao.specs.js +++ b/dest/temp/specs/angular-dao.specs.js @@ -437,4 +437,75 @@ describe('Angular DAO', function () { ModelManager.get(ModelManager.query().populate('model2')); httpBackend.flush(); }); + + it('Should not make a diff with dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }); + model.save(); + }); + + it('Should make a diff with dates if needed', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }); + model.when = new Date(203939); + httpBackend.expectPUT('http://MOCKURL.com/model1/007', { + when: new Date(203939) + }).respond(); + model.save(); + }); + + it('Should not make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: ['77777', '4444'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + model.save(); + }); + + it('Should make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888' + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + model.model2 = { _id: '999' }; + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + model2: '999' + }).respond(); + model.save(); + }); + + it('Should make a diff between populated and unpopulated nested fields array', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush(); + model.models2.push({ _id: '999' }); + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + models2: ['888', '999'] + }).respond(); + model.save(); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index ba7b50c..63498a3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-2", + "version": "2.0.0-alpha-3", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 8bde4eb..775d6ae 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -13,7 +13,7 @@ var deep = require('deep-diff').diff * @param name String Name of the Model (must be the same as described in 'ref' of others models relations. * @returns {$ES6_CLASS$} */ -export default function ActiveRecord (model, name, SManager=SessionManager(model)) { +export default function ActiveRecord (model, name, SManager = SessionManager(model)) { let sl = ServiceLocator.instance; let session = new SManager() @@ -64,8 +64,20 @@ export default function ActiveRecord (model, name, SManager=SessionManager(model this[ key ] = this.buildField(field, options[ key ]) } } - if (options && options._id) { - sess[ key ] = _.cloneDeep(this[ key ]) + /** Save state of the object for diff purpose */ + if (options && options._id && this[ key ]) { + let toSave + /** If the field is a ref, only save id or the ids array */ + if (_.isArray(field) && field[ 0 ].ref || field.ref) { + if (_.isArray(field)) { + toSave = this[ key ].map(entry => (typeof entry === 'string') ? entry : entry._id) + } else { + toSave = typeof this[ key ] === 'string' ? this[ key ] : this[ key ]._id + } + } else { + toSave = this[ key ] + } + sess[ key ] = _.cloneDeep(toSave) } }); session.save(sess) @@ -203,14 +215,10 @@ export default function ActiveRecord (model, name, SManager=SessionManager(model } - beforeSave (obj, opts={}) { + beforeSave (obj, opts = {}) { obj = obj || _.cloneDeep(this); let old = session.retrieve(this._id) || {} _.each(model, (field, key)=> { - if (!opts.force && !deep(obj[ key ], old[ key ])) { - delete obj[ key ] - return - } if (obj[ key ] && (field.ref || (_.isArray(field) && field[ 0 ].ref))) { if (_.isArray(obj[ key ])) { @@ -227,6 +235,9 @@ export default function ActiveRecord (model, name, SManager=SessionManager(model } else if (obj[ key ] && (field.type === Date || (_.isArray(field) && field[ 0 ].type === Date))) { obj[ key ] = new Date(moment(obj[ key ])).toISOString(); } + if (!opts.force && !deep(obj[ key ], old[ key ])) { + delete obj[ key ] + } }); delete obj.rootUrl; delete obj.$injector; @@ -235,10 +246,10 @@ export default function ActiveRecord (model, name, SManager=SessionManager(model } - save (opts={}) { + save (opts = {}) { var toSave = this.beforeSave(null, opts); if (_.isEmpty(toSave)) { - return this.$injector.get('$q')(resolve => resolve({data: this})) + return this.$injector.get('$q')(resolve => resolve({ data: this })) } var callback; if (opts.populate) { @@ -332,7 +343,7 @@ export default function ActiveRecord (model, name, SManager=SessionManager(model return model; } - static getSession(){ + static getSession () { return session; } diff --git a/tst/models/tstModel1.js b/tst/models/tstModel1.js index 1fbddcb..e8a8d83 100644 --- a/tst/models/tstModel1.js +++ b/tst/models/tstModel1.js @@ -12,6 +12,8 @@ var model = { //private: true }, + when: Date, + label: String, num: Number, diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js index 64e07c9..189710d 100644 --- a/tst/specs/angular-dao.specs.js +++ b/tst/specs/angular-dao.specs.js @@ -298,7 +298,7 @@ describe('Angular DAO', function () { model2: { _id: '888', name: 'tutu' }, models2: [ { _id: '999' } ] }) - model.save({force: true, populate: [ 'model2', 'models2' ]}).then(function (pop) { + model.save({ force: true, populate: [ 'model2', 'models2' ] }).then(function (pop) { expect(pop.models2[ 0 ]._id).toEqual('999') }) httpBackend.flush() @@ -313,7 +313,7 @@ describe('Angular DAO', function () { _id: '1234656', model2: '888' }) - model.save({force:true}).then(function () { + model.save({ force: true }).then(function () { expect(model.model2._id).toEqual('888') }) httpBackend.flush() @@ -328,7 +328,7 @@ describe('Angular DAO', function () { _id: '1234656', model2: '777' }) - model.save({force: true}).then(function () { + model.save({ force: true }).then(function () { expect(model.model2).toEqual('777') }) httpBackend.flush() @@ -343,7 +343,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: [ '888' ] }); - model.save({force: true}).then(function () { + model.save({ force: true }).then(function () { expect(model.models2[ 0 ]._id).toEqual('888') }) httpBackend.flush() @@ -358,7 +358,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: [ '888', '999' ] }); - model.save({force:true}).then(function () { + model.save({ force: true }).then(function () { expect(model.models2.length).toEqual(2) expect(model.models2[ 0 ]._id).toEqual('888') expect(model.models2[ 0 ].name).toEqual('tutu') @@ -376,7 +376,7 @@ describe('Angular DAO', function () { _id: '1234656', models2: [ '999', '111' ] }); - model.save({force: true}).then(function () { + model.save({ force: true }).then(function () { expect(model.models2.length).toEqual(2) expect(model.models2[ 0 ]).toEqual('999') expect(model.models2[ 1 ]._id).toEqual('111') @@ -400,11 +400,11 @@ describe('Angular DAO', function () { it('should save only modified values in arrays', function () { var model = ModelManager.createModel({ _id: '123456', - models2: ['7777'], + models2: [ '7777' ], label: 'toto' }) model.models2.push('8888') - httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: ['7777', '8888'] }).respond() + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: [ '7777', '8888' ] }).respond() model.save() httpBackend.flush() }) @@ -413,7 +413,7 @@ describe('Angular DAO', function () { var model = ModelManager.createModel({ model2: '77777' }); - httpBackend.expectPOST('http://MOCKURL.com/model1', {model2: '77777'}).respond(); + httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); model.save(); httpBackend.flush(); }); @@ -445,4 +445,75 @@ describe('Angular DAO', function () { }) + it('Should not make a diff with dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }) + model.save() + }) + + it('Should make a diff with dates if needed', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }) + model.when = new Date(203939) + httpBackend.expectPUT('http://MOCKURL.com/model1/007', { + when: new Date(203939) + }).respond(); + model.save() + }) + + it('Should not make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: [ '77777', '4444' ] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777'); + expect(model.models2[ 1 ]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + model.save() + }); + + it('Should make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888' + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + model.model2 = { _id: '999' } + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + model2: '999' + }).respond(); + model.save() + }); + + it('Should make a diff between populated and unpopulated nested fields array', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush() + model.models2.push({ _id: '999' }) + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + models2: ['888', '999'] + }).respond(); + model.save() + }); + }); From b09016e72c88f54fdac2e4e278a2c1f328bc802c Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 10:30:02 +0200 Subject: [PATCH 04/26] do not save irrelevant fields --- build/app.js | 32 +++++++++++++++++----------- dest/temp/ActiveRecord.js | 32 +++++++++++++++++----------- dest/temp/specs/angular-dao.specs.js | 22 +++++++++++++++++++ package.json | 2 +- src/ActiveRecord.js | 9 ++++---- tst/specs/angular-dao.specs.js | 23 ++++++++++++++++++++ 6 files changed, 88 insertions(+), 32 deletions(-) diff --git a/build/app.js b/build/app.js index 80aa520..eec02ca 100644 --- a/build/app.js +++ b/build/app.js @@ -253,9 +253,18 @@ function ActiveRecord(model, name) { }, { key: 'beforeSave', value: function beforeSave(obj) { + // istanbul ignore next + + var _this3 = this; + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - obj = obj || _.cloneDeep(this); + if (!obj) { + obj = {}; + _.each(_.keys(model), function (k) { + return obj[k] = _this3[k]; + }); + } var old = session.retrieve(this._id) || {}; _.each(model, function (field, key) { if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { @@ -278,9 +287,6 @@ function ActiveRecord(model, name) { delete obj[key]; } }); - delete obj.rootUrl; - delete obj.$injector; - delete obj._injector; return obj; } }, { @@ -288,25 +294,25 @@ function ActiveRecord(model, name) { value: function save() { // istanbul ignore next - var _this3 = this; + var _this4 = this; var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var toSave = this.beforeSave(null, opts); if (_.isEmpty(toSave)) { return this.$injector.get('$q')(function (resolve) { - return resolve({ data: _this3 }); + return resolve({ data: _this4 }); }); } var callback; if (opts.populate) { var dao = sl.getDao(name); callback = function () { - return dao.getById(_this3._id, dao.query().populate(opts.populate)); + return dao.getById(_this4._id, dao.query().populate(opts.populate)); }; } else { callback = function (data) { - _this3.build(data.data); + _this4.build(data.data); return data; }; } @@ -322,28 +328,28 @@ function ActiveRecord(model, name) { value: function saveDeep(populate) { // istanbul ignore next - var _this4 = this; + var _this5 = this; var promises = []; /** Find ref properties that might need to be saved */ _.each(model, function (v, k) { - if (_this4[k]) { + if (_this5[k]) { if (_.isArray(v)) { if (v[0].ref) { /** Array of nested Objects. Check if need to save each */ - _this4[k].forEach(function (e) { + _this5[k].forEach(function (e) { if (!e._id && e.saveDeep) promises.push(e.saveDeep()); }); } } else if (v.ref) { /** Single nested object, save it if needed */ - if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); + if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); } } }); var $q = this._injector.get('$q'); return $q.all(promises).then(function () { - return _this4.save(populate); + return _this5.save(populate); }); } }, { diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 2a13d2c..dcef01d 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -252,9 +252,18 @@ function ActiveRecord(model, name) { }, { key: 'beforeSave', value: function beforeSave(obj) { + // istanbul ignore next + + var _this3 = this; + var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - obj = obj || _.cloneDeep(this); + if (!obj) { + obj = {}; + _.each(_.keys(model), function (k) { + return obj[k] = _this3[k]; + }); + } var old = session.retrieve(this._id) || {}; _.each(model, function (field, key) { if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { @@ -277,9 +286,6 @@ function ActiveRecord(model, name) { delete obj[key]; } }); - delete obj.rootUrl; - delete obj.$injector; - delete obj._injector; return obj; } }, { @@ -287,25 +293,25 @@ function ActiveRecord(model, name) { value: function save() { // istanbul ignore next - var _this3 = this; + var _this4 = this; var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var toSave = this.beforeSave(null, opts); if (_.isEmpty(toSave)) { return this.$injector.get('$q')(function (resolve) { - return resolve({ data: _this3 }); + return resolve({ data: _this4 }); }); } var callback; if (opts.populate) { var dao = sl.getDao(name); callback = function () { - return dao.getById(_this3._id, dao.query().populate(opts.populate)); + return dao.getById(_this4._id, dao.query().populate(opts.populate)); }; } else { callback = function (data) { - _this3.build(data.data); + _this4.build(data.data); return data; }; } @@ -321,28 +327,28 @@ function ActiveRecord(model, name) { value: function saveDeep(populate) { // istanbul ignore next - var _this4 = this; + var _this5 = this; var promises = []; /** Find ref properties that might need to be saved */ _.each(model, function (v, k) { - if (_this4[k]) { + if (_this5[k]) { if (_.isArray(v)) { if (v[0].ref) { /** Array of nested Objects. Check if need to save each */ - _this4[k].forEach(function (e) { + _this5[k].forEach(function (e) { if (!e._id && e.saveDeep) promises.push(e.saveDeep()); }); } } else if (v.ref) { /** Single nested object, save it if needed */ - if (!_this4[k]._id && _this4[k].saveDeep) promises.push(_this4[k].saveDeep()); + if (!_this5[k]._id && _this5[k].saveDeep) promises.push(_this5[k].saveDeep()); } } }); var $q = this._injector.get('$q'); return $q.all(promises).then(function () { - return _this4.save(populate); + return _this5.save(populate); }); } }, { diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js index 2049528..4a066a3 100644 --- a/dest/temp/specs/angular-dao.specs.js +++ b/dest/temp/specs/angular-dao.specs.js @@ -508,4 +508,26 @@ describe('Angular DAO', function () { }).respond(); model.save(); }); + + it('Should not save irrelevant fields', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + model.iShouldntBeThere = 'but I am'; + model.save(); + }); + + it('should not save irrelevant fields, but still save others', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }); + model.label = 'tutu'; + model.iShouldntBeThere = 'but I am'; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); + model.save(); + httpBackend.flush(); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index 63498a3..dc6294b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-3", + "version": "2.0.0-alpha-4", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 775d6ae..777cc81 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -216,7 +216,10 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod } beforeSave (obj, opts = {}) { - obj = obj || _.cloneDeep(this); + if (!obj){ + obj = {} + _.each(_.keys(model), k => obj[k] = this[k]) + } let old = session.retrieve(this._id) || {} _.each(model, (field, key)=> { if (obj[ key ] && (field.ref || (_.isArray(field) && field[ 0 ].ref))) { @@ -239,11 +242,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod delete obj[ key ] } }); - delete obj.rootUrl; - delete obj.$injector; - delete obj._injector; return obj; - } save (opts = {}) { diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js index 189710d..bc1e4d6 100644 --- a/tst/specs/angular-dao.specs.js +++ b/tst/specs/angular-dao.specs.js @@ -516,4 +516,27 @@ describe('Angular DAO', function () { model.save() }); + it ('Should not save irrelevant fields', function(){ + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + model.iShouldntBeThere = 'but I am' + model.save() + }) + + + it('should not save irrelevant fields, but still save others', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }) + model.label = 'tutu' + model.iShouldntBeThere = 'but I am' + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() + model.save() + httpBackend.flush() + }) + }); From cdaa20fa64635809ae10e52324de81c33a78aa8a Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 10:50:25 +0200 Subject: [PATCH 05/26] remove useless dependency to momentjs --- CHANGELOG.md | 3 +++ build/app.js | 2 +- dest/temp/ActiveRecord.js | 2 +- package.json | 2 +- src/ActiveRecord.js | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be26369..6e4fae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ If you rewrote beforeSave methods, be careful and pass the optional `opts` objec - BREAKING CHANGES - Save behaviour If you wish to keep sending all your fields to the server each you save (which you shouldn't). Pass `{force: true}` as options in the `save` method. + - Save behaviour + Only fields declared in the model are to be saved. This should actually be beneficial, since we don't send metadata or externally added values, but be carefull if you rely on the orm saving values your added manually. + Also, If you rewrote the before-save method, you are responsible for what you put in the object to be saved. Namely, you have to implement the behaviour if you want it. - re-population after save. Until 2.0.0 when you wanted to repopulate an object after saving it (ie making another query), you had to pass it as the first argument of the save method. Now, it has to be given as an options object containing `{populate: whateverYouNeedToPopulate}` - Empty arrays: when building an active record, undefined arrays will not be instantiated to empty arrays (to prevent overriding unselected fields). Check for the actual presence of the array before trying to push. diff --git a/build/app.js b/build/app.js index eec02ca..e57d8de 100644 --- a/build/app.js +++ b/build/app.js @@ -281,7 +281,7 @@ function ActiveRecord(model, name) { obj[key] = obj[key]._id || obj[key]; } } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { - obj[key] = new Date(moment(obj[key])).toISOString(); + obj[key] = new Date(obj[key]).toISOString(); } if (!opts.force && !deep(obj[key], old[key])) { delete obj[key]; diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index dcef01d..a0de137 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -280,7 +280,7 @@ function ActiveRecord(model, name) { obj[key] = obj[key]._id || obj[key]; } } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { - obj[key] = new Date(moment(obj[key])).toISOString(); + obj[key] = new Date(obj[key]).toISOString(); } if (!opts.force && !deep(obj[key], old[key])) { delete obj[key]; diff --git a/package.json b/package.json index dc6294b..6a2ebb1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-4", + "version": "2.0.0-alpha-5", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 777cc81..f98dcba 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -236,7 +236,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod obj[ key ] = obj[ key ]._id || obj[ key ]; } } else if (obj[ key ] && (field.type === Date || (_.isArray(field) && field[ 0 ].type === Date))) { - obj[ key ] = new Date(moment(obj[ key ])).toISOString(); + obj[ key ] = new Date(obj[ key ]).toISOString(); } if (!opts.force && !deep(obj[ key ], old[ key ])) { delete obj[ key ] From 7a08591ad5635863d6a590d5053609cbd99a2ca3 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 11:14:37 +0200 Subject: [PATCH 06/26] correctly check for date values --- package.json | 2 +- src/ActiveRecord.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6a2ebb1..c31cb78 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-5", + "version": "2.0.0-alpha-6", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index f98dcba..0370349 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -235,7 +235,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod } else { obj[ key ] = obj[ key ]._id || obj[ key ]; } - } else if (obj[ key ] && (field.type === Date || (_.isArray(field) && field[ 0 ].type === Date))) { + } else if (obj[ key ] && _.isDate(obj[ key ])) { obj[ key ] = new Date(obj[ key ]).toISOString(); } if (!opts.force && !deep(obj[ key ], old[ key ])) { From 4a218c9daa8ed9ac252d88e8a55c2a8c3dbf3812 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 15:21:15 +0200 Subject: [PATCH 07/26] add pristine property --- build/app.js | 9 +++++++-- dest/temp/ActiveRecord.js | 9 +++++++-- dest/temp/specs/angular-dao.specs.js | 15 ++++++++++++++- package.json | 2 +- src/ActiveRecord.js | 7 +++++-- tst/specs/angular-dao.specs.js | 16 +++++++++++++++- 6 files changed, 49 insertions(+), 9 deletions(-) diff --git a/build/app.js b/build/app.js index e57d8de..2211aa1 100644 --- a/build/app.js +++ b/build/app.js @@ -280,10 +280,10 @@ function ActiveRecord(model, name) { } else { obj[key] = obj[key]._id || obj[key]; } - } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { + } else if (obj[key] && _.isDate(obj[key])) { obj[key] = new Date(obj[key]).toISOString(); } - if (!opts.force && !deep(obj[key], old[key])) { + if (!opts.force && !deep(old[key], obj[key])) { delete obj[key]; } }); @@ -365,6 +365,11 @@ function ActiveRecord(model, name) { get: function get() { return this.$injector.get('$http'); } + }, { + key: '$$pristine', + get: function get() { + return _.isEmpty(this.beforeSave()); + } }], [{ key: 'getName', value: function getName() { diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index a0de137..be99438 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -279,10 +279,10 @@ function ActiveRecord(model, name) { } else { obj[key] = obj[key]._id || obj[key]; } - } else if (obj[key] && (field.type === Date || _.isArray(field) && field[0].type === Date)) { + } else if (obj[key] && _.isDate(obj[key])) { obj[key] = new Date(obj[key]).toISOString(); } - if (!opts.force && !deep(obj[key], old[key])) { + if (!opts.force && !deep(old[key], obj[key])) { delete obj[key]; } }); @@ -364,6 +364,11 @@ function ActiveRecord(model, name) { get: function get() { return this.$injector.get('$http'); } + }, { + key: '$$pristine', + get: function get() { + return _.isEmpty(this.beforeSave()); + } }], [{ key: 'getName', value: function getName() { diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js index 4a066a3..83a447d 100644 --- a/dest/temp/specs/angular-dao.specs.js +++ b/dest/temp/specs/angular-dao.specs.js @@ -386,6 +386,7 @@ describe('Angular DAO', function () { label: 'toto' }); model.label = 'tutu'; + expect(model.$$pristine).toBeFalsy(); httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); model.save(); httpBackend.flush(); @@ -398,6 +399,7 @@ describe('Angular DAO', function () { label: 'toto' }); model.models2.push('8888'); + expect(model.$$pristine).toBeFalsy(); httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: ['7777', '8888'] }).respond(); model.save(); httpBackend.flush(); @@ -407,6 +409,7 @@ describe('Angular DAO', function () { var model = ModelManager.createModel({ model2: '77777' }); + expect(model.$$pristine).toBeFalsy(); httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); model.save(); httpBackend.flush(); @@ -417,6 +420,7 @@ describe('Angular DAO', function () { _id: '123456', model2: '77777' }); + expect(model.$$pristine).toBeTruthy(); model.save(); }); @@ -441,8 +445,17 @@ describe('Angular DAO', function () { it('Should not make a diff with dates', function () { var model = ModelManager.create({ _id: '007', - when: new Date() + when: new Date().toISOString() + }); + model.save(); + }); + + it('Should not make a diff with JS dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() }); + model.when = new Date(model.when); model.save(); }); diff --git a/package.json b/package.json index c31cb78..4c89a5a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-6", + "version": "2.0.0-alpha-7", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 0370349..b1699df 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -126,9 +126,12 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod return this.$injector.get('$http'); } + get $$pristine(){ + return _.isEmpty(this.beforeSave()) + } + buildField (model, value) { return _.clone(value); - } archive () { @@ -238,7 +241,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod } else if (obj[ key ] && _.isDate(obj[ key ])) { obj[ key ] = new Date(obj[ key ]).toISOString(); } - if (!opts.force && !deep(obj[ key ], old[ key ])) { + if (!opts.force && !deep(old[ key ], obj[ key ])) { delete obj[ key ] } }); diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js index bc1e4d6..4e486b8 100644 --- a/tst/specs/angular-dao.specs.js +++ b/tst/specs/angular-dao.specs.js @@ -392,6 +392,7 @@ describe('Angular DAO', function () { label: 'toto' }) model.label = 'tutu' + expect(model.$$pristine).toBeFalsy() httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() model.save() httpBackend.flush() @@ -404,6 +405,7 @@ describe('Angular DAO', function () { label: 'toto' }) model.models2.push('8888') + expect(model.$$pristine).toBeFalsy() httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: [ '7777', '8888' ] }).respond() model.save() httpBackend.flush() @@ -413,6 +415,7 @@ describe('Angular DAO', function () { var model = ModelManager.createModel({ model2: '77777' }); + expect(model.$$pristine).toBeFalsy() httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); model.save(); httpBackend.flush(); @@ -423,6 +426,7 @@ describe('Angular DAO', function () { _id: '123456', model2: '77777' }); + expect(model.$$pristine).toBeTruthy() model.save(); }); @@ -448,8 +452,18 @@ describe('Angular DAO', function () { it('Should not make a diff with dates', function () { var model = ModelManager.create({ _id: '007', - when: new Date() + when: new Date().toISOString() + }) + model.save() + }) + + + it('Should not make a diff with JS dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() }) + model.when = new Date(model.when) model.save() }) From 1d5a3fc105ffcebab36e91188faa78c1cf58d3e6 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 17:29:32 +0200 Subject: [PATCH 08/26] handle nested fields with ref --- build/app.js | 193 ++++++++++++++++++++++++--- dest/temp/ActiveRecord.js | 60 ++++++--- dest/temp/app.js | 7 +- dest/temp/managers/tstManager3.js | 50 +++++++ dest/temp/models/tstModel1.js | 12 +- dest/temp/models/tstModel3.js | 52 ++++++++ dest/temp/specs/angular-dao.specs.js | 12 ++ package.json | 2 +- src/ActiveRecord.js | 61 ++++++--- tst/app.js | 4 +- tst/managers/tstManager3.js | 10 ++ tst/models/tstModel1.js | 12 +- tst/models/tstModel3.js | 25 ++++ tst/specs/angular-dao.specs.js | 12 ++ 14 files changed, 453 insertions(+), 59 deletions(-) create mode 100644 dest/temp/managers/tstManager3.js create mode 100644 dest/temp/models/tstModel3.js create mode 100644 tst/managers/tstManager3.js create mode 100644 tst/models/tstModel3.js diff --git a/build/app.js b/build/app.js index 2211aa1..22bd436 100644 --- a/build/app.js +++ b/build/app.js @@ -259,30 +259,60 @@ function ActiveRecord(model, name) { var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + /** If no object is provided, clone `this` + * by copying only relevant keys (keys in model) + * If you write your own beforeSave method, it is your responsibility to clone `this` + * the way you want it cloned + */ if (!obj) { obj = {}; _.each(_.keys(model), function (k) { - return obj[k] = _this3[k]; + if (_this3[k]) { + obj[k] = _this3[k]; + } }); } + /** Retrieve object saved in session to perform the diff */ var old = session.retrieve(this._id) || {}; + _.each(model, function (field, key) { - if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { - - if (_.isArray(obj[key])) { - obj[key] = _.compact(obj[key].map(function (val) { - if (typeof val === 'object') { - return field.nested ? val : val._id; - } else if (typeof val === 'string') { - return val; + if (obj[key]) { + /** If the field is a ref to another field, replace it by its _id */ + if ((field.ref || _.isArray(field) && field[0].ref) && + /** Unless it is specifically marked as nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { + /** Transforms an array of refs to just an array of _ids + * Handles mixed arrays */ + if (_.isArray(obj[key])) { + obj[key] = _.compact(obj[key].map(function (val) { + if (typeof val === 'object') { + return field.nested ? val : val._id; + } else if (typeof val === 'string') { + return val; + } + })); + } else { + /** Transforms just a single ref into its _id if needed */ + obj[key] = obj[key]._id || obj[key]; + } + /** Nested SubModel, pass it through beforeSave() so that + * only relevant fields are kept + * {force: true} so that all fields of the nested object are returned + * */ + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + obj[key] = obj[key].map(function (e) { + return e.beforeSave(null, { force: true }); + }); + } else { + obj[key] = obj[key].beforeSave(null, { force: true }); } - })); - } else { - obj[key] = obj[key]._id || obj[key]; - } - } else if (obj[key] && _.isDate(obj[key])) { - obj[key] = new Date(obj[key]).toISOString(); + } else if (_.isDate(obj[key])) { + /** Make sure the date is an ISOString */ + obj[key] = new Date(obj[key]).toISOString(); + } } + /** IMPORTANT this is where the diff is made. If we didn't force in the options*/ if (!opts.force && !deep(old[key], obj[key])) { delete obj[key]; } @@ -437,7 +467,7 @@ function ActiveRecord(model, name) { } module.exports = exports['default']; -},{"./ServiceLocator":5,"./SessionManager":6,"deep-diff":12}],2:[function(require,module,exports){ +},{"./ServiceLocator":5,"./SessionManager":6,"deep-diff":14}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1026,11 +1056,16 @@ var _managersTstManager2 = require('./managers/tstManager2'); var _managersTstManager22 = _interopRequireDefault(_managersTstManager2); +var _managersTstManager3 = require('./managers/tstManager3'); + +var _managersTstManager32 = _interopRequireDefault(_managersTstManager3); + var _module = angular.module('tstModule', []); _DaoHelper2['default'].registerService(_module, 'ModelManager', _managersTstManager12['default']); _DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); -},{"./DaoHelper":2,"./managers/tstManager1":8,"./managers/tstManager2":9}],8:[function(require,module,exports){ +_DaoHelper2['default'].registerService(_module, 'ModelManager3', _managersTstManager32['default']); +},{"./DaoHelper":2,"./managers/tstManager1":8,"./managers/tstManager2":9,"./managers/tstManager3":10}],8:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1081,7 +1116,7 @@ var ModelManager = (function (_DAO) { exports['default'] = ModelManager; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel1.js":10}],9:[function(require,module,exports){ +},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel1.js":11}],9:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1132,7 +1167,58 @@ var ModelManager2 = (function (_DAO) { exports['default'] = ModelManager2; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel2.js":11}],10:[function(require,module,exports){ +},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel2.js":12}],10:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel3Js = require('./../models/tstModel3.js'); + +var _modelsTstModel3Js2 = _interopRequireDefault(_modelsTstModel3Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _QueryBuilder = require('../QueryBuilder'); + +var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel3Js2['default']); + +var ModelManager3 = (function (_DAO) { + _inherits(ModelManager3, _DAO); + + function ModelManager3() { + _classCallCheck(this, ModelManager3); + + _get(Object.getPrototypeOf(ModelManager3.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager3; +})(DAO); + +exports['default'] = ModelManager3; +; +module.exports = exports['default']; +},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel3.js":13}],11:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1180,7 +1266,17 @@ var model = { model2: { type: String, ref: 'Model2' - } + }, + + model3: { + ref: 'Model3', + nested: true + }, + + models3: [{ + ref: 'Model3', + nested: true + }] }; var AR = (0, _ActiveRecord2['default'])(model, 'Model1'); @@ -1199,7 +1295,7 @@ var Model = (function (_AR) { exports['default'] = Model; module.exports = exports['default']; -},{"../ActiveRecord":1}],11:[function(require,module,exports){ +},{"../ActiveRecord":1}],12:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1252,7 +1348,60 @@ var Model2 = (function (_AR) { exports['default'] = Model2; module.exports = exports['default']; -},{"../ActiveRecord":1}],12:[function(require,module,exports){ +},{"../ActiveRecord":1}],13:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model3'); + +var Model3 = (function (_AR) { + _inherits(Model3, _AR); + + function Model3() { + _classCallCheck(this, Model3); + + _get(Object.getPrototypeOf(Model3.prototype), 'constructor', this).apply(this, arguments); + } + + return Model3; +})(AR); + +exports['default'] = Model3; +module.exports = exports['default']; +},{"../ActiveRecord":1}],14:[function(require,module,exports){ (function (global){ /*! * deep-diff. diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index be99438..418c7a5 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -258,30 +258,60 @@ function ActiveRecord(model, name) { var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + /** If no object is provided, clone `this` + * by copying only relevant keys (keys in model) + * If you write your own beforeSave method, it is your responsibility to clone `this` + * the way you want it cloned + */ if (!obj) { obj = {}; _.each(_.keys(model), function (k) { - return obj[k] = _this3[k]; + if (_this3[k]) { + obj[k] = _this3[k]; + } }); } + /** Retrieve object saved in session to perform the diff */ var old = session.retrieve(this._id) || {}; + _.each(model, function (field, key) { - if (obj[key] && (field.ref || _.isArray(field) && field[0].ref)) { - - if (_.isArray(obj[key])) { - obj[key] = _.compact(obj[key].map(function (val) { - if (typeof val === 'object') { - return field.nested ? val : val._id; - } else if (typeof val === 'string') { - return val; + if (obj[key]) { + /** If the field is a ref to another field, replace it by its _id */ + if ((field.ref || _.isArray(field) && field[0].ref) && + /** Unless it is specifically marked as nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { + /** Transforms an array of refs to just an array of _ids + * Handles mixed arrays */ + if (_.isArray(obj[key])) { + obj[key] = _.compact(obj[key].map(function (val) { + if (typeof val === 'object') { + return field.nested ? val : val._id; + } else if (typeof val === 'string') { + return val; + } + })); + } else { + /** Transforms just a single ref into its _id if needed */ + obj[key] = obj[key]._id || obj[key]; + } + /** Nested SubModel, pass it through beforeSave() so that + * only relevant fields are kept + * {force: true} so that all fields of the nested object are returned + * */ + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + obj[key] = obj[key].map(function (e) { + return e.beforeSave(null, { force: true }); + }); + } else { + obj[key] = obj[key].beforeSave(null, { force: true }); } - })); - } else { - obj[key] = obj[key]._id || obj[key]; - } - } else if (obj[key] && _.isDate(obj[key])) { - obj[key] = new Date(obj[key]).toISOString(); + } else if (_.isDate(obj[key])) { + /** Make sure the date is an ISOString */ + obj[key] = new Date(obj[key]).toISOString(); + } } + /** IMPORTANT this is where the diff is made. If we didn't force in the options*/ if (!opts.force && !deep(old[key], obj[key])) { delete obj[key]; } diff --git a/dest/temp/app.js b/dest/temp/app.js index b2ba792..5f03486 100644 --- a/dest/temp/app.js +++ b/dest/temp/app.js @@ -16,7 +16,12 @@ var _managersTstManager2 = require('./managers/tstManager2'); var _managersTstManager22 = _interopRequireDefault(_managersTstManager2); +var _managersTstManager3 = require('./managers/tstManager3'); + +var _managersTstManager32 = _interopRequireDefault(_managersTstManager3); + var _module = angular.module('tstModule', []); _DaoHelper2['default'].registerService(_module, 'ModelManager', _managersTstManager12['default']); -_DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); \ No newline at end of file +_DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); +_DaoHelper2['default'].registerService(_module, 'ModelManager3', _managersTstManager32['default']); \ No newline at end of file diff --git a/dest/temp/managers/tstManager3.js b/dest/temp/managers/tstManager3.js new file mode 100644 index 0000000..7e6d774 --- /dev/null +++ b/dest/temp/managers/tstManager3.js @@ -0,0 +1,50 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel3Js = require('./../models/tstModel3.js'); + +var _modelsTstModel3Js2 = _interopRequireDefault(_modelsTstModel3Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _QueryBuilder = require('../QueryBuilder'); + +var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel3Js2['default']); + +var ModelManager3 = (function (_DAO) { + _inherits(ModelManager3, _DAO); + + function ModelManager3() { + _classCallCheck(this, ModelManager3); + + _get(Object.getPrototypeOf(ModelManager3.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager3; +})(DAO); + +exports['default'] = ModelManager3; +; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/models/tstModel1.js b/dest/temp/models/tstModel1.js index 9332e36..ab5a44b 100644 --- a/dest/temp/models/tstModel1.js +++ b/dest/temp/models/tstModel1.js @@ -45,7 +45,17 @@ var model = { model2: { type: String, ref: 'Model2' - } + }, + + model3: { + ref: 'Model3', + nested: true + }, + + models3: [{ + ref: 'Model3', + nested: true + }] }; var AR = (0, _ActiveRecord2['default'])(model, 'Model1'); diff --git a/dest/temp/models/tstModel3.js b/dest/temp/models/tstModel3.js new file mode 100644 index 0000000..cf6de90 --- /dev/null +++ b/dest/temp/models/tstModel3.js @@ -0,0 +1,52 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model3'); + +var Model3 = (function (_AR) { + _inherits(Model3, _AR); + + function Model3() { + _classCallCheck(this, Model3); + + _get(Object.getPrototypeOf(Model3.prototype), 'constructor', this).apply(this, arguments); + } + + return Model3; +})(AR); + +exports['default'] = Model3; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js index 83a447d..d808809 100644 --- a/dest/temp/specs/angular-dao.specs.js +++ b/dest/temp/specs/angular-dao.specs.js @@ -235,6 +235,18 @@ describe('Angular DAO', function () { expect(clone.models2[2]).toEqual('444'); }); + it('should prepare to save refs and nested refs correctly', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '303030' }, + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }], + model3: { _id: '00002' }, + models3: [{ _id: '0001' }] + }); + expect(model.beforeSave()).toEqual({ model3: { _id: '00002' }, models3: [{ _id: '0001' }] }); + console.log(model.beforeSave()); + }); + it('Should make subPopulate queries on arrays', function () { var model = ModelManager.create({ _id: '1234656', diff --git a/package.json b/package.json index 4c89a5a..185399f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-7", + "version": "2.0.0-alpha-8", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index b1699df..559704f 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -126,7 +126,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod return this.$injector.get('$http'); } - get $$pristine(){ + get $$pristine () { return _.isEmpty(this.beforeSave()) } @@ -219,28 +219,55 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod } beforeSave (obj, opts = {}) { - if (!obj){ + /** If no object is provided, clone `this` + * by copying only relevant keys (keys in model) + * If you write your own beforeSave method, it is your responsibility to clone `this` + * the way you want it cloned + */ + if (!obj) { obj = {} - _.each(_.keys(model), k => obj[k] = this[k]) + _.each(_.keys(model), k => {if (this[ k ]) { obj[ k ] = this[ k ]}}) } + /** Retrieve object saved in session to perform the diff */ let old = session.retrieve(this._id) || {} + _.each(model, (field, key)=> { - if (obj[ key ] && (field.ref || (_.isArray(field) && field[ 0 ].ref))) { - - if (_.isArray(obj[ key ])) { - obj[ key ] = _.compact(obj[ key ].map((val) => { - if (typeof(val) === 'object') { - return field.nested ? val : val._id; - } else if (typeof(val) === 'string') { - return val; - } - })); - } else { - obj[ key ] = obj[ key ]._id || obj[ key ]; + if (obj [ key ]) { + /** If the field is a ref to another field, replace it by its _id */ + if ((field.ref || (_.isArray(field) && field[ 0 ].ref)) + /** Unless it is specifically marked as nested */ + && !(field.nested || (_.isArray(field) && field[ 0 ].nested )) + ) { + /** Transforms an array of refs to just an array of _ids + * Handles mixed arrays */ + if (_.isArray(obj[ key ])) { + obj[ key ] = _.compact(obj[ key ].map((val) => { + if (typeof(val) === 'object') { + return field.nested ? val : val._id; + } else if (typeof(val) === 'string') { + return val; + } + })); + } else { + /** Transforms just a single ref into its _id if needed */ + obj[ key ] = obj[ key ]._id || obj[ key ]; + } + /** Nested SubModel, pass it through beforeSave() so that + * only relevant fields are kept + * {force: true} so that all fields of the nested object are returned + * */ + } else if (field.nested || (_.isArray(field) && field[ 0 ].nested )) { + if (_.isArray(field)) { + obj[ key ] = obj[ key ].map(e => e.beforeSave(null, { force: true })) + } else { + obj[ key ] = obj[ key ].beforeSave(null, { force: true }) + } + } else if (_.isDate(obj[ key ])) { + /** Make sure the date is an ISOString */ + obj[ key ] = new Date(obj[ key ]).toISOString(); } - } else if (obj[ key ] && _.isDate(obj[ key ])) { - obj[ key ] = new Date(obj[ key ]).toISOString(); } + /** IMPORTANT this is where the diff is made. If we didn't force in the options*/ if (!opts.force && !deep(old[ key ], obj[ key ])) { delete obj[ key ] } diff --git a/tst/app.js b/tst/app.js index d40626a..1ab3a14 100644 --- a/tst/app.js +++ b/tst/app.js @@ -3,10 +3,12 @@ import DaoHelper from './DaoHelper'; import ModelManager from './managers/tstManager1'; import ModelManager2 from './managers/tstManager2'; +import ModelManager3 from './managers/tstManager3'; var module = angular .module('tstModule', []); DaoHelper.registerService(module, 'ModelManager', ModelManager); -DaoHelper.registerService(module, 'ModelManager2', ModelManager2); \ No newline at end of file +DaoHelper.registerService(module, 'ModelManager2', ModelManager2); +DaoHelper.registerService(module, 'ModelManager3', ModelManager3); \ No newline at end of file diff --git a/tst/managers/tstManager3.js b/tst/managers/tstManager3.js new file mode 100644 index 0000000..c2a063f --- /dev/null +++ b/tst/managers/tstManager3.js @@ -0,0 +1,10 @@ +'use strict'; + +import Model3 from './../models/tstModel3.js'; +import GenericDao from '../GenericDao'; +import QueryBuilder from '../QueryBuilder'; + +var DAO = GenericDao(Model3); + +export default class ModelManager3 extends DAO { +}; diff --git a/tst/models/tstModel1.js b/tst/models/tstModel1.js index e8a8d83..06bf35e 100644 --- a/tst/models/tstModel1.js +++ b/tst/models/tstModel1.js @@ -28,7 +28,17 @@ var model = { model2: { type: String, ref: 'Model2' - } + }, + + model3: { + ref: 'Model3', + nested: true + }, + + models3: [{ + ref: 'Model3', + nested: true + }] }; diff --git a/tst/models/tstModel3.js b/tst/models/tstModel3.js new file mode 100644 index 0000000..83f45a7 --- /dev/null +++ b/tst/models/tstModel3.js @@ -0,0 +1,25 @@ +'use strict'; + + +import ActiveRecord from '../ActiveRecord' + + +var model = { + + _id: { + type : String, + unique: true, + //private: true + }, + + name: String, +}; + + +var AR = ActiveRecord(model, 'Model3'); + +export default class Model3 extends AR { + + +} + diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js index 4e486b8..660549a 100644 --- a/tst/specs/angular-dao.specs.js +++ b/tst/specs/angular-dao.specs.js @@ -241,6 +241,18 @@ describe('Angular DAO', function () { expect(clone.models2[ 2 ]).toEqual('444') }) + it ('should prepare to save refs and nested refs correctly', function(){ + var model = ModelManager.create({ + _id: '1234656', + model2: {_id: '303030'}, + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ], + model3: {_id: '00002'}, + models3:[{_id: '0001'}] + }); + expect(model.beforeSave()).toEqual({model3:{_id:'00002'},models3:[{_id:'0001'}]}) + console.log(model.beforeSave()) + }) + it('Should make subPopulate queries on arrays', function () { var model = ModelManager.create({ _id: '1234656', From 0dbd2b3e605cc196125c01b67b19a04168d5ccc7 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 11 Oct 2016 18:10:19 +0200 Subject: [PATCH 09/26] fix diff on nested fields --- build/app.js | 12 +++++++++++- dest/temp/ActiveRecord.js | 12 +++++++++++- dest/temp/specs/angular-dao.specs.js | 19 +++++++++++++++---- package.json | 2 +- src/ActiveRecord.js | 10 +++++++++- tst/specs/angular-dao.specs.js | 19 +++++++++++++++---- 6 files changed, 62 insertions(+), 12 deletions(-) diff --git a/build/app.js b/build/app.js index 22bd436..4a4abc2 100644 --- a/build/app.js +++ b/build/app.js @@ -99,7 +99,9 @@ function ActiveRecord(model, name) { if (options && options._id && _this[key]) { var toSave = undefined; /** If the field is a ref, only save id or the ids array */ - if (_.isArray(field) && field[0].ref || field.ref) { + if ((_.isArray(field) && field[0].ref || field.ref) && + /** Unless it is marked nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { if (_.isArray(field)) { toSave = _this[key].map(function (entry) { return typeof entry === 'string' ? entry : entry._id; @@ -107,6 +109,14 @@ function ActiveRecord(model, name) { } else { toSave = typeof _this[key] === 'string' ? _this[key] : _this[key]._id; } + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + toSave = _this[key].map(function (e) { + return e.beforeSave(null, { force: true }); + }); + } else { + toSave = _this[key].beforeSave(null, { force: true }); + } } else { toSave = _this[key]; } diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 418c7a5..a2c9d2d 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -98,7 +98,9 @@ function ActiveRecord(model, name) { if (options && options._id && _this[key]) { var toSave = undefined; /** If the field is a ref, only save id or the ids array */ - if (_.isArray(field) && field[0].ref || field.ref) { + if ((_.isArray(field) && field[0].ref || field.ref) && + /** Unless it is marked nested */ + !(field.nested || _.isArray(field) && field[0].nested)) { if (_.isArray(field)) { toSave = _this[key].map(function (entry) { return typeof entry === 'string' ? entry : entry._id; @@ -106,6 +108,14 @@ function ActiveRecord(model, name) { } else { toSave = typeof _this[key] === 'string' ? _this[key] : _this[key]._id; } + } else if (field.nested || _.isArray(field) && field[0].nested) { + if (_.isArray(field)) { + toSave = _this[key].map(function (e) { + return e.beforeSave(null, { force: true }); + }); + } else { + toSave = _this[key].beforeSave(null, { force: true }); + } } else { toSave = _this[key]; } diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js index d808809..4dcebd9 100644 --- a/dest/temp/specs/angular-dao.specs.js +++ b/dest/temp/specs/angular-dao.specs.js @@ -2,7 +2,7 @@ describe('Angular DAO', function () { - var ModelManager, httpBackend, $rootScope, $timeout; + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; beforeEach(function () { @@ -11,8 +11,9 @@ describe('Angular DAO', function () { ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); }); - inject(function (_ModelManager_, $httpBackend, _$rootScope_, _$timeout_) { + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; httpBackend = $httpBackend; $rootScope = _$rootScope_; $timeout = _$timeout_; @@ -236,6 +237,17 @@ describe('Angular DAO', function () { }); it('should prepare to save refs and nested refs correctly', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '303030' }, + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] + }); + model.model3 = ModelManager3.create({ _id: '00002' }); + model.models3 = [ModelManager3.create({ _id: '0001' })]; + expect(model.beforeSave()).toEqual({ model3: { _id: '00002' }, models3: [{ _id: '0001' }] }); + }); + + it('should diff nested refs correctly', function () { var model = ModelManager.create({ _id: '1234656', model2: { _id: '303030' }, @@ -243,8 +255,7 @@ describe('Angular DAO', function () { model3: { _id: '00002' }, models3: [{ _id: '0001' }] }); - expect(model.beforeSave()).toEqual({ model3: { _id: '00002' }, models3: [{ _id: '0001' }] }); - console.log(model.beforeSave()); + expect(model.beforeSave()).toEqual({}); }); it('Should make subPopulate queries on arrays', function () { diff --git a/package.json b/package.json index 185399f..8979f13 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-8", + "version": "2.0.0-alpha-9", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 559704f..cc5d2dc 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -68,12 +68,20 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod if (options && options._id && this[ key ]) { let toSave /** If the field is a ref, only save id or the ids array */ - if (_.isArray(field) && field[ 0 ].ref || field.ref) { + if ((_.isArray(field) && field[ 0 ].ref || field.ref) + /** Unless it is marked nested */ + && !(field.nested || (_.isArray(field) && field[ 0 ].nested))) { if (_.isArray(field)) { toSave = this[ key ].map(entry => (typeof entry === 'string') ? entry : entry._id) } else { toSave = typeof this[ key ] === 'string' ? this[ key ] : this[ key ]._id } + } else if (field.nested || (_.isArray(field) && field[ 0 ].nested)) { + if (_.isArray(field)){ + toSave = this[ key ].map(e => e.beforeSave(null, {force: true})) + } else { + toSave = this[ key ].beforeSave(null, {force: true}) + } } else { toSave = this[ key ] } diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js index 660549a..adc26e0 100644 --- a/tst/specs/angular-dao.specs.js +++ b/tst/specs/angular-dao.specs.js @@ -1,6 +1,6 @@ describe('Angular DAO', function () { - var ModelManager, httpBackend, $rootScope, $timeout; + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; beforeEach(function () { @@ -9,8 +9,9 @@ describe('Angular DAO', function () { ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); }); - inject(function (_ModelManager_, $httpBackend, _$rootScope_, _$timeout_) { + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; httpBackend = $httpBackend; $rootScope = _$rootScope_; $timeout = _$timeout_; @@ -242,6 +243,17 @@ describe('Angular DAO', function () { }) it ('should prepare to save refs and nested refs correctly', function(){ + var model = ModelManager.create({ + _id: '1234656', + model2: {_id: '303030'}, + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] + }); + model.model3 = ModelManager3.create({_id: '00002'}) + model.models3 = [ModelManager3.create({_id: '0001'})] + expect(model.beforeSave()).toEqual({model3:{_id:'00002'},models3:[{_id:'0001'}]}) + }) + + it ('should diff nested refs correctly', function(){ var model = ModelManager.create({ _id: '1234656', model2: {_id: '303030'}, @@ -249,8 +261,7 @@ describe('Angular DAO', function () { model3: {_id: '00002'}, models3:[{_id: '0001'}] }); - expect(model.beforeSave()).toEqual({model3:{_id:'00002'},models3:[{_id:'0001'}]}) - console.log(model.beforeSave()) + expect(model.beforeSave()).toEqual({}) }) it('Should make subPopulate queries on arrays', function () { From c75f5074989f6dd0bae528f80b119e82d4968ab6 Mon Sep 17 00:00:00 2001 From: Julien Michel Date: Thu, 13 Oct 2016 11:01:44 +0200 Subject: [PATCH 10/26] fix beforeSave with falsy values --- build/app.js | 4 ++-- dest/temp/ActiveRecord.js | 4 ++-- dest/temp/specs/angular-dao.specs.js | 14 ++++++++++++++ package.json | 2 +- src/ActiveRecord.js | 4 ++-- tst/specs/angular-dao.specs.js | 14 ++++++++++++++ 6 files changed, 35 insertions(+), 7 deletions(-) diff --git a/build/app.js b/build/app.js index 4a4abc2..0ed3c9e 100644 --- a/build/app.js +++ b/build/app.js @@ -277,7 +277,7 @@ function ActiveRecord(model, name) { if (!obj) { obj = {}; _.each(_.keys(model), function (k) { - if (_this3[k]) { + if (!_.isUndefined(_this3[k])) { obj[k] = _this3[k]; } }); @@ -286,7 +286,7 @@ function ActiveRecord(model, name) { var old = session.retrieve(this._id) || {}; _.each(model, function (field, key) { - if (obj[key]) { + if (!_.isUndefined(obj[key])) { /** If the field is a ref to another field, replace it by its _id */ if ((field.ref || _.isArray(field) && field[0].ref) && /** Unless it is specifically marked as nested */ diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index a2c9d2d..9da6211 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -276,7 +276,7 @@ function ActiveRecord(model, name) { if (!obj) { obj = {}; _.each(_.keys(model), function (k) { - if (_this3[k]) { + if (!_.isUndefined(_this3[k])) { obj[k] = _this3[k]; } }); @@ -285,7 +285,7 @@ function ActiveRecord(model, name) { var old = session.retrieve(this._id) || {}; _.each(model, function (field, key) { - if (obj[key]) { + if (!_.isUndefined(obj[key])) { /** If the field is a ref to another field, replace it by its _id */ if ((field.ref || _.isArray(field) && field[0].ref) && /** Unless it is specifically marked as nested */ diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js index 4dcebd9..3f3bb80 100644 --- a/dest/temp/specs/angular-dao.specs.js +++ b/dest/temp/specs/angular-dao.specs.js @@ -447,6 +447,20 @@ describe('Angular DAO', function () { model.save(); }); + it('Should detect change when change is falsy', function () { + var model = ModelManager.createModel({ + _id: '123456', + label: 'AZE', + num: 10 + }); + model.label = ''; + model.num = 0; + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: '', num: 0 }).respond(); + model.save(); + httpBackend.flush(); + }); + it('Should deep nested objects', function () { var model = ModelManager.createModel({ label: 'toto', diff --git a/package.json b/package.json index 8979f13..4be3653 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-9", + "version": "2.0.0-alpha-10", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index cc5d2dc..7d28aa8 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -234,13 +234,13 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod */ if (!obj) { obj = {} - _.each(_.keys(model), k => {if (this[ k ]) { obj[ k ] = this[ k ]}}) + _.each(_.keys(model), k => {if (!_.isUndefined(this[ k ])) { obj[ k ] = this[ k ]}}) } /** Retrieve object saved in session to perform the diff */ let old = session.retrieve(this._id) || {} _.each(model, (field, key)=> { - if (obj [ key ]) { + if (!_.isUndefined(obj [ key ])) { /** If the field is a ref to another field, replace it by its _id */ if ((field.ref || (_.isArray(field) && field[ 0 ].ref)) /** Unless it is specifically marked as nested */ diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js index adc26e0..fb1965b 100644 --- a/tst/specs/angular-dao.specs.js +++ b/tst/specs/angular-dao.specs.js @@ -453,6 +453,20 @@ describe('Angular DAO', function () { model.save(); }); + it('Should detect change when change is falsy', function () { + var model = ModelManager.createModel({ + _id: '123456', + label: 'AZE', + num: 10 + }) + model.label = '' + model.num = 0 + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: '', num: 0}).respond() + model.save(); + httpBackend.flush() + }) + it('Should deep nested objects', function () { var model = ModelManager.createModel({ label: 'toto', From ad2652f5d4f20ecd64a826d9822057638d9dcd72 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Fri, 14 Oct 2016 15:07:00 +0200 Subject: [PATCH 11/26] split test in multiple files --- dest/temp/specs/angular-dao-base.specs.js | 85 +++ dest/temp/specs/angular-dao-clone.specs.js | 58 ++ dest/temp/specs/angular-dao-populate.specs.js | 186 ++++++ dest/temp/specs/angular-dao-queries.specs.js | 148 +++++ dest/temp/specs/angular-dao-save.specs.js | 213 +++++++ dest/temp/specs/angular-dao.specs.js | 569 ----------------- tst/specs/angular-dao-base.specs.js | 86 +++ tst/specs/angular-dao-clone.specs.js | 62 ++ tst/specs/angular-dao-populate.specs.js | 187 ++++++ tst/specs/angular-dao-queries.specs.js | 151 +++++ tst/specs/angular-dao-save.specs.js | 217 +++++++ tst/specs/angular-dao.specs.js | 579 ------------------ 12 files changed, 1393 insertions(+), 1148 deletions(-) create mode 100644 dest/temp/specs/angular-dao-base.specs.js create mode 100644 dest/temp/specs/angular-dao-clone.specs.js create mode 100644 dest/temp/specs/angular-dao-populate.specs.js create mode 100644 dest/temp/specs/angular-dao-queries.specs.js create mode 100644 dest/temp/specs/angular-dao-save.specs.js delete mode 100644 dest/temp/specs/angular-dao.specs.js create mode 100644 tst/specs/angular-dao-base.specs.js create mode 100644 tst/specs/angular-dao-clone.specs.js create mode 100644 tst/specs/angular-dao-populate.specs.js create mode 100644 tst/specs/angular-dao-queries.specs.js create mode 100644 tst/specs/angular-dao-save.specs.js delete mode 100644 tst/specs/angular-dao.specs.js diff --git a/dest/temp/specs/angular-dao-base.specs.js b/dest/temp/specs/angular-dao-base.specs.js new file mode 100644 index 0000000..aed9888 --- /dev/null +++ b/dest/temp/specs/angular-dao-base.specs.js @@ -0,0 +1,85 @@ +'use strict'; + +describe('Angular DAO Basics', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('should have a modelManager', function () { + expect(ModelManager).toBeDefined(); + }); + + it('should query for the list', function () { + httpBackend.expectGET('http://MOCKURL.com/model1').respond(); + ModelManager.get(); + }); + + it('should query for one element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); + ModelManager.getById('ididid'); + }); + + it('should deserialize numbers', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); + ModelManager.getById('ididid').then(function (data) { + expect(data.num).toBeDefined(); + }); + httpBackend.flush(); + }); + + it('should make a post query', function () { + httpBackend.expectPOST('http://MOCKURL.com/model1/filters').respond(); + ModelManager.post(); + }); + + it('should provide with a query builder', function () { + var qb = ModelManager.query(); + expect(qb).toBeDefined(); + }); + + it('Should have created field specific methods', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); + ModelManager.selectByLabel('toto'); + httpBackend.flush(); + }); + + it('Should have created field specific methods and extract object', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2([{ _id: "2" }]); + httpBackend.flush(); + }); + it('Should have created field specific methods and extract object with key', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2(["2"]); + httpBackend.flush(); + }); + + it('Should return an ActiveRecord element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ + _id: '1234656' + }); + ModelManager.getById('123').then(function (data) { + expect(data.save).toBeDefined(); + }); + httpBackend.flush(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-clone.specs.js b/dest/temp/specs/angular-dao-clone.specs.js new file mode 100644 index 0000000..8d5962d --- /dev/null +++ b/dest/temp/specs/angular-dao-clone.specs.js @@ -0,0 +1,58 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('Should have a clone function', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + var clone = model.clone(); + expect(clone._id).not.toBeDefined(); + }); + + it('Should Clone Deep', function () { + var model = ModelManager.create({ + _id: '123456', + model2: { + _id: '1111', + name: 'toto' + }, + models2: [{ + _id: '2222', + name: 'tutu' + }, { + _id: '3333', + name: 'titi' + }, '444'] + }); + var clone = model.cloneDeep(); + expect(clone._id).not.toBeDefined(); + expect(clone.model2._id).not.toBeDefined(); + expect(clone.models2[0]._id).not.toBeDefined(); + expect(clone.models2[1]._id).not.toBeDefined(); + expect(clone.models2[2]).toEqual('444'); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-populate.specs.js b/dest/temp/specs/angular-dao-populate.specs.js new file mode 100644 index 0000000..194ac32 --- /dev/null +++ b/dest/temp/specs/angular-dao-populate.specs.js @@ -0,0 +1,186 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); + + it('Should make subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); + model.populate('models2').then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('4444'); + expect(model.models2[2]._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('Should not make useless subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' }] + }); + model.populate('models2').then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('888'); + }); + }); + + it('Should make subPopulate queries on several fields', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: ['77777', '4444'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('should populate again after save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: ['999'] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888', + models2: ['999'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: [{ _id: '999' }] + }); + model.save({ force: true, populate: ['model2', 'models2'] }).then(function (pop) { + expect(pop.models2[0]._id).toEqual('999'); + }); + httpBackend.flush(); + }); + + it('should not override populated values on save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888' + }); + model.save({ force: true }).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('should override if populated value isnt the same', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '777' + }); + model.save({ force: true }).then(function () { + expect(model.model2).toEqual('777'); + }); + httpBackend.flush(); + }); + + it('Should not override populated arrays on save', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '888', name: 'tutu' }] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: ['888'] + }); + model.save({ force: true }).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush(); + }); + + it('Should not override populated arrays values, but be able to add newly added values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '888', name: 'tutu' }] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: ['888', '999'] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2); + expect(model.models2[0]._id).toEqual('888'); + expect(model.models2[0].name).toEqual('tutu'); + expect(model.models2[1]).toEqual('999'); + }); + httpBackend.flush(); + }); + + it('Should not override populated arrays values, but detect deleted values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [{ _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' }] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: ['999', '111'] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2); + expect(model.models2[0]).toEqual('999'); + expect(model.models2[1]._id).toEqual('111'); + expect(model.models2[1].name).toEqual('toto'); + }); + httpBackend.flush(); + }); + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-queries.specs.js b/dest/temp/specs/angular-dao-queries.specs.js new file mode 100644 index 0000000..5052054 --- /dev/null +++ b/dest/temp/specs/angular-dao-queries.specs.js @@ -0,0 +1,148 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('Should query by Id if not key specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select('2')); + httpBackend.flush(); + }); + + it('Should query directly if array size === 1', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select(['2'])); + httpBackend.flush(); + }); + + it('Should query with $in operator', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'])); + httpBackend.flush(); + }); + + it('Should query on another field if specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select(['2'], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two single queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two arrays queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4', '5'], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, single then array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select(['3', '4'], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, array then single', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select('4', 'label')); + httpBackend.flush(); + }); + it('Should merge queries on the same field, array then single-array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4'], 'label')); + httpBackend.flush(); + }); + + it('Should query for archives', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); + ModelManager.get(ModelManager.query().archived(true)); + httpBackend.flush(); + }); + + it('Should query for deleted', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); + ModelManager.get(ModelManager.query().deleted(true)); + httpBackend.flush(); + }); + + it('Should paginate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); + ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); + httpBackend.flush(); + }); + it('Should limit', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); + ModelManager.get(ModelManager.query().limit(10)); + httpBackend.flush(); + }); + + it('Should sort', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); + ModelManager.get(ModelManager.query().sort("toto")); + httpBackend.flush(); + }); + + it('Should select specific fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); + ModelManager.get(ModelManager.query().fields(["toto", "tutu"])); + httpBackend.flush(); + }); + + it('Should search', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto')); + httpBackend.flush(); + }); + + it('Should search on another field', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', 'label')); + httpBackend.flush(); + }); + + it('Should search on multiple fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', ['name', 'label'])); + httpBackend.flush(); + }); + + it('Should count', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); + ModelManager.count(); + httpBackend.flush(); + }); + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-save.specs.js b/dest/temp/specs/angular-dao-save.specs.js new file mode 100644 index 0000000..acbb533 --- /dev/null +++ b/dest/temp/specs/angular-dao-save.specs.js @@ -0,0 +1,213 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); + + it('should prepare to save refs and nested refs correctly', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '303030' }, + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] + }); + model.model3 = ModelManager3.create({ _id: '00002' }); + model.models3 = [ModelManager3.create({ _id: '0001' })]; + expect(model.beforeSave()).toEqual({ model3: { _id: '00002' }, models3: [{ _id: '0001' }] }); + }); + + it('should diff nested refs correctly', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '303030' }, + models2: ['77777', '4444', { _id: '888', toto: 'tutu' }], + model3: { _id: '00002' }, + models3: [{ _id: '0001' }] + }); + expect(model.beforeSave()).toEqual({}); + }); + + it('should save only modified values', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }); + model.label = 'tutu'; + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('should save only modified values in arrays', function () { + var model = ModelManager.createModel({ + _id: '123456', + models2: ['7777'], + label: 'toto' + }); + model.models2.push('8888'); + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: ['7777', '8888'] }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should save new objects', function () { + var model = ModelManager.createModel({ + model2: '77777' + }); + expect(model.$$pristine).toBeFalsy(); + httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should not save unedited objects', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '77777' + }); + expect(model.$$pristine).toBeTruthy(); + model.save(); + }); + + it('Should deep nested objects', function () { + var model = ModelManager.createModel({ + label: 'toto', + model2: { name: 'toto' } + }); + httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); + httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); + model.saveDeep(); + httpBackend.flush(); + expect(model.model2._id).toEqual('1111'); + }); + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + }); + + it('Should not make a diff with dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }); + model.save(); + }); + + it('Should not make a diff with JS dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }); + model.when = new Date(model.when); + model.save(); + }); + + it('Should make a diff with dates if needed', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }); + model.when = new Date(203939); + httpBackend.expectPUT('http://MOCKURL.com/model1/007', { + when: new Date(203939) + }).respond(); + model.save(); + }); + + it('Should not make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: ['77777', '4444'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('77777'); + expect(model.models2[1]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + model.save(); + }); + + it('Should make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888' + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate(['models2', 'model2']).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush(); + model.model2 = { _id: '999' }; + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + model2: '999' + }).respond(); + model.save(); + }); + + it('Should make a diff between populated and unpopulated nested fields array', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); + model.populate(['models2', 'model2']).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush(); + model.models2.push({ _id: '999' }); + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + models2: ['888', '999'] + }).respond(); + model.save(); + }); + + it('Should not save irrelevant fields', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + model.iShouldntBeThere = 'but I am'; + model.save(); + }); + + it('should not save irrelevant fields, but still save others', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }); + model.label = 'tutu'; + model.iShouldntBeThere = 'but I am'; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); + model.save(); + httpBackend.flush(); + }); +}); \ No newline at end of file diff --git a/dest/temp/specs/angular-dao.specs.js b/dest/temp/specs/angular-dao.specs.js deleted file mode 100644 index 4dcebd9..0000000 --- a/dest/temp/specs/angular-dao.specs.js +++ /dev/null @@ -1,569 +0,0 @@ -'use strict'; - -describe('Angular DAO', function () { - - var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; - - beforeEach(function () { - - module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { - ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); - ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); - }); - - inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { - ModelManager = _ModelManager_; - ModelManager3 = _ModelManager3_; - httpBackend = $httpBackend; - $rootScope = _$rootScope_; - $timeout = _$timeout_; - }); - }); - - afterEach(function () { - httpBackend.verifyNoOutstandingExpectation(); - }); - - it('should have a modelManager', function () { - expect(ModelManager).toBeDefined(); - }); - - it('should query for the list', function () { - httpBackend.expectGET('http://MOCKURL.com/model1').respond(); - ModelManager.get(); - }); - - it('should query for one element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); - ModelManager.getById('ididid'); - }); - - it('should deserialize numbers', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); - ModelManager.getById('ididid').then(function (data) { - expect(data.num).toBeDefined(); - }); - httpBackend.flush(); - }); - - it('should make a post query', function () { - httpBackend.expectPOST('http://MOCKURL.com/model1/filters').respond(); - ModelManager.post(); - }); - - it('should provide with a query builder', function () { - var qb = ModelManager.query(); - expect(qb).toBeDefined(); - }); - - it('Should have created field specific methods', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); - ModelManager.selectByLabel('toto'); - httpBackend.flush(); - }); - - it('Should have created field specific methods and extract object', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2([{ _id: "2" }]); - httpBackend.flush(); - }); - it('Should have created field specific methods and extract object with key', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2(["2"]); - httpBackend.flush(); - }); - - it('Should return an ActiveRecord element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ - _id: '1234656' - }); - ModelManager.getById('123').then(function (data) { - expect(data.save).toBeDefined(); - }); - httpBackend.flush(); - }); - - it('Should query by Id if not key specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select('2')); - httpBackend.flush(); - }); - - it('Should query directly if array size === 1', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select(['2'])); - httpBackend.flush(); - }); - it('Should query with $in operator', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'])); - httpBackend.flush(); - }); - - it('Should query on another field if specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select(['2'], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two single queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two arrays queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4', '5'], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, single then array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select(['3', '4'], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, array then single', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select('4', 'label')); - httpBackend.flush(); - }); - it('Should merge queries on the same field, array then single-array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select(['2', '3'], 'label').select(['4'], 'label')); - httpBackend.flush(); - }); - - it('Should query for archives', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); - ModelManager.get(ModelManager.query().archived(true)); - httpBackend.flush(); - }); - - it('Should query for deleted', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); - ModelManager.get(ModelManager.query().deleted(true)); - httpBackend.flush(); - }); - - it('Should paginate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); - ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); - httpBackend.flush(); - }); - it('Should limit', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); - ModelManager.get(ModelManager.query().limit(10)); - httpBackend.flush(); - }); - - it('Should sort', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); - ModelManager.get(ModelManager.query().sort("toto")); - httpBackend.flush(); - }); - - it('Should select specific fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); - ModelManager.get(ModelManager.query().fields(["toto", "tutu"])); - httpBackend.flush(); - }); - - it('Should search', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto')); - httpBackend.flush(); - }); - - it('Should search on another field', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', 'label')); - httpBackend.flush(); - }); - - it('Should search on multiple fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', ['name', 'label'])); - httpBackend.flush(); - }); - - it('Should count', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); - ModelManager.count(); - httpBackend.flush(); - }); - - it('Should make subPopulate queries', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); - model.populate('model2'); - httpBackend.flush(); - }); - - it('Should have a clone function', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - var clone = model.clone(); - expect(clone._id).not.toBeDefined(); - }); - - it('Should Clone Deep', function () { - var model = ModelManager.create({ - _id: '123456', - model2: { - _id: '1111', - name: 'toto' - }, - models2: [{ - _id: '2222', - name: 'tutu' - }, { - _id: '3333', - name: 'titi' - }, '444'] - }); - var clone = model.cloneDeep(); - expect(clone._id).not.toBeDefined(); - expect(clone.model2._id).not.toBeDefined(); - expect(clone.models2[0]._id).not.toBeDefined(); - expect(clone.models2[1]._id).not.toBeDefined(); - expect(clone.models2[2]).toEqual('444'); - }); - - it('should prepare to save refs and nested refs correctly', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '303030' }, - models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] - }); - model.model3 = ModelManager3.create({ _id: '00002' }); - model.models3 = [ModelManager3.create({ _id: '0001' })]; - expect(model.beforeSave()).toEqual({ model3: { _id: '00002' }, models3: [{ _id: '0001' }] }); - }); - - it('should diff nested refs correctly', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '303030' }, - models2: ['77777', '4444', { _id: '888', toto: 'tutu' }], - model3: { _id: '00002' }, - models3: [{ _id: '0001' }] - }); - expect(model.beforeSave()).toEqual({}); - }); - - it('Should make subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: ['77777', '4444', { _id: '888', toto: 'tutu' }] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); - model.populate('models2').then(function () { - expect(model.models2[0]._id).toEqual('77777'); - expect(model.models2[1]._id).toEqual('4444'); - expect(model.models2[2]._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('Should not make useless subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' }] - }); - model.populate('models2').then(function () { - expect(model.models2[0]._id).toEqual('77777'); - expect(model.models2[1]._id).toEqual('888'); - }); - }); - - it('Should make subPopulate queries on several fields', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888', - models2: ['77777', '4444'] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate(['models2', 'model2']).then(function () { - expect(model.models2[0]._id).toEqual('77777'); - expect(model.models2[1]._id).toEqual('4444'); - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('should populate again after save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: ['999'] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888', - models2: ['999'] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: [{ _id: '999' }] - }); - model.save({ force: true, populate: ['model2', 'models2'] }).then(function (pop) { - expect(pop.models2[0]._id).toEqual('999'); - }); - httpBackend.flush(); - }); - - it('should not override populated values on save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888' - }); - model.save({ force: true }).then(function () { - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('should override if populated value isnt the same', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '777' - }); - model.save({ force: true }).then(function () { - expect(model.model2).toEqual('777'); - }); - httpBackend.flush(); - }); - - it('Should not override populated arrays on save', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '888', name: 'tutu' }] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: ['888'] - }); - model.save({ force: true }).then(function () { - expect(model.models2[0]._id).toEqual('888'); - }); - httpBackend.flush(); - }); - - it('Should not override populated arrays values, but be able to add newly added values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '888', name: 'tutu' }] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: ['888', '999'] - }); - model.save({ force: true }).then(function () { - expect(model.models2.length).toEqual(2); - expect(model.models2[0]._id).toEqual('888'); - expect(model.models2[0].name).toEqual('tutu'); - expect(model.models2[1]).toEqual('999'); - }); - httpBackend.flush(); - }); - - it('Should not override populated arrays values, but detect deleted values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [{ _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' }] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: ['999', '111'] - }); - model.save({ force: true }).then(function () { - expect(model.models2.length).toEqual(2); - expect(model.models2[0]).toEqual('999'); - expect(model.models2[1]._id).toEqual('111'); - expect(model.models2[1].name).toEqual('toto'); - }); - httpBackend.flush(); - }); - - it('should save only modified values', function () { - var model = ModelManager.createModel({ - _id: '123456', - model2: '7777', - label: 'toto' - }); - model.label = 'tutu'; - expect(model.$$pristine).toBeFalsy(); - httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); - model.save(); - httpBackend.flush(); - }); - - it('should save only modified values in arrays', function () { - var model = ModelManager.createModel({ - _id: '123456', - models2: ['7777'], - label: 'toto' - }); - model.models2.push('8888'); - expect(model.$$pristine).toBeFalsy(); - httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: ['7777', '8888'] }).respond(); - model.save(); - httpBackend.flush(); - }); - - it('Should save new objects', function () { - var model = ModelManager.createModel({ - model2: '77777' - }); - expect(model.$$pristine).toBeFalsy(); - httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); - model.save(); - httpBackend.flush(); - }); - - it('Should not save unedited objects', function () { - var model = ModelManager.createModel({ - _id: '123456', - model2: '77777' - }); - expect(model.$$pristine).toBeTruthy(); - model.save(); - }); - - it('Should deep nested objects', function () { - var model = ModelManager.createModel({ - label: 'toto', - model2: { name: 'toto' } - }); - httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); - httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); - model.saveDeep(); - httpBackend.flush(); - expect(model.model2._id).toEqual('1111'); - }); - - it('Should make a query with populate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); - ModelManager.get(ModelManager.query().populate('model2')); - httpBackend.flush(); - }); - - it('Should not make a diff with dates', function () { - var model = ModelManager.create({ - _id: '007', - when: new Date().toISOString() - }); - model.save(); - }); - - it('Should not make a diff with JS dates', function () { - var model = ModelManager.create({ - _id: '007', - when: new Date().toISOString() - }); - model.when = new Date(model.when); - model.save(); - }); - - it('Should make a diff with dates if needed', function () { - var model = ModelManager.create({ - _id: '007', - when: new Date() - }); - model.when = new Date(203939); - httpBackend.expectPUT('http://MOCKURL.com/model1/007', { - when: new Date(203939) - }).respond(); - model.save(); - }); - - it('Should not make a diff between populated and unpopulated nested field', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888', - models2: ['77777', '4444'] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([{ _id: '77777' }, { _id: '4444' }]); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate(['models2', 'model2']).then(function () { - expect(model.models2[0]._id).toEqual('77777'); - expect(model.models2[1]._id).toEqual('4444'); - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush(); - model.save(); - }); - - it('Should make a diff between populated and unpopulated nested field', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888' - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate(['models2', 'model2']).then(function () { - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush(); - model.model2 = { _id: '999' }; - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { - model2: '999' - }).respond(); - model.save(); - }); - - it('Should make a diff between populated and unpopulated nested fields array', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: ['888'] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); - model.populate(['models2', 'model2']).then(function () { - expect(model.models2[0]._id).toEqual('888'); - }); - httpBackend.flush(); - model.models2.push({ _id: '999' }); - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { - models2: ['888', '999'] - }).respond(); - model.save(); - }); - - it('Should not save irrelevant fields', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: ['888'] - }); - model.iShouldntBeThere = 'but I am'; - model.save(); - }); - - it('should not save irrelevant fields, but still save others', function () { - var model = ModelManager.createModel({ - _id: '123456', - model2: '7777', - label: 'toto' - }); - model.label = 'tutu'; - model.iShouldntBeThere = 'but I am'; - httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond(); - model.save(); - httpBackend.flush(); - }); -}); \ No newline at end of file diff --git a/tst/specs/angular-dao-base.specs.js b/tst/specs/angular-dao-base.specs.js new file mode 100644 index 0000000..59a275a --- /dev/null +++ b/tst/specs/angular-dao-base.specs.js @@ -0,0 +1,86 @@ +describe('Angular DAO Basics', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('should have a modelManager', function () { + expect(ModelManager).toBeDefined(); + }); + + it('should query for the list', function () { + httpBackend.expectGET('http://MOCKURL.com/model1').respond(); + ModelManager.get(); + }); + + it('should query for one element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); + ModelManager.getById('ididid'); + }); + + it('should deserialize numbers', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); + ModelManager.getById('ididid').then(function (data) { + expect(data.num).toBeDefined() + }) + httpBackend.flush(); + }); + + it('should make a post query', function () { + httpBackend.expectPOST('http://MOCKURL.com/model1/filters').respond(); + ModelManager.post(); + }); + + it('should provide with a query builder', function () { + var qb = ModelManager.query(); + expect(qb).toBeDefined(); + }); + + it('Should have created field specific methods', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); + ModelManager.selectByLabel('toto'); + httpBackend.flush(); + }); + + it('Should have created field specific methods and extract object', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2([ { _id: "2" } ]); + httpBackend.flush(); + }); + it('Should have created field specific methods and extract object with key', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); + ModelManager.selectByModel2([ "2" ]); + httpBackend.flush(); + }); + + it('Should return an ActiveRecord element', function () { + httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ + _id: '1234656' + }); + ModelManager.getById('123').then(function (data) { + expect(data.save).toBeDefined(); + }); + httpBackend.flush(); + }); + +}); diff --git a/tst/specs/angular-dao-clone.specs.js b/tst/specs/angular-dao-clone.specs.js new file mode 100644 index 0000000..4e4cb82 --- /dev/null +++ b/tst/specs/angular-dao-clone.specs.js @@ -0,0 +1,62 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_, _ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('Should have a clone function', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + var clone = model.clone() + expect(clone._id).not.toBeDefined() + }); + + it('Should Clone Deep', function () { + var model = ModelManager.create({ + _id: '123456', + model2: { + _id: '1111', + name: 'toto' + }, + models2: [ + { + _id: '2222', + name: 'tutu' + }, + { + _id: '3333', + name: 'titi' + }, + '444' + ] + }) + var clone = model.cloneDeep() + expect(clone._id).not.toBeDefined() + expect(clone.model2._id).not.toBeDefined() + expect(clone.models2[0]._id).not.toBeDefined() + expect(clone.models2[1]._id).not.toBeDefined() + expect(clone.models2[2]).toEqual('444') + }) +}); diff --git a/tst/specs/angular-dao-populate.specs.js b/tst/specs/angular-dao-populate.specs.js new file mode 100644 index 0000000..ec9d7b3 --- /dev/null +++ b/tst/specs/angular-dao-populate.specs.js @@ -0,0 +1,187 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); + + it('Should make subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); + model.populate('models2').then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777') + expect(model.models2[ 1 ]._id).toEqual('4444') + expect(model.models2[ 2 ]._id).toEqual('888') + }); + httpBackend.flush() + }); + + it('Should not make useless subPopulate queries on arrays', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' } ] + }); + model.populate('models2').then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777') + expect(model.models2[ 1 ]._id).toEqual('888') + }); + }); + + it('Should make subPopulate queries on several fields', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: [ '77777', '4444' ] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777'); + expect(model.models2[ 1 ]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + }); + + it('should populate again after save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: [ '999' ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888', + models2: [ '999' ] + }) + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' }, + models2: [ { _id: '999' } ] + }) + model.save({ force: true, populate: [ 'model2', 'models2' ] }).then(function (pop) { + expect(pop.models2[ 0 ]._id).toEqual('999') + }) + httpBackend.flush() + }) + + it('should not override populated values on save', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '888' + }) + model.save({ force: true }).then(function () { + expect(model.model2._id).toEqual('888') + }) + httpBackend.flush() + }) + + it('should override if populated value isnt the same', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: { _id: '888', name: 'tutu' } + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + model2: '777' + }) + model.save({ force: true }).then(function () { + expect(model.model2).toEqual('777') + }) + httpBackend.flush() + }) + + it('Should not override populated arrays on save', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '888', name: 'tutu' } ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: [ '888' ] + }); + model.save({ force: true }).then(function () { + expect(model.models2[ 0 ]._id).toEqual('888') + }) + httpBackend.flush() + }) + + it('Should not override populated arrays values, but be able to add newly added values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '888', name: 'tutu' } ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: [ '888', '999' ] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2) + expect(model.models2[ 0 ]._id).toEqual('888') + expect(model.models2[ 0 ].name).toEqual('tutu') + expect(model.models2[ 1 ]).toEqual('999') + }) + httpBackend.flush() + }) + + it('Should not override populated arrays values, but detect deleted values', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: [ { _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' } ] + }); + httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ + _id: '1234656', + models2: [ '999', '111' ] + }); + model.save({ force: true }).then(function () { + expect(model.models2.length).toEqual(2) + expect(model.models2[ 0 ]).toEqual('999') + expect(model.models2[ 1 ]._id).toEqual('111') + expect(model.models2[ 1 ].name).toEqual('toto') + }) + httpBackend.flush() + }) + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + + }) +}); diff --git a/tst/specs/angular-dao-queries.specs.js b/tst/specs/angular-dao-queries.specs.js new file mode 100644 index 0000000..7e9d530 --- /dev/null +++ b/tst/specs/angular-dao-queries.specs.js @@ -0,0 +1,151 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it('Should query by Id if not key specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select('2')); + httpBackend.flush(); + }); + + it('Should query directly if array size === 1', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2' ])); + httpBackend.flush(); + }); + + it('Should query with $in operator', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ])); + httpBackend.flush(); + }); + + it('Should query on another field if specified', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2' ], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two single queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, two arrays queries', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4', '5' ], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, single then array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select('2', 'label').select([ '3', '4' ], 'label')); + httpBackend.flush(); + }); + + it('Should merge queries on the same field, array then single', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select('4', 'label')); + httpBackend.flush(); + }); + it('Should merge queries on the same field, array then single-array', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); + ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4' ], 'label')); + httpBackend.flush(); + }); + + it('Should query for archives', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); + ModelManager.get(ModelManager.query().archived(true)); + httpBackend.flush(); + + }); + + it('Should query for deleted', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); + ModelManager.get(ModelManager.query().deleted(true)); + httpBackend.flush(); + + }); + + it('Should paginate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); + ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); + httpBackend.flush(); + }); + it('Should limit', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); + ModelManager.get(ModelManager.query().limit(10)); + httpBackend.flush(); + }); + + it('Should sort', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); + ModelManager.get(ModelManager.query().sort("toto")); + httpBackend.flush(); + }); + + it('Should select specific fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); + ModelManager.get(ModelManager.query().fields([ "toto", "tutu" ])); + httpBackend.flush(); + }); + + it('Should search', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto')); + httpBackend.flush(); + }); + + it('Should search on another field', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', 'label')); + httpBackend.flush(); + }); + + it('Should search on multiple fields', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); + ModelManager.get(ModelManager.query().search('toto', [ 'name', 'label' ])); + httpBackend.flush(); + }); + + it('Should count', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); + ModelManager.count() + httpBackend.flush() + }) + + it('Should make subPopulate queries', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '77777' + }); + httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); + model.populate('model2'); + httpBackend.flush(); + }); + +}); diff --git a/tst/specs/angular-dao-save.specs.js b/tst/specs/angular-dao-save.specs.js new file mode 100644 index 0000000..647dc95 --- /dev/null +++ b/tst/specs/angular-dao-save.specs.js @@ -0,0 +1,217 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { + ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); + ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); + }); + + inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager = _ModelManager_; + ModelManager3 = _ModelManager3_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + + }) + + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + + it ('should prepare to save refs and nested refs correctly', function(){ + var model = ModelManager.create({ + _id: '1234656', + model2: {_id: '303030'}, + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] + }); + model.model3 = ModelManager3.create({_id: '00002'}) + model.models3 = [ModelManager3.create({_id: '0001'})] + expect(model.beforeSave()).toEqual({model3:{_id:'00002'},models3:[{_id:'0001'}]}) + }) + + it ('should diff nested refs correctly', function(){ + var model = ModelManager.create({ + _id: '1234656', + model2: {_id: '303030'}, + models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ], + model3: {_id: '00002'}, + models3:[{_id: '0001'}] + }); + expect(model.beforeSave()).toEqual({}) + }) + + it('should save only modified values', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }) + model.label = 'tutu' + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() + model.save() + httpBackend.flush() + }) + + it('should save only modified values in arrays', function () { + var model = ModelManager.createModel({ + _id: '123456', + models2: [ '7777' ], + label: 'toto' + }) + model.models2.push('8888') + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: [ '7777', '8888' ] }).respond() + model.save() + httpBackend.flush() + }) + + it('Should save new objects', function () { + var model = ModelManager.createModel({ + model2: '77777' + }); + expect(model.$$pristine).toBeFalsy() + httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); + model.save(); + httpBackend.flush(); + }); + + it('Should not save unedited objects', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '77777' + }); + expect(model.$$pristine).toBeTruthy() + model.save(); + }); + + it('Should deep nested objects', function () { + var model = ModelManager.createModel({ + label: 'toto', + model2: { name: 'toto' } + }); + httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); + httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); + model.saveDeep(); + httpBackend.flush(); + expect(model.model2._id).toEqual('1111') + }); + + it('Should make a query with populate', function () { + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); + ModelManager.get(ModelManager.query().populate('model2')); + httpBackend.flush(); + + }) + + it('Should not make a diff with dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }) + model.save() + }) + + + it('Should not make a diff with JS dates', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date().toISOString() + }) + model.when = new Date(model.when) + model.save() + }) + + it('Should make a diff with dates if needed', function () { + var model = ModelManager.create({ + _id: '007', + when: new Date() + }) + model.when = new Date(203939) + httpBackend.expectPUT('http://MOCKURL.com/model1/007', { + when: new Date(203939) + }).respond(); + model.save() + }) + + it('Should not make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888', + models2: [ '77777', '4444' ] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[ 0 ]._id).toEqual('77777'); + expect(model.models2[ 1 ]._id).toEqual('4444'); + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + model.save() + }); + + it('Should make a diff between populated and unpopulated nested field', function () { + var model = ModelManager.create({ + _id: '1234656', + model2: '888' + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.model2._id).toEqual('888'); + }); + httpBackend.flush() + model.model2 = { _id: '999' } + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + model2: '999' + }).respond(); + model.save() + }); + + it('Should make a diff between populated and unpopulated nested fields array', function () { + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); + model.populate([ 'models2', 'model2' ]).then(function () { + expect(model.models2[0]._id).toEqual('888'); + }); + httpBackend.flush() + model.models2.push({ _id: '999' }) + httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { + models2: ['888', '999'] + }).respond(); + model.save() + }); + + it ('Should not save irrelevant fields', function(){ + var model = ModelManager.create({ + _id: '1234656', + models2: ['888'] + }); + model.iShouldntBeThere = 'but I am' + model.save() + }) + + + it('should not save irrelevant fields, but still save others', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: '7777', + label: 'toto' + }) + model.label = 'tutu' + model.iShouldntBeThere = 'but I am' + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() + model.save() + httpBackend.flush() + }) + +}); diff --git a/tst/specs/angular-dao.specs.js b/tst/specs/angular-dao.specs.js deleted file mode 100644 index adc26e0..0000000 --- a/tst/specs/angular-dao.specs.js +++ /dev/null @@ -1,579 +0,0 @@ -describe('Angular DAO', function () { - - var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; - - beforeEach(function () { - - module('tstModule', function (ModelManagerProvider, ModelManager2Provider) { - ModelManagerProvider.setRootUrl('http://MOCKURL.com/model1'); - ModelManager2Provider.setRootUrl('http://MOCKURL.com/model2'); - }); - - inject(function (_ModelManager_,_ModelManager3_, $httpBackend, _$rootScope_, _$timeout_) { - ModelManager = _ModelManager_; - ModelManager3 = _ModelManager3_; - httpBackend = $httpBackend; - $rootScope = _$rootScope_; - $timeout = _$timeout_; - - }) - - }); - - afterEach(function () { - httpBackend.verifyNoOutstandingExpectation() - }); - - it('should have a modelManager', function () { - expect(ModelManager).toBeDefined(); - }); - - it('should query for the list', function () { - httpBackend.expectGET('http://MOCKURL.com/model1').respond(); - ModelManager.get(); - }); - - it('should query for one element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond(); - ModelManager.getById('ididid'); - }); - - it('should deserialize numbers', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/ididid').respond({ num: 0, label: 'toto' }); - ModelManager.getById('ididid').then(function (data) { - expect(data.num).toBeDefined() - }) - httpBackend.flush(); - }); - - it('should make a post query', function () { - httpBackend.expectPOST('http://MOCKURL.com/model1/filters').respond(); - ModelManager.post(); - }); - - it('should provide with a query builder', function () { - var qb = ModelManager.query(); - expect(qb).toBeDefined(); - }); - - it('Should have created field specific methods', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"toto"}')).respond([]); - ModelManager.selectByLabel('toto'); - httpBackend.flush(); - }); - - it('Should have created field specific methods and extract object', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2([ { _id: "2" } ]); - httpBackend.flush(); - }); - it('Should have created field specific methods and extract object with key', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"model2":"2"}')).respond([]); - ModelManager.selectByModel2([ "2" ]); - httpBackend.flush(); - }); - - it('Should return an ActiveRecord element', function () { - httpBackend.expectGET('http://MOCKURL.com/model1/123').respond({ - _id: '1234656' - }); - ModelManager.getById('123').then(function (data) { - expect(data.save).toBeDefined(); - }); - httpBackend.flush(); - }); - - it('Should query by Id if not key specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select('2')); - httpBackend.flush(); - }); - - it('Should query directly if array size === 1', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2' ])); - httpBackend.flush(); - }); - it('Should query with $in operator', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"_id":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ])); - httpBackend.flush(); - }); - - it('Should query on another field if specified', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":"2"}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2' ], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two single queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select('3', 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, two arrays queries', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4","5"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4', '5' ], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, single then array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select('2', 'label').select([ '3', '4' ], 'label')); - httpBackend.flush(); - }); - - it('Should merge queries on the same field, array then single', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select('4', 'label')); - httpBackend.flush(); - }); - it('Should merge queries on the same field, array then single-array', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$in":["2","3","4"]}}')).respond([]); - ModelManager.get(ModelManager.query().select([ '2', '3' ], 'label').select([ '4' ], 'label')); - httpBackend.flush(); - }); - - it('Should query for archives', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?archived=true')).respond([]); - ModelManager.get(ModelManager.query().archived(true)); - httpBackend.flush(); - - }); - - it('Should query for deleted', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?deleted=true')).respond([]); - ModelManager.get(ModelManager.query().deleted(true)); - httpBackend.flush(); - - }); - - it('Should paginate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10&skip=5')).respond([]); - ModelManager.get(ModelManager.query().paginate({ skip: 5, limit: 10 })); - httpBackend.flush(); - }); - it('Should limit', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?limit=10')).respond([]); - ModelManager.get(ModelManager.query().limit(10)); - httpBackend.flush(); - }); - - it('Should sort', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?sort=toto')).respond([]); - ModelManager.get(ModelManager.query().sort("toto")); - httpBackend.flush(); - }); - - it('Should select specific fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?select=toto+tutu')).respond([]); - ModelManager.get(ModelManager.query().fields([ "toto", "tutu" ])); - httpBackend.flush(); - }); - - it('Should search', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"name":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto')); - httpBackend.flush(); - }); - - it('Should search on another field', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"label":{"$regex":".*toto.*","$options":"i"}}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', 'label')); - httpBackend.flush(); - }); - - it('Should search on multiple fields', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?conditions={"$or":[{"name":{"$regex":".*toto.*","$options":"i"}},{"label":{"$regex":".*toto.*","$options":"i"}}]}')).respond([]); - ModelManager.get(ModelManager.query().search('toto', [ 'name', 'label' ])); - httpBackend.flush(); - }); - - it('Should count', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?count=true')).respond([]); - ModelManager.count() - httpBackend.flush() - }) - - it('Should make subPopulate queries', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - httpBackend.expectGET('http://MOCKURL.com/model2/77777').respond(); - model.populate('model2'); - httpBackend.flush(); - }); - - it('Should have a clone function', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '77777' - }); - var clone = model.clone() - expect(clone._id).not.toBeDefined() - }); - - it('Should Clone Deep', function () { - var model = ModelManager.create({ - _id: '123456', - model2: { - _id: '1111', - name: 'toto' - }, - models2: [ - { - _id: '2222', - name: 'tutu' - }, - { - _id: '3333', - name: 'titi' - }, - '444' - ] - }) - var clone = model.cloneDeep() - expect(clone._id).not.toBeDefined() - expect(clone.model2._id).not.toBeDefined() - expect(clone.models2[ 0 ]._id).not.toBeDefined() - expect(clone.models2[ 1 ]._id).not.toBeDefined() - expect(clone.models2[ 2 ]).toEqual('444') - }) - - it ('should prepare to save refs and nested refs correctly', function(){ - var model = ModelManager.create({ - _id: '1234656', - model2: {_id: '303030'}, - models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] - }); - model.model3 = ModelManager3.create({_id: '00002'}) - model.models3 = [ModelManager3.create({_id: '0001'})] - expect(model.beforeSave()).toEqual({model3:{_id:'00002'},models3:[{_id:'0001'}]}) - }) - - it ('should diff nested refs correctly', function(){ - var model = ModelManager.create({ - _id: '1234656', - model2: {_id: '303030'}, - models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ], - model3: {_id: '00002'}, - models3:[{_id: '0001'}] - }); - expect(model.beforeSave()).toEqual({}) - }) - - it('Should make subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ '77777', '4444', { _id: '888', toto: 'tutu' } ] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); - model.populate('models2').then(function () { - expect(model.models2[ 0 ]._id).toEqual('77777') - expect(model.models2[ 1 ]._id).toEqual('4444') - expect(model.models2[ 2 ]._id).toEqual('888') - }); - httpBackend.flush() - }); - - it('Should not make useless subPopulate queries on arrays', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '77777', name: 'toto' }, { _id: '888', toto: 'tutu' } ] - }); - model.populate('models2').then(function () { - expect(model.models2[ 0 ]._id).toEqual('77777') - expect(model.models2[ 1 ]._id).toEqual('888') - }); - }); - - it('Should make subPopulate queries on several fields', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888', - models2: [ '77777', '4444' ] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate([ 'models2', 'model2' ]).then(function () { - expect(model.models2[ 0 ]._id).toEqual('77777'); - expect(model.models2[ 1 ]._id).toEqual('4444'); - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush() - }); - - it('should populate again after save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: [ '999' ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888', - models2: [ '999' ] - }) - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1/1234656?populate=[{"path":"models2"},{"path":"model2"}]')).respond({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' }, - models2: [ { _id: '999' } ] - }) - model.save({ force: true, populate: [ 'model2', 'models2' ] }).then(function (pop) { - expect(pop.models2[ 0 ]._id).toEqual('999') - }) - httpBackend.flush() - }) - - it('should not override populated values on save', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '888' - }) - model.save({ force: true }).then(function () { - expect(model.model2._id).toEqual('888') - }) - httpBackend.flush() - }) - - it('should override if populated value isnt the same', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: { _id: '888', name: 'tutu' } - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - model2: '777' - }) - model.save({ force: true }).then(function () { - expect(model.model2).toEqual('777') - }) - httpBackend.flush() - }) - - it('Should not override populated arrays on save', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '888', name: 'tutu' } ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: [ '888' ] - }); - model.save({ force: true }).then(function () { - expect(model.models2[ 0 ]._id).toEqual('888') - }) - httpBackend.flush() - }) - - it('Should not override populated arrays values, but be able to add newly added values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '888', name: 'tutu' } ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: [ '888', '999' ] - }); - model.save({ force: true }).then(function () { - expect(model.models2.length).toEqual(2) - expect(model.models2[ 0 ]._id).toEqual('888') - expect(model.models2[ 0 ].name).toEqual('tutu') - expect(model.models2[ 1 ]).toEqual('999') - }) - httpBackend.flush() - }) - - it('Should not override populated arrays values, but detect deleted values', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: [ { _id: '888', name: 'tutu' }, { _id: '111', name: 'toto' } ] - }); - httpBackend.expectPUT(encodeURI('http://MOCKURL.com/model1/1234656')).respond({ - _id: '1234656', - models2: [ '999', '111' ] - }); - model.save({ force: true }).then(function () { - expect(model.models2.length).toEqual(2) - expect(model.models2[ 0 ]).toEqual('999') - expect(model.models2[ 1 ]._id).toEqual('111') - expect(model.models2[ 1 ].name).toEqual('toto') - }) - httpBackend.flush() - }) - - it('should save only modified values', function () { - var model = ModelManager.createModel({ - _id: '123456', - model2: '7777', - label: 'toto' - }) - model.label = 'tutu' - expect(model.$$pristine).toBeFalsy() - httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() - model.save() - httpBackend.flush() - }) - - it('should save only modified values in arrays', function () { - var model = ModelManager.createModel({ - _id: '123456', - models2: [ '7777' ], - label: 'toto' - }) - model.models2.push('8888') - expect(model.$$pristine).toBeFalsy() - httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { models2: [ '7777', '8888' ] }).respond() - model.save() - httpBackend.flush() - }) - - it('Should save new objects', function () { - var model = ModelManager.createModel({ - model2: '77777' - }); - expect(model.$$pristine).toBeFalsy() - httpBackend.expectPOST('http://MOCKURL.com/model1', { model2: '77777' }).respond(); - model.save(); - httpBackend.flush(); - }); - - it('Should not save unedited objects', function () { - var model = ModelManager.createModel({ - _id: '123456', - model2: '77777' - }); - expect(model.$$pristine).toBeTruthy() - model.save(); - }); - - it('Should deep nested objects', function () { - var model = ModelManager.createModel({ - label: 'toto', - model2: { name: 'toto' } - }); - httpBackend.expectPOST('http://MOCKURL.com/model2').respond({ name: 'toto', _id: '1111' }); - httpBackend.expectPOST('http://MOCKURL.com/model1').respond({ label: 'toto' }); - model.saveDeep(); - httpBackend.flush(); - expect(model.model2._id).toEqual('1111') - }); - - it('Should make a query with populate', function () { - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model1?populate=[{"path":"model2"}]')).respond([]); - ModelManager.get(ModelManager.query().populate('model2')); - httpBackend.flush(); - - }) - - it('Should not make a diff with dates', function () { - var model = ModelManager.create({ - _id: '007', - when: new Date().toISOString() - }) - model.save() - }) - - - it('Should not make a diff with JS dates', function () { - var model = ModelManager.create({ - _id: '007', - when: new Date().toISOString() - }) - model.when = new Date(model.when) - model.save() - }) - - it('Should make a diff with dates if needed', function () { - var model = ModelManager.create({ - _id: '007', - when: new Date() - }) - model.when = new Date(203939) - httpBackend.expectPUT('http://MOCKURL.com/model1/007', { - when: new Date(203939) - }).respond(); - model.save() - }) - - it('Should not make a diff between populated and unpopulated nested field', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888', - models2: [ '77777', '4444' ] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":{"$in":["77777","4444"]}}')).respond([ { _id: '77777' }, { _id: '4444' } ]); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate([ 'models2', 'model2' ]).then(function () { - expect(model.models2[ 0 ]._id).toEqual('77777'); - expect(model.models2[ 1 ]._id).toEqual('4444'); - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush() - model.save() - }); - - it('Should make a diff between populated and unpopulated nested field', function () { - var model = ModelManager.create({ - _id: '1234656', - model2: '888' - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2/888')).respond({ _id: '888' }); - model.populate([ 'models2', 'model2' ]).then(function () { - expect(model.model2._id).toEqual('888'); - }); - httpBackend.flush() - model.model2 = { _id: '999' } - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { - model2: '999' - }).respond(); - model.save() - }); - - it('Should make a diff between populated and unpopulated nested fields array', function () { - var model = ModelManager.create({ - _id: '1234656', - models2: ['888'] - }); - httpBackend.expectGET(encodeURI('http://MOCKURL.com/model2?conditions={"_id":"888"}')).respond([{ _id: '888' }]); - model.populate([ 'models2', 'model2' ]).then(function () { - expect(model.models2[0]._id).toEqual('888'); - }); - httpBackend.flush() - model.models2.push({ _id: '999' }) - httpBackend.expectPUT('http://MOCKURL.com/model1/1234656', { - models2: ['888', '999'] - }).respond(); - model.save() - }); - - it ('Should not save irrelevant fields', function(){ - var model = ModelManager.create({ - _id: '1234656', - models2: ['888'] - }); - model.iShouldntBeThere = 'but I am' - model.save() - }) - - - it('should not save irrelevant fields, but still save others', function () { - var model = ModelManager.createModel({ - _id: '123456', - model2: '7777', - label: 'toto' - }) - model.label = 'tutu' - model.iShouldntBeThere = 'but I am' - httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { label: 'tutu' }).respond() - model.save() - httpBackend.flush() - }) - -}); From aff97edb28e7784a5659b3724d3df1003d9b103f Mon Sep 17 00:00:00 2001 From: Julien Michel Date: Mon, 31 Oct 2016 16:39:05 +0100 Subject: [PATCH 12/26] fix issue with save on null nested field --- build/app.js | 4 +++- dest/temp/ActiveRecord.js | 4 +++- dest/temp/specs/angular-dao-save.specs.js | 15 +++++++++++++++ package.json | 2 +- src/ActiveRecord.js | 4 +++- tst/specs/angular-dao-save.specs.js | 15 +++++++++++++++ 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/build/app.js b/build/app.js index ececda3..7339b79 100644 --- a/build/app.js +++ b/build/app.js @@ -315,7 +315,9 @@ function ActiveRecord(model, name) { return e.beforeSave(null, { force: true }); }); } else { - obj[key] = obj[key].beforeSave(null, { force: true }); + if (obj[key] && obj[key] !== null) { + obj[key] = obj[key].beforeSave(null, { force: true }); + } } } else if (_.isDate(obj[key])) { /** Make sure the date is an ISOString */ diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 9da6211..39ad685 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -314,7 +314,9 @@ function ActiveRecord(model, name) { return e.beforeSave(null, { force: true }); }); } else { - obj[key] = obj[key].beforeSave(null, { force: true }); + if (obj[key] && obj[key] !== null) { + obj[key] = obj[key].beforeSave(null, { force: true }); + } } } else if (_.isDate(obj[key])) { /** Make sure the date is an ISOString */ diff --git a/dest/temp/specs/angular-dao-save.specs.js b/dest/temp/specs/angular-dao-save.specs.js index 0b4c209..41fd273 100644 --- a/dest/temp/specs/angular-dao-save.specs.js +++ b/dest/temp/specs/angular-dao-save.specs.js @@ -224,4 +224,19 @@ describe('Angular DAO', function () { model.save(); httpBackend.flush(); }); + + it('should allow to save a nested field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model3: { + _id: '23456', + name: 'toto' + } + }); + + model.model3 = null; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model3: null }).respond(); + model.save(); + httpBackend.flush(); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index 4be3653..d7d0c57 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-10", + "version": "2.0.0-alpha-11", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 7d28aa8..23acb43 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -268,7 +268,9 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod if (_.isArray(field)) { obj[ key ] = obj[ key ].map(e => e.beforeSave(null, { force: true })) } else { - obj[ key ] = obj[ key ].beforeSave(null, { force: true }) + if (obj[ key ] && obj[ key ] !== null) { + obj[ key ] = obj[ key ].beforeSave(null, { force: true }) + } } } else if (_.isDate(obj[ key ])) { /** Make sure the date is an ISOString */ diff --git a/tst/specs/angular-dao-save.specs.js b/tst/specs/angular-dao-save.specs.js index 216b7f6..2bd85ef 100644 --- a/tst/specs/angular-dao-save.specs.js +++ b/tst/specs/angular-dao-save.specs.js @@ -228,4 +228,19 @@ describe('Angular DAO', function () { httpBackend.flush() }) + it('should allow to save a nested field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model3: { + _id: '23456', + name: 'toto' + } + }) + + model.model3 = null + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model3: null}).respond() + model.save() + httpBackend.flush() + }) + }); From 13c912196a314198f27d94e9b73e16f1cf1072ab Mon Sep 17 00:00:00 2001 From: Julien Michel Date: Thu, 24 Nov 2016 11:04:29 +0100 Subject: [PATCH 13/26] add inheritence --- build/app.js | 248 ++++++++++++++++-- dest/temp/ActiveRecord.js | 4 + dest/temp/DaoHelper.js | 9 + dest/temp/Discriminator.js | 64 +++++ dest/temp/GenericDao.js | 28 +- dest/temp/app.js | 7 +- dest/temp/managers/tstManager4.js | 53 ++++ dest/temp/models/tstModel4.js | 54 ++++ .../specs/angular-dao-discriminators.specs.js | 58 ++++ package.json | 2 +- src/ActiveRecord.js | 1 + src/DaoHelper.js | 8 + src/Discriminator.js | 24 ++ src/GenericDao.js | 28 +- tst/app.js | 4 +- tst/managers/tstManager4.js | 14 + tst/models/tstModel4.js | 24 ++ tst/specs/angular-dao-discriminators.specs.js | 59 +++++ 18 files changed, 663 insertions(+), 26 deletions(-) create mode 100644 dest/temp/Discriminator.js create mode 100644 dest/temp/managers/tstManager4.js create mode 100644 dest/temp/models/tstModel4.js create mode 100644 dest/temp/specs/angular-dao-discriminators.specs.js create mode 100644 src/Discriminator.js create mode 100644 tst/managers/tstManager4.js create mode 100644 tst/models/tstModel4.js create mode 100644 tst/specs/angular-dao-discriminators.specs.js diff --git a/build/app.js b/build/app.js index 7339b79..094e573 100644 --- a/build/app.js +++ b/build/app.js @@ -18,6 +18,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'd function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } +var _Discriminator = require('./Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); @@ -479,7 +483,7 @@ function ActiveRecord(model, name) { } module.exports = exports['default']; -},{"./ServiceLocator":5,"./SessionManager":6,"deep-diff":14}],2:[function(require,module,exports){ +},{"./Discriminator":3,"./ServiceLocator":6,"./SessionManager":7,"deep-diff":17}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -531,6 +535,15 @@ var DaoHelper = (function () { value: function setRootUrl(url) { this.dao.url = url; } + }, { + key: 'setDiscriminatorUrl', + value: function setDiscriminatorUrl(type, url) { + _.each(this.dao.discriminators, function (discriminator) { + if (discriminator.type === type) { + discriminator.discriminatorUrl = url; + } + }); + } }, { key: '$get', value: function $get() { @@ -555,7 +568,72 @@ var DaoHelper = (function () { exports['default'] = DaoHelper; module.exports = exports['default']; -},{"./ServiceLocator":5}],3:[function(require,module,exports){ +},{"./ServiceLocator":6}],3:[function(require,module,exports){ +//_ = require('lodash'); +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +exports['default'] = Discriminator; +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + +function Discriminator(Model, type, url) { + + var Discriminator = (function (_Model) { + _inherits(Discriminator, _Model); + + _createClass(Discriminator, null, [{ + key: 'type', + get: function get() { + return type; + } + }]); + + function Discriminator($injector, rootUrl, options) { + _classCallCheck(this, Discriminator); + + _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).call(this, $injector, rootUrl, options); + this.rootUrl = url; + } + + // static get discriminatorUrl(){ + // return url + // } + return Discriminator; + })(Model); + + return Discriminator; +} + +module.exports = exports['default']; +},{"./ServiceLocator":6,"./SessionManager":7}],4:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -582,7 +660,7 @@ var _QueryBuilder = require('./QueryBuilder'); var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); -function GenericDao(model, qb) { +function GenericDao(model, qb, discriminators) { var sl = _ServiceLocator2['default'].instance; sl.registerModel(model.getName(), model); var myClass = (function () { @@ -593,6 +671,7 @@ function GenericDao(model, qb) { // this.$http = $injector.get('$http'); this.url = url; this.model = model; + this.discriminators = discriminators; } _createClass(myClass, [{ @@ -652,7 +731,15 @@ function GenericDao(model, qb) { if (Array.isArray(data)) { return data.map(this.build, this); } - return new model(this.$injector, this.url, data); + var url = this.url; + if (data.__t) { + _.each(this.discriminators, function (discriminator) { + if (discriminator.type == data.__t) { + url = discriminator.discriminatorUrl; + } + }); + } + return new model(this.$injector, url, data); } }, { key: 'post', @@ -687,8 +774,21 @@ function GenericDao(model, qb) { }, { key: 'create', value: function create(params) { - return new this.model(this.$injector, this.url, params); + var url = this.url; + if (params.__t) { + _.each(this.discriminators, function (discriminator) { + if (discriminator.type == params.__t) { + + url = discriminator.discriminatorUrl; + } + }); + } + return new this.model(this.$injector, url, params); } + + // get discriminators(){ + // return discriminators + // } }, { key: '$http', get: function get() { @@ -761,7 +861,7 @@ function GenericDao(model, qb) { } module.exports = exports['default']; -},{"./QueryBuilder":4,"./ServiceLocator":5}],4:[function(require,module,exports){ +},{"./QueryBuilder":5,"./ServiceLocator":6}],5:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -923,7 +1023,7 @@ var QueryBuilder = (function () { exports['default'] = QueryBuilder; module.exports = exports['default']; -},{}],5:[function(require,module,exports){ +},{}],6:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { @@ -993,7 +1093,7 @@ var ServiceLocator = (function () { exports["default"] = ServiceLocator; module.exports = exports["default"]; -},{}],6:[function(require,module,exports){ +},{}],7:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1054,7 +1154,7 @@ function SessionManager(model) { } module.exports = exports['default']; -},{"./ServiceLocator":5}],7:[function(require,module,exports){ +},{"./ServiceLocator":6}],8:[function(require,module,exports){ 'use strict'; // istanbul ignore next @@ -1077,12 +1177,17 @@ var _managersTstManager3 = require('./managers/tstManager3'); var _managersTstManager32 = _interopRequireDefault(_managersTstManager3); +var _managersTstManager4 = require('./managers/tstManager4'); + +var _managersTstManager42 = _interopRequireDefault(_managersTstManager4); + var _module = angular.module('tstModule', []); _DaoHelper2['default'].registerService(_module, 'ModelManager', _managersTstManager12['default']); _DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); _DaoHelper2['default'].registerService(_module, 'ModelManager3', _managersTstManager32['default']); -},{"./DaoHelper":2,"./managers/tstManager1":8,"./managers/tstManager2":9,"./managers/tstManager3":10}],8:[function(require,module,exports){ +_DaoHelper2['default'].registerService(_module, 'ModelManager4', _managersTstManager42['default']); +},{"./DaoHelper":2,"./managers/tstManager1":9,"./managers/tstManager2":10,"./managers/tstManager3":11,"./managers/tstManager4":12}],9:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1133,7 +1238,7 @@ var ModelManager = (function (_DAO) { exports['default'] = ModelManager; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel1.js":11}],9:[function(require,module,exports){ +},{"../GenericDao":4,"../QueryBuilder":5,"./../models/tstModel1.js":13}],10:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1184,7 +1289,7 @@ var ModelManager2 = (function (_DAO) { exports['default'] = ModelManager2; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel2.js":12}],10:[function(require,module,exports){ +},{"../GenericDao":4,"../QueryBuilder":5,"./../models/tstModel2.js":14}],11:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1235,7 +1340,61 @@ var ModelManager3 = (function (_DAO) { exports['default'] = ModelManager3; ; module.exports = exports['default']; -},{"../GenericDao":3,"../QueryBuilder":4,"./../models/tstModel3.js":13}],11:[function(require,module,exports){ +},{"../GenericDao":4,"../QueryBuilder":5,"./../models/tstModel3.js":15}],12:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel4Js = require('./../models/tstModel4.js'); + +var _modelsTstModel4Js2 = _interopRequireDefault(_modelsTstModel4Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _Discriminator = require('../Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + +var D1 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type1'); +var D2 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type2'); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel4Js2['default'], undefined, [D1, D2]); + +var ModelManager4 = (function (_DAO) { + _inherits(ModelManager4, _DAO); + + function ModelManager4() { + _classCallCheck(this, ModelManager4); + + _get(Object.getPrototypeOf(ModelManager4.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager4; +})(DAO); + +exports['default'] = ModelManager4; +; +module.exports = exports['default']; +},{"../Discriminator":3,"../GenericDao":4,"./../models/tstModel4.js":16}],13:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1312,7 +1471,7 @@ var Model = (function (_AR) { exports['default'] = Model; module.exports = exports['default']; -},{"../ActiveRecord":1}],12:[function(require,module,exports){ +},{"../ActiveRecord":1}],14:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1365,7 +1524,7 @@ var Model2 = (function (_AR) { exports['default'] = Model2; module.exports = exports['default']; -},{"../ActiveRecord":1}],13:[function(require,module,exports){ +},{"../ActiveRecord":1}],15:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { @@ -1418,7 +1577,62 @@ var Model3 = (function (_AR) { exports['default'] = Model3; module.exports = exports['default']; -},{"../ActiveRecord":1}],14:[function(require,module,exports){ +},{"../ActiveRecord":1}],16:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String, + + __t: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model4'); + +var Model4 = (function (_AR) { + _inherits(Model4, _AR); + + function Model4() { + _classCallCheck(this, Model4); + + _get(Object.getPrototypeOf(Model4.prototype), 'constructor', this).apply(this, arguments); + } + + return Model4; +})(AR); + +exports['default'] = Model4; +module.exports = exports['default']; +},{"../ActiveRecord":1}],17:[function(require,module,exports){ (function (global){ /*! * deep-diff. @@ -1844,4 +2058,4 @@ module.exports = exports['default']; })); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}]},{},[7]); +},{}]},{},[8]); diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 39ad685..2dac47a 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -17,6 +17,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'd function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } +var _Discriminator = require('./Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + var _ServiceLocator = require('./ServiceLocator'); var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); diff --git a/dest/temp/DaoHelper.js b/dest/temp/DaoHelper.js index e654d49..9eb7518 100644 --- a/dest/temp/DaoHelper.js +++ b/dest/temp/DaoHelper.js @@ -49,6 +49,15 @@ var DaoHelper = (function () { value: function setRootUrl(url) { this.dao.url = url; } + }, { + key: 'setDiscriminatorUrl', + value: function setDiscriminatorUrl(type, url) { + _.each(this.dao.discriminators, function (discriminator) { + if (discriminator.type === type) { + discriminator.discriminatorUrl = url; + } + }); + } }, { key: '$get', value: function $get() { diff --git a/dest/temp/Discriminator.js b/dest/temp/Discriminator.js new file mode 100644 index 0000000..0c8974f --- /dev/null +++ b/dest/temp/Discriminator.js @@ -0,0 +1,64 @@ +//_ = require('lodash'); +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +exports['default'] = Discriminator; +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ServiceLocator = require('./ServiceLocator'); + +var _ServiceLocator2 = _interopRequireDefault(_ServiceLocator); + +var _SessionManager = require('./SessionManager'); + +var _SessionManager2 = _interopRequireDefault(_SessionManager); + +function Discriminator(Model, type, url) { + + var Discriminator = (function (_Model) { + _inherits(Discriminator, _Model); + + _createClass(Discriminator, null, [{ + key: 'type', + get: function get() { + return type; + } + }]); + + function Discriminator($injector, rootUrl, options) { + _classCallCheck(this, Discriminator); + + _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).call(this, $injector, rootUrl, options); + this.rootUrl = url; + } + + // static get discriminatorUrl(){ + // return url + // } + return Discriminator; + })(Model); + + return Discriminator; +} + +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/GenericDao.js b/dest/temp/GenericDao.js index 01c57a4..3421232 100644 --- a/dest/temp/GenericDao.js +++ b/dest/temp/GenericDao.js @@ -24,7 +24,7 @@ var _QueryBuilder = require('./QueryBuilder'); var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); -function GenericDao(model, qb) { +function GenericDao(model, qb, discriminators) { var sl = _ServiceLocator2['default'].instance; sl.registerModel(model.getName(), model); var myClass = (function () { @@ -35,6 +35,7 @@ function GenericDao(model, qb) { // this.$http = $injector.get('$http'); this.url = url; this.model = model; + this.discriminators = discriminators; } _createClass(myClass, [{ @@ -94,7 +95,15 @@ function GenericDao(model, qb) { if (Array.isArray(data)) { return data.map(this.build, this); } - return new model(this.$injector, this.url, data); + var url = this.url; + if (data.__t) { + _.each(this.discriminators, function (discriminator) { + if (discriminator.type == data.__t) { + url = discriminator.discriminatorUrl; + } + }); + } + return new model(this.$injector, url, data); } }, { key: 'post', @@ -129,8 +138,21 @@ function GenericDao(model, qb) { }, { key: 'create', value: function create(params) { - return new this.model(this.$injector, this.url, params); + var url = this.url; + if (params.__t) { + _.each(this.discriminators, function (discriminator) { + if (discriminator.type == params.__t) { + + url = discriminator.discriminatorUrl; + } + }); + } + return new this.model(this.$injector, url, params); } + + // get discriminators(){ + // return discriminators + // } }, { key: '$http', get: function get() { diff --git a/dest/temp/app.js b/dest/temp/app.js index 5f03486..6375294 100644 --- a/dest/temp/app.js +++ b/dest/temp/app.js @@ -20,8 +20,13 @@ var _managersTstManager3 = require('./managers/tstManager3'); var _managersTstManager32 = _interopRequireDefault(_managersTstManager3); +var _managersTstManager4 = require('./managers/tstManager4'); + +var _managersTstManager42 = _interopRequireDefault(_managersTstManager4); + var _module = angular.module('tstModule', []); _DaoHelper2['default'].registerService(_module, 'ModelManager', _managersTstManager12['default']); _DaoHelper2['default'].registerService(_module, 'ModelManager2', _managersTstManager22['default']); -_DaoHelper2['default'].registerService(_module, 'ModelManager3', _managersTstManager32['default']); \ No newline at end of file +_DaoHelper2['default'].registerService(_module, 'ModelManager3', _managersTstManager32['default']); +_DaoHelper2['default'].registerService(_module, 'ModelManager4', _managersTstManager42['default']); \ No newline at end of file diff --git a/dest/temp/managers/tstManager4.js b/dest/temp/managers/tstManager4.js new file mode 100644 index 0000000..7e718b0 --- /dev/null +++ b/dest/temp/managers/tstManager4.js @@ -0,0 +1,53 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _modelsTstModel4Js = require('./../models/tstModel4.js'); + +var _modelsTstModel4Js2 = _interopRequireDefault(_modelsTstModel4Js); + +var _GenericDao = require('../GenericDao'); + +var _GenericDao2 = _interopRequireDefault(_GenericDao); + +var _Discriminator = require('../Discriminator'); + +var _Discriminator2 = _interopRequireDefault(_Discriminator); + +var D1 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type1'); +var D2 = (0, _Discriminator2['default'])(_modelsTstModel4Js2['default'], 'Type2'); + +var DAO = (0, _GenericDao2['default'])(_modelsTstModel4Js2['default'], undefined, [D1, D2]); + +var ModelManager4 = (function (_DAO) { + _inherits(ModelManager4, _DAO); + + function ModelManager4() { + _classCallCheck(this, ModelManager4); + + _get(Object.getPrototypeOf(ModelManager4.prototype), 'constructor', this).apply(this, arguments); + } + + return ModelManager4; +})(DAO); + +exports['default'] = ModelManager4; +; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/models/tstModel4.js b/dest/temp/models/tstModel4.js new file mode 100644 index 0000000..3ff267d --- /dev/null +++ b/dest/temp/models/tstModel4.js @@ -0,0 +1,54 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +// istanbul ignore next + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +// istanbul ignore next + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +// istanbul ignore next + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +// istanbul ignore next + +function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var _ActiveRecord = require('../ActiveRecord'); + +var _ActiveRecord2 = _interopRequireDefault(_ActiveRecord); + +var model = { + + _id: { + type: String, + unique: true + }, + + //private: true + name: String, + + __t: String +}; + +var AR = (0, _ActiveRecord2['default'])(model, 'Model4'); + +var Model4 = (function (_AR) { + _inherits(Model4, _AR); + + function Model4() { + _classCallCheck(this, Model4); + + _get(Object.getPrototypeOf(Model4.prototype), 'constructor', this).apply(this, arguments); + } + + return Model4; +})(AR); + +exports['default'] = Model4; +module.exports = exports['default']; \ No newline at end of file diff --git a/dest/temp/specs/angular-dao-discriminators.specs.js b/dest/temp/specs/angular-dao-discriminators.specs.js new file mode 100644 index 0000000..39d45fb --- /dev/null +++ b/dest/temp/specs/angular-dao-discriminators.specs.js @@ -0,0 +1,58 @@ +'use strict'; + +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManager4Provider) { + ModelManager4Provider.setRootUrl('http://MOCKURL.com/model2'); + ModelManager4Provider.setDiscriminatorUrl('Type1', 'http://fakeurl/type1'); + ModelManager4Provider.setDiscriminatorUrl('Type2', 'http://fakeurl/type2'); + }); + + inject(function (_ModelManager4_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager4 = _ModelManager4_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }); + }); + + it('should be possible to instantiate discriminators', function () { + expect(ModelManager4.discriminators.length).toEqual(2); + expect(ModelManager4.discriminators[0].type).toEqual('Type1'); + expect(ModelManager4.discriminators[1].type).toEqual('Type2'); + expect(ModelManager4.discriminators[0].discriminatorUrl).toEqual('http://fakeurl/type1'); + expect(ModelManager4.discriminators[1].discriminatorUrl).toEqual('http://fakeurl/type2'); + }); + + it('should make GET queries on parent url', function () { + httpBackend.expectGET('http://MOCKURL.com/model2').respond(); + ModelManager4.get(); + httpBackend.flush(); + }); + + it('should instanciate discriminators according to __t value', function () { + // TODO : make a GET request, respond with two types, and check their urls + httpBackend.expectGET('http://MOCKURL.com/model2').respond([{ _id: 123, name: 'Type1', __t: 'Type1' }, { _id: 321, name: 'Type2', __t: 'Type2' }]); + + ModelManager4.get().then(function (data) { + expect(data.data[0].rootUrl).toEqual('http://fakeurl/type1'); + expect(data.data[1].rootUrl).toEqual('http://fakeurl/type2'); + }); + httpBackend.flush(); + }); + + it('should make POST queries on correct discriminator URL', function () { + var obj = ModelManager4.createModel({ _id: 123456, name: '12345', __t: 'Type1' }); + obj.name = '23456'; + httpBackend.expectPUT('http://fakeurl/type1/123456', { name: '23456' }).respond(); + obj.save(); + }); + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation(); + }); +}); \ No newline at end of file diff --git a/package.json b/package.json index d7d0c57..c06a2b8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-11", + "version": "2.0.0-alpha-12", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 23acb43..37f7cd5 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -1,4 +1,5 @@ //_ = require('lodash'); +import Discriminator from './Discriminator' import ServiceLocator from './ServiceLocator' import SessionManager from './SessionManager' var deep = require('deep-diff').diff diff --git a/src/DaoHelper.js b/src/DaoHelper.js index d971d33..cabfef4 100644 --- a/src/DaoHelper.js +++ b/src/DaoHelper.js @@ -24,6 +24,14 @@ export default class DaoHelper{ this.dao.url = url; } + setDiscriminatorUrl(type, url) { + _.each(this.dao.discriminators, (discriminator) => { + if (discriminator.type === type) { + discriminator.discriminatorUrl = url + } + }) + } + $get(){ return this.dao; } diff --git a/src/Discriminator.js b/src/Discriminator.js new file mode 100644 index 0000000..f2a8588 --- /dev/null +++ b/src/Discriminator.js @@ -0,0 +1,24 @@ +//_ = require('lodash'); +import ServiceLocator from './ServiceLocator' +import SessionManager from './SessionManager' + + +export default function Discriminator (Model, type, url) { + + let Discriminator = class extends Model{ + static get type (){ + return type + } + + constructor ($injector, rootUrl, options) { + super($injector, rootUrl, options); + this.rootUrl = url + } + + // static get discriminatorUrl(){ + // return url + // } + }; + + return Discriminator; +} diff --git a/src/GenericDao.js b/src/GenericDao.js index f4a7372..e4e440e 100644 --- a/src/GenericDao.js +++ b/src/GenericDao.js @@ -1,7 +1,7 @@ import ServiceLocator from './ServiceLocator'; import QueryBuilder from './QueryBuilder'; -export default function GenericDao(model, qb){ +export default function GenericDao(model, qb, discriminators){ let sl = ServiceLocator.instance; sl.registerModel(model.getName(), model); let myClass = class { @@ -10,6 +10,7 @@ export default function GenericDao(model, qb){ // this.$http = $injector.get('$http'); this.url = url; this.model = model + this.discriminators = discriminators } get $http(){ @@ -58,7 +59,15 @@ export default function GenericDao(model, qb){ if (Array.isArray(data)){ return data.map(this.build, this); } - return new model(this.$injector, this.url, data); + let url = this.url; + if (data.__t) { + _.each(this.discriminators, function (discriminator) { + if (discriminator.type == data.__t) { + url = discriminator.discriminatorUrl + } + }); + } + return new model(this.$injector, url, data); } post(qb = this.query()){ @@ -85,8 +94,21 @@ export default function GenericDao(model, qb){ } create(params){ - return new this.model(this.$injector, this.url, params); + let url = this.url; + if (params.__t) { + _.each(this.discriminators, function (discriminator) { + if (discriminator.type == params.__t) { + + url = discriminator.discriminatorUrl + } + }); + } + return new this.model(this.$injector, url, params); } + + // get discriminators(){ + // return discriminators + // } }; diff --git a/tst/app.js b/tst/app.js index 1ab3a14..1249e7e 100644 --- a/tst/app.js +++ b/tst/app.js @@ -4,6 +4,7 @@ import DaoHelper from './DaoHelper'; import ModelManager from './managers/tstManager1'; import ModelManager2 from './managers/tstManager2'; import ModelManager3 from './managers/tstManager3'; +import ModelManager4 from './managers/tstManager4'; var module = angular @@ -11,4 +12,5 @@ var module = angular DaoHelper.registerService(module, 'ModelManager', ModelManager); DaoHelper.registerService(module, 'ModelManager2', ModelManager2); -DaoHelper.registerService(module, 'ModelManager3', ModelManager3); \ No newline at end of file +DaoHelper.registerService(module, 'ModelManager3', ModelManager3); +DaoHelper.registerService(module, 'ModelManager4', ModelManager4); \ No newline at end of file diff --git a/tst/managers/tstManager4.js b/tst/managers/tstManager4.js new file mode 100644 index 0000000..a94e810 --- /dev/null +++ b/tst/managers/tstManager4.js @@ -0,0 +1,14 @@ +'use strict'; + +import Model4 from './../models/tstModel4.js'; +import GenericDao from '../GenericDao'; +import Discriminator from '../Discriminator' + +var D1 = Discriminator(Model4, 'Type1') +var D2 = Discriminator(Model4, 'Type2') + + +var DAO = GenericDao(Model4, undefined, [D1, D2]); + +export default class ModelManager4 extends DAO { +}; diff --git a/tst/models/tstModel4.js b/tst/models/tstModel4.js new file mode 100644 index 0000000..f0e0432 --- /dev/null +++ b/tst/models/tstModel4.js @@ -0,0 +1,24 @@ +'use strict'; + + +import ActiveRecord from '../ActiveRecord' + + +var model = { + + _id: { + type : String, + unique: true, + //private: true + }, +  + name: String, + + __t: String +}; + + +var AR = ActiveRecord(model, 'Model4'); + +export default class Model4 extends AR {} + diff --git a/tst/specs/angular-dao-discriminators.specs.js b/tst/specs/angular-dao-discriminators.specs.js new file mode 100644 index 0000000..d09eee6 --- /dev/null +++ b/tst/specs/angular-dao-discriminators.specs.js @@ -0,0 +1,59 @@ +describe('Angular DAO', function () { + + var ModelManager, ModelManager3, httpBackend, $rootScope, $timeout; + + beforeEach(function () { + + module('tstModule', function (ModelManager4Provider) { + ModelManager4Provider.setRootUrl('http://MOCKURL.com/model2'); + ModelManager4Provider.setDiscriminatorUrl('Type1', 'http://fakeurl/type1'); + ModelManager4Provider.setDiscriminatorUrl('Type2', 'http://fakeurl/type2'); + }); + + inject(function (_ModelManager4_, $httpBackend, _$rootScope_, _$timeout_) { + ModelManager4 = _ModelManager4_; + httpBackend = $httpBackend; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + }) + }); + + it ('should be possible to instantiate discriminators', function(){ + expect(ModelManager4.discriminators.length).toEqual(2) + expect(ModelManager4.discriminators[0].type).toEqual('Type1') + expect(ModelManager4.discriminators[1].type).toEqual('Type2') + expect(ModelManager4.discriminators[0].discriminatorUrl).toEqual('http://fakeurl/type1') + expect(ModelManager4.discriminators[1].discriminatorUrl).toEqual('http://fakeurl/type2') + }) + + it ('should make GET queries on parent url', function(){ + httpBackend.expectGET('http://MOCKURL.com/model2').respond(); + ModelManager4.get(); + httpBackend.flush(); + }) + + it ('should instanciate discriminators according to __t value', function(){ + // TODO : make a GET request, respond with two types, and check their urls + httpBackend.expectGET('http://MOCKURL.com/model2').respond([{_id: 123, name: 'Type1', __t: 'Type1'}, {_id: 321, name: 'Type2', __t: 'Type2'}]); + + ModelManager4.get().then(function (data) { + expect(data.data[0].rootUrl).toEqual('http://fakeurl/type1'); + expect(data.data[1].rootUrl).toEqual('http://fakeurl/type2'); + }); + httpBackend.flush(); + }) + + it ('should make POST queries on correct discriminator URL', function () { + var obj = ModelManager4.createModel({_id: 123456, name: '12345', __t: 'Type1'}) + obj.name = '23456' + httpBackend.expectPUT('http://fakeurl/type1/123456', {name: '23456'}).respond() + obj.save() + }) + + + + afterEach(function () { + httpBackend.verifyNoOutstandingExpectation() + }); + +}); From c34372460f556706af8e9089f0c41ba6a3325597 Mon Sep 17 00:00:00 2001 From: Julien Michel Date: Thu, 24 Nov 2016 11:32:14 +0100 Subject: [PATCH 14/26] instanciate correct discriminator --- build/app.js | 47 +++++++++++++++----------------------- dest/temp/Discriminator.js | 18 ++++++--------- dest/temp/GenericDao.js | 29 ++++++++++------------- src/Discriminator.js | 11 +-------- src/GenericDao.js | 29 ++++++++++------------- 5 files changed, 51 insertions(+), 83 deletions(-) diff --git a/build/app.js b/build/app.js index 094e573..5c61ad0 100644 --- a/build/app.js +++ b/build/app.js @@ -604,11 +604,17 @@ var _SessionManager = require('./SessionManager'); var _SessionManager2 = _interopRequireDefault(_SessionManager); -function Discriminator(Model, type, url) { +function Discriminator(Model, type) { var Discriminator = (function (_Model) { _inherits(Discriminator, _Model); + function Discriminator() { + _classCallCheck(this, Discriminator); + + _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).apply(this, arguments); + } + _createClass(Discriminator, null, [{ key: 'type', get: function get() { @@ -616,16 +622,6 @@ function Discriminator(Model, type, url) { } }]); - function Discriminator($injector, rootUrl, options) { - _classCallCheck(this, Discriminator); - - _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).call(this, $injector, rootUrl, options); - this.rootUrl = url; - } - - // static get discriminatorUrl(){ - // return url - // } return Discriminator; })(Model); @@ -731,15 +727,13 @@ function GenericDao(model, qb, discriminators) { if (Array.isArray(data)) { return data.map(this.build, this); } - var url = this.url; - if (data.__t) { - _.each(this.discriminators, function (discriminator) { - if (discriminator.type == data.__t) { - url = discriminator.discriminatorUrl; - } - }); + if (this.discriminators && data.__t) { + var disc = _.find(this.discriminators, { type: data.__t }); + if (disc) { + return new disc(this.$injector, disc.discriminatorUrl, data); + } } - return new model(this.$injector, url, data); + return new model(this.$injector, this.url, data); } }, { key: 'post', @@ -774,16 +768,13 @@ function GenericDao(model, qb, discriminators) { }, { key: 'create', value: function create(params) { - var url = this.url; - if (params.__t) { - _.each(this.discriminators, function (discriminator) { - if (discriminator.type == params.__t) { - - url = discriminator.discriminatorUrl; - } - }); + if (this.discriminators && params.__t) { + var disc = _.find(this.discriminators, { type: params.__t }); + if (disc) { + return new disc(this.$injector, disc.discriminatorUrl, params); + } } - return new this.model(this.$injector, url, params); + return new this.model(this.$injector, this.url, params); } // get discriminators(){ diff --git a/dest/temp/Discriminator.js b/dest/temp/Discriminator.js index 0c8974f..120325a 100644 --- a/dest/temp/Discriminator.js +++ b/dest/temp/Discriminator.js @@ -33,11 +33,17 @@ var _SessionManager = require('./SessionManager'); var _SessionManager2 = _interopRequireDefault(_SessionManager); -function Discriminator(Model, type, url) { +function Discriminator(Model, type) { var Discriminator = (function (_Model) { _inherits(Discriminator, _Model); + function Discriminator() { + _classCallCheck(this, Discriminator); + + _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).apply(this, arguments); + } + _createClass(Discriminator, null, [{ key: 'type', get: function get() { @@ -45,16 +51,6 @@ function Discriminator(Model, type, url) { } }]); - function Discriminator($injector, rootUrl, options) { - _classCallCheck(this, Discriminator); - - _get(Object.getPrototypeOf(Discriminator.prototype), 'constructor', this).call(this, $injector, rootUrl, options); - this.rootUrl = url; - } - - // static get discriminatorUrl(){ - // return url - // } return Discriminator; })(Model); diff --git a/dest/temp/GenericDao.js b/dest/temp/GenericDao.js index 3421232..5475d52 100644 --- a/dest/temp/GenericDao.js +++ b/dest/temp/GenericDao.js @@ -95,15 +95,13 @@ function GenericDao(model, qb, discriminators) { if (Array.isArray(data)) { return data.map(this.build, this); } - var url = this.url; - if (data.__t) { - _.each(this.discriminators, function (discriminator) { - if (discriminator.type == data.__t) { - url = discriminator.discriminatorUrl; - } - }); + if (this.discriminators && data.__t) { + var disc = _.find(this.discriminators, { type: data.__t }); + if (disc) { + return new disc(this.$injector, disc.discriminatorUrl, data); + } } - return new model(this.$injector, url, data); + return new model(this.$injector, this.url, data); } }, { key: 'post', @@ -138,16 +136,13 @@ function GenericDao(model, qb, discriminators) { }, { key: 'create', value: function create(params) { - var url = this.url; - if (params.__t) { - _.each(this.discriminators, function (discriminator) { - if (discriminator.type == params.__t) { - - url = discriminator.discriminatorUrl; - } - }); + if (this.discriminators && params.__t) { + var disc = _.find(this.discriminators, { type: params.__t }); + if (disc) { + return new disc(this.$injector, disc.discriminatorUrl, params); + } } - return new this.model(this.$injector, url, params); + return new this.model(this.$injector, this.url, params); } // get discriminators(){ diff --git a/src/Discriminator.js b/src/Discriminator.js index f2a8588..75837f7 100644 --- a/src/Discriminator.js +++ b/src/Discriminator.js @@ -3,21 +3,12 @@ import ServiceLocator from './ServiceLocator' import SessionManager from './SessionManager' -export default function Discriminator (Model, type, url) { +export default function Discriminator (Model, type) { let Discriminator = class extends Model{ static get type (){ return type } - - constructor ($injector, rootUrl, options) { - super($injector, rootUrl, options); - this.rootUrl = url - } - - // static get discriminatorUrl(){ - // return url - // } }; return Discriminator; diff --git a/src/GenericDao.js b/src/GenericDao.js index e4e440e..56ca287 100644 --- a/src/GenericDao.js +++ b/src/GenericDao.js @@ -59,15 +59,13 @@ export default function GenericDao(model, qb, discriminators){ if (Array.isArray(data)){ return data.map(this.build, this); } - let url = this.url; - if (data.__t) { - _.each(this.discriminators, function (discriminator) { - if (discriminator.type == data.__t) { - url = discriminator.discriminatorUrl - } - }); + if (this.discriminators && data.__t) { + let disc = _.find(this.discriminators, {type: data.__t}); + if (disc) { + return new disc(this.$injector, disc.discriminatorUrl, data); + } } - return new model(this.$injector, url, data); + return new model(this.$injector, this.url, data); } post(qb = this.query()){ @@ -94,16 +92,13 @@ export default function GenericDao(model, qb, discriminators){ } create(params){ - let url = this.url; - if (params.__t) { - _.each(this.discriminators, function (discriminator) { - if (discriminator.type == params.__t) { - - url = discriminator.discriminatorUrl - } - }); + if (this.discriminators && params.__t) { + let disc = _.find(this.discriminators,{type: params.__t}); + if (disc) { + return new disc(this.$injector, disc.discriminatorUrl, params); + } } - return new this.model(this.$injector, url, params); + return new this.model(this.$injector, this.url, params); } // get discriminators(){ From 395a79059a87f0b61756814ab3319881d129d5d6 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Sat, 17 Dec 2016 10:20:16 +0100 Subject: [PATCH 15/26] issue a warning but don't crash when nested object is not an active record --- build/app.js | 12 ++++++++++-- dest/temp/ActiveRecord.js | 12 ++++++++++-- src/ActiveRecord.js | 16 ++++++++++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/build/app.js b/build/app.js index 5c61ad0..23b82fd 100644 --- a/build/app.js +++ b/build/app.js @@ -116,10 +116,18 @@ function ActiveRecord(model, name) { } else if (field.nested || _.isArray(field) && field[0].nested) { if (_.isArray(field)) { toSave = _this[key].map(function (e) { - return e.beforeSave(null, { force: true }); + if (e.beforeSave) return e.beforeSave(null, { force: true });else { + console.warn('The values at ' + key + ' should be an ActiveRecord instance for diff purpose'); + return e; + } }); } else { - toSave = _this[key].beforeSave(null, { force: true }); + if (_this[key].beforeSave) { + toSave = _this[key].beforeSave(null, { force: true }); + } else { + toSave = _this[key]; + console.warn('The value at ' + key + ' should be an ActiveRecord instance for diff purpose'); + } } } else { toSave = _this[key]; diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 2dac47a..94524c0 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -115,10 +115,18 @@ function ActiveRecord(model, name) { } else if (field.nested || _.isArray(field) && field[0].nested) { if (_.isArray(field)) { toSave = _this[key].map(function (e) { - return e.beforeSave(null, { force: true }); + if (e.beforeSave) return e.beforeSave(null, { force: true });else { + console.warn('The values at ' + key + ' should be an ActiveRecord instance for diff purpose'); + return e; + } }); } else { - toSave = _this[key].beforeSave(null, { force: true }); + if (_this[key].beforeSave) { + toSave = _this[key].beforeSave(null, { force: true }); + } else { + toSave = _this[key]; + console.warn('The value at ' + key + ' should be an ActiveRecord instance for diff purpose'); + } } } else { toSave = _this[key]; diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 37f7cd5..6f0976a 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -79,9 +79,21 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod } } else if (field.nested || (_.isArray(field) && field[ 0 ].nested)) { if (_.isArray(field)){ - toSave = this[ key ].map(e => e.beforeSave(null, {force: true})) + toSave = this[ key ].map(e => { + if (e.beforeSave) + return e.beforeSave(null, {force: true}) + else { + console.warn(`The values at ${key} should be an ActiveRecord instance for diff purpose`); + return e; + } + }) } else { - toSave = this[ key ].beforeSave(null, {force: true}) + if (this[ key ].beforeSave){ + toSave = this[ key ].beforeSave(null, {force: true}) + } else { + toSave = this[ key ] + console.warn(`The value at ${key} should be an ActiveRecord instance for diff purpose`); + } } } else { toSave = this[ key ] From a735266c4618a3c299ca8f64692919fb78165e69 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Sat, 17 Dec 2016 10:22:41 +0100 Subject: [PATCH 16/26] up version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c06a2b8..4f80703 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-12", + "version": "2.0.0-alpha-13", "description": "", "homepage": "", "dependencies": { From efa09c26fa74532a6e679d3f2bea9fa2b7240110 Mon Sep 17 00:00:00 2001 From: Julien Michel Date: Wed, 18 Jan 2017 15:41:22 +0100 Subject: [PATCH 17/26] fix issue when saving populated field as null --- build/app.js | 2 +- dest/temp/ActiveRecord.js | 2 +- dest/temp/specs/angular-dao-save.specs.js | 15 +++++++++++++++ package.json | 2 +- src/ActiveRecord.js | 2 +- tst/specs/angular-dao-save.specs.js | 15 +++++++++++++++ 6 files changed, 34 insertions(+), 4 deletions(-) diff --git a/build/app.js b/build/app.js index 1b039e0..264890b 100644 --- a/build/app.js +++ b/build/app.js @@ -315,7 +315,7 @@ function ActiveRecord(model, name) { })); } else { /** Transforms just a single ref into its _id if needed */ - obj[key] = obj[key]._id || obj[key]; + obj[key] = _.get(obj, [key, '_id']) || obj[key]; } /** Nested SubModel, pass it through beforeSave() so that * only relevant fields are kept diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 94524c0..6441bf3 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -314,7 +314,7 @@ function ActiveRecord(model, name) { })); } else { /** Transforms just a single ref into its _id if needed */ - obj[key] = obj[key]._id || obj[key]; + obj[key] = _.get(obj, [key, '_id']) || obj[key]; } /** Nested SubModel, pass it through beforeSave() so that * only relevant fields are kept diff --git a/dest/temp/specs/angular-dao-save.specs.js b/dest/temp/specs/angular-dao-save.specs.js index 41fd273..406a120 100644 --- a/dest/temp/specs/angular-dao-save.specs.js +++ b/dest/temp/specs/angular-dao-save.specs.js @@ -239,4 +239,19 @@ describe('Angular DAO', function () { model.save(); httpBackend.flush(); }); + + it('should allow to save a populated field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: { + _id: '234567', + name: 'toto' + } + }); + + model.model2 = null; + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model2: null }).respond(); + model.save(); + httpBackend.flush(); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index 8b53f8b..39af000 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-14", + "version": "2.0.0-alpha-15", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 6f0976a..d97541e 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -271,7 +271,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod })); } else { /** Transforms just a single ref into its _id if needed */ - obj[ key ] = obj[ key ]._id || obj[ key ]; + obj[ key ] = _.get(obj, [ key , '_id']) || obj[ key ]; } /** Nested SubModel, pass it through beforeSave() so that * only relevant fields are kept diff --git a/tst/specs/angular-dao-save.specs.js b/tst/specs/angular-dao-save.specs.js index 2bd85ef..92002d4 100644 --- a/tst/specs/angular-dao-save.specs.js +++ b/tst/specs/angular-dao-save.specs.js @@ -243,4 +243,19 @@ describe('Angular DAO', function () { httpBackend.flush() }) + it('should allow to save a populated field as null', function () { + var model = ModelManager.createModel({ + _id: '123456', + model2: { + _id: '234567', + name: 'toto' + } + }) + + model.model2 = null + httpBackend.expectPUT('http://MOCKURL.com/model1/123456', { model2: null }).respond() + model.save() + httpBackend.flush() + }) + }); From c34016234b80f9b463878a503981580b54e04cf5 Mon Sep 17 00:00:00 2001 From: Julien Michel Date: Thu, 19 Jan 2017 18:04:39 +0100 Subject: [PATCH 18/26] fix version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 39af000..1f74a8a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0-alpha-15", + "version": "2.0.0", "description": "", "homepage": "", "dependencies": { From a828f854b6ac632de47b1f6ae0094a4e27094869 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Fri, 20 Jan 2017 14:53:55 +0100 Subject: [PATCH 19/26] fix arrays with undefined value --- build/app.js | 10 +++++++--- dest/temp/ActiveRecord.js | 10 +++++++--- dest/temp/specs/angular-dao-base.specs.js | 8 ++++++++ package.json | 2 +- src/ActiveRecord.js | 6 ++++-- tst/specs/angular-dao-base.specs.js | 7 +++++++ 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/build/app.js b/build/app.js index 264890b..cae6f15 100644 --- a/build/app.js +++ b/build/app.js @@ -107,7 +107,9 @@ function ActiveRecord(model, name) { /** Unless it is marked nested */ !(field.nested || _.isArray(field) && field[0].nested)) { if (_.isArray(field)) { - toSave = _this[key].map(function (entry) { + toSave = _this[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (entry) { return typeof entry === 'string' ? entry : entry._id; }); } else { @@ -323,8 +325,10 @@ function ActiveRecord(model, name) { * */ } else if (field.nested || _.isArray(field) && field[0].nested) { if (_.isArray(field)) { - obj[key] = obj[key].map(function (e) { - return e.beforeSave(null, { force: true }); + obj[key] = obj[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (e) { + return e.beforeSave ? e.beforeSave(null, { force: true }) : e; }); } else { if (obj[key] && obj[key] !== null) { diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 6441bf3..cec8316 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -106,7 +106,9 @@ function ActiveRecord(model, name) { /** Unless it is marked nested */ !(field.nested || _.isArray(field) && field[0].nested)) { if (_.isArray(field)) { - toSave = _this[key].map(function (entry) { + toSave = _this[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (entry) { return typeof entry === 'string' ? entry : entry._id; }); } else { @@ -322,8 +324,10 @@ function ActiveRecord(model, name) { * */ } else if (field.nested || _.isArray(field) && field[0].nested) { if (_.isArray(field)) { - obj[key] = obj[key].map(function (e) { - return e.beforeSave(null, { force: true }); + obj[key] = obj[key].filter(function (e) { + return !_.isUndefined(e); + }).map(function (e) { + return e.beforeSave ? e.beforeSave(null, { force: true }) : e; }); } else { if (obj[key] && obj[key] !== null) { diff --git a/dest/temp/specs/angular-dao-base.specs.js b/dest/temp/specs/angular-dao-base.specs.js index c92b969..7f10126 100644 --- a/dest/temp/specs/angular-dao-base.specs.js +++ b/dest/temp/specs/angular-dao-base.specs.js @@ -87,4 +87,12 @@ describe('Angular DAO Basics', function () { }); httpBackend.flush(); }); + + it('Should not fail when a nested property is undefined', function () { + var m = ModelManager.create({ + _id: "111", + models2: [{ _id: "121212" }, undefined, { _id: null }, null], + model2: undefined + }); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index 1f74a8a..08786c4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.0", + "version": "2.0.1", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index d97541e..d5ec9d7 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -73,7 +73,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod /** Unless it is marked nested */ && !(field.nested || (_.isArray(field) && field[ 0 ].nested))) { if (_.isArray(field)) { - toSave = this[ key ].map(entry => (typeof entry === 'string') ? entry : entry._id) + toSave = this[ key ].filter(e => !_.isUndefined(e)).map(entry => (typeof entry === 'string') ? entry : entry._id) } else { toSave = typeof this[ key ] === 'string' ? this[ key ] : this[ key ]._id } @@ -279,7 +279,9 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod * */ } else if (field.nested || (_.isArray(field) && field[ 0 ].nested )) { if (_.isArray(field)) { - obj[ key ] = obj[ key ].map(e => e.beforeSave(null, { force: true })) + obj[ key ] = obj[ key ] + .filter(e => !_.isUndefined(e)) + .map(e => e.beforeSave ? e.beforeSave(null, { force: true }) : e) } else { if (obj[ key ] && obj[ key ] !== null) { obj[ key ] = obj[ key ].beforeSave(null, { force: true }) diff --git a/tst/specs/angular-dao-base.specs.js b/tst/specs/angular-dao-base.specs.js index 31c9d0c..247c307 100644 --- a/tst/specs/angular-dao-base.specs.js +++ b/tst/specs/angular-dao-base.specs.js @@ -86,7 +86,14 @@ describe('Angular DAO Basics', function () { expect(data).toEqual({data: []}) }); httpBackend.flush(); + }) + it ('Should not fail when a nested property is undefined', function(){ + var m = ModelManager.create({ + _id: "111", + models2: [{_id: "121212"}, undefined, {_id: null}, null], + model2: undefined + }) }) }); From 24e187d74383788f8c29ee5c19f0e8c18fc8a80f Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Thu, 27 Apr 2017 16:58:40 +0200 Subject: [PATCH 20/26] add ability to pass options to populate and getById methods --- build/app.js | 25 ++++++++----------------- dest/temp/ActiveRecord.js | 6 +++--- dest/temp/GenericDao.js | 19 +++++-------------- package.json | 2 +- src/ActiveRecord.js | 6 +++--- src/GenericDao.js | 15 ++++----------- 6 files changed, 24 insertions(+), 49 deletions(-) diff --git a/build/app.js b/build/app.js index cae6f15..965c554 100644 --- a/build/app.js +++ b/build/app.js @@ -201,7 +201,7 @@ function ActiveRecord(model, name) { } }, { key: 'populate', - value: function populate(field, query) { + value: function populate(field, query, opts) { // istanbul ignore next var _this2 = this; @@ -237,7 +237,7 @@ function ActiveRecord(model, name) { if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then(function (d) { + return dao.get(dao.query(query).select(grouped.string), opts).then(function (d) { /** To preserve order, we map the existing field, replacing only the populated values */ self[field] = self[field].map(function (f) { if (typeof f === 'string') { @@ -264,7 +264,7 @@ function ActiveRecord(model, name) { if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.getById(this[field]).then(function (sub) { + return dao.getById(this[field], query, opts).then(function (sub) { self[field] = sub; return self; }); @@ -808,19 +808,17 @@ function GenericDao(model, qb, discriminators) { var _this2 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query : arguments[1]; + var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - return this.$http.get(this.url + '/' + value, { params: qb.opts }).then(function (data) { + return this.$http.get(this.url + '/' + value, _.merge(opts, { params: qb.opts })).then(function (data) { return new model(_this2.$injector, _this2.url, data.data); }); }; } else { myClass.prototype['selectBy' + _.capitalize(key)] = function (toSelect) { - // istanbul ignore next - - var _this3 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; if (toSelect && toSelect.length) { if (value.ref) { @@ -836,14 +834,7 @@ function GenericDao(model, qb, discriminators) { } qb.setQuery(obj); } - return this.$http.get(this.url, { params: qb.opts }).then(function (data) { - if (!data.data) { - data.data = []; - } - return { - data: data.data.map(_this3.build, _this3), meta: { total: data.headers('X-Total-Count') } - }; - }); + return this.get(qb, opts); }; } //myClass.prototype diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index cec8316..604ef13 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -200,7 +200,7 @@ function ActiveRecord(model, name) { } }, { key: 'populate', - value: function populate(field, query) { + value: function populate(field, query, opts) { // istanbul ignore next var _this2 = this; @@ -236,7 +236,7 @@ function ActiveRecord(model, name) { if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then(function (d) { + return dao.get(dao.query(query).select(grouped.string), opts).then(function (d) { /** To preserve order, we map the existing field, replacing only the populated values */ self[field] = self[field].map(function (f) { if (typeof f === 'string') { @@ -263,7 +263,7 @@ function ActiveRecord(model, name) { if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.getById(this[field]).then(function (sub) { + return dao.getById(this[field], query, opts).then(function (sub) { self[field] = sub; return self; }); diff --git a/dest/temp/GenericDao.js b/dest/temp/GenericDao.js index 66d96c2..0d8a78c 100644 --- a/dest/temp/GenericDao.js +++ b/dest/temp/GenericDao.js @@ -164,19 +164,17 @@ function GenericDao(model, qb, discriminators) { var _this2 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query : arguments[1]; + var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - return this.$http.get(this.url + '/' + value, { params: qb.opts }).then(function (data) { + return this.$http.get(this.url + '/' + value, _.merge(opts, { params: qb.opts })).then(function (data) { return new model(_this2.$injector, _this2.url, data.data); }); }; } else { myClass.prototype['selectBy' + _.capitalize(key)] = function (toSelect) { - // istanbul ignore next - - var _this3 = this; - var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; + var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; if (toSelect && toSelect.length) { if (value.ref) { @@ -192,14 +190,7 @@ function GenericDao(model, qb, discriminators) { } qb.setQuery(obj); } - return this.$http.get(this.url, { params: qb.opts }).then(function (data) { - if (!data.data) { - data.data = []; - } - return { - data: data.data.map(_this3.build, _this3), meta: { total: data.headers('X-Total-Count') } - }; - }); + return this.get(qb, opts); }; } //myClass.prototype diff --git a/package.json b/package.json index 08786c4..0cdd947 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.1", + "version": "2.0.2", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index d5ec9d7..df2d27c 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -171,7 +171,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod return this.$http.delete(this.rootUrl + '/' + this._id); } - populate (field, query) { + populate (field, query, opts) { var $q = this.$injector.get('$q'); if (Array.isArray(field)) { @@ -199,7 +199,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.get(dao.query(query).select(grouped.string)).then((d)=> { + return dao.get(dao.query(query).select(grouped.string), opts).then((d)=> { /** To preserve order, we map the existing field, replacing only the populated values */ self[ field ] = self[ field ].map((f) => { if (typeof f === 'string') { @@ -226,7 +226,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod if (!dao) { deferred.reject('Cannot Populate: unknown DAO'); } else { - return dao.getById(this[ field ]).then(function (sub) { + return dao.getById(this[ field ], query, opts).then(function (sub) { self[ field ] = sub; return self; }); diff --git a/src/GenericDao.js b/src/GenericDao.js index 8c7d344..2059eb6 100644 --- a/src/GenericDao.js +++ b/src/GenericDao.js @@ -111,13 +111,13 @@ export default function GenericDao(model, qb, discriminators){ _.forIn(model.getModel(), function(value, key){ var v = Array.isArray(value) ? value[0] : value; if (key === '_id'){ - myClass.prototype['findById'] = myClass.prototype['getById'] = function(value, qb = this.query){ - return this.$http.get(this.url + '/' + value, {params: qb.opts}).then((data)=>{ + myClass.prototype['findById'] = myClass.prototype['getById'] = function(value, qb = this.query(), opts = {}){ + return this.$http.get(this.url + '/' + value, _.merge(opts, {params: qb.opts})).then((data)=>{ return new model(this.$injector, this.url, data.data); }) } } else{ - myClass.prototype['selectBy' + _.capitalize(key)] = function(toSelect, qb = this.query()){ + myClass.prototype['selectBy' + _.capitalize(key)] = function(toSelect, qb = this.query(), opts={}){ if (toSelect && toSelect.length){ if (value.ref){ toSelect = extractId(toSelect); @@ -132,14 +132,7 @@ export default function GenericDao(model, qb, discriminators){ } qb.setQuery(obj); } - return this.$http.get(this.url, {params: qb.opts}).then((data)=>{ - if (!data.data) { - data.data = [] - } - return { - data: data.data.map(this.build, this), meta: {total: data.headers('X-Total-Count')} - }; - }) + return this.get(qb, opts) } } From c6aa765038dc58c578f162204b6e5abd3d3aeb82 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Thu, 27 Apr 2017 17:03:36 +0200 Subject: [PATCH 21/26] up version to 2.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0cdd947..2ce719d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.2", + "version": "2.0.3", "description": "", "homepage": "", "dependencies": { From e13de7f4c75e360f8fcda6da91e3a1c9380a79cf Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Wed, 24 May 2017 17:59:59 +0200 Subject: [PATCH 22/26] fix populate all returning nested --- package.json | 2 +- src/ActiveRecord.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2ce719d..da31b97 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.3", + "version": "2.0.4", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index df2d27c..a6248d4 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -377,8 +377,8 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod // Get from the model the fields that reference another object var pop = _.pick(model, function (v) { if (_.isArray(v)) - return v[ 0 ].ref; - return v.ref; + return v[ 0 ].ref && !v[ 0 ].nested; + return v.ref && !v.nested; }); if (populateArray === 'all') { From 8520a3db496917747fa718286974ef7e7e5d6bca Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Fri, 6 Oct 2017 16:24:21 +0200 Subject: [PATCH 23/26] better handling of options to enable headers --- src/GenericDao.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/GenericDao.js b/src/GenericDao.js index 2059eb6..a0795d3 100644 --- a/src/GenericDao.js +++ b/src/GenericDao.js @@ -34,12 +34,12 @@ export default function GenericDao(model, qb, discriminators){ return qb ? new qb(this, q) : new QueryBuilder(this, q); } - getHeaders(){ - + getHeaders(opts={}){ + return opts.headers } getOptions(opts={}){ - opts.headers = this.getHeaders(); + opts.headers = this.getHeaders(opts); return opts; } @@ -112,12 +112,14 @@ export default function GenericDao(model, qb, discriminators){ var v = Array.isArray(value) ? value[0] : value; if (key === '_id'){ myClass.prototype['findById'] = myClass.prototype['getById'] = function(value, qb = this.query(), opts = {}){ + opts = this.getOptions(opts) return this.$http.get(this.url + '/' + value, _.merge(opts, {params: qb.opts})).then((data)=>{ return new model(this.$injector, this.url, data.data); }) } } else{ myClass.prototype['selectBy' + _.capitalize(key)] = function(toSelect, qb = this.query(), opts={}){ + opts = this.getOptions(opts) if (toSelect && toSelect.length){ if (value.ref){ toSelect = extractId(toSelect); From 84cd5a94f7989049cbf53e36a128d747a3b2fea1 Mon Sep 17 00:00:00 2001 From: Julien Michel Date: Tue, 5 Jun 2018 11:09:49 +0200 Subject: [PATCH 24/26] fix beforeSave on ActiveRecord --- .gitignore | 3 ++- CHANGELOG.md | 4 ++++ build/app.js | 16 +++++++++++----- dest/temp/ActiveRecord.js | 6 +++--- dest/temp/GenericDao.js | 10 ++++++++-- package.json | 4 ++-- src/ActiveRecord.js | 2 +- 7 files changed, 31 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 088ccb8..e776e27 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Created by .ignore support plugin (hsz.mobi) bower_components node_modules -coverage \ No newline at end of file +coverage +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4fae5..e20a2ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2.0.5 + +- Fix `beforeSave` on object with nested model without `beforeSave` function + ## 2.0.0 - Diff on save diff --git a/build/app.js b/build/app.js index 965c554..b156b85 100644 --- a/build/app.js +++ b/build/app.js @@ -332,7 +332,7 @@ function ActiveRecord(model, name) { }); } else { if (obj[key] && obj[key] !== null) { - obj[key] = obj[key].beforeSave(null, { force: true }); + obj[key] = obj[key].beforeSave ? obj[key].beforeSave(null, { force: true }) : obj[key]; } } } else if (_.isDate(obj[key])) { @@ -461,8 +461,8 @@ function ActiveRecord(model, name) { // Get from the model the fields that reference another object var pop = _.pick(model, function (v) { - if (_.isArray(v)) return v[0].ref; - return v.ref; + if (_.isArray(v)) return v[0].ref && !v[0].nested; + return v.ref && !v.nested; }); if (populateArray === 'all') { @@ -704,13 +704,17 @@ function GenericDao(model, qb, discriminators) { } }, { key: 'getHeaders', - value: function getHeaders() {} + value: function getHeaders() { + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + return opts.headers; + } }, { key: 'getOptions', value: function getOptions() { var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - opts.headers = this.getHeaders(); + opts.headers = this.getHeaders(opts); return opts; } }, { @@ -811,6 +815,7 @@ function GenericDao(model, qb, discriminators) { var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + opts = this.getOptions(opts); return this.$http.get(this.url + '/' + value, _.merge(opts, { params: qb.opts })).then(function (data) { return new model(_this2.$injector, _this2.url, data.data); }); @@ -820,6 +825,7 @@ function GenericDao(model, qb, discriminators) { var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + opts = this.getOptions(opts); if (toSelect && toSelect.length) { if (value.ref) { toSelect = extractId(toSelect); diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 604ef13..bd08f44 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -331,7 +331,7 @@ function ActiveRecord(model, name) { }); } else { if (obj[key] && obj[key] !== null) { - obj[key] = obj[key].beforeSave(null, { force: true }); + obj[key] = obj[key].beforeSave ? obj[key].beforeSave(null, { force: true }) : obj[key]; } } } else if (_.isDate(obj[key])) { @@ -460,8 +460,8 @@ function ActiveRecord(model, name) { // Get from the model the fields that reference another object var pop = _.pick(model, function (v) { - if (_.isArray(v)) return v[0].ref; - return v.ref; + if (_.isArray(v)) return v[0].ref && !v[0].nested; + return v.ref && !v.nested; }); if (populateArray === 'all') { diff --git a/dest/temp/GenericDao.js b/dest/temp/GenericDao.js index 0d8a78c..badb987 100644 --- a/dest/temp/GenericDao.js +++ b/dest/temp/GenericDao.js @@ -60,13 +60,17 @@ function GenericDao(model, qb, discriminators) { } }, { key: 'getHeaders', - value: function getHeaders() {} + value: function getHeaders() { + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + return opts.headers; + } }, { key: 'getOptions', value: function getOptions() { var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - opts.headers = this.getHeaders(); + opts.headers = this.getHeaders(opts); return opts; } }, { @@ -167,6 +171,7 @@ function GenericDao(model, qb, discriminators) { var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + opts = this.getOptions(opts); return this.$http.get(this.url + '/' + value, _.merge(opts, { params: qb.opts })).then(function (data) { return new model(_this2.$injector, _this2.url, data.data); }); @@ -176,6 +181,7 @@ function GenericDao(model, qb, discriminators) { var qb = arguments.length <= 1 || arguments[1] === undefined ? this.query() : arguments[1]; var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + opts = this.getOptions(opts); if (toSelect && toSelect.length) { if (value.ref) { toSelect = extractId(toSelect); diff --git a/package.json b/package.json index da31b97..eb2bace 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.4", + "version": "2.0.5", "description": "", "homepage": "", "dependencies": { - "deep-diff": "~0.3.4" + "deep-diff": "0.3.4" }, "files": [ "src/**/*.js" diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index a6248d4..657c613 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -284,7 +284,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod .map(e => e.beforeSave ? e.beforeSave(null, { force: true }) : e) } else { if (obj[ key ] && obj[ key ] !== null) { - obj[ key ] = obj[ key ].beforeSave(null, { force: true }) + obj[ key ] = obj[ key ].beforeSave ? obj[ key ].beforeSave(null, { force: true }) : obj[ key ] } } } else if (_.isDate(obj[ key ])) { From 4868cc1304b32c9601a7b6809efd561ab72deea5 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 19 Jun 2018 10:39:49 +0200 Subject: [PATCH 25/26] make injector great again --- build/app.js | 26 +++++++++++++++----------- dest/temp/ActiveRecord.js | 26 +++++++++++++++----------- package.json | 2 +- src/ActiveRecord.js | 20 +++++++++----------- 4 files changed, 40 insertions(+), 34 deletions(-) diff --git a/build/app.js b/build/app.js index b156b85..cb5625b 100644 --- a/build/app.js +++ b/build/app.js @@ -53,8 +53,20 @@ function ActiveRecord(model, name) { function ActiveRecord($injector, rootUrl, options) { _classCallCheck(this, ActiveRecord); - this.$injector = $injector; - this.rootUrl = rootUrl; + //this.$injector = $injector; + Object.defineProperty(this, '$injector', { + get: function get() { + return $injector; + }, + enumerable: false + }); + Object.defineProperty(this, 'rootUrl', { + get: function get() { + return rootUrl; + }, + enumerable: false + }); + this.build(options); } @@ -405,19 +417,11 @@ function ActiveRecord(model, name) { } } }); - var $q = this._injector.get('$q'); + var $q = this.$injector.get('$q'); return $q.all(promises).then(function () { return _this5.save(populate); }); } - }, { - key: '$injector', - get: function get() { - return this._injector; - }, - set: function set($injector) { - this._injector = $injector; - } }, { key: '$http', get: function get() { diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index bd08f44..7d10a2c 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -52,8 +52,20 @@ function ActiveRecord(model, name) { function ActiveRecord($injector, rootUrl, options) { _classCallCheck(this, ActiveRecord); - this.$injector = $injector; - this.rootUrl = rootUrl; + //this.$injector = $injector; + Object.defineProperty(this, '$injector', { + get: function get() { + return $injector; + }, + enumerable: false + }); + Object.defineProperty(this, 'rootUrl', { + get: function get() { + return rootUrl; + }, + enumerable: false + }); + this.build(options); } @@ -404,19 +416,11 @@ function ActiveRecord(model, name) { } } }); - var $q = this._injector.get('$q'); + var $q = this.$injector.get('$q'); return $q.all(promises).then(function () { return _this5.save(populate); }); } - }, { - key: '$injector', - get: function get() { - return this._injector; - }, - set: function set($injector) { - this._injector = $injector; - } }, { key: '$http', get: function get() { diff --git a/package.json b/package.json index eb2bace..8cf8937 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.5", + "version": "2.0.6", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 657c613..2260475 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -20,8 +20,14 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod let ActiveRecord = class { constructor ($injector, rootUrl, options) { - this.$injector = $injector; - this.rootUrl = rootUrl; + Object.defineProperty(this, '$injector', { + get: function(){return $injector}, + enumerable: false + }) + Object.defineProperty(this, 'rootUrl', { + get: function(){return rootUrl}, + enumerable: false + }) this.build(options); } @@ -135,14 +141,6 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod return clone } - get $injector () { - return this._injector; - } - - set $injector ($injector) { - this._injector = $injector; - } - get $http () { return this.$injector.get('$http'); } @@ -342,7 +340,7 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod } } }) - var $q = this._injector.get('$q') + var $q = this.$injector.get('$q') return $q.all(promises).then(() => { return this.save(populate) }) From 08a8f55c7c1a419f5a781c16a84bb24fb47d1d38 Mon Sep 17 00:00:00 2001 From: Florent Gouget Date: Tue, 19 Jun 2018 11:35:30 +0200 Subject: [PATCH 26/26] it was all a very bad idea --- build/app.js | 16 ++-------------- dest/temp/ActiveRecord.js | 16 ++-------------- package.json | 2 +- src/ActiveRecord.js | 10 ++-------- 4 files changed, 7 insertions(+), 37 deletions(-) diff --git a/build/app.js b/build/app.js index cb5625b..4b242f6 100644 --- a/build/app.js +++ b/build/app.js @@ -53,20 +53,8 @@ function ActiveRecord(model, name) { function ActiveRecord($injector, rootUrl, options) { _classCallCheck(this, ActiveRecord); - //this.$injector = $injector; - Object.defineProperty(this, '$injector', { - get: function get() { - return $injector; - }, - enumerable: false - }); - Object.defineProperty(this, 'rootUrl', { - get: function get() { - return rootUrl; - }, - enumerable: false - }); - + this.$injector = $injector; + this.rootUrl = rootUrl; this.build(options); } diff --git a/dest/temp/ActiveRecord.js b/dest/temp/ActiveRecord.js index 7d10a2c..e60a511 100644 --- a/dest/temp/ActiveRecord.js +++ b/dest/temp/ActiveRecord.js @@ -52,20 +52,8 @@ function ActiveRecord(model, name) { function ActiveRecord($injector, rootUrl, options) { _classCallCheck(this, ActiveRecord); - //this.$injector = $injector; - Object.defineProperty(this, '$injector', { - get: function get() { - return $injector; - }, - enumerable: false - }); - Object.defineProperty(this, 'rootUrl', { - get: function get() { - return rootUrl; - }, - enumerable: false - }); - + this.$injector = $injector; + this.rootUrl = rootUrl; this.build(options); } diff --git a/package.json b/package.json index 8cf8937..7963434 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "flogou ", "name": "angular-orm", - "version": "2.0.6", + "version": "2.0.7", "description": "", "homepage": "", "dependencies": { diff --git a/src/ActiveRecord.js b/src/ActiveRecord.js index 2260475..9794785 100644 --- a/src/ActiveRecord.js +++ b/src/ActiveRecord.js @@ -20,14 +20,8 @@ export default function ActiveRecord (model, name, SManager = SessionManager(mod let ActiveRecord = class { constructor ($injector, rootUrl, options) { - Object.defineProperty(this, '$injector', { - get: function(){return $injector}, - enumerable: false - }) - Object.defineProperty(this, 'rootUrl', { - get: function(){return rootUrl}, - enumerable: false - }) + this.$injector = $injector + this.rootUrl = rootUrl this.build(options); }