dripdrop 0.3.1 → 0.4.0

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 (38) hide show
  1. data/README.md +23 -28
  2. data/VERSION +1 -1
  3. data/dripdrop.gemspec +25 -3
  4. data/example/combined.rb +33 -0
  5. data/example/pubsub.rb +7 -15
  6. data/example/stats_app/core.rb +113 -0
  7. data/example/stats_app/public/.sass-cache/b48b4299d80c05f528daf63fe51d85e5e3c10d98/stats.scssc +0 -0
  8. data/example/stats_app/public/backbone.js +16 -0
  9. data/example/stats_app/public/build_templates.rb +5 -0
  10. data/example/stats_app/public/json2.js +482 -0
  11. data/example/stats_app/public/protovis-r3.2.js +277 -0
  12. data/example/stats_app/public/stats.css +5 -0
  13. data/example/stats_app/public/stats.haml +61 -0
  14. data/example/stats_app/public/stats.html +26 -0
  15. data/example/stats_app/public/stats.js +113 -0
  16. data/example/stats_app/public/stats.scss +10 -0
  17. data/example/stats_app/public/underscore.js +17 -0
  18. data/example/xreq_xrep.rb +9 -11
  19. data/js/dripdrop.js +6 -2
  20. data/lib/dripdrop/handlers/base.rb +18 -0
  21. data/lib/dripdrop/handlers/http.rb +18 -18
  22. data/lib/dripdrop/handlers/websockets.rb +33 -26
  23. data/lib/dripdrop/handlers/zeromq.rb +30 -24
  24. data/lib/dripdrop/message.rb +5 -0
  25. data/lib/dripdrop/node/nodelet.rb +29 -0
  26. data/lib/dripdrop/node.rb +103 -25
  27. data/spec/gimite-websocket.rb +442 -0
  28. data/spec/message_spec.rb +5 -0
  29. data/spec/node/http_spec.rb +2 -8
  30. data/spec/node/nodelet_spec.rb +57 -0
  31. data/spec/node/routing_spec.rb +68 -0
  32. data/spec/node/websocket_spec.rb +88 -0
  33. data/spec/node/zmq_pushpull_spec.rb +2 -6
  34. data/spec/node/zmq_xrepxreq_spec.rb +24 -24
  35. data/spec/node_spec.rb +0 -1
  36. data/spec/spec_helper.rb +17 -3
  37. metadata +27 -5
  38. data/js/jack.js +0 -876
@@ -0,0 +1,26 @@
1
+ <html>
2
+ <head>
3
+ <title>DripDrop Stats</title>
4
+ <link href='stats.css' rel='stylesheet' />
5
+ <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js' type='text/javascript'></script>
6
+ <script src='json2.js' type='text/javascript'></script>
7
+ <script src='../../../js/dripdrop.js' type='text/javascript'></script>
8
+ <script src='underscore.js' type='text/javascript'></script>
9
+ <script src='backbone.js' type='text/javascript'></script>
10
+ <script src='protovis-r3.2.js' type='text/javascript'></script>
11
+ <script src='stats.js' type='text/javascript'></script>
12
+ </head>
13
+ <body>
14
+ <h1>DripDrop Stats</h1>
15
+ <form id="traceroute">
16
+ Traceroute IP:<input type="text" id="traceroute-ip">
17
+ <a href="#" onclick='submitTraceForm(); return false;'>Submit</a>
18
+ </form>
19
+ <div id="force-cont"></div>
20
+ <script type='text/javascript'>
21
+ $(function () {
22
+ window.renderForce();
23
+ });
24
+ </script>
25
+ </body>
26
+ </html>
@@ -0,0 +1,113 @@
1
+ $(function() {
2
+ window.submitTraceForm = function () {
3
+ var ip = $('#traceroute-ip').val();
4
+ var msg = new DD.Message('ip_trace_req', {body: {ip: ip}});
5
+ $.post('http://stringer.andrewvc.com:8082', msg.jsonEncoded(), function (msg) {
6
+ console.log(msg);
7
+ });
8
+ return false;
9
+ };
10
+
11
+ var forceData = {};
12
+
13
+ var ws = new DD.WebSocket('ws://stringer.andrewvc.com:2202');
14
+ ws.onOpen(function () {
15
+ }).onRecv(function (message) {
16
+ if (message.name != 'ip_route') return;
17
+
18
+ forceData[message.body.ip] = message.body.route;
19
+ }).onClose(function () {
20
+
21
+ }).onError(function () {
22
+
23
+ });
24
+
25
+ setInterval(function() {
26
+ window.renderForce();
27
+ }, 3000);
28
+
29
+
30
+ var processForceData = function () {
31
+ var oldforceData = {
32
+ '127.0.0.1': ['192.168.2.1', '192.291.12.2', 'router.what.com'],
33
+ '127.0.2.1': ['192.168.3.1', '192.291.12.2', 'router.what.com'],
34
+ '127.5.0.1': ['192.168.2.1', '192.291.12.2', 'router.what.com'],
35
+ '127.2.5.1': ['192.16.6.1', '192.291.12.2', 'router.what.com'],
36
+ '127.2.1.1': ['192.16.2.6', '192.211.12.2', 'router.what.com'],
37
+ '127.0.0.2': ['192.168.2.1', '192.291.12.2', 'router.what.com'],
38
+ }
39
+
40
+ var addrs = _.reduce(
41
+ forceData,
42
+ function (memo,addrs) {
43
+ _.each(addrs, function(addr) {
44
+ if (! memo[addr]) {
45
+ memo[addr] = 1;
46
+ }
47
+ });
48
+ return memo;
49
+ },
50
+ {}
51
+ );
52
+
53
+ var i = 0;
54
+ var nodes = [];
55
+ var addrs_idx_map = {};
56
+ for (var addr in addrs) {
57
+ nodes.push({nodeName: addr, group: 1});
58
+ addrs_idx_map[addr] = i;
59
+ i++;
60
+ };
61
+
62
+ var links = [];
63
+ _.each(
64
+ forceData,
65
+ function (addrs) {
66
+ if (addrs.length >= 2) {
67
+ //We can skip the last element
68
+ for (var i=0; i < addrs.length - 1; i++) {
69
+ var source = addrs_idx_map[addrs[i]];
70
+ var target = addrs_idx_map[addrs[i+1]];
71
+ links.push({source: source, target: target, value: 2});
72
+ }
73
+ }
74
+ }
75
+ );
76
+
77
+ return({nodes: nodes, links: links});
78
+ }
79
+ window.renderForce = function () {
80
+ var data = processForceData();
81
+
82
+ var w = document.body.clientWidth,
83
+ h = document.body.clientHeight,
84
+ colors = pv.Colors.category19();
85
+
86
+ var vis = new pv.Panel()
87
+ .canvas($('#force-cont')[0])
88
+ .width(w)
89
+ .height(h)
90
+ .fillStyle("white")
91
+ .event("mousedown", pv.Behavior.pan())
92
+ .event("mousewheel", pv.Behavior.zoom());
93
+
94
+ var force = vis.add(pv.Layout.Force)
95
+ .nodes(data.nodes)
96
+ .links(data.links);
97
+ //.nodes([{nodeName: '127.0.0.1', group: 1}, {nodeName: '192.168.1.1', group: 2}])
98
+ //.links([{source: 1, target: 0, value: 1}]);
99
+
100
+ force.link.add(pv.Line);
101
+
102
+ force.node.add(pv.Dot)
103
+ .size(function(d) {return (d.linkDegree + 4) * Math.pow(this.scale, -1.5)})
104
+ .fillStyle(function(d) { return d.fix ? "brown" : colors(d.group) })
105
+ .strokeStyle(function() { return this.fillStyle().darker() } )
106
+ .lineWidth(1)
107
+ .title(function(d) { return d.nodeName } )
108
+ .event("mousedown", function () { return pv.Behavior.drag() })
109
+ .event("drag", function () { return force });
110
+
111
+ vis.render();
112
+ }
113
+ });
@@ -0,0 +1,10 @@
1
+ h1 {
2
+
3
+ }
4
+
5
+ #console-messages {
6
+ min-width: 800px;
7
+ width: 80%;
8
+ height:500px;
9
+ border: 1px solid black;
10
+ }
@@ -0,0 +1,17 @@
1
+ (function(){var o=this,A=o._,r=typeof StopIteration!=="undefined"?StopIteration:"__break__",k=Array.prototype,m=Object.prototype,i=k.slice,B=k.unshift,C=m.toString,p=m.hasOwnProperty,s=k.forEach,t=k.map,u=k.reduce,v=k.reduceRight,w=k.filter,x=k.every,y=k.some,n=k.indexOf,z=k.lastIndexOf;m=Array.isArray;var D=Object.keys,b=function(a){return new l(a)};if(typeof exports!=="undefined")exports._=b;o._=b;b.VERSION="1.1.2";var j=b.each=b.forEach=function(a,c,d){try{if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=
2
+ 0,f=a.length;e<f;e++)c.call(d,a[e],e,a);else for(e in a)p.call(a,e)&&c.call(d,a[e],e,a)}catch(g){if(g!=r)throw g;}return a};b.map=function(a,c,d){if(t&&a.map===t)return a.map(c,d);var e=[];j(a,function(f,g,h){e[e.length]=c.call(d,f,g,h)});return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){if(u&&a.reduce===u){if(e)c=b.bind(c,e);return a.reduce(c,d)}j(a,function(f,g,h){d=c.call(e,d,f,g,h)});return d};b.reduceRight=b.foldr=function(a,c,d,e){if(v&&a.reduceRight===v){if(e)c=b.bind(c,e);return a.reduceRight(c,
3
+ d)}a=(b.isArray(a)?a.slice():b.toArray(a)).reverse();return b.reduce(a,c,d,e)};b.find=b.detect=function(a,c,d){var e;j(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.filter=b.select=function(a,c,d){if(w&&a.filter===w)return a.filter(c,d);var e=[];j(a,function(f,g,h){if(c.call(d,f,g,h))e[e.length]=f});return e};b.reject=function(a,c,d){var e=[];j(a,function(f,g,h){c.call(d,f,g,h)||(e[e.length]=f)});return e};b.every=b.all=function(a,c,d){c=c||b.identity;if(x&&a.every===x)return a.every(c,
4
+ d);var e=true;j(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.some=b.any=function(a,c,d){c=c||b.identity;if(y&&a.some===y)return a.some(c,d);var e=false;j(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=b.contains=function(a,c){if(n&&a.indexOf===n)return a.indexOf(c)!=-1;var d=false;j(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
5
+ function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};j(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};j(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(e,
6
+ f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){var g=e.criteria,h=f.criteria;return g<h?-1:g>h?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return i.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,c,d){return c&&!d?i.call(a,
7
+ 0,c):a[0]};b.rest=b.tail=function(a,c,d){return i.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.filter(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c[c.length]=d;return c},[])};b.without=function(a){var c=i.call(arguments,1);return b.filter(a,function(d){return!b.include(c,d)})};b.uniq=b.unique=function(a,c){return b.reduce(a,function(d,e,f){if(0==f||(c===true?
8
+ b.last(d)!=e:!b.include(d,e)))d[d.length]=e;return d},[])};b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(d){return b.every(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c){if(n&&a.indexOf===n)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(z&&a.lastIndexOf===
9
+ z)return a.lastIndexOf(c);for(var d=a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=i.call(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.max(Math.ceil((c-a)/d),0);f=0;for(var g=Array(e);f<e;){g[f++]=a;a+=d}return g};b.bind=function(a,c){var d=i.call(arguments,2);return function(){return a.apply(c||{},d.concat(i.call(arguments)))}};b.bindAll=function(a){var c=i.call(arguments,1);if(c.length==0)c=b.functions(a);j(c,function(d){a[d]=b.bind(a[d],a)});return a};
10
+ b.memoize=function(a,c){var d={};c=c||b.identity;return function(){var e=c.apply(this,arguments);return e in d?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,c){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.wrap=function(a,c){return function(){var d=[a].concat(i.call(arguments));return c.apply(c,d)}};b.compose=function(){var a=i.call(arguments);return function(){for(var c=i.call(arguments),
11
+ d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=D||function(a){if(b.isArray(a))return b.range(0,a.length);var c=[],d;for(d in a)if(p.call(a,d))c[c.length]=d;return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){j(i.call(arguments,1),function(c){for(var d in c)a[d]=c[d]});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=
12
+ function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return false;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;
13
+ d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(p.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=m||function(a){return!!(a&&a.concat&&a.unshift&&!a.callee)};b.isArguments=function(a){return!!(a&&a.callee)};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};
14
+ b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||C.call(a)==="[object Number]"};b.isBoolean=function(a){return a===true||a===false};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=
15
+ function(){o._=A;return this};b.identity=function(a){return a};b.times=function(a,c,d){for(var e=0;e<a;e++)c.call(d,e)};b.breakLoop=function(){throw r;};b.mixin=function(a){j(b.functions(a),function(c){E(c,b[c]=a[c])})};var F=0;b.uniqueId=function(a){var c=F++;return a?a+c:c};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/'/g,
16
+ "\\'").replace(d.interpolate,function(e,f){return"',"+f.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(e,f){return"');"+f.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d};var l=function(a){this._wrapped=a};b.prototype=l.prototype;var q=function(a,c){return c?b(a).chain():a},E=function(a,c){l.prototype[a]=function(){var d=i.call(arguments);B.call(d,
17
+ this._wrapped);return q(c.apply(b,d),this._chain)}};b.mixin(b);j(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=k[a];l.prototype[a]=function(){c.apply(this._wrapped,arguments);return q(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var c=k[a];l.prototype[a]=function(){return q(c.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}})();
data/example/xreq_xrep.rb CHANGED
@@ -2,24 +2,22 @@ require 'dripdrop/node'
2
2
  Thread.abort_on_exception = true
3
3
 
4
4
  DripDrop::Node.new do
5
- z_addr = 'tcp://127.0.0.1:2200'
5
+ route :xrep_server, :zmq_xrep, 'tcp://127.0.0.1:2200', :bind
6
+ route :xreq_client, :zmq_xreq, xrep_server.address, :connect
6
7
 
7
- rep = zmq_xrep(z_addr, :bind)
8
- rep.on_recv do |message,identities,seq|
8
+ xrep_server.on_recv do |message,response|
9
9
  puts "REP #{message.body}"
10
- rep.send_message(message,identities,seq)
10
+ response.send_message(message)
11
11
  end
12
12
 
13
- req = zmq_xreq(z_addr, :connect)
14
-
15
- i = 0
16
- k = 0
17
-
13
+ i = 0; k = 0
18
14
  zm_reactor.periodical_timer(1000) do
19
- req.send_message(:name => 'test', :body => "Test Payload i#{i}") do |message|
15
+ i += 1; k += 1
16
+
17
+ xreq_client.send_message(:name => 'test', :body => "Test Payload i#{i}") do |message|
20
18
  puts "RECV I RESP #{message.inspect}"
21
19
  end
22
- req.send_message(:name => 'test', :body => "Test Payload k#{i}") do |message|
20
+ xreq_client.send_message(:name => 'test', :body => "Test Payload k#{i}") do |message|
23
21
  puts "RECV K RESP #{message.inspect}"
24
22
  end
25
23
  end
data/js/dripdrop.js CHANGED
@@ -9,7 +9,7 @@ function DripDrop() {
9
9
  this.head = (opts && opts.head !== undefined) ? opts.head : {empty:''};
10
10
 
11
11
  this.jsonEncoded = function() {
12
- return $.JSON.encode({name: this.name, head: this.head, body: this.body});
12
+ return JSON.stringify({name: this.name, head: this.head, body: this.body});
13
13
  };
14
14
  };
15
15
 
@@ -22,6 +22,7 @@ function DripDrop() {
22
22
 
23
23
  this.onOpen = function(callback) {
24
24
  this.socket.onopen = callback;
25
+ return this;
25
26
  };
26
27
 
27
28
  this.onRecv = function(callback) {
@@ -30,20 +31,23 @@ function DripDrop() {
30
31
  var message = new DD.Message(json.name, {head: json.head, body: json.body});
31
32
 
32
33
  callback(message);
33
- return this;
34
34
  }
35
+ return this;
35
36
  };
36
37
 
37
38
  this.onClose = function(callback) {
38
39
  this.socket.onclose = callback;
40
+ return this;
39
41
  };
40
42
 
41
43
  this.onError = function(callback) {
42
44
  this.socket.onerror = callback;
45
+ return this;
43
46
  };
44
47
 
45
48
  this.sendMessage = function(message) {
46
49
  this.socket.send(message.jsonEncoded());
50
+ return this;
47
51
  };
48
52
  };
49
53
 
@@ -0,0 +1,18 @@
1
+ class DripDrop
2
+ class BaseHandler
3
+
4
+ private
5
+ # Normalize Hash objs and DripDrop::Message objs into DripDrop::Message objs
6
+ def dd_messagify(message)
7
+ if message.is_a?(Hash)
8
+ return DripDrop::Message.new(message[:name], :head => message[:head],
9
+ :body => message[:body])
10
+ elsif message.is_a?(DripDrop::Message)
11
+ return message
12
+ else
13
+ return message
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -2,7 +2,7 @@ require 'thin'
2
2
  require 'json'
3
3
 
4
4
  class DripDrop
5
- class HTTPDeferrableBody
5
+ class HTTPDeferrableBody < BaseHandler
6
6
  include EventMachine::Deferrable
7
7
 
8
8
  def call(body)
@@ -15,13 +15,14 @@ class DripDrop
15
15
  @body_callback = blk
16
16
  end
17
17
 
18
- def send_message(msg)
18
+ def send_message(raw_msg)
19
+ msg = dd_messagify(raw_msg)
19
20
  if msg.class == DripDrop::Message
20
21
  json = msg.encode_json
21
22
  self.call([json])
22
23
  self.succeed
23
24
  else
24
- raise "Message Type not supported"
25
+ raise "Message Type '#{msg.class}' not supported"
25
26
  end
26
27
  end
27
28
  end
@@ -33,7 +34,6 @@ class DripDrop
33
34
  def initialize(msg_format,&block)
34
35
  @msg_format = msg_format
35
36
  @recv_cbak = block
36
- super
37
37
  end
38
38
 
39
39
  def call(env)
@@ -45,8 +45,7 @@ class DripDrop
45
45
  case @msg_format
46
46
  when :dripdrop_json
47
47
  msg = DripDrop::Message.decode_json(env['rack.input'].read)
48
- msg.head[:http_env] = env
49
- @recv_cbak.call(msg,body)
48
+ @recv_cbak.call(msg,body,env)
50
49
  else
51
50
  raise "Unsupported message type #{@msg_format}"
52
51
  end
@@ -57,43 +56,44 @@ class DripDrop
57
56
  end
58
57
  end
59
58
 
60
- class HTTPServerHandler
59
+ class HTTPServerHandler < BaseHandler
61
60
  attr_reader :address, :opts
62
61
 
63
- def initialize(address,opts={})
64
- @address = address
62
+ def initialize(uri,opts={})
63
+ @uri = uri
64
+ @address = uri.to_s
65
65
  @opts = opts
66
66
  end
67
67
 
68
68
  def on_recv(msg_format=:dripdrop_json,&block)
69
- #Rack middleware was not meant to be used this way...
70
69
  #Thin's error handling only rescues stuff w/o a backtrace
71
70
  begin
72
- Thin::Logging.debug = false
73
- Thin::Logging.trace = false
74
- Thin::Server.start(@address.host, @address.port) do
71
+ Thin::Logging.silent = true
72
+
73
+ Thin::Server.start(@uri.host, @uri.port) do
75
74
  map '/' do
76
75
  run HTTPApp.new(msg_format,&block)
77
76
  end
78
77
  end
79
78
  rescue Exception => e
80
- puts e.message; puts e.backtrace.join("\n");
79
+ puts "Error in Thin server: #{e.message}\n#{e.backtrace.join("\n")}"
81
80
  end
82
81
  end
83
82
  end
84
83
 
85
- class HTTPClientHandler
84
+ class HTTPClientHandler < BaseHandler
86
85
  attr_reader :address, :opts
87
86
 
88
- def initialize(address, opts={})
89
- @address = address
87
+ def initialize(uri, opts={})
88
+ @uri = uri
89
+ @address = @uri.to_s
90
90
  @opts = opts
91
91
  end
92
92
 
93
93
  def send_message(msg,&block)
94
94
  if msg.class == DripDrop::Message
95
95
  req = EM::Protocols::HttpClient.request(
96
- :host => address.host, :port => address.port,
96
+ :host => @uri.host, :port => @uri.port,
97
97
  :request => '/', :verb => 'POST',
98
98
  :contenttype => 'application/json',
99
99
  :content => msg.encode_json
@@ -2,39 +2,33 @@ require 'em-websocket'
2
2
  require 'json'
3
3
 
4
4
  class DripDrop
5
- class WebSocketHandler
5
+ class WebSocketHandler < BaseHandler
6
6
  attr_reader :ws, :address, :thread
7
7
 
8
8
  def initialize(address,opts={})
9
9
  @raw = false #Deal in strings or ZMQ::Message objects
10
- @thread = Thread.new do
11
- host, port = address.host, address.port.to_i
12
- @debug = opts[:debug] || false
10
+ host, port = address.host, address.port.to_i
11
+ @debug = opts[:debug] || false
13
12
 
14
- ws_conn = EventMachine::WebSocket::Connection
15
- EventMachine::start_server(host,port,ws_conn,:debug => @debug) do |ws|
16
- @ws = ws
17
- @ws.onopen do
18
- @onopen_handler.call(ws) if @onopen_handler
19
- end
20
- @ws.onmessage do |message|
21
- unless @raw
22
- begin
23
- parsed = JSON.parse(message)
24
- message = DripDrop::Message.new(parsed['name'], :body => parsed['body'], :head => parsed['head'] || {})
25
- rescue StandardError => e
26
- puts "Could not parse message: #{e.message}"
27
- end
28
- end
29
- @onmessage_handler.call(message,ws) if @onmessage_handler
30
- end
31
- @ws.onclose do
32
- @onclose_handler.call(@ws) if @onclose_handler
33
- end
34
- @ws.onerror do
35
- @onerror_handler.call(@ws) if @onerror_handler
13
+ EventMachine::WebSocket.start(:host => host,:port => port,:debug => @debug) do |ws|
14
+ #A WebSocketHandler:Connection gets passed to all callbacks
15
+ dd_conn = Connection.new(ws)
16
+
17
+ ws.onopen { @onopen_handler.call(dd_conn) if @onopen_handler }
18
+ ws.onclose { @onclose_handler.call(dd_conn) if @onclose_handler }
19
+ ws.onerror { @onerror_handler.call(dd_conn) if @onerror_handler }
20
+
21
+ ws.onmessage do |message|
22
+ if @onmessage_handler
23
+ begin
24
+ message = DripDrop::Message.decode_json(message) unless @raw
25
+ rescue StandardError => e
26
+ puts "Could not parse message: #{e.message}"
36
27
  end
28
+
29
+ @onmessage_handler.call(message,dd_conn)
37
30
  end
31
+ end
38
32
  end
39
33
  end
40
34
 
@@ -65,4 +59,17 @@ class DripDrop
65
59
  self
66
60
  end
67
61
  end
62
+
63
+ class WebSocketHandler::Connection < BaseHandler
64
+ attr_reader :ws, :signature, :handler
65
+
66
+ def initialize(ws)
67
+ @ws = ws
68
+ @signature = @ws.signature
69
+ end
70
+
71
+ def send_message(message)
72
+ @ws.send(dd_messagify(message).to_hash.to_json)
73
+ end
74
+ end
68
75
  end
@@ -8,11 +8,12 @@ class DripDrop
8
8
  DripDrop.default_message_class = DripDrop::Message
9
9
  end
10
10
 
11
- class ZMQBaseHandler
11
+ class ZMQBaseHandler < BaseHandler
12
12
  attr_reader :address, :socket_ctype, :socket
13
13
 
14
- def initialize(address,zm_reactor,socket_ctype,opts={})
15
- @address = address
14
+ def initialize(zaddress,zm_reactor,socket_ctype,opts={})
15
+ @zaddress = zaddress
16
+ @address = @zaddress.to_s
16
17
  @zm_reactor = zm_reactor
17
18
  @socket_ctype = socket_ctype # :bind or :connect
18
19
  @debug = opts[:debug] # TODO: Start actually using this
@@ -22,9 +23,9 @@ class DripDrop
22
23
  def on_attach(socket)
23
24
  @socket = socket
24
25
  if @socket_ctype == :bind
25
- socket.bind(@address)
26
+ socket.bind(@zaddress)
26
27
  elsif @socket_ctype == :connect
27
- socket.connect(@address)
28
+ socket.connect(@zaddress)
28
29
  else
29
30
  raise "Unsupported socket ctype '#{@socket_ctype}'. Expected :bind or :connect"
30
31
  end
@@ -35,20 +36,6 @@ class DripDrop
35
36
  @recv_cbak = block
36
37
  self
37
38
  end
38
-
39
- private
40
-
41
- # Normalize Hash objs and DripDrop::Message objs into DripDrop::Message objs
42
- def dd_messagify(message)
43
- if message.is_a?(Hash)
44
- return DripDrop::Message.new(message[:name], :head => message[:head],
45
- :body => message[:body])
46
- elsif message.is_a?(DripDrop::Message)
47
- return message
48
- else
49
- return message
50
- end
51
- end
52
39
  end
53
40
 
54
41
  module ZMQWritableHandler
@@ -127,8 +114,12 @@ class DripDrop
127
114
  class ZMQSubHandler < ZMQBaseHandler
128
115
  include ZMQReadableHandler
129
116
 
130
- attr_reader :address, :socket_ctype
131
117
  attr_accessor :topic_filter
118
+
119
+ def initialize(*args)
120
+ super(*args)
121
+ self.topic_filter = @opts[:topic_filter]
122
+ end
132
123
 
133
124
  def on_attach(socket)
134
125
  super(socket)
@@ -187,10 +178,11 @@ class DripDrop
187
178
  def on_readable(socket,messages)
188
179
  if @msg_format == :dripdrop
189
180
  identities = messages[0..-2].map {|m| m.copy_out_string}
190
- body = messages.last.copy_out_string
191
- message = decode_message(body)
192
- seq = message.head['_dripdrop/x_seq_counter']
193
- @recv_cbak.call(message,identities,seq)
181
+ body = messages.last.copy_out_string
182
+ message = decode_message(body)
183
+ seq = message.head['_dripdrop/x_seq_counter']
184
+ response = ZMQXRepHandler::Response.new(self, identities,seq)
185
+ @recv_cbak.call(message,response)
194
186
  else
195
187
  super(socket,messages)
196
188
  end
@@ -205,6 +197,20 @@ class DripDrop
205
197
  end
206
198
  end
207
199
  end
200
+
201
+ class ZMQXRepHandler::Response
202
+ attr_accessor :xrep, :seq, :identities
203
+
204
+ def initialize(xrep,identities,seq)
205
+ @xrep = xrep
206
+ @seq = seq
207
+ @identities = identities
208
+ end
209
+
210
+ def send_message(message)
211
+ @xrep.send_message(message,identities,seq)
212
+ end
213
+ end
208
214
 
209
215
  class ZMQXReqHandler < ZMQBaseHandler
210
216
  include ZMQWritableHandler
@@ -97,6 +97,11 @@ class DripDrop
97
97
  puts "Could not parse msg '#{str}': #{e.message}"
98
98
  return nil
99
99
  end
100
+
101
+ # Keep this consistent
102
+ json_hash['head'][:msg_class] = json_hash['head'][:msg_class]
103
+ json_hash['head'].delete('msg_class')
104
+
100
105
  self.new(json_hash['name'], :head => json_hash['head'], :body => json_hash['body'])
101
106
  end
102
107
  end
@@ -0,0 +1,29 @@
1
+ class DripDrop::Node
2
+ # See the documentation for +nodelet+ in DripDrop::Node
3
+ class Nodelet
4
+ attr_accessor :name, :routing
5
+
6
+ def initialize(name, routes)
7
+ @name = name
8
+ @routing = {}
9
+ routes.each do |route_name,handler|
10
+ # Copy the original routing table
11
+ route route_name, handler
12
+
13
+ # Define short versions of the local routes for
14
+ # this nodelet's routing table
15
+ if (route_name.to_s =~ /^#{name}_(.+)$/)
16
+ short_name = $1
17
+ route short_name, handler
18
+ end
19
+ end
20
+ end
21
+
22
+ def route(name,handler)
23
+ @routing[name] = handler
24
+ (class << self; self; end).class_eval do
25
+ define_method(name) { handler }
26
+ end
27
+ end
28
+ end
29
+ end