Skip to content

Commit d7bfcc2

Browse files
committed
fix: support multiple createRecord calls (fixes #239)
This commit changes how ember-pouch implements `Adapter.createRecord`, which is invoked after calling `.save()` on a new record, so that it does not create multiple records if `.save()` is called more than once before the first operation has finished. If `.save()` is only invoked once before the record has finished persisting to the DB (i.e. the promise that it returns has resolved) then the behavior is unchanged. However, subsequent calls will wait for the previously returned promise to resolve and then, if changes have been made to the record, it will delegate the task to `updateRecord`. To avoid a problem caused by ember-data changing the ID associated with the internalModel/record when the record has finished persisting, the `Adapter.generateIdForRecord` method has been implemented so that it is available immediately. Previously ember-pouch had still been generating this id during `createRecord`, but ember-data was not being made aware of this until its returned promise resolved. Finally, rather than rely on `adapter.db.rel.uuid()` to generate an RFC4122 v4 UUID (requiring initialization to have completed), this has been replaced by the equivalent `uuid` module from npm, and the ember-auto-import addon has been installed to make it easy to access this from within ember-pouch.
1 parent 9bfde5c commit d7bfcc2

File tree

3 files changed

+50
-19
lines changed

3 files changed

+50
-19
lines changed

addon/adapters/pouch.js

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Ember from 'ember';
22
import DS from 'ember-data';
33
import { pluralize } from 'ember-inflector';
4+
import { v4 } from 'uuid';
45
//import BelongsToRelationship from 'ember-data/-private/system/relationships/state/belongs-to';
56

67
import {
@@ -153,7 +154,7 @@ export default DS.RESTAdapter.extend({
153154
willDestroy: function() {
154155
this._stopChangesListener();
155156
},
156-
157+
157158
_indexPromises: [],
158159

159160
_init: function (store, type) {
@@ -206,8 +207,9 @@ export default DS.RESTAdapter.extend({
206207
relModel = (typeof rel.type === 'string' ? store.modelFor(rel.type) : rel.type);
207208
if (relModel) {
208209
let includeRel = true;
209-
if (!('options' in rel)) rel.options = {};
210-
210+
if (!('options' in rel)) {
211+
rel.options = {};
212+
}
211213
if (typeof(rel.options.async) === "undefined") {
212214
rel.options.async = config.emberPouch && !Ember.isEmpty(config.emberPouch.async) ? config.emberPouch.async : true;//default true from https://github.com/emberjs/data/pull/3366
213215
}
@@ -464,27 +466,46 @@ export default DS.RESTAdapter.extend({
464466
});
465467
},
466468

469+
generateIdForRecord: function(/* store, type, inputProperties */) {
470+
return v4();
471+
},
472+
467473
createdRecords: {},
468-
createRecord: function(store, type, record) {
469-
this._init(store, type);
470-
var data = this._recordToData(store, type, record);
471-
let rel = this.get('db').rel;
472-
473-
let id = data.id;
474-
if (!id) {
475-
id = data.id = rel.uuid();
474+
createRecord: function(store, type, snapshot) {
475+
const record = snapshot.record;
476+
if (record._emberPouchSavePromise) {
477+
const changes = record.changedAttributes();
478+
record._emberPouchSavePromise = record._emberPouchSavePromise.then(records => {
479+
// If there have been changes since the document was created then we should update the record now
480+
if (Object.keys(changes).length > 0) {
481+
const rev = records[Object.keys(records)[0]][0].rev;
482+
(snapshot.__attributes || snapshot._attributes).rev = rev; // FIXME: it should be possible to do this elsewhere
483+
return this.updateRecord(store, type, snapshot);
484+
}
485+
return records;
486+
});
487+
return record._emberPouchSavePromise;
476488
}
489+
490+
this._init(store, type);
491+
var data = this._recordToData(store, type, snapshot);
492+
const rel = this.get('db').rel;
493+
const id = data.id;
477494
this.createdRecords[id] = true;
478-
479-
return rel.save(this.getRecordTypeName(type), data).catch((e) => {
480-
delete this.createdRecords[id];
481-
throw e;
495+
Object.defineProperty(record, '_emberPouchSavePromise', {
496+
enumerable: false,
497+
writable: true,
498+
value: rel.save(this.getRecordTypeName(type), data).catch((e) => {
499+
delete this.createdRecords[id];
500+
throw e;
501+
}),
482502
});
503+
return record._emberPouchSavePromise;
483504
},
484505

485-
updateRecord: function (store, type, record) {
506+
updateRecord: function (store, type, snapshot) {
486507
this._init(store, type);
487-
var data = this._recordToData(store, type, record);
508+
var data = this._recordToData(store, type, snapshot);
488509
return this.get('db').rel.save(this.getRecordTypeName(type), data);
489510
},
490511

ember-cli-build.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
55

66
module.exports = function(defaults) {
77
let app = new EmberAddon(defaults, {
8-
// Add options here
8+
autoImport: {
9+
webpack: {
10+
node: {
11+
global: true
12+
}
13+
},
14+
// We could use ember-auto-import for these, but index.js is already handling them
15+
exclude: ['pouchdb', 'pouchdb-find', 'relational-pouch']
16+
}
917
});
1018

1119
/*

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@
6262
"dependencies": {
6363
"broccoli-file-creator": "^2.1.1",
6464
"broccoli-stew": "^2.1.0",
65+
"ember-auto-import": "^1.5.3",
66+
"ember-cli-babel": "^7.7.3",
6567
"pouchdb": "^7.1.1",
6668
"relational-pouch": "^3.1.0",
67-
"ember-cli-babel": "^7.7.3"
69+
"uuid": "^3.3.3"
6870
},
6971
"ember-addon": {
7072
"configPath": "tests/dummy/config"

0 commit comments

Comments
 (0)