Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## sofa

## Operations

* figure out when exactly `fastboot.deferRendering`
* wait until there are zero ops for 1-2(?) runloops

### Relationship classes

* paginated relationship helper `Relationship.extend({ paginated: paginated(...) })`
Expand Down Expand Up @@ -40,8 +45,9 @@

### other

* remove *-destroy.js
* is it possible to provide `promise` prop for `PassiveRelationLoaderStateMixin`?
* `model.save()`, `model.delete()`, ... second call while 1st is pending should return the same promise
* `model.save()`, `model.delete()`, ... second call while 1st is pending should return the same promise. `internalModel.running().then...`
* option to delete documents by saving with `_deleted:true` or delete with `{_deleted: true, type:..}`
* per-database models (each database is initialized with model folder name which is returned by `store.databaseOptionsForIdentifier`)
* embedded models (persisted as a `{ key: { model } }`)
Expand Down
2 changes: 2 additions & 0 deletions addon/couch.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Ember from 'ember';
import CouchSession from './couch/couch-session';
import CouchChanges from './couch/couch-changes';
import CouchInternalChangesIdentity from './couch/couch-internal-changes-identity';
import CouchOperations from './couch/couch-operations';
import CouchDestroy from './couch/couch-destroy';

const {
Expand All @@ -20,6 +21,7 @@ export default Ember.Object.extend(
CouchSession,
CouchChanges,
CouchInternalChangesIdentity,
CouchOperations,
CouchDestroy, {

documents: null,
Expand Down
3 changes: 3 additions & 0 deletions addon/couch/couch-operations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import forward from '../operations/forward-register-operation';

export default forward('store');
2 changes: 2 additions & 0 deletions addon/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import DatabaseSecurity from './database/database-security';
import DatabaseShoebox from './database/database-shoebox';
import DatabaseChanges from './database/database-changes';
import DatabaseInternalChangesIdentity from './database/database-internal-changes-identity';
import DatabaseOperations from './database/database-operations';
import DatabaseDestroy from './database/database-destroy';

export default Ember.Object.extend(
Expand All @@ -25,6 +26,7 @@ export default Ember.Object.extend(
DatabaseShoebox,
DatabaseChanges,
DatabaseInternalChangesIdentity,
DatabaseOperations,
DatabaseDestroy, {

identifier: null,
Expand Down
67 changes: 30 additions & 37 deletions addon/database/database-internal-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export default Ember.Mixin.create({
let documents = this.get('documents');
let resume;

return resolve().then(() => {
return this._registerOperation(resolve().then(() => {
return this._onInternalModelWillSave(internal);
}).then(() => {
let doc = this._serializeInternalModelToDocument(internal, 'document');
Expand All @@ -135,7 +135,7 @@ export default Ember.Mixin.create({
resume();
}
return reject(err);
});
}));
},

_onInternalModelLoading(internal) {
Expand Down Expand Up @@ -194,12 +194,12 @@ export default Ember.Mixin.create({

this._onInternalModelLoading(internal);

return documents.load(docId).then(doc => {
return this._registerOperation(documents.load(docId).then(doc => {
return this._onInternalModelLoaded(internal, doc);
}, err => {
this._onInternalModelLoadFailed(internal, err);
return reject(err);
});
}));
},

_loadInternalModel(internal, opts) {
Expand All @@ -217,16 +217,7 @@ export default Ember.Mixin.create({
let documents = this.get('documents');
let ids = A(array.map(internal => internal.docId));

// TODO: chunk array in 300 ids per request
// let size = 1;
// let chunks = chunkArray(array, size);
// return allSettled(chunks.map(chunk => {
// return documents.all({ include_docs: true, keys: chunk.map(internal => internal.docId) });
// })).then(blocks => {
// }).then(hash => {
// });

return documents.all({ include_docs: true, keys: ids }).then(json => {
return this._registerOperation(documents.all({ include_docs: true, keys: ids }).then(json => {
let rows = json.rows;
return allSettled(array.map((internal, idx) => {
let row = rows[idx];
Expand All @@ -248,7 +239,7 @@ export default Ember.Mixin.create({
return reject(new Errors(rejected.map(rejection => rejection.reason)));
}
return array;
});
}));
},

_loadInternalModelForDocId(docId, opts) {
Expand All @@ -257,9 +248,11 @@ export default Ember.Mixin.create({
return this._loadInternalModel(internal, opts);
}

return this.get('documents').load(docId).then(doc => {
let documents = this.get('documents');

return this._registerOperation(documents.load(docId).then(doc => {
return this._deserializeSavedDocumentToInternalModel(doc, null, false);
});
}));
},

_loadInternalModelForModelName(modelName, modelId, opts) {
Expand All @@ -272,9 +265,11 @@ export default Ember.Mixin.create({
let definition = this._definitionForModelClass(modelClass);
let docId = definition.docId(modelId);

return this.get('documents').load(docId).then(doc => {
let documents = this.get('documents');

return this._registerOperation(documents.load(docId).then(doc => {
return this._deserializeSavedDocumentToInternalModel(doc, modelClass, false);
});
}));
},

_onInternalModelDeleted(internal, json) {
Expand All @@ -300,18 +295,13 @@ export default Ember.Mixin.create({
_invokeInternalWillDeleteCallbacks(internal) {
let model = internal.model;
if(!model) {
return resolve();
return;
}

return resolve().then(() => {
return model.willDelete();
});
return resolve(model.willDelete());
},

_onInternalModelWillDelete(internal) {
return resolve().then(() => {
return this._invokeInternalWillDeleteCallbacks(internal);
});
return resolve(this._invokeInternalWillDeleteCallbacks(internal));
},

_onInternalModelDeleting(internal) {
Expand All @@ -337,7 +327,7 @@ export default Ember.Mixin.create({
let rev = internal.rev;
let documents = this.get('documents');

return resolve().then(() => {
return this._registerOperation(resolve().then(() => {
return this._onInternalModelWillDelete(internal);
}).then(() => {
this._onInternalModelDeleting(internal);
Expand All @@ -347,7 +337,7 @@ export default Ember.Mixin.create({
return internal;
}, err => {
return this._onInternalModelDeleteFailed(internal, err);
});
}));
},

_expectedModelClassFromOpts(opts) {
Expand Down Expand Up @@ -377,9 +367,10 @@ export default Ember.Mixin.create({
let optional = this._optionalFromOpts(opts);

let documents = this.get('documents');
return documents.view(ddoc, view, opts).then(json => {

return this._registerOperation(documents.view(ddoc, view, opts).then(json => {
return this._deserializeDocuments(A(json.rows).map(row => row.doc), expectedModelClass, optional);
});
}));
},

_internalModelMango(opts) {
Expand All @@ -397,9 +388,10 @@ export default Ember.Mixin.create({
}

let mango = this.get('documents.mango');
return mango.find(opts).then(json => {

return this._registerOperation(mango.find(opts).then(json => {
return this._deserializeDocuments(json.docs, expectedModelClass, optional);
});
}));
},

_internalModelAll(opts) {
Expand All @@ -409,9 +401,10 @@ export default Ember.Mixin.create({
let optional = this._optionalFromOpts(opts);

let documents = this.get('documents');
return documents.all(opts).then(json => {

return this._registerOperation(documents.all(opts).then(json => {
return this._deserializeDocuments(A(json.rows).map(row => row.doc), expectedModelClass, optional);
});
}));
},

//
Expand All @@ -431,8 +424,8 @@ export default Ember.Mixin.create({
let ddoc = opts.ddoc;
let selector = opts.selector;

let result = (type) => {
return (result) => {
let result = type => {
return result => {
return { result, type };
};
};
Expand Down
3 changes: 3 additions & 0 deletions addon/database/database-operations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import forward from '../operations/forward-register-operation';

export default forward('store');
9 changes: 9 additions & 0 deletions addon/operations/forward-register-operation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Ember from 'ember';

export default name => Ember.Mixin.create({

_registerOperation() {
return this.get(name)._registerOperation(...arguments);
}

});
38 changes: 38 additions & 0 deletions addon/operations/internal-operation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Ember from 'ember';

const {
assert
} = Ember;

const noop = () => {};

export default class InternalOperation {

constructor(owner, promise) {
assert(`promise must have promise.then`, promise && promise.then);
this.owner = owner;
this.promise = promise;
this.isDone = false;
}

set promise(promise) {
assert(`promise already set`, !this._promise);
this._promise = promise;
this._done = this._promise.then(noop, noop).finally(() => this._setDone());
}

get promise() {
return this._promise;
}

get done() {
return this._done;
}

_setDone() {
assert(`already done`, !this.isDone);
this.isDone = true;
this.owner._internalOperationDidFinish(this);
}

}
23 changes: 23 additions & 0 deletions addon/operations/operations-mixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Ember from 'ember';
import { lookup } from '../util/computed';

const operations = () => lookup('sofa:operations');

export default Ember.Mixin.create({

operations: operations(),

_registerOperation(promise) {
this.get('operations').register(promise);
return promise;
},

willDestroy() {
this._super();
let ops = this.cacheFor('operations');
if(ops) {
ops.destroy();
}
}

});
48 changes: 48 additions & 0 deletions addon/operations/operations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Ember from 'ember';
import InternalOperation from './internal-operation';
import { array } from '../util/computed';
import { next } from '../util/run';

const {
RSVP: { all }
} = Ember;

const iteration = (owner, resolve, idx=0) => {
next().then(() => {
let promises = owner.promises();
console.log(`operations.settle #${idx}: ${promises.length} promise(s)`);
if(promises.length === 0) {
resolve();
return;
}
all(promises).then(() => iteration(owner, resolve, ++idx));
});
};

export default Ember.Object.extend({

internalOperations: array(),

register(promise) {
let op = new InternalOperation(this, promise);
this.get('internalOperations').pushObject(op);
return op;
},

promises() {
return this.get('internalOperations').map(op => op.done);
},

wait() {
return all(this.promises());
},

settle() {
return new Promise(resolve => iteration(this, resolve));
},

_internalOperationDidFinish(op) {
this.get('internalOperations').removeObject(op);
}

});
Loading