dripdrop 0.10.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.document +5 -0
  2. data/.gitignore +31 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +20 -0
  5. data/README.md +164 -0
  6. data/Rakefile +16 -0
  7. data/dripdrop.gemspec +37 -0
  8. data/example/agent_test.rb +14 -0
  9. data/example/combined.rb +33 -0
  10. data/example/complex/README +22 -0
  11. data/example/complex/client.rb +20 -0
  12. data/example/complex/server.rb +102 -0
  13. data/example/complex/service.rb +8 -0
  14. data/example/complex/websocket.rb +442 -0
  15. data/example/http.rb +23 -0
  16. data/example/pubsub.rb +29 -0
  17. data/example/pushpull.rb +21 -0
  18. data/example/subclass.rb +54 -0
  19. data/example/xreq_xrep.rb +24 -0
  20. data/js/dripdrop.html +186 -0
  21. data/js/dripdrop.js +107 -0
  22. data/js/qunit.css +155 -0
  23. data/js/qunit.js +1261 -0
  24. data/lib/dripdrop.rb +2 -0
  25. data/lib/dripdrop/agent.rb +40 -0
  26. data/lib/dripdrop/handlers/base.rb +42 -0
  27. data/lib/dripdrop/handlers/http_client.rb +38 -0
  28. data/lib/dripdrop/handlers/http_server.rb +59 -0
  29. data/lib/dripdrop/handlers/mongrel2.rb +163 -0
  30. data/lib/dripdrop/handlers/websocket_server.rb +86 -0
  31. data/lib/dripdrop/handlers/zeromq.rb +300 -0
  32. data/lib/dripdrop/message.rb +190 -0
  33. data/lib/dripdrop/node.rb +351 -0
  34. data/lib/dripdrop/node/nodelet.rb +35 -0
  35. data/lib/dripdrop/version.rb +3 -0
  36. data/spec/gimite-websocket.rb +442 -0
  37. data/spec/message_spec.rb +94 -0
  38. data/spec/node/http_spec.rb +77 -0
  39. data/spec/node/nodelet_spec.rb +67 -0
  40. data/spec/node/routing_spec.rb +67 -0
  41. data/spec/node/websocket_spec.rb +98 -0
  42. data/spec/node/zmq_m2_spec.rb +77 -0
  43. data/spec/node/zmq_pushpull_spec.rb +54 -0
  44. data/spec/node/zmq_xrepxreq_spec.rb +108 -0
  45. data/spec/node_spec.rb +85 -0
  46. data/spec/spec_helper.rb +20 -0
  47. metadata +167 -0
data/example/http.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'dripdrop/node'
2
+ Thread.abort_on_exception = true
3
+
4
+ DripDrop::Node.new do
5
+ addr = 'http://127.0.0.1:2200'
6
+
7
+ i = 0
8
+ http_server(addr).on_recv do |msg,response|
9
+ i += 1
10
+ response.send_message(msg)
11
+ end
12
+
13
+ EM::PeriodicTimer.new(1) do
14
+ client = http_client(addr)
15
+ msg = DripDrop::Message.new('http/status', :body => "Success #{i}")
16
+ client.send_message(msg) do |resp_msg|
17
+ puts "RESP: #{resp_msg.inspect}"
18
+ end
19
+ end
20
+
21
+ #Keep zmqmachine from spinning around using up all our CPU by creating a socket
22
+ req = zmq_xreq('tcp://127.0.0.1:2091', :connect)
23
+ end.start!
data/example/pubsub.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'dripdrop/node'
3
+ Thread.abort_on_exception = true
4
+
5
+ #Define our handlers
6
+ DripDrop::Node.new do
7
+ route :pub, :zmq_publish, 'tcp://localhost:2200', :bind
8
+ route :sub1, :zmq_subscribe, pub.address, :connect, :topic_filter => /[13579]$/
9
+ route :sub2, :zmq_subscribe, pub.address, :connect, :topic_filter => /[02468]$/
10
+ route :sub3, :zmq_subscribe, pub.address, :connect
11
+
12
+ sub1.on_recv do |message|
13
+ puts "Receiver 1 #{message.inspect}"
14
+ end
15
+
16
+ sub2.on_recv do |message|
17
+ puts "Receiver 2 #{message.inspect}"
18
+ end
19
+
20
+ sub3.on_recv do |message|
21
+ puts "Receiver 3 #{message.inspect}"
22
+ end
23
+
24
+ zm_reactor.periodical_timer(500) do
25
+ puts "Sending!"
26
+ #Sending a hash as a message implicitly transforms it into a DripDrop::Message
27
+ pub.send_message(:name => Time.now.to_i.to_s, :body => 'Test Payload')
28
+ end
29
+ end.start! #Start the reactor and block until complete
@@ -0,0 +1,21 @@
1
+ require 'dripdrop/node'
2
+ Thread.abort_on_exception = true
3
+
4
+ DripDrop::Node.new do
5
+ z_addr = 'tcp://127.0.0.1:2200'
6
+
7
+ zmq_pull(z_addr, :connect).on_recv do |message|
8
+ puts "Receiver 2 #{message.body}"
9
+ end
10
+ zmq_pull(z_addr, :connect).on_recv do |message|
11
+ puts "Receiver 1 #{message.body}"
12
+ end
13
+ push = zmq_push(z_addr, :bind)
14
+
15
+ i = 0
16
+ EM::PeriodicTimer.new(1) do
17
+ i += 1
18
+ puts i
19
+ push.send_message(:name => 'test', :body => "Test Payload #{i}")
20
+ end
21
+ end.start!
@@ -0,0 +1,54 @@
1
+ require 'dripdrop'
2
+ Thread.abort_on_exception = true
3
+
4
+ #We will create a subclass of the Message class
5
+ #which will add a timestamp to the header every
6
+ #time it is passed around
7
+
8
+ #First our subclass
9
+
10
+ class TimestampedMessage < DripDrop::Message
11
+ def self.create_message(*args)
12
+ obj = super
13
+ obj.head['timestamps'] = []
14
+ obj.head['timestamps'] << Time.now.to_s
15
+ obj
16
+ end
17
+
18
+ def self.recreate_message(*args)
19
+ obj = super
20
+ obj.head['timestamps'] << Time.now.to_s
21
+ obj
22
+ end
23
+ end
24
+
25
+ #Define our handlers
26
+ #We'll create a batch of 5 push/pull queues them to
27
+ #show the timestamp array getting larger
28
+ #as we go along
29
+
30
+ DripDrop.default_message_class = TimestampedMessage
31
+
32
+ node = DripDrop::Node.new do
33
+ push1 = zmq_push("tcp://127.0.0.1:2201", :bind)
34
+ push2 = zmq_push("tcp://127.0.0.1:2202", :bind)
35
+
36
+ pull1 = zmq_pull("tcp://127.0.0.1:2201", :connect)
37
+ pull2 = zmq_pull("tcp://127.0.0.1:2202", :connect)
38
+
39
+ pull1.on_recv do |msg|
40
+ puts "Pull 1 #{msg.head.inspect}"
41
+ sleep 1
42
+ push2.send_message(msg)
43
+ end
44
+
45
+ pull2.on_recv do |msg|
46
+ puts "Pull 2 #{msg.head.inspect}"
47
+ end
48
+
49
+ push1.send_message(TimestampedMessage.create_message(:name => 'test', :body => "Hello there"))
50
+ end
51
+
52
+ node.start
53
+ sleep 5
54
+ node.stop
@@ -0,0 +1,24 @@
1
+ require 'dripdrop/node'
2
+ Thread.abort_on_exception = true
3
+
4
+ DripDrop::Node.new do
5
+ route :xrep_server, :zmq_xrep, 'tcp://127.0.0.1:2200', :bind
6
+ route :xreq_client, :zmq_xreq, xrep_server.address, :connect
7
+
8
+ xrep_server.on_recv do |message,response|
9
+ puts "REP #{message.body}"
10
+ response.send_message(message)
11
+ end
12
+
13
+ i = 0; k = 0
14
+ EM::PeriodicTimer.new(1) do
15
+ i += 1; k += 1
16
+
17
+ xreq_client.send_message(:name => 'test', :body => "Test Payload i#{i}") do |message|
18
+ puts "RECV I RESP #{message.inspect}"
19
+ end
20
+ xreq_client.send_message(:name => 'test', :body => "Test Payload k#{i}") do |message|
21
+ puts "RECV K RESP #{message.inspect}"
22
+ end
23
+ end
24
+ end.start!
data/js/dripdrop.html ADDED
@@ -0,0 +1,186 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2
+ "http://www.w3.org/TR/html4/loose.dtd">
3
+ <html>
4
+ <head>
5
+ <script src="../jquery.js"></script>
6
+ <script src="../jquery.json.js"></script>
7
+ <script src="../dripdrop.js"></script>
8
+
9
+ <link rel="stylesheet" href="qunit.css" type="text/css" media="screen" />
10
+ <script type="text/javascript" src="qunit.js"></script>
11
+ <script type="text/javascript" src="jack.js"></script>
12
+
13
+ <script>
14
+ $(document).ready(function(){
15
+
16
+ /* Test Helpers */
17
+ function TSock() {return new DD.WebSocket('ws://localhost:2702')};
18
+ function THTTP() {return new DD.HTTP('http://localhost:2703')};
19
+
20
+ //Faked message to for a websocket
21
+ function FakeWSMessage(ddMessage) {
22
+ var msg = (ddMessage === undefined) ? new DD.Message('test') : ddMessage;
23
+ this.data = msg.jsonEncoded();
24
+ }
25
+
26
+ /* Tests */
27
+
28
+ test("The DD Singleton should be constructed from DripDrop", function() {
29
+ equals( DripDrop, DD.constructor, "Match");
30
+ });
31
+
32
+ module("DD.Message");
33
+
34
+ var msgVals = {
35
+ name: 'foo',
36
+ body: 'bar',
37
+ head: {key: 'baz'}
38
+ }
39
+ test("Creation: Name Only", function() {
40
+ var msg = new DD.Message(msgVals.name);
41
+ equals(msg.name, msgVals.name, "name");
42
+ });
43
+ test("Creation: Name + Body", function() {
44
+ var msg = new DD.Message(msgVals.name, {body : msgVals.body});
45
+ equals(msg.name, msgVals.name, "name");
46
+ equals(msg.body, msgVals.body, "body");
47
+ });
48
+ test("Creation: Name + Head + Body", function() {
49
+ var msg = new DD.Message(msgVals.name,
50
+ {body : msgVals.body, head : msgVals.head});
51
+ expect(3);
52
+ equals(msg.name, msgVals.name, "name");
53
+ equals(msg.head, msgVals.head, "head");
54
+ equals(msg.body, msgVals.body, "body");
55
+ });
56
+ test("Creation: No Head should still have head as an object", function() {
57
+ var msg = new DD.Message(msgVals.name);
58
+ equals(msg.head.constructor, Object, "head");
59
+ });
60
+
61
+ test("JSON Export", function() {
62
+ var msg = new DD.Message(msgVals.name,
63
+ {body : msgVals.body, head : msgVals.head});
64
+ equals(msg.jsonEncoded().constructor, String, "jsonEncoded");
65
+ });
66
+
67
+ module("DD.WebSocket");
68
+
69
+ test("Creating a websocket should make available the raw socket", function() {
70
+ var tSock = new TSock;
71
+ equals(tSock.socket.constructor, WebSocket,'raw socket');
72
+ });
73
+
74
+ $([['onOpen','onopen'],
75
+ ['onClose','onclose'],
76
+ ['onError','onerror']]).each(function(i,pair) {
77
+ var dd_func = pair[0];
78
+ var ws_func = pair[1];
79
+
80
+ test("DD function " + dd_func + " should set WS func " + ws_func, function() {
81
+ var tSock = new TSock;
82
+ var tFunc = function() {};
83
+ tSock[dd_func](tFunc);
84
+ equals(tSock.socket[ws_func], tFunc, "Func Set");
85
+ });
86
+ });
87
+
88
+ test("onRecv should trigger the passed in callback", function() {
89
+ expect(3);
90
+
91
+ var expectedMsg = new DD.Message('foo');
92
+
93
+ var tSock = new TSock;
94
+ tSock.onRecv(function(msg) {
95
+ ok(true, "Function executed");
96
+ equals(msg.constructor,DD.Message, "Is a DD.Message");
97
+ equals(msg.name, expectedMsg.name);
98
+ });
99
+
100
+ tSock.socket.onmessage(new FakeWSMessage(expectedMsg));
101
+ });
102
+
103
+ test("sendMessage should send a string to the websocket", function() {
104
+ expect(1);
105
+
106
+ var tSock = new TSock;
107
+ var tMsg = new DD.Message('foo');
108
+ tSock.socket.send = function(message) {
109
+ equals(tMsg.jsonEncoded(), message);
110
+ }
111
+ console.log(tSock.sendMessage)
112
+ tSock.sendMessage(tMsg);
113
+ });
114
+
115
+ module("DD.HTTP");
116
+
117
+ test("Creation", function() {
118
+ var dht = new DD.HTTP;
119
+ equals(dht.constructor, DD.HTTP, "Constructor Match");
120
+ });
121
+
122
+ test("Sending a message should call a get posting a JSON representation of the data",function() {
123
+ expect(1);
124
+ var tHTTP = new THTTP;
125
+ tHTTP.sendMessage();
126
+ });
127
+
128
+
129
+ module("DD.Pipeline");
130
+
131
+ test("Creation", function() {
132
+ var pl = new DD.Pipeline;
133
+ equals(pl.constructor, DD.Pipeline, "Constructor Match");
134
+ });
135
+
136
+ test("Should expose the pipeline stages as an array", function() {
137
+ var pl = new DD.Pipeline;
138
+ equals(pl.stages.constructor, Array, "Constructor Match");
139
+ });
140
+
141
+ test("Should return null when no stages", function() {
142
+ equals((new DD.Pipeline).execute(new DD.Message('test')), null);
143
+ });
144
+
145
+ test("Should return null when no stages", function() {
146
+ equals((new DD.Pipeline).execute(new DD.Message('test')), null);
147
+ });
148
+
149
+ //This tests too many things at once, should be broken apart
150
+ test("Execution, Order, Message Transformation", function() {
151
+ expect(5);
152
+
153
+ var pl = new DD.Pipeline;
154
+
155
+ var s1 = new DD.PipelineStage('t1','Test1',function(message) {
156
+ ok(true,"Test1 Executed");
157
+ equals(message.name,0,"Executed first job first");
158
+ message.name = 1;
159
+ return message;
160
+ });
161
+ var s2 = new DD.PipelineStage('t2','Test2',function(message) {
162
+ ok(true,"Test2 Executed");
163
+ equals(message.name,1,"Executed second job after first");
164
+ message.name = 2;
165
+
166
+ return message;
167
+ });
168
+ pl.stages = [s1,s2];
169
+
170
+ var resMsg = pl.execute(new DD.Message('0'));
171
+ equals(resMsg.name,2);
172
+ });
173
+
174
+ }); //End of tests
175
+
176
+ </script>
177
+
178
+ </head>
179
+ <body>
180
+ <h1 id="qunit-header">DripDrop Tests</h1>
181
+ <h2 id="qunit-banner"></h2>
182
+ <h2 id="qunit-userAgent"></h2>
183
+ <ol id="qunit-tests"></ol>
184
+ <div id="qunit-fixture">test markup, will be hidden</div>
185
+ </body>
186
+ </html>
data/js/dripdrop.js ADDED
@@ -0,0 +1,107 @@
1
+ function DripDrop() {
2
+ /* JavaScript object for DripDrop Messages */
3
+ this.Message = function(name,opts) {
4
+ this.name = name;
5
+ if (opts && opts.body) {
6
+ this.body = opts.body;
7
+ };
8
+
9
+ this.head = (opts && opts.head !== undefined) ? opts.head : {empty:''};
10
+
11
+ this.jsonEncoded = function() {
12
+ return JSON.stringify({name: this.name, head: this.head, body: this.body});
13
+ };
14
+ };
15
+
16
+ /* A DripDrop friendly WebSocket Object.
17
+ This automatically converts messages to DD.Message objects.
18
+ Additionally, this uses friendlier callback methods, closer to the DripDrop
19
+ server-side API, like onOpen, onRecv, onError, and onClose. */
20
+ this.WebSocket = function(url) {
21
+ this.socket = new WebSocket(url);
22
+
23
+ this.onOpen = function(callback) {
24
+ this.socket.onopen = callback;
25
+ return this;
26
+ };
27
+
28
+ this.onRecv = function(callback) {
29
+ this.socket.onmessage = function(wsMessage) {
30
+ var json = $.parseJSON(wsMessage.data)
31
+ var message = new DD.Message(json.name, {head: json.head, body: json.body});
32
+
33
+ callback(message);
34
+ }
35
+ return this;
36
+ };
37
+
38
+ this.onClose = function(callback) {
39
+ this.socket.onclose = callback;
40
+ return this;
41
+ };
42
+
43
+ this.onError = function(callback) {
44
+ this.socket.onerror = callback;
45
+ return this;
46
+ };
47
+
48
+ this.sendMessage = function(message) {
49
+ this.socket.send(message.jsonEncoded());
50
+ return this;
51
+ };
52
+ };
53
+
54
+ this.HTTPResponse = function() {
55
+
56
+ };
57
+
58
+ /* A DripDrop friendly HTTP Request. */
59
+ this.HTTP = function(url) {
60
+ this.url = url;
61
+
62
+ this.onRecv = function(data) {};
63
+ this.sendMessage = function() {
64
+ var response = new this.HTTPResponse;
65
+ $.post(this.url, function(json) {
66
+ this.onRecv(new DD.Message(json.name, {head: json.head, body: json.body}));
67
+ });
68
+ };
69
+ };
70
+
71
+ /* An Object for reperesenting pipeline processing.
72
+ Ex:
73
+ var mypl = new DD.Pipeline;
74
+ mypl.stages.push(new DD.PipelineStage{'namecapper','Name Capper',
75
+ function(message) { message.name = message.name.toUpperCase() });
76
+ mypl.execute(message); //Message must be a valid DD.Message
77
+
78
+
79
+ All functions must either return a message, or false.
80
+ If false is returned the pipeline short-circuits and returns false, not running
81
+ subsequent stages */
82
+ this.PipelineStage = function(id,name,action) {
83
+ this.id = id;
84
+ this.name = name;
85
+ this.action = action;
86
+ };
87
+
88
+ this.Pipeline = function() {
89
+ this.stages = [];
90
+
91
+ this.execute = function(message) {
92
+ if (this.stages.length == 0) {
93
+ return null;
94
+ };
95
+
96
+ for (var i=0,l=this.stages.length; i < l; i++) {
97
+ var stage = this.stages[i];
98
+ message = stage.action(message);
99
+ };
100
+
101
+ return message;
102
+ };
103
+ };
104
+ };
105
+
106
+ //Use this as shorthand
107
+ DD = new DripDrop;