(#3444 #3921) - use custom clone, fix Dates
diff --git a/lib/adapter.js b/lib/adapter.js index a40b86e..be5a109 100644 --- a/lib/adapter.js +++ b/lib/adapter.js
@@ -106,7 +106,7 @@ offset: opts.skip }; return Promise.all(keys.map(function (key) { - var subOpts = utils.extend(true, {key: key, deleted: 'ok'}, opts); + var subOpts = utils.extend({key: key, deleted: 'ok'}, opts); ['limit', 'skip', 'keys'].forEach(function (optKey) { delete subOpts[optKey]; }); @@ -722,7 +722,7 @@ opts = {}; } - opts = utils.clone(opts); + opts = utils.clone(opts || {}); if (Array.isArray(req)) { req = {
diff --git a/lib/adapters/http/index.js b/lib/adapters/http/index.js index 6fda834..a312847 100644 --- a/lib/adapters/http/index.js +++ b/lib/adapters/http/index.js
@@ -142,7 +142,7 @@ var ajaxOpts = opts.ajax || {}; opts = clone(opts); function ajax(options, callback) { - var reqOpts = utils.extend(true, clone(ajaxOpts), options); + var reqOpts = utils.extend(clone(ajaxOpts), options); log(reqOpts.method + ' ' + reqOpts.url); return utils.ajax(reqOpts, callback); } @@ -351,7 +351,7 @@ url: genDBUrl(host, id + params) }; var getRequestAjaxOpts = opts.ajax || {}; - utils.extend(true, options, getRequestAjaxOpts); + utils.extend(options, getRequestAjaxOpts); function fetchAttachments(doc) { var atts = doc._attachments;
diff --git a/lib/adapters/idb/index.js b/lib/adapters/idb/index.js index b692149..dc78024 100644 --- a/lib/adapters/idb/index.js +++ b/lib/adapters/idb/index.js
@@ -292,11 +292,8 @@ var doc; var metadata; var err; - var txn; - opts = utils.clone(opts); - if (opts.ctx) { - txn = opts.ctx; - } else { + var txn = opts.ctx; + if (!txn) { var txnResult = openTransactionSafely(idb, [DOC_STORE, BY_SEQ_STORE, ATTACH_STORE], 'readonly'); if (txnResult.error) { @@ -345,7 +342,6 @@ api._getAttachment = function (attachment, opts, callback) { var txn; - opts = utils.clone(opts); if (opts.ctx) { txn = opts.ctx; } else {
diff --git a/lib/adapters/websql/index.js b/lib/adapters/websql/index.js index 6eb0328..812f2cb 100644 --- a/lib/adapters/websql/index.js +++ b/lib/adapters/websql/index.js
@@ -540,18 +540,15 @@ }; api._get = function (id, opts, callback) { - opts = utils.clone(opts); var doc; var metadata; var err; - if (!opts.ctx) { - db.readTransaction(function (txn) { - opts.ctx = txn; - api._get(id, opts, callback); - }); - return; - } var tx = opts.ctx; + if (!tx) { + return db.readTransaction(function (txn) { + api._get(id, utils.extend({ctx: txn}, opts), callback); + }); + } function finish() { callback(err, {doc: doc, metadata: metadata, ctx: tx});
diff --git a/lib/deps/ajax/ajax-core.js b/lib/deps/ajax/ajax-core.js index 1f9151d..5640f29 100644 --- a/lib/deps/ajax/ajax-core.js +++ b/lib/deps/ajax/ajax-core.js
@@ -20,7 +20,7 @@ cache: false }; - options = utils.extend(true, defaultOptions, options); + options = utils.extend(defaultOptions, options); function onSuccess(obj, resp, cb) {
diff --git a/lib/deps/clone.js b/lib/deps/clone.js new file mode 100644 index 0000000..1f9e7ba --- /dev/null +++ b/lib/deps/clone.js
@@ -0,0 +1,55 @@ +'use strict'; + +function isPlainObject(object) { + // dead-simple "is this a straight-up object" test, taken + // from pouchdb-extend ala jQuery 1.9.0 + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + if (typeof object.hasOwnProperty !== 'function') { + return false; + } + + var key; + for (key in object) {} + return key === undefined || object.hasOwnProperty(key); +} + +module.exports = function clone(object) { + var newObject; + var i; + var len; + + if (!object || typeof object !== 'object') { + return object; + } + + if (Array.isArray(object)) { + newObject = []; + for (i = 0, len = object.length; i < len; i++) { + newObject[i] = clone(object[i]); + } + return newObject; + } + + // special case: to avoid inconsistencies between IndexedDB + // and other backends, we automatically stringify Dates + if (object instanceof Date) { + return object.toISOString(); + } + + if (!isPlainObject(object)) { + return object; + } + + newObject = {}; + for (i in object) { + if (object.hasOwnProperty(i)) { + var value = clone(object[i]); + if (typeof value !== 'undefined') { + newObject[i] = value; + } + } + } + return newObject; +}; \ No newline at end of file
diff --git a/lib/deps/extend.js b/lib/deps/extend.js new file mode 100644 index 0000000..a192a47 --- /dev/null +++ b/lib/deps/extend.js
@@ -0,0 +1,22 @@ +'use strict'; + +var clone = require('./clone'); + +function extendInner(obj, otherObj) { + for (var key in otherObj) { + if (otherObj.hasOwnProperty(key)) { + var value = clone(otherObj[key]); + if (typeof value !== 'undefined') { + obj[key] = value; + } + } + } +} + +module.exports = function extend(obj, obj2, obj3) { + extendInner(obj, obj2); + if (obj3) { + extendInner(obj, obj3); + } + return obj; +}; \ No newline at end of file
diff --git a/lib/mapreduce/create-view.js b/lib/mapreduce/create-view.js index d160632..e5f6983 100644 --- a/lib/mapreduce/create-view.js +++ b/lib/mapreduce/create-view.js
@@ -1,8 +1,8 @@ 'use strict'; var upsert = require('./upsert'); -var utils = require('./utils'); -var Promise = utils.Promise; +var Promise = require('../deps/promise'); +var md5 = require('./md5'); module.exports = function (opts) { var sourceDB = opts.db; @@ -25,7 +25,7 @@ return sourceDB.info().then(function (info) { var depDbName = info.db_name + '-mrview-' + - (temporary ? 'temp' : utils.MD5(viewSignature)); + (temporary ? 'temp' : md5(viewSignature)); // save the view name in the source db so it can be cleaned up if necessary // (e.g. when the _design doc is deleted, remove all associated view data)
diff --git a/lib/mapreduce/index.js b/lib/mapreduce/index.js index 2ed7484..cf2486a 100644 --- a/lib/mapreduce/index.js +++ b/lib/mapreduce/index.js
@@ -17,7 +17,8 @@ log = function () {}; } var utils = require('./utils'); -var Promise = utils.Promise; +var Promise = require('../deps/promise'); +var inherits = require('inherits'); var persistentQueues = {}; var tempViewQueue = new TaskQueue(); var CHANGES_BATCH_SIZE = 50; @@ -850,7 +851,7 @@ callback = opts; opts = {}; } - opts = utils.extend(true, {}, opts); + opts = opts || {}; if (typeof fun === 'function') { fun = {map : fun}; @@ -874,7 +875,7 @@ } catch (e) {} } -utils.inherits(QueryParseError, Error); +inherits(QueryParseError, Error); function NotFoundError(message) { this.status = 404; @@ -886,7 +887,7 @@ } catch (e) {} } -utils.inherits(NotFoundError, Error); +inherits(NotFoundError, Error); function BuiltInError(message) { this.status = 500; @@ -898,4 +899,4 @@ } catch (e) {} } -utils.inherits(BuiltInError, Error); \ No newline at end of file +inherits(BuiltInError, Error); \ No newline at end of file
diff --git a/lib/mapreduce/taskqueue.js b/lib/mapreduce/taskqueue.js index d1eb652..e233c18 100644 --- a/lib/mapreduce/taskqueue.js +++ b/lib/mapreduce/taskqueue.js
@@ -4,7 +4,7 @@ * callbacks will eventually fire (once). */ -var Promise = require('./utils').Promise; +var Promise = require('../deps/promise'); function TaskQueue() { this.promise = new Promise(function (fulfill) {fulfill(); });
diff --git a/lib/mapreduce/utils.js b/lib/mapreduce/utils.js index fce0f0e..92177a5 100644 --- a/lib/mapreduce/utils.js +++ b/lib/mapreduce/utils.js
@@ -1,8 +1,5 @@ 'use strict'; -exports.Promise = require('../deps/promise'); -exports.inherits = require('inherits'); -exports.extend = require('pouchdb-extend'); var argsarray = require('argsarray'); exports.promisedCallback = function (promise, callback) { @@ -78,6 +75,4 @@ output[i] = keys[i].substring(1); } return output; -}; - -exports.MD5 = require('./md5'); \ No newline at end of file +}; \ No newline at end of file
diff --git a/lib/setup.js b/lib/setup.js index e3a2ad9..f7755ed 100644 --- a/lib/setup.js +++ b/lib/setup.js
@@ -125,7 +125,7 @@ name = undefined; } - opts = utils.extend(true, {}, defaultOpts, opts); + opts = utils.extend({}, defaultOpts, opts); PouchDB.call(this, name, opts, callback); } @@ -141,7 +141,7 @@ opts = name; name = undefined; } - opts = utils.extend(true, {}, defaultOpts, opts); + opts = utils.extend({}, defaultOpts, opts); return PouchDB.destroy(name, opts, callback); });
diff --git a/lib/utils.js b/lib/utils.js index ca95e51..89acace 100644 --- a/lib/utils.js +++ b/lib/utils.js
@@ -1,6 +1,5 @@ /*jshint strict: false */ var merge = require('./merge'); -exports.extend = require('pouchdb-extend'); exports.ajax = require('./deps/ajax/prequest'); exports.uuid = require('./deps/uuid'); exports.getArguments = require('argsarray'); @@ -24,9 +23,8 @@ // TODO: only used by the integration tests exports.binaryStringToBlobOrBuffer = binStringToBlobOrBuffer; -exports.clone = function (obj) { - return exports.extend(true, {}, obj); -}; +exports.clone = require('./deps/clone'); +exports.extend = require('./deps/extend'); exports.pick = require('./deps/pick'); exports.inherits = require('inherits');
diff --git a/package.json b/package.json index 9557cee..b79a08a 100644 --- a/package.json +++ b/package.json
@@ -31,7 +31,6 @@ "miller-rabin": "1.1.1", "pouchdb-collate": "^1.2.0", "pouchdb-collections": "^1.0.0", - "pouchdb-extend": "^0.1.2", "pouchdb-upsert": "^1.0.2", "request": "~2.28.0", "spark-md5": "0.0.5",
diff --git a/tests/integration/test.basics.js b/tests/integration/test.basics.js index e5786d1..83b98f0 100644 --- a/tests/integration/test.basics.js +++ b/tests/integration/test.basics.js
@@ -626,6 +626,38 @@ }); }); + it('#4126 should not store raw Dates', function () { + var date = new Date(); + var date2 = new Date(); + var date3 = new Date(); + var origDocs = [ + { _id: '1', mydate: date }, + { _id: '2', array: [date2] }, + { _id: '3', deep: { deeper: { deeperstill: date3 } } + } + ]; + return new PouchDB(dbs.name).then(function (db) { + return db.bulkDocs(origDocs).then(function () { + return db.allDocs({include_docs: true}); + }).then(function (res) { + var docs = res.rows.map(function (row) { + delete row.doc._rev; + return row.doc; + }); + docs.should.deep.equal([ + { _id: '1', mydate: date.toJSON() }, + { _id: '2', array: [date2.toJSON()] }, + { _id: '3', deep: { deeper: { deeperstill: date3.toJSON() } } + } + ]); + origDocs[0].mydate.should.be.instanceof(Date, 'date not modified'); + origDocs[1].array[0].should.be.instanceof(Date, 'date not modified'); + origDocs[2].deep.deeper.deeperstill.should.be.instanceof(Date, + 'date not modified'); + }); + }); + }); + it('Error when document is not an object', function (done) { var db = new PouchDB(dbs.name); var doc1 = [{ _id: 'foo' }, { _id: 'bar' }];