Skip to content

Commit

Permalink
💥 Rename Doc op events
Browse files Browse the repository at this point in the history
The `Doc` op events are currently a little confusing: a `json0` "op" can
be shattered into multiple "component ops".

In the current naming, the "op" emits a `'op batch'` event, and the
"op component" emits an `'op'` event.

However, calling the op an "op batch" is a little bit misleading,
because the "batch" is always an op representing a single version number
increase, and potentially considered a single conceptual change.

For example, a remote client might submit a single op:

```js
[
  {p: ['a'], oi: 'foo'},
  {p: ['b'], oi: 'bar'},
]
```

Under the **current** naming scheme, this emits the following events:

 1 `'before op batch'`: `[{p: ['a'], oi: 'foo'}, {p: ['b'], oi: 'bar'}]`
 2 `'before op'`: `[{p: ['a'], oi: 'foo'}]`
 3 `'op'`: `[{p: ['a'], oi: 'foo'}]`
 4 `'before op'`: `[{p: ['b'], oi: 'bar'}]`
 5 `'op'`: `[{p: ['b'], oi: 'bar'}]`
 6 `'op batch'`: `[{p: ['a'], oi: 'foo'}, {p: ['b'], oi: 'bar'}]`

This can be considered a little surprising, and you may expect the
`'op'` event to be emitted after the whole op (not its shattered
components) have been applied.

Under the **new** naming scheme, the following events are emitted:

 1 `'beforeOp'`: `[{p: ['a'], oi: 'foo'}, {p: ['b'], oi: 'bar'}]`
 2 `'beforeOpComponent'`: `[{p: ['a'], oi: 'foo'}]`
 3 `'opComponent'`: `[{p: ['a'], oi: 'foo'}]`
 4 `'beforeOpComponent'`: `[{p: ['b'], oi: 'bar'}]`
 5 `'opComponent'`: `[{p: ['b'], oi: 'bar'}]`
 6 `'op'`: `[{p: ['a'], oi: 'foo'}, {p: ['b'], oi: 'bar'}]`

 This way, you get the `'op'` event after the whole op has been applied.
 It also makes it more explicit that you're actively listening out for
 the shattered op components where applicable.

 Note that we also move the events to camelCase to be consistent with
 the Backend middleware actions.
  • Loading branch information
alecgibson committed Jun 15, 2021
1 parent 087eda3 commit 945d69e
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 50 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

### Breaking changes

* Rename `Doc` op events:
* `before op batch` -> `beforeOp`
* `before op` -> `beforeOpComponent`
* `op` -> `opComponent`
* `op batch` -> `op`

## v1.0-beta

### Breaking changes
Expand Down
20 changes: 10 additions & 10 deletions docs/api/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,12 @@ The document was created. The doc will now have a [`type`](#type--type)
> {{ page.copy.event_source }}
### `'before op'`
### `'beforeOpComponent'`
An operation is about to be applied to the [`data`](#data--object)
```js
doc.on('before op', function(op, source) { ... })
doc.on('beforeOpComponent', function(op, source) { ... })
```
`op` -- Object
Expand All @@ -388,20 +388,20 @@ doc.on('before op', function(op, source) { ... })
> {{ page.copy.event_source }}
### `'op'`
### `'opComponent'`
An operation was applied to the data.
```js
doc.on('op', function(op, source) { ... })
doc.on('opComponent', function(op, source) { ... })
```
{: .info }
The difference between this event and [`'op batch'`](#op-batch) is that for [`json0`]({{ site.baseurl }}{% link types/json0.md %}), the op will be shattered into its constituent parts.
The difference between this event and [`'op'`](#op) is that for [`json0`]({{ site.baseurl }}{% link types/json0.md %}), the op will be shattered into its constituent parts.
<br/>
For example, `[{p: ['list', 0], li: 'a'}, {p: ['list', 1], li: 'b'}]` would be split into two components: `[{p: ['list', 0], li: 'a'}]` and `[{p: ['list', 1], li: 'b'}]`.
<br/>
The `'op'` event will be called once for each of these op components, but `'op batch'` will only be called once.
The `'opComponent'` event will be called once for each of these op components, but `'op'` will only be called once.
`op` -- Object
Expand All @@ -411,12 +411,12 @@ The `'op'` event will be called once for each of these op components, but `'op b
> {{ page.copy.event_source }}
### `'before op batch'`
### `'beforeOp'`
A potentially multi-part operation is about to be applied to the [`data`](#data--object).
```js
doc.on('before op batch', function(op, source) { ... })
doc.on('beforeOp', function(op, source) { ... })
```
`op` -- Object
Expand All @@ -427,12 +427,12 @@ doc.on('before op batch', function(op, source) { ... })
> {{ page.copy.event_source }}
### `'op batch'`
### `'op'`
A potentially multi-part operation was applied to the [`data`](#data--object)
```js
doc.on('op batch', function(op, source) { ... })
doc.on('op', function(op, source) { ... })
```
`op` -- Object
Expand Down
16 changes: 8 additions & 8 deletions lib/client/doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ Doc.prototype._otApply = function(op, source) {

// NB: If we need to add another argument to this event, we should consider
// the fact that the 'op' event has op.src as its 3rd argument
this.emit('before op batch', op.op, source);
this.emit('beforeOp', op.op, source);

// Iteratively apply multi-component remote operations and rollback ops
// (source === false) for the default JSON0 OT type. It could use
Expand Down Expand Up @@ -620,28 +620,28 @@ Doc.prototype._otApply = function(op, source) {
if (transformErr) return this._hardRollback(transformErr);
}
// Apply the individual op component
this.emit('before op', componentOp.op, source, op.src);
this.emit('beforeOpComponent', componentOp.op, source, op.src);
this.data = this.type.apply(this.data, componentOp.op);
this.emit('op', componentOp.op, source, op.src);
this.emit('opComponent', componentOp.op, source, op.src);
}
this.emit('op batch', op.op, source);
this.emit('op', op.op, source);
// Pop whatever was submitted since we started applying this op
this._popApplyStack(stackLength);
return;
}

// The 'before op' event enables clients to pull any necessary data out of
// The 'beforeOpComponent' event enables clients to pull any necessary data out of
// the snapshot before it gets changed
this.emit('before op', op.op, source, op.src);
this.emit('beforeOpComponent', op.op, source, op.src);
// Apply the operation to the local data, mutating it in place
this.data = this.type.apply(this.data, op.op);
// Emit an 'op' event once the local data includes the changes from the
// op. For locally submitted ops, this will be synchronously with
// submission and before the server or other clients have received the op.
// For ops from other clients, this will be after the op has been
// committed to the database and published
this.emit('op', op.op, source, op.src);
this.emit('op batch', op.op, source);
this.emit('opComponent', op.op, source, op.src);
this.emit('op', op.op, source);
return;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/client/presence/local-doc-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ LocalDocPresence.prototype.submit = function(value, callback) {
};

LocalDocPresence.prototype.destroy = function(callback) {
this._doc.removeListener('op', this._opHandler);
this._doc.removeListener('opComponent', this._opHandler);
this._doc.removeListener('create', this._createOrDelHandler);
this._doc.removeListener('del', this._createOrDelHandler);
this._doc.removeListener('load', this._loadHandler);
Expand Down Expand Up @@ -67,7 +67,7 @@ LocalDocPresence.prototype._sendPending = function() {
};

LocalDocPresence.prototype._registerWithDoc = function() {
this._doc.on('op', this._opHandler);
this._doc.on('opComponent', this._opHandler);
this._doc.on('create', this._createOrDelHandler);
this._doc.on('del', this._createOrDelHandler);
this._doc.on('load', this._loadHandler);
Expand Down
4 changes: 2 additions & 2 deletions lib/client/presence/remote-doc-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ RemoteDocPresence.prototype.receiveUpdate = function(message) {
};

RemoteDocPresence.prototype.destroy = function(callback) {
this._doc.removeListener('op', this._opHandler);
this._doc.removeListener('opComponent', this._opHandler);
this._doc.removeListener('create', this._createDelHandler);
this._doc.removeListener('del', this._createDelHandler);
this._doc.removeListener('load', this._loadHandler);
Expand All @@ -40,7 +40,7 @@ RemoteDocPresence.prototype.destroy = function(callback) {
};

RemoteDocPresence.prototype._registerWithDoc = function() {
this._doc.on('op', this._opHandler);
this._doc.on('opComponent', this._opHandler);
this._doc.on('create', this._createDelHandler);
this._doc.on('del', this._createDelHandler);
this._doc.on('load', this._loadHandler);
Expand Down
14 changes: 7 additions & 7 deletions test/client/doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ describe('Doc', function() {
expect(doc.data).eql({color: 'black'});
}
];
doc.on('op', function(op, source) {
doc.on('opComponent', function(op, source) {
var handler = handlers.shift();
handler(op, source);
});
Expand Down Expand Up @@ -199,7 +199,7 @@ describe('Doc', function() {
expect(doc.data).eql({color: 'black', weight: 40, age: 5, owner: 'sue'});
}
];
doc.on('op', function(op, source) {
doc.on('opComponent', function(op, source) {
var handler = handlers.shift();
handler(op, source);
});
Expand Down Expand Up @@ -250,7 +250,7 @@ describe('Doc', function() {
expect(doc.data).eql({tricks: ['shake', 'tug stick']});
}
];
doc.on('op', function(op, source) {
doc.on('opComponent', function(op, source) {
var handler = handlers.shift();
handler(op, source);
});
Expand All @@ -277,13 +277,13 @@ describe('Doc', function() {
{p: ['tricks', 0], li: 'stand'}
];

doc.on('before op batch', function(op, source) {
doc.on('beforeOp', function(op, source) {
expect(op).to.eql(submittedOp);
expect(source).to.be.true;
beforeOpBatchCount++;
});

doc.on('op batch', function(op, source) {
doc.on('op', function(op, source) {
expect(op).to.eql(submittedOp);
expect(source).to.be.true;
expect(beforeOpBatchCount).to.equal(1);
Expand All @@ -304,13 +304,13 @@ describe('Doc', function() {
{p: ['tricks', 0], li: 'stand'}
];

doc.on('before op batch', function(op, source) {
doc.on('beforeOp', function(op, source) {
expect(op).to.eql(submittedOp);
expect(source).to.be.false;
beforeOpBatchCount++;
});

doc.on('op batch', function(op, source) {
doc.on('op', function(op, source) {
expect(op).to.eql(submittedOp);
expect(source).to.be.false;
expect(beforeOpBatchCount).to.equal(1);
Expand Down
10 changes: 5 additions & 5 deletions test/client/presence/doc-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ describe('DocPresence', function() {
function(next) {
doc1.submitOp({index: 5, value: 'ern'}, errorHandler(done));

doc2.once('op', function() {
doc2.once('opComponent', function() {
presencePauser.resume();
});

Expand Down Expand Up @@ -324,7 +324,7 @@ describe('DocPresence', function() {
// doc2 has received this op, so we know that when we finally receive our
// presence, it will be stale
doc1.submitOp({index: 5, value: 'ern'}, errorHandler(done));
doc2.once('op', function() {
doc2.once('opComponent', function() {
next();
});
},
Expand All @@ -350,7 +350,7 @@ describe('DocPresence', function() {
doc1.submitOp({index: 0, value: 'The'}, function(error) {
if (error) return done(error);
doc1.submitOp({index: 3, value: ' '}, errorHandler(done));
doc2.on('op', function() {
doc2.on('opComponent', function() {
// This will get fired for v3 and then v4, so check for the later one
if (doc1.version === 4 && doc2.version === 4) {
// Only once doc2 has received the ops, should we resume our
Expand Down Expand Up @@ -395,7 +395,7 @@ describe('DocPresence', function() {
},
function(next) {
doc1.submitOp({index: 5, value: 'ern'}, errorHandler(done));
doc2.once('op', function() {
doc2.once('opComponent', function() {
next();
});
},
Expand All @@ -417,7 +417,7 @@ describe('DocPresence', function() {
], errorHandler(done));
};

doc2.on('op', function() {
doc2.on('opComponent', function() {
if (doc2.version !== 5) return;
presencePauser.resume();
presence2.once('receive', function(id, presence) {
Expand Down
4 changes: 2 additions & 2 deletions test/client/projections.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ module.exports = function(options) {
var fido = connection2.get('dogs_summary', 'fido');
fido.subscribe(function(err) {
if (err) return done(err);
fido.on('op', function() {
fido.on('opComponent', function() {
expect(fido.data).eql(expected);
expect(fido.version).eql(2);
done();
Expand Down Expand Up @@ -164,7 +164,7 @@ module.exports = function(options) {
var fido = connection2.get('dogs_summary', 'fido');
connection2.createSubscribeQuery('dogs_summary', matchAllQuery, null, function(err) {
if (err) return done(err);
fido.on('op', function() {
fido.on('opComponent', function() {
expect(fido.data).eql(expected);
expect(fido.version).eql(2);
done();
Expand Down
Loading

0 comments on commit 945d69e

Please sign in to comment.