Skip to content

Commit f1cf1e8

Browse files
committed
Merge pull request #23 from oortcloud/ejson
EJSON support
2 parents ff8b184 + 695f309 commit f1cf1e8

File tree

6 files changed

+171
-58
lines changed

6 files changed

+171
-58
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
0.3.4 - 2013-08-28
2+
- added EJSON support (default is off) with a couple tests
3+
14
0.3.3 - 2013-05-29
25
- fixed bug where an exception could be thrown when sending a message on a socket that is not opened anymore (issue #18)
36
- added some tests (work in progress)

README.markdown

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A callback style DDP ([Meteor](http://meteor.com/)'s Distributed Data Protocol)
55

66
Based _heavily_ on alansikora's [node-js_ddp-client](https://github.com/alansikora/node-js_ddp-client), and meteor's python client. Uses a more callback style approach.
77

8-
The client implements the pre1 version of DDP. It is unfinished at this point, but should do most of what you want it to do.
8+
The client implements the pre1 version of DDP, with optional [EJSON](http://docs.meteor.com/#ejson) support. It is unfinished at this point, but should do most of what you want it to do.
99

1010
Installation
1111
============
@@ -23,12 +23,13 @@ Please see the example in `examples/example.js`. Or here for reference:
2323
var DDPClient = require("ddp");
2424

2525
var ddpclient = new DDPClient({
26-
host: "localhost",
27-
port: 3000,
28-
/* optional: */
29-
auto_reconnect: true,
30-
auto_reconnect_timer: 500
31-
});
26+
host: "localhost",
27+
port: 3000,
28+
/* optional: */
29+
auto_reconnect: true,
30+
auto_reconnect_timer: 500,
31+
use_ejson: true // default is false
32+
});
3233

3334
ddpclient.connect(function(error) {
3435
if (error) {
@@ -52,17 +53,18 @@ ddpclient.connect(function(error) {
5253
* Useful for debugging and learning the ddp protocol
5354
*/
5455
ddpclient.on('message', function(msg) {
55-
console.log("ddp message: " + msg);
56-
});
56+
console.log("ddp message: " + msg);
57+
});
5758

5859
/*
5960
* If you need to do something specific on close or errors.
6061
* (You can also disable auto_reconnect and call ddpclient.connect()
61-
* when you are ready to re-connect.)
62+
* when you are ready to re-connect.)
6263
*/
6364
ddpclient.on('socket-close', function(code, message) {
6465
console.log("Close: %s %s", code, message);
6566
});
67+
6668
ddpclient.on('socket-error', function(error) {
6769
console.log("Error: %j", error);
6870
});
@@ -75,7 +77,6 @@ Unimplemented Features
7577
* 'movedBefore'
7678
* 'error'
7779
* 'updated'
78-
* EJSON support
7980

8081

8182

examples/example.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
var DDPClient = require("../lib/ddp-client");
22

33
var ddpclient = new DDPClient({
4-
host: "localhost",
5-
port: 3000,
6-
/* optional: */
7-
auto_reconnect: true,
8-
auto_reconnect_timer: 500
9-
});
4+
host: "localhost",
5+
port: 3000,
6+
/* optional: */
7+
auto_reconnect: true,
8+
auto_reconnect_timer: 500,
9+
use_ejson: true // default is false
10+
});
1011

1112
ddpclient.connect(function(error) {
1213
console.log('connected!');
13-
14+
1415
if (error) {
1516
console.log('DDP connection error!');
1617
return;
@@ -30,7 +31,7 @@ ddpclient.connect(function(error) {
3031
* Useful for debugging and learning the ddp protocol
3132
*/
3233
ddpclient.on('message', function(msg) {
33-
console.log("ddp message: " + msg);
34+
console.log("ddp message: " + msg);
3435
});
3536

3637
/*
@@ -41,6 +42,7 @@ ddpclient.on('message', function(msg) {
4142
ddpclient.on('socket-close', function(code, message) {
4243
console.log("Close: %s %s", code, message);
4344
});
45+
4446
ddpclient.on('socket-error', function(error) {
4547
console.log("Error: %j", error);
46-
});
48+
});

lib/ddp-client.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
var WebSocket = require('ws'),
22
_ = require('underscore'),
33
util = require('util'),
4-
events = require('events');
4+
events = require('events'),
5+
EJSON = require('meteor-ejson');
56

67
DDPClient = function(opts) {
78
var self = this;
@@ -15,6 +16,9 @@ DDPClient = function(opts) {
1516
self.auto_reconnect = ('auto_reconnect' in opts) ? opts.auto_reconnect : true;
1617
self.auto_reconnect_timer = ('auto_reconnect_timer' in opts) ? opts.auto_reconnect_timer : 500;
1718

19+
// add support for EJSON, off by default to avoid breaking change
20+
self.use_ejson = opts.use_ejson || false;
21+
1822
// very very simple collections (name -> [{id -> document}])
1923
self.collections = {};
2024

@@ -40,7 +44,7 @@ DDPClient.prototype._prepareHandlers = function() {
4044
};
4145

4246
// if reconnecting, try existing DDP session
43-
// removed for now, per conversatino with sixolet on IRC,
47+
// removed for now, per conversation with sixolet on IRC,
4448
// reconnect on server side still needs work
4549

4650
/*
@@ -72,23 +76,35 @@ DDPClient.prototype._recoverNetworkError = function() {
7276
if (self.auto_reconnect && ! self._connectionFailed && ! self._isClosing) {
7377
setTimeout(function() { self.connect(); }, self.auto_reconnect_timer);
7478
}
75-
}
79+
};
7680

7781
///////////////////////////////////////////////////////////////////////////
7882
// RAW, low level functions
7983
DDPClient.prototype._send = function(data) {
80-
this.socket.send(JSON.stringify(data), function(error) {
84+
var self = this;
85+
86+
if (self.use_ejson) {
87+
data = EJSON.stringify(data);
88+
} else {
89+
data = JSON.stringify(data);
90+
}
91+
92+
this.socket.send(data, function(error) {
8193
// This callback to avoid an exception being thrown.
8294
// if an error happened, the socket will throw an event and we will recover.
8395
});
96+
8497
};
8598

8699
// handle a message from the server
87100
DDPClient.prototype._message = function(data, flags) {
88101
var self = this;
89102

90-
// TODO: EJSON parsing
91-
var data = JSON.parse(data);
103+
if (self.use_ejson) {
104+
data = EJSON.parse(data);
105+
} else {
106+
data = JSON.parse(data);
107+
}
92108

93109
// TODO: 'error'
94110
// TODO: 'updated' -- not sure exactly what the point is here
@@ -97,10 +113,10 @@ DDPClient.prototype._message = function(data, flags) {
97113

98114
if (!data.msg) {
99115
return;
100-
116+
101117
} else if (data.msg === 'failed') {
102118
self.emit('failed', data);
103-
119+
104120
} else if (data.msg === 'connected') {
105121
self.session = data.session;
106122
self.emit('connected');
@@ -209,10 +225,10 @@ DDPClient.prototype.connect = function(connected) {
209225
self.addListener("connected", connected);
210226
self.addListener("failed", function(error) {
211227
self._connectionFailed = true;
212-
connected(error)
228+
connected(error);
213229
});
214230
}
215-
231+
216232
// websocket
217233
var protocol = self.use_ssl ? 'wss://' : 'ws://';
218234
self.socket = new WebSocket(protocol + self.host + ':' + self.port + '/' + self.path);

package.json

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,40 @@
11
{
2-
"name": "ddp",
3-
"version": "0.3.3",
4-
"description": "Node.js module to connect to servers using DDP protocol.",
5-
"author": "Tom Coleman <[email protected]> (http://tom.thesnail.org), Mike Bannister <[email protected]> (http://po.ssibiliti.es)",
6-
"main": "lib/ddp-client",
7-
"keywords": ["ddp", "meteor", "protocol"],
8-
"repository" : {
9-
"type": "git",
10-
"url": "https://github.com/oortcloud/node-ddp-client.git"
11-
},
12-
"dependencies": {
13-
"ws": ">=0.4.23",
14-
"underscore": ">=1.3.3"
15-
},
16-
"devDependencies": {
17-
"mocha": "1.9.x",
18-
"sinon": "1.7.x",
19-
"rewire": "1.1.x"
20-
},
21-
"scripts": {
22-
"test": "./node_modules/mocha/bin/mocha test"
23-
},
24-
"engines": { "node": "*" }
25-
}
2+
"name": "ddp",
3+
"version": "0.3.4",
4+
"description": "Node.js module to connect to servers using DDP protocol.",
5+
"author": "Tom Coleman <[email protected]> (http://tom.thesnail.org)",
6+
"contributors": [
7+
"Thomas Sarlandie <[email protected]> (http://www.sarfata.org)",
8+
"Mason Gravitt <[email protected]>",
9+
"Mike Bannister <[email protected]> (http://po.ssibiliti.es)",
10+
"Chris Mather <[email protected]> (http://eventedmind.com)"
11+
],
12+
"main": "lib/ddp-client",
13+
"keywords": [
14+
"ddp",
15+
"meteor",
16+
"protocol"
17+
],
18+
"repository": {
19+
"type": "git",
20+
"url": "https://github.com/oortcloud/node-ddp-client.git"
21+
},
22+
"dependencies": {
23+
"ws": ">=0.4.23",
24+
"underscore": ">=1.3.3",
25+
"meteor-ejson": ">=0.6.3"
26+
},
27+
"devDependencies": {
28+
"mocha": "1.9.x",
29+
"sinon": "1.7.x",
30+
"rewire": "1.1.x",
31+
"meteor-ejson": ">=0.6.3"
32+
},
33+
"scripts": {
34+
"test": "./node_modules/mocha/bin/mocha test"
35+
},
36+
"engines": {
37+
"node": "*"
38+
},
39+
"bugs": "https://github.com/oortcloud/node-ddp-client/issues"
40+
}

test/ddp-client.js

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
var assert = require('assert'),
2-
sinon = require('sinon'),
2+
sinon = require('sinon'),
33
rewire = require('rewire'),
4-
events = require('events');
4+
events = require('events'),
5+
EJSON = require('meteor-ejson');
56

67
var DDPClient = rewire("../lib/ddp-client");
78

@@ -26,7 +27,7 @@ describe("Connect to remote server", function() {
2627

2728
assert(wsConstructor.calledOnce);
2829
assert(wsConstructor.calledWithNew());
29-
assert(wsConstructor.call)
30+
assert(wsConstructor.call);
3031
assert.deepEqual(wsConstructor.args, [['ws://localhost:3000/websocket']]);
3132
});
3233
it('should connect to the provided host', function() {
@@ -72,7 +73,7 @@ describe('Automatic reconnection', function() {
7273
describe("Network errors", function() {
7374
beforeEach(function() {
7475
prepareMocks();
75-
})
76+
});
7677

7778
// For some weird reasons (hard to reproduce) it happens that we try to send a message and
7879
// get an exception throws at us because the connection is not opened anymore.
@@ -106,3 +107,78 @@ describe("Network errors", function() {
106107
assert(!errorCB.calledOnce);
107108
});
108109
});
110+
111+
112+
describe('EJSON', function() {
113+
114+
var DDPMessage = '{"msg":"added","collection":"posts","id":"2trpvcQ4pn32ZYXco","fields":{"date":{"$date":1371591394454},"bindata":{"$binary":"QUJDRA=="}}}';
115+
var EJSONObject = EJSON.parse(DDPMessage);
116+
117+
it('should not be enabled by default', function(done) {
118+
var ddpclient = new DDPClient();
119+
120+
assert(!ddpclient.use_ejson);
121+
122+
done();
123+
});
124+
125+
it('should not be used when disabled', function(done) {
126+
var ddpclient = new DDPClient({ use_ejson : false });
127+
128+
assert(!ddpclient.use_ejson);
129+
130+
ddpclient._message(DDPMessage);
131+
132+
// ensure received dates not decoded from EJSON
133+
assert.deepEqual(ddpclient.collections.posts['2trpvcQ4pn32ZYXco'].date, {"$date":1371591394454});
134+
135+
// ensure received binary data not decoded from EJSON date
136+
assert.deepEqual(ddpclient.collections.posts['2trpvcQ4pn32ZYXco'].bindata, {"$binary":"QUJDRA=="});
137+
138+
ddpclient.socket = {};
139+
ddpclient.socket.send = function (opts) {
140+
// ensure sent dates not encoded into EJSON
141+
assert(opts.indexOf("date") !== -1);
142+
assert(opts.indexOf("$date") === -1);
143+
assert(opts.indexOf("1371591394454") === -1);
144+
145+
// ensure sent binary data not encoded into EJSON
146+
assert(opts.indexOf("bindata") !== -1);
147+
assert(opts.indexOf("$binary") === -1);
148+
assert(opts.indexOf("QUJDRA==") === -1);
149+
};
150+
151+
ddpclient._send(EJSONObject.fields);
152+
153+
done();
154+
});
155+
156+
it('should be used if specifically enabled', function(done) {
157+
var ddpclient = new DDPClient({ use_ejson : true });
158+
159+
assert(ddpclient.use_ejson);
160+
161+
ddpclient._message(DDPMessage);
162+
163+
assert.deepEqual(ddpclient.collections.posts['2trpvcQ4pn32ZYXco'].date, new Date(1371591394454));
164+
165+
assert.deepEqual(ddpclient.collections.posts['2trpvcQ4pn32ZYXco'].bindata, new Uint8Array([65, 66, 67, 68]));
166+
167+
ddpclient.socket = {};
168+
ddpclient.socket.send = function (opts) {
169+
assert(opts.indexOf("date") !== -1);
170+
assert(opts.indexOf("$date") !== -1);
171+
assert(opts.indexOf("1371591394454") !== -1);
172+
173+
assert(opts.indexOf("bindata") !== -1);
174+
assert(opts.indexOf("$binary") !== -1);
175+
assert(opts.indexOf("QUJDRA==") !== -1);
176+
};
177+
178+
ddpclient._send(EJSONObject.fields);
179+
180+
done();
181+
});
182+
183+
});
184+

0 commit comments

Comments
 (0)