dripdrop 0.10.0-java
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.
- data/.document +5 -0
- data/.gitignore +31 -0
- data/Gemfile +5 -0
- data/LICENSE +20 -0
- data/README.md +164 -0
- data/Rakefile +16 -0
- data/dripdrop.gemspec +37 -0
- data/example/agent_test.rb +14 -0
- data/example/combined.rb +33 -0
- data/example/complex/README +22 -0
- data/example/complex/client.rb +20 -0
- data/example/complex/server.rb +102 -0
- data/example/complex/service.rb +8 -0
- data/example/complex/websocket.rb +442 -0
- data/example/http.rb +23 -0
- data/example/pubsub.rb +29 -0
- data/example/pushpull.rb +21 -0
- data/example/subclass.rb +54 -0
- data/example/xreq_xrep.rb +24 -0
- data/js/dripdrop.html +186 -0
- data/js/dripdrop.js +107 -0
- data/js/qunit.css +155 -0
- data/js/qunit.js +1261 -0
- data/lib/dripdrop.rb +2 -0
- data/lib/dripdrop/agent.rb +40 -0
- data/lib/dripdrop/handlers/base.rb +42 -0
- data/lib/dripdrop/handlers/http_client.rb +38 -0
- data/lib/dripdrop/handlers/http_server.rb +59 -0
- data/lib/dripdrop/handlers/mongrel2.rb +163 -0
- data/lib/dripdrop/handlers/websocket_server.rb +86 -0
- data/lib/dripdrop/handlers/zeromq.rb +300 -0
- data/lib/dripdrop/message.rb +190 -0
- data/lib/dripdrop/node.rb +351 -0
- data/lib/dripdrop/node/nodelet.rb +35 -0
- data/lib/dripdrop/version.rb +3 -0
- data/spec/gimite-websocket.rb +442 -0
- data/spec/message_spec.rb +94 -0
- data/spec/node/http_spec.rb +77 -0
- data/spec/node/nodelet_spec.rb +67 -0
- data/spec/node/routing_spec.rb +67 -0
- data/spec/node/websocket_spec.rb +98 -0
- data/spec/node/zmq_m2_spec.rb +77 -0
- data/spec/node/zmq_pushpull_spec.rb +54 -0
- data/spec/node/zmq_xrepxreq_spec.rb +108 -0
- data/spec/node_spec.rb +85 -0
- data/spec/spec_helper.rb +20 -0
- 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
|
data/example/pushpull.rb
ADDED
@@ -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!
|
data/example/subclass.rb
ADDED
@@ -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;
|