rjr 0.12.2 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/README.md +49 -36
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +11 -9
  4. data/bin/rjr-server +12 -10
  5. data/examples/amqp.rb +29 -0
  6. data/examples/client.rb +32 -0
  7. data/examples/complete.rb +36 -0
  8. data/examples/local.rb +29 -0
  9. data/examples/server.rb +26 -0
  10. data/examples/tcp.rb +29 -0
  11. data/examples/web.rb +22 -0
  12. data/examples/ws.rb +29 -0
  13. data/lib/rjr/common.rb +7 -12
  14. data/lib/rjr/dispatcher.rb +171 -239
  15. data/lib/rjr/em_adapter.rb +33 -66
  16. data/lib/rjr/message.rb +43 -12
  17. data/lib/rjr/node.rb +197 -103
  18. data/lib/rjr/nodes/amqp.rb +216 -0
  19. data/lib/rjr/nodes/easy.rb +159 -0
  20. data/lib/rjr/nodes/local.rb +118 -0
  21. data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
  22. data/lib/rjr/nodes/multi.rb +79 -0
  23. data/lib/rjr/nodes/tcp.rb +211 -0
  24. data/lib/rjr/nodes/web.rb +197 -0
  25. data/lib/rjr/nodes/ws.rb +187 -0
  26. data/lib/rjr/stats.rb +70 -0
  27. data/lib/rjr/thread_pool.rb +178 -123
  28. data/site/index.html +45 -0
  29. data/site/jquery-latest.js +9404 -0
  30. data/site/jrw.js +297 -0
  31. data/site/json.js +199 -0
  32. data/specs/dispatcher_spec.rb +244 -198
  33. data/specs/em_adapter_spec.rb +52 -80
  34. data/specs/message_spec.rb +223 -197
  35. data/specs/node_spec.rb +67 -163
  36. data/specs/nodes/amqp_spec.rb +82 -0
  37. data/specs/nodes/easy_spec.rb +13 -0
  38. data/specs/nodes/local_spec.rb +72 -0
  39. data/specs/nodes/multi_spec.rb +65 -0
  40. data/specs/nodes/tcp_spec.rb +75 -0
  41. data/specs/nodes/web_spec.rb +77 -0
  42. data/specs/nodes/ws_spec.rb +78 -0
  43. data/specs/stats_spec.rb +59 -0
  44. data/specs/thread_pool_spec.rb +44 -35
  45. metadata +40 -30
  46. data/lib/rjr/amqp_node.rb +0 -330
  47. data/lib/rjr/inspect.rb +0 -65
  48. data/lib/rjr/local_node.rb +0 -150
  49. data/lib/rjr/multi_node.rb +0 -65
  50. data/lib/rjr/tcp_node.rb +0 -323
  51. data/lib/rjr/thread_pool2.rb +0 -272
  52. data/lib/rjr/util.rb +0 -104
  53. data/lib/rjr/web_node.rb +0 -266
  54. data/lib/rjr/ws_node.rb +0 -289
  55. data/lib/rjr.rb +0 -16
  56. data/specs/amqp_node_spec.rb +0 -31
  57. data/specs/inspect_spec.rb +0 -60
  58. data/specs/local_node_spec.rb +0 -43
  59. data/specs/multi_node_spec.rb +0 -45
  60. data/specs/tcp_node_spec.rb +0 -33
  61. data/specs/util_spec.rb +0 -46
  62. data/specs/web_node_spec.rb +0 -32
  63. data/specs/ws_node_spec.rb +0 -32
  64. /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
  65. /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
data/site/jrw.js ADDED
@@ -0,0 +1,297 @@
1
+ /* Json-Rpc over HTTP and Websockets
2
+ *
3
+ * Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
4
+ * Licensed under the Apache License, Version 2.0
5
+ */
6
+
7
+ ///////////////////////////////////////////////////////
8
+ // Helpers to generate a uuid
9
+ function S4() {
10
+ return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
11
+ }
12
+ function guid() {
13
+ return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
14
+ }
15
+
16
+ ///////////////////////////////////////////////////////
17
+ // JRMessage
18
+
19
+ // Encapsulates a json-rpc message
20
+ function JRMessage(){
21
+ this.id = null;
22
+ this.rpc_method = null;
23
+ this.args = null;
24
+
25
+ this.data = null;
26
+ this.json = null;
27
+
28
+ this.error = null;
29
+ this.result = null;
30
+
31
+ this.onresponse = null;
32
+ this.success = null;
33
+
34
+ this.to_json = function(){
35
+ this.json = $.toJSON(this.data);
36
+ return this.json;
37
+ }
38
+
39
+ this.handle_response = function(res){
40
+ res.success = (res.error == undefined);
41
+ if(this.onresponse)
42
+ this.onresponse(res);
43
+ }
44
+ }
45
+
46
+ // Create new request message to send
47
+ JRMessage.new_request = function(rpc_method, args){
48
+ var msg = new JRMessage();
49
+ msg.id = guid();
50
+ msg.rpc_method = rpc_method;
51
+ msg.args = args;
52
+ msg.data = {jsonrpc: '2.0',
53
+ method: msg.rpc_method,
54
+ params: msg.args,
55
+ id: msg.id};
56
+ return msg;
57
+ }
58
+
59
+ // Internal helper to generate request from common args.
60
+ //
61
+ // rpc method, parameter list, and optional response callback
62
+ // will be extracted from the 'args' param in that order.
63
+ //
64
+ // node_id and headers will be set on request message before it
65
+ // is returned
66
+ JRMessage.pretty_request = function(args, node_id, headers){
67
+ // create request message
68
+ var rpc_method = args[0];
69
+ var params = [];
70
+ var cb = null;
71
+ for(a = 1; a < args.length; a++){
72
+ if(a == args.length - 1 && typeof args[a] === 'function')
73
+ cb = args[a];
74
+ else
75
+ params.push(args[a]);
76
+ }
77
+ var req = JRMessage.new_request(rpc_method, params);
78
+ if(node_id) req.data['node_id'] = node_id;
79
+ for(var header in headers)
80
+ req.data[header] = headers[header];
81
+
82
+ // register callback if last argument is a function
83
+ if(cb) req.onresponse = cb;
84
+
85
+ return req;
86
+ }
87
+
88
+ // Parse message received in json string
89
+ JRMessage.from_msg = function(dat){
90
+ var msg = new JRMessage();
91
+ msg.json = dat;
92
+ msg.data = $.evalJSON(dat);
93
+
94
+ msg.id = msg.data['id'];
95
+ msg.rpc_method = msg.data['method'];
96
+ if(msg.data['params']){
97
+ msg.params = msg.data['params'];
98
+ for(p=0;p<msg.params.length;++p){
99
+ if(JRObject.is_jrobject(msg.params[p]))
100
+ msg.params[p] = JRObject.from_json(msg.params[p]);
101
+ else if(JRObject.is_jrobject_array(msg.params[p]))
102
+ msg.params[p] = JRObject.from_json_array(msg.params[p]);
103
+ }
104
+ }
105
+ msg.error = msg.data['error'];
106
+ msg.result = msg.data['result'];
107
+ if(msg.result && JRObject.is_jrobject(msg.result))
108
+ msg.result = JRObject.from_json(msg.result);
109
+ else if(JRObject.is_jrobject_array(msg.result))
110
+ msg.result = JRObject.from_json_array(msg.result);
111
+ return msg;
112
+ }
113
+
114
+ ///////////////////////////////////////////////////////
115
+ // JRObject
116
+
117
+ // Encapsulates an object w/ type
118
+ // - adaptor for the ruby 'json' library
119
+ function JRObject (type, value, ignore_properties){
120
+ this.type = type;
121
+ this.value = value;
122
+ this.ignore_properties = (typeof(ignore_properties) != 'undefined') ?
123
+ ignore_properties : ["toJSON"];
124
+ this.toJSON = function(){
125
+ var data = {};
126
+ for(p in this.value)
127
+ if($.inArray(p, this.ignore_properties) == -1)
128
+ data[p] = value[p];
129
+ return {json_class: this.type, data: data };
130
+ };
131
+ };
132
+
133
+ // Return boolean indicating if json contains encapsulated JRObject
134
+ JRObject.is_jrobject = function(json){
135
+ return json && json['json_class'] && json['data'];
136
+ };
137
+
138
+ // Return boolean indicating if json contains array of encapsulated JRObjects
139
+ JRObject.is_jrobject_array = function(json){
140
+ return json && typeof(json) == "object" && json.length > 0 && JRObject.is_jrobject(json[0]);
141
+ };
142
+
143
+ // Convert json to JRObject
144
+ JRObject.from_json = function(json){
145
+ var obj = json['data'];
146
+ obj.json_class = json['json_class'];
147
+ for(var p in obj){
148
+ if(JRObject.is_jrobject(obj[p]))
149
+ obj[p] = JRObject.from_json(obj[p]);
150
+ else if(JRObject.is_jrobject_array(obj[p])){
151
+ obj[p] = JRObject.from_json_array(obj[p]);
152
+ }
153
+ }
154
+ return obj;
155
+ };
156
+
157
+ // Convert json to array of JRObjects
158
+ JRObject.from_json_array = function(json){
159
+ var objs = [];
160
+ for(var i in json)
161
+ if(JRObject.is_jrobject(json[i]))
162
+ objs[i] = JRObject.from_json(json[i]);
163
+ return objs;
164
+ };
165
+
166
+ ///////////////////////////////////////////////////////
167
+ // WSNode
168
+
169
+ // Main json-rpc client websocket interface
170
+ function WSNode (host, port){
171
+ var node = this;
172
+ this.opened = false;
173
+ this.node_id = null;
174
+ this.headers = {};
175
+ this.messages = {};
176
+
177
+ // Open socket connection
178
+ this.open = function(){
179
+ node.socket = new WebSocket("ws://" + host + ":" + port);
180
+
181
+ node.socket.onclose = function (){
182
+ if(node.onclose)
183
+ node.onclose();
184
+ };
185
+
186
+ node.socket.onmessage = function (evnt){
187
+ var msg = JRMessage.from_msg(evnt.data);
188
+
189
+ // match response w/ outstanding request
190
+ if(msg.id){
191
+ var req = node.messages[msg.id];
192
+ delete node.messages[msg.id];
193
+ req.handle_response(msg)
194
+
195
+ // if err msg, run node.onerror
196
+ if(msg.error)
197
+ if(node.onerror)
198
+ node.onerror(msg)
199
+
200
+ }else{
201
+ // relying on clients to handle notifications via message_received
202
+ // TODO add notification (and request?) handler support here
203
+ //node.invoke_method(msg.rpc_method, msg.params)
204
+ if(node.message_received)
205
+ node.message_received(msg);
206
+
207
+ }
208
+ };
209
+
210
+ node.socket.onerror = function(e){
211
+ if(node.onerror)
212
+ node.onerror(e);
213
+ }
214
+
215
+ node.socket.onopen = function (){
216
+ // send queued messages
217
+ for(var m in node.messages)
218
+ node.socket.send(node.messages[m].to_json());
219
+
220
+ node.opened = true;
221
+
222
+ // invoke client callback
223
+ if(node.onopen)
224
+ node.onopen();
225
+ };
226
+ };
227
+
228
+ // Close socket connection
229
+ this.close = function(){
230
+ this.socket.close();
231
+ };
232
+
233
+ // Invoke request on socket, may be invoked before or after socket is opened.
234
+ //
235
+ // Pass in the rpc method, arguments to invoke method with, and optional callback
236
+ // to be invoked upon received response.
237
+ this.invoke = function(){
238
+ var req = JRMessage.pretty_request(arguments, this.node_id, this.headers);
239
+
240
+ // store requests for later retrieval
241
+ this.messages[req.id] = req;
242
+
243
+ if(node.opened)
244
+ this.socket.send(req.to_json());
245
+
246
+ return req;
247
+ };
248
+ };
249
+
250
+ ///////////////////////////////////////////////////////
251
+ // WebNode
252
+
253
+ // Main json-rpc www interface
254
+ function WebNode (uri){
255
+ var node = this;
256
+ this.node_id = null;
257
+ this.headers = {};
258
+
259
+ // Invoke request via http
260
+ //
261
+ // Pass in the rpc method, arguments to invoke method with, and optional callback
262
+ // to be invoked upon received response.
263
+ this.invoke = function(){
264
+ var req = JRMessage.pretty_request(arguments, this.node_id, this.headers);
265
+
266
+ $.ajax({type: 'POST',
267
+ url: uri,
268
+ data: req.to_json(),
269
+ dataType: 'text', // using text so we can parse json ourselves
270
+
271
+ success: function(data){
272
+ var msg = JRMessage.from_msg(data);
273
+ // js web client doesn't support notifications
274
+ //if(node.message_received)
275
+ //node.message_received(msg);
276
+
277
+ req.handle_response(msg)
278
+
279
+ // if err msg, run node.onerror
280
+ if(msg.error)
281
+ if(node.onerror)
282
+ node.onerror(msg);
283
+ },
284
+
285
+ error: function(jqXHR, textStatus, errorThrown){
286
+ var err = { 'error' : {'code' : jqXHR.status,
287
+ 'message' : textStatus,
288
+ 'class' : errorThrown } };
289
+ if(node.onerror)
290
+ node.onerror(err);
291
+
292
+ req.handle_response(err)
293
+ }});
294
+
295
+ return req;
296
+ };
297
+ };
data/site/json.js ADDED
@@ -0,0 +1,199 @@
1
+ /**
2
+ * jQuery JSON plugin 2.4.0
3
+ *
4
+ * @author Brantley Harris, 2009-2011
5
+ * @author Timo Tijhof, 2011-2012
6
+ * @source This plugin is heavily influenced by MochiKit's serializeJSON, which is
7
+ * copyrighted 2005 by Bob Ippolito.
8
+ * @source Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
9
+ * website's http://www.json.org/json2.js, which proclaims:
10
+ * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
11
+ * I uphold.
12
+ * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
13
+ */
14
+ (function ($) {
15
+ 'use strict';
16
+
17
+ var escape = /["\\\x00-\x1f\x7f-\x9f]/g,
18
+ meta = {
19
+ '\b': '\\b',
20
+ '\t': '\\t',
21
+ '\n': '\\n',
22
+ '\f': '\\f',
23
+ '\r': '\\r',
24
+ '"' : '\\"',
25
+ '\\': '\\\\'
26
+ },
27
+ hasOwn = Object.prototype.hasOwnProperty;
28
+
29
+ /**
30
+ * jQuery.toJSON
31
+ * Converts the given argument into a JSON representation.
32
+ *
33
+ * @param o {Mixed} The json-serializable *thing* to be converted
34
+ *
35
+ * If an object has a toJSON prototype, that will be used to get the representation.
36
+ * Non-integer/string keys are skipped in the object, as are keys that point to a
37
+ * function.
38
+ *
39
+ */
40
+ $.toJSON = typeof JSON === 'object' && JSON.stringify ? JSON.stringify : function (o) {
41
+ if (o === null) {
42
+ return 'null';
43
+ }
44
+
45
+ var pairs, k, name, val,
46
+ type = $.type(o);
47
+
48
+ if (type === 'undefined') {
49
+ return undefined;
50
+ }
51
+
52
+ // Also covers instantiated Number and Boolean objects,
53
+ // which are typeof 'object' but thanks to $.type, we
54
+ // catch them here. I don't know whether it is right
55
+ // or wrong that instantiated primitives are not
56
+ // exported to JSON as an {"object":..}.
57
+ // We choose this path because that's what the browsers did.
58
+ if (type === 'number' || type === 'boolean') {
59
+ return String(o);
60
+ }
61
+ if (type === 'string') {
62
+ return $.quoteString(o);
63
+ }
64
+ if (typeof o.toJSON === 'function') {
65
+ return $.toJSON(o.toJSON());
66
+ }
67
+ if (type === 'date') {
68
+ var month = o.getUTCMonth() + 1,
69
+ day = o.getUTCDate(),
70
+ year = o.getUTCFullYear(),
71
+ hours = o.getUTCHours(),
72
+ minutes = o.getUTCMinutes(),
73
+ seconds = o.getUTCSeconds(),
74
+ milli = o.getUTCMilliseconds();
75
+
76
+ if (month < 10) {
77
+ month = '0' + month;
78
+ }
79
+ if (day < 10) {
80
+ day = '0' + day;
81
+ }
82
+ if (hours < 10) {
83
+ hours = '0' + hours;
84
+ }
85
+ if (minutes < 10) {
86
+ minutes = '0' + minutes;
87
+ }
88
+ if (seconds < 10) {
89
+ seconds = '0' + seconds;
90
+ }
91
+ if (milli < 100) {
92
+ milli = '0' + milli;
93
+ }
94
+ if (milli < 10) {
95
+ milli = '0' + milli;
96
+ }
97
+ return '"' + year + '-' + month + '-' + day + 'T' +
98
+ hours + ':' + minutes + ':' + seconds +
99
+ '.' + milli + 'Z"';
100
+ }
101
+
102
+ pairs = [];
103
+
104
+ if ($.isArray(o)) {
105
+ for (k = 0; k < o.length; k++) {
106
+ pairs.push($.toJSON(o[k]) || 'null');
107
+ }
108
+ return '[' + pairs.join(',') + ']';
109
+ }
110
+
111
+ // Any other object (plain object, RegExp, ..)
112
+ // Need to do typeof instead of $.type, because we also
113
+ // want to catch non-plain objects.
114
+ if (typeof o === 'object') {
115
+ for (k in o) {
116
+ // Only include own properties,
117
+ // Filter out inherited prototypes
118
+ if (hasOwn.call(o, k)) {
119
+ // Keys must be numerical or string. Skip others
120
+ type = typeof k;
121
+ if (type === 'number') {
122
+ name = '"' + k + '"';
123
+ } else if (type === 'string') {
124
+ name = $.quoteString(k);
125
+ } else {
126
+ continue;
127
+ }
128
+ type = typeof o[k];
129
+
130
+ // Invalid values like these return undefined
131
+ // from toJSON, however those object members
132
+ // shouldn't be included in the JSON string at all.
133
+ if (type !== 'function' && type !== 'undefined') {
134
+ val = $.toJSON(o[k]);
135
+ pairs.push(name + ':' + val);
136
+ }
137
+ }
138
+ }
139
+ return '{' + pairs.join(',') + '}';
140
+ }
141
+ };
142
+
143
+ /**
144
+ * jQuery.evalJSON
145
+ * Evaluates a given json string.
146
+ *
147
+ * @param str {String}
148
+ */
149
+ $.evalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
150
+ /*jshint evil: true */
151
+ return eval('(' + str + ')');
152
+ };
153
+
154
+ /**
155
+ * jQuery.secureEvalJSON
156
+ * Evals JSON in a way that is *more* secure.
157
+ *
158
+ * @param str {String}
159
+ */
160
+ $.secureEvalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
161
+ var filtered =
162
+ str
163
+ .replace(/\\["\\\/bfnrtu]/g, '@')
164
+ .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
165
+ .replace(/(?:^|:|,)(?:\s*\[)+/g, '');
166
+
167
+ if (/^[\],:{}\s]*$/.test(filtered)) {
168
+ /*jshint evil: true */
169
+ return eval('(' + str + ')');
170
+ }
171
+ throw new SyntaxError('Error parsing JSON, source is not valid.');
172
+ };
173
+
174
+ /**
175
+ * jQuery.quoteString
176
+ * Returns a string-repr of a string, escaping quotes intelligently.
177
+ * Mostly a support function for toJSON.
178
+ * Examples:
179
+ * >>> jQuery.quoteString('apple')
180
+ * "apple"
181
+ *
182
+ * >>> jQuery.quoteString('"Where are we going?", she asked.')
183
+ * "\"Where are we going?\", she asked."
184
+ */
185
+ $.quoteString = function (str) {
186
+ if (str.match(escape)) {
187
+ return '"' + str.replace(escape, function (a) {
188
+ var c = meta[a];
189
+ if (typeof c === 'string') {
190
+ return c;
191
+ }
192
+ c = a.charCodeAt();
193
+ return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
194
+ }) + '"';
195
+ }
196
+ return '"' + str + '"';
197
+ };
198
+
199
+ }(jQuery));