Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 354 lines (272 sloc) 11.805 kb
cd50a59 James Halliday pared down the readme
authored
1 dnode
05ad7c4 James Halliday more examples in the readme
authored
2 =====
3
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
4 DNode is an object-oriented RPC system for node.js.
0f6fe5f James Halliday updated readme
authored
5
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
6 With dnode you call remote methods from a server's exposed functions.
7 Any functions you pass along to remote functions as arguments are automatically
8 wrapped so that the remote end can call you back even if those functions are
9 deeply nested in an object.
0f6fe5f James Halliday updated readme
authored
10
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
11 If you've used drb from ruby it's a similar idea.
12 Both drb and dnode are bidirectional so each side of the connection can call
13 methods exposed on the other side.
14 Unlike drb, all dnode methods are asynchronous, so to get the results of remote
15 operations, you supply callbacks.
6f17f01 James Halliday updated readme with new 0.0.3 browser deploy style and simonw quote
authored
16
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
17 Also unlike drb, remote methods can return objects with methods of their own
18 which are automatically and recursively wrapped. Since you're not stuck with the
19 methods that the server exposes directly at the dnode object entry-point, you
20 can expose dynamic interfaces with rich object hierarchies to remote clients.
6f17f01 James Halliday updated readme with new 0.0.3 browser deploy style and simonw quote
authored
21
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
22 This trick works over plain old tcp sockets or over websockets
23 courtesy of [socket.io](http://github.com/LearnBoost/Socket.IO-node).
24
25 The only catch is that everything is asynchronous, so you've got to write your
26 methods in
cd50a59 James Halliday pared down the readme
authored
27 [continuation passing style](http://en.wikipedia.org/wiki/Continuation-passing_style).
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
28 Instead of using `return` like this:
cd50a59 James Halliday pared down the readme
authored
29 function foo () { return 555 }
30 you call a function passed in as an argument
31 function foo (cb) { cb(555) }
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
32
33 Using CPS means that you can pass in multiple callbacks embedded arbitrarily
34 in the argument lists to remote functions or no callbacks at all. There are no
35 implicit return callbacks to fret over.
36
37 dnode(object or constructor)
38 ============================
39
40 Just pass `dnode()` an object with functions and attributes you want to expose
41 or a constructor function that creates a new object with functions and
42 attributes for each client. The constructor function will be passed a reference to
43 the remote object and a connection object.
44 `dnode()` returns an object with `listen()` and `connect()` functions described
45 below.
46
47 connection
48 ----------
49
50 The connection object emits 'ready'
51 when the remote object has been fully populated.
52 The connect object has an `id` attribute that uniquely identifies clients.
53
54 connect(port)
55 -------------
56
57 Connect to a remote dnode service. Pass in a port, host, block, or options
58 object in any order. The block function if present will be executed with the
59 remote object and the connection object once the remote object is ready.
60
164f85e James Halliday note about reconnect
authored
61 To reconnect when the connection drops, specify `reconnect` in the options
62 object as a millisecond delay between reconnection attempts. This is
63 experimental.
64
e565c1c James Halliday updated the intro and put some function usage before the examples sectio...
authored
65 listen(port)
66 ------------
67
68 Listen for incoming dnode clients. Pass in a port, host, block, or options
69 object in any order. The block function if present will be executed with the
70 remote object and the connection object once the remote object is ready for each
71 client.
2d36d00 James Halliday dependency info in the readme
authored
72
f2d386d James Halliday bump to 0.5 and readme notes about .use()
authored
73 use(middleware)
74 ---------------
75
76 You can write your own dnode middleware with `.use()`. The `middleware` function
77 you pass will be called just like the constructor function that `dnode()` takes.
78 You can modify `this`, `remote`, and `conn` objects after the instance computed
79 with the `dnode()` constructor executes but before the methods are sent over the
80 wire.
81
05ad7c4 James Halliday more examples in the readme
authored
82 Examples
83 ========
84
cd50a59 James Halliday pared down the readme
authored
85 Silly simple thing
86 ------------------
979efc1 James Halliday very basic .listen() part with a README
authored
87
2a9e1e7 James Halliday socket.io instructions for installation
authored
88 Server:
89
cd50a59 James Halliday pared down the readme
authored
90 var dnode = require('dnode');
6b00223 James Halliday blarg caps fixes
authored
91
cd50a59 James Halliday pared down the readme
authored
92 dnode({
93 decify : function (n,f) { f(n * 10) }
979efc1 James Halliday very basic .listen() part with a README
authored
94 }).listen(6060);
2a9e1e7 James Halliday socket.io instructions for installation
authored
95
96 Client:
97
cd50a59 James Halliday pared down the readme
authored
98 var dnode = require('dnode');
979efc1 James Halliday very basic .listen() part with a README
authored
99
6b00223 James Halliday blarg caps fixes
authored
100 dnode.connect(6060, function (remote) {
cd50a59 James Halliday pared down the readme
authored
101 remote.decify(5, function (n) {
102 console.log(n); // prints 50, woo!
05ad7c4 James Halliday more examples in the readme
authored
103 });
104 });
105
cd50a59 James Halliday pared down the readme
authored
106 Bidirectional
107 -------------
05ad7c4 James Halliday more examples in the readme
authored
108
cd50a59 James Halliday pared down the readme
authored
109 Clients and servers aren't special in dnode.
110 Each side of the link can provide methods to the other side.
c8a0d8f James Halliday bidirectional example does something worthwhile now
authored
111
2a9e1e7 James Halliday socket.io instructions for installation
authored
112 Server:
113
cd50a59 James Halliday pared down the readme
authored
114 var dnode = require('dnode');
115 dnode(function (client) {
c8a0d8f James Halliday bidirectional example does something worthwhile now
authored
116 // Poll the client's own temperature() in celsius and convert that value to
117 // fahrenheit in the supplied callback
118 this.clientTempF = function (cb) {
119 client.temperature(function (degC) {
120 var degF = Math.round(degC * 9 / 5 + 32);
121 cb(degF);
05ad7c4 James Halliday more examples in the readme
authored
122 });
0f6fe5f James Halliday updated readme
authored
123 };
05ad7c4 James Halliday more examples in the readme
authored
124 }).listen(6060);
c8a0d8f James Halliday bidirectional example does something worthwhile now
authored
125
2a9e1e7 James Halliday socket.io instructions for installation
authored
126 Client:
c8a0d8f James Halliday bidirectional example does something worthwhile now
authored
127
cd50a59 James Halliday pared down the readme
authored
128 var dnode = require('dnode');
129 dnode({
c8a0d8f James Halliday bidirectional example does something worthwhile now
authored
130 // Compute the client's temperature and stuff that value into the callback
131 temperature : function (cb) {
132 var degC = Math.round(20 + Math.random() * 10 - 5);
133 console.log(degC + '° C');
134 cb(degC);
135 }
5cb33f8 James Halliday switched the order of dnode and remote in .connect(function (dnode,remot...
authored
136 }).connect(6060, function (remote) {
c8a0d8f James Halliday bidirectional example does something worthwhile now
authored
137 // Call the server's conversion routine, which polls the client's
138 // temperature in celsius degrees and converts to fahrenheit
139 remote.clientTempF(function (degF) {
140 console.log(degF + '° F');
05ad7c4 James Halliday more examples in the readme
authored
141 });
979efc1 James Halliday very basic .listen() part with a README
authored
142 });
05ad7c4 James Halliday more examples in the readme
authored
143
cd50a59 James Halliday pared down the readme
authored
144 In the Browser
145 --------------
05ad7c4 James Halliday more examples in the readme
authored
146
cd50a59 James Halliday pared down the readme
authored
147 You can make dnode connections in the browser too!
f6b3373 James Halliday note about /dnode.js in the browser example
authored
148
cd50a59 James Halliday pared down the readme
authored
149 web.js:
05ad7c4 James Halliday more examples in the readme
authored
150
7e1b24b James Halliday simplified readme a little and using connect for the web example
authored
151 var connect = require('connect');
cd50a59 James Halliday pared down the readme
authored
152 var server = connect.createServer();
153 server.use(connect.staticProvider(__dirname));
154
6b00223 James Halliday blarg caps fixes
authored
155 var dnode = require('dnode');
156 dnode(function (client) {
7e1b24b James Halliday simplified readme a little and using connect for the web example
authored
157 this.cat = function (cb) {
158 cb('meow');
0f6fe5f James Halliday updated readme
authored
159 };
7e1b24b James Halliday simplified readme a little and using connect for the web example
authored
160 }).listen(server);
cd50a59 James Halliday pared down the readme
authored
161
162 server.listen(6857);
163 console.log('http://localhost:6857/');
7e1b24b James Halliday simplified readme a little and using connect for the web example
authored
164
cd50a59 James Halliday pared down the readme
authored
165 index.html:
7e1b24b James Halliday simplified readme a little and using connect for the web example
authored
166
167 <html>
168 <head>
169 <script src="/dnode.js" type="text/javascript"></script>
170 <script type="text/javascript">
171 window.onload = function () {
172 DNode.connect(function (remote) {
173 remote.cat(function (says) {
174 document.getElementById('says').innerHTML = says;
175 });
176 });
177 };
178 </script>
179 </head>
180 <body>
181 The cat says <span id="says">?</span>.
182 </body>
183 </html>
05ad7c4 James Halliday more examples in the readme
authored
184
185 Also note that .listen() returns "this", so you can bind multiple listeners to
6b00223 James Halliday blarg caps fixes
authored
186 the same dnode instance by chaining .listen() calls. This is useful when
05ad7c4 James Halliday more examples in the readme
authored
187 socket.io clients need to access the same service as regular node.js network
188 sockets.
189
cd50a59 James Halliday pared down the readme
authored
190 Installation
191 ============
192
193 Using npm:
194
195 npm install dnode
196
197 Or check out the repository and link your development copy:
198
199 git clone http://github.com/substack/dnode.git
200 cd dnode && npm link
201
6b00223 James Halliday blarg caps fixes
authored
202 dnode depends on
cd50a59 James Halliday pared down the readme
authored
203 [socket.io](http://github.com/LearnBoost/Socket.IO-node),
204 [traverse](http://github.com/substack/js-traverse),
205 and [lazy](http://github.com/pkrumins/node-lazy),
206 which are all on npm and will be automatically fetched when you `npm install
207 dnode` or `npm link` in the project directory.
208
209 You can also fetch them from github too:
210
211 git clone http://github.com/LearnBoost/Socket.IO-node.git
212 git clone http://github.com/substack/js-traverse.git
213 git clone http://github.com/pkrumins/node-lazy.git
214
7646c59 James Halliday new message protocol description
authored
215 Conventions
216 ===========
217
218 For the most part, when a method supplies a single return value, the callback
219 function should be the method's last argument, like blocks in ruby.
0f6fe5f James Halliday updated readme
authored
220 Incidentally, this module was inspired by ruby's DRb.
7646c59 James Halliday new message protocol description
authored
221
3ffeee3 James Halliday updated error handling in the readme, dnode-ruby link
authored
222 Error Handling
223 ==============
6b00223 James Halliday blarg caps fixes
authored
224 dnode emits `localError` events through the connection object when an exception
3ffeee3 James Halliday updated error handling in the readme, dnode-ruby link
authored
225 is thrown on the local side and `remoteError` when the remote side throws an
3184e00 James Halliday updated readme for the new error printing behavior
authored
226 uncaught exception. It doesn't emit `error` because that would crash the service
227 and that's probably not what you want.
3ffeee3 James Halliday updated error handling in the readme, dnode-ruby link
authored
228
3184e00 James Halliday updated readme for the new error printing behavior
authored
229 var client = Dnode({ /* ... */ }).connect(port);
230 client.on('localError', function (err) {
231 console.log('Local Error: ' + err);
232 });
233 client.on('remoteError', function (err) {
234 console.log('Remote Error: ' + err);
235 });
3ffeee3 James Halliday updated error handling in the readme, dnode-ruby link
authored
236
237 The stack trace is obscured for remoteErrors to avoid leaking sensitive
238 information.
239
3184e00 James Halliday updated readme for the new error printing behavior
authored
240 By default on nextTick a `localError` is registered that prints a stack trace if
241 no listeners have been bound.
242
05ad7c4 James Halliday more examples in the readme
authored
243 Protocol
244 ========
245
6b00223 James Halliday blarg caps fixes
authored
246 dnode uses newline-terminated JSON messages. Each side of the connection may
7646c59 James Halliday new message protocol description
authored
247 request that a method be invoked on the other side.
05ad7c4 James Halliday more examples in the readme
authored
248
0f6fe5f James Halliday updated readme
authored
249 Data Fields
250 -----------
7646c59 James Halliday new message protocol description
authored
251
252 All messages have this format:
20ab3bf James Halliday list missing an opening newline
authored
253
7646c59 James Halliday new message protocol description
authored
254 * method :: String or Integer
05ad7c4 James Halliday more examples in the readme
authored
255 * arguments :: Array
7646c59 James Halliday new message protocol description
authored
256 * callbacks :: Object
7e213dc James Halliday unicode test, note about dnode-perl
authored
257 * links :: Array
7646c59 James Halliday new message protocol description
authored
258
259 When the method field is a string, it refers to a named method at the remote.
260 When the method field is an integer, it refers to an anonymous function
261 declared in the callbacks field of a previous request.
05ad7c4 James Halliday more examples in the readme
authored
262
7646c59 James Halliday new message protocol description
authored
263 The arguments field contains the data to supply the remote method or callback.
f79411b James Halliday updated callback format in the readme
authored
264 The callbacks field maps an integral callback ID to an Array of elements
265 representing the callback's path in the arguments structure. For instance,
266 an arguments array before transformation of
267 [ 50, 3, { "b" : function () {}, "c" : 4 }, function () {} ]
268 could result in a callback field of
269 { 103 : [ 2, "b" ], 104 : [ 3 ] }
270 if the functions were assigned IDs of 103 and 104 from left to right
271 respectively. Function 103 is in the object at element index 2 and at the key
6f17f01 James Halliday updated readme with new 0.0.3 browser deploy style and simonw quote
authored
272 "b", so its path is [ 2, "b" ]. Function 104 is just at index 3 in the argument
f79411b James Halliday updated callback format in the readme
authored
273 field so its path is just [ 3 ].
274
275 The contents of the arguments array at a callback location is not used, so it
276 may contain any value or may be left undefined.
05ad7c4 James Halliday more examples in the readme
authored
277
7e213dc James Halliday unicode test, note about dnode-perl
authored
278 The Array and Object fields can be omitted, in which case they default to [] and
279 {}.
280
0f6fe5f James Halliday updated readme
authored
281 Methods
282 -------
283
284 After the connection is established, each side should send a message with the
0ad7253 James Halliday updated readme for new methods protocol
authored
285 method field set to "methods". The arguments fields should contain an array with
286 a single element: the object that should be wrapped. The callbacks field is
287 populated from the arguments array given the procedure above.
05ad7c4 James Halliday more examples in the readme
authored
288
0ad7253 James Halliday updated readme for new methods protocol
authored
289 Example of this initial methods message:
0f6fe5f James Halliday updated readme
authored
290 {
291 "method" : "methods",
0ad7253 James Halliday updated readme for new methods protocol
authored
292 "arguments" : [ { "timesTen" : "[Function]", "moo" : "[Function]" } ],
293 "callbacks" : { "0" : ["0","timesTen"], "1" : ["0","moo"] }
0f6fe5f James Halliday updated readme
authored
294 }
05ad7c4 James Halliday more examples in the readme
authored
295
0ad7253 James Halliday updated readme for new methods protocol
authored
296 Note that the string "[Function]" is just a placeholder and its value is
297 unimportant.
298
299 After methods are exchanged, each side may request methods from the other based
300 on named keys or numeric callback IDs.
de90265 James Halliday updated readme with protocol linking
authored
301
302 Links
303 -----
304
305 An optional field, "links" supports representing cyclic data structures over
306 JSON. The "links" field is an array of hashes with "from" and "to" keys set. The
307 values of the "from" and "two" keys are array encoding paths through the data
308 structure from the root, as in the "callbacks" field.
309
310 Example of a method call with cyclic references:
311 {
312 "method" : 12,
49e7c26 James Halliday fixed the readme links section and added an example
authored
313 "arguments" : [ { "a" : 5, "b" : [ { "c" : 5 } ] } ],
de90265 James Halliday updated readme with protocol linking
authored
314 "callbacks" : {},
315 "links" : [ { "from" : [ 0 ], "to" : [ 0, "b", 1 ] } ]
316 }
317 This example creates a link to the first argument within the first argument's
49e7c26 James Halliday fixed the readme links section and added an example
authored
318 "b" key's second element. The previous data structure could be generated from
319 the following javascript where `fn` comes from the remote:
320
321 var data = { a : 5, b : [ { c : 5 } ] };
322 data.b.push(data);
323 fn(data);
de90265 James Halliday updated readme with protocol linking
authored
324
325 Note that links need not necessarily be cyclic, they can just more efficiently
326 encode duplicate data, for instance.
7e213dc James Halliday unicode test, note about dnode-perl
authored
327
328 Other Languages
329 ===============
330
6b00223 James Halliday blarg caps fixes
authored
331 These libraries implement the dnode protocol too so you can make RPC calls
7e213dc James Halliday unicode test, note about dnode-perl
authored
332 between scripts written in different languages.
333
cd50a59 James Halliday pared down the readme
authored
334 * [dnode-perl](http://github.com/substack/dnode-perl)
335 * [dnode-ruby](http://github.com/substack/dnode-ruby)
336
337 There's a python one in the works too at
338 * [dnode-python](https://github.com/jesusabdullah/dnode-python)
339 but it's not finished yet.
340
341 Press!
342 ======
343
344 [Simon Willison](http://simonwillison.net/2010/Jul/11/dnode/) says:
345
346 > Mind-bendingly clever. DNode lets you expose a JavaScript function so that it
347 > can be called from another machine using a simple JSON-based network protocol.
348 > That’s relatively straight-forward... but DNode is designed for asynchronous
349 > environments, and so also lets you pass callback functions which will be
350 > translated in to references and used to make remote method invocations back to
351 > your original client. And to top it off, there’s a browser client library so
352 > you can perform the same trick over a WebSocket between a browser and a
353 > server.
Something went wrong with that request. Please try again.