droid 1.0.2pre → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +22 -22
- data/VERSION +1 -1
- data/bin/bleedq +1 -1
- data/droid.gemspec +2 -2
- data/examples/async_reply.rb +15 -15
- data/examples/heroku_async_reply.rb +12 -12
- data/examples/sync.rb +13 -13
- data/examples/worker.rb +46 -46
- data/lib/droid.rb +122 -122
- data/lib/droid/em.rb +53 -53
- data/lib/droid/heroku.rb +83 -83
- data/lib/droid/json_server.rb +104 -104
- data/lib/droid/monkey.rb +6 -6
- data/lib/droid/publish.rb +22 -25
- data/lib/droid/queue.rb +194 -194
- data/lib/droid/request.rb +106 -106
- data/lib/droid/sync.rb +74 -74
- data/lib/droid/utilization.rb +96 -96
- data/lib/droid/utils.rb +107 -107
- data/spec/publish_spec.rb +20 -20
- data/spec/response_spec.rb +43 -43
- data/spec/utils_spec.rb +39 -39
- data/spec/wait_for_port_spec.rb +9 -9
- metadata +5 -24
data/lib/droid/utils.rb
CHANGED
@@ -2,112 +2,112 @@ require 'socket'
|
|
2
2
|
require 'digest/md5'
|
3
3
|
|
4
4
|
class Droid
|
5
|
-
|
5
|
+
DEFAULT_TTL = 300
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
7
|
+
class BadPayload < RuntimeError; end
|
8
|
+
|
9
|
+
module Utils
|
10
|
+
def self.parse_message(raw)
|
11
|
+
return { } unless raw
|
12
|
+
JSON.parse(raw)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse_custom_headers(headers)
|
16
|
+
return { } unless headers
|
17
|
+
|
18
|
+
h = headers.dup
|
19
|
+
|
20
|
+
h[:published_on] = h[:published_on].to_i
|
21
|
+
|
22
|
+
h[:ttl] = h[:ttl].to_i rescue -1
|
23
|
+
h[:ttl] = -1 if h[:ttl] == 0
|
24
|
+
|
25
|
+
h
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.create_event_hash
|
29
|
+
s = Time.now.to_s + self.object_id.to_s + rand(100).to_s
|
30
|
+
'd' + Digest::MD5.hexdigest(s)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.extract_custom_headers(hash, opts={}, popts={})
|
34
|
+
popts[:headers] ||= {}
|
35
|
+
headers = popts[:headers]
|
36
|
+
|
37
|
+
headers[:published_on] ||= hash.delete('published_on') || opts[:published_on] || Time.now.getgm.to_i
|
38
|
+
headers[:ttl] ||= hash.delete('ttl') || (opts[:ttl] || Droid::DEFAULT_TTL).to_i
|
39
|
+
headers[:reply_to] ||= opts[:reply_to] if opts[:reply_to]
|
40
|
+
|
41
|
+
# this is the event hash that gets transferred through various publish/reply actions
|
42
|
+
headers[:event_hash] ||= hash.delete('event_hash') || opts[:event_hash] || create_event_hash
|
43
|
+
|
44
|
+
# this value should be unique for each published/received message pair
|
45
|
+
headers[:message_id] ||= create_event_hash
|
46
|
+
|
47
|
+
# some strange behavior with integers makes it better to
|
48
|
+
# convert all amqp headers to strings to avoid any problems
|
49
|
+
headers.each { |k,v| headers[k] = v.to_s }
|
50
|
+
|
51
|
+
[hash, headers]
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.format_publish(data, opts={}, popts={})
|
55
|
+
raise Droid::BadPayload unless data.is_a?(Hash)
|
56
|
+
|
57
|
+
hash, headers = extract_custom_headers(data, opts, popts)
|
58
|
+
|
59
|
+
popts[:content_type] ||= 'application/json'
|
60
|
+
|
61
|
+
[hash.to_json, popts]
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.generate_queue(exchange_name, second_name=nil)
|
65
|
+
second_name ||= $$
|
66
|
+
"#{generate_name_for_instance(exchange_name)}.#{second_name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.generate_name_for_instance(name)
|
70
|
+
"#{name}.#{Socket.gethostname}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.generate_reply_to(name)
|
74
|
+
"temp.reply.#{name}.#{self.generate_sym}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.generate_sym
|
78
|
+
values = [
|
79
|
+
rand(0x0010000),
|
80
|
+
rand(0x0010000),
|
81
|
+
rand(0x0010000),
|
82
|
+
rand(0x0010000),
|
83
|
+
rand(0x0010000),
|
84
|
+
rand(0x1000000),
|
85
|
+
rand(0x1000000),
|
86
|
+
]
|
87
|
+
"%04x%04x%04x%04x%04x%06x%06x" % values
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.data_summary(json)
|
91
|
+
return '-> (empty)' if json.empty?
|
92
|
+
summary = json.map do |k,v|
|
93
|
+
v = v.to_s
|
94
|
+
v = v[0..37] + '...' if v.size > 40
|
95
|
+
"#{k}=#{v}"
|
96
|
+
end.join(', ')
|
97
|
+
"-> #{summary}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.format_data_summary(data, headers)
|
101
|
+
"(data) " + Droid::Utils.data_summary(data) + " (headers) " + Droid::Utils.data_summary(headers)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Need this to be backwards compatible
|
106
|
+
def self.gen_queue(droid, name)
|
107
|
+
dn = droid
|
108
|
+
dn = dn.name if dn.respond_to?(:name)
|
109
|
+
dn ||= "d"
|
110
|
+
dn = dn.gsub(" ", "")
|
111
|
+
Droid::Utils.generate_queue(name, droid)
|
112
|
+
end
|
113
113
|
end
|
data/spec/publish_spec.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'AMQP Publish' do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
before do
|
5
|
+
@json, @publish_opts = Droid::Utils.format_publish({:x => 1, :y => 2}, {}, {})
|
6
|
+
Droid::Utils.stubs(:format_publish).with({:x => 1, :y => 2}, {}, {}).returns([@json, @publish_opts])
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
it "publishes a message to a queue" do
|
10
|
+
@q = mock('queue')
|
11
|
+
::MQ.expects(:queue).with('topic').returns(@q)
|
12
|
+
@q.expects(:publish).with(@json, @publish_opts)
|
13
|
+
Droid.publish_to_q('topic', :x => 1, :y => 2)
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
it "publishes a message to an exchange" do
|
17
|
+
@ex = mock('exchange')
|
18
|
+
::MQ.expects(:direct).with('topic').returns(@ex)
|
19
|
+
@ex.expects(:publish).with(@json, @publish_opts)
|
20
|
+
Droid.publish_to_ex('topic', :x => 1, :y => 2)
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
it "by default publishes to an exchange" do
|
24
|
+
Droid.expects(:publish_to_ex).with('topic', {:x => 1, :y => 2}, {}, {})
|
25
|
+
Droid.publish('topic', :x => 1, :y => 2)
|
26
|
+
end
|
27
27
|
end
|
data/spec/response_spec.rb
CHANGED
@@ -1,47 +1,47 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Request' do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
4
|
+
before do
|
5
|
+
@mq = mock("MQ")
|
6
|
+
@exchange = mock("MQ Exchange")
|
7
|
+
@queue = mock("MQ Queue")
|
8
|
+
|
9
|
+
@qobj = mock("Droid::BasicQueue Instance")
|
10
|
+
@qobj.stubs(:q).returns(@queue)
|
11
|
+
@qobj.stubs(:mq).returns(@mq)
|
12
|
+
@qobj.stubs(:ex).returns(@exchange)
|
13
|
+
|
14
|
+
@header = mock("amqp header", :headers => {})
|
15
|
+
|
16
|
+
@raw_message = '{"x":123,"y":"abc"}'
|
17
|
+
|
18
|
+
@res = Droid::Request.new(@qobj, @header, @raw_message)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "allows access to response via symbols or strings in array access" do
|
22
|
+
@res['x'].should == 123
|
23
|
+
@res[:x].should == 123
|
24
|
+
@res['y'].should == 'abc'
|
25
|
+
@res[:y].should == 'abc'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "calls ack on the header" do
|
29
|
+
@header.expects(:ack)
|
30
|
+
@res.ack
|
31
|
+
end
|
32
|
+
|
33
|
+
it "calls q on the qobj" do
|
34
|
+
@qobj.expects(:q)
|
35
|
+
@res.q
|
36
|
+
end
|
37
|
+
|
38
|
+
it "calls ex on the qobj" do
|
39
|
+
@qobj.expects(:ex)
|
40
|
+
@res.ex
|
41
|
+
end
|
42
|
+
|
43
|
+
it "calls mq on the qobj" do
|
44
|
+
@qobj.expects(:mq)
|
45
|
+
@res.mq
|
46
|
+
end
|
47
47
|
end
|
data/spec/utils_spec.rb
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'Utils' do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
4
|
+
before do
|
5
|
+
end
|
6
|
+
|
7
|
+
it "parses a json message" do
|
8
|
+
Droid::Utils.parse_message('{"x":1,"y":2}').should == {'x' => 1, 'y' => 2}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "parses custom headers and force integers on a few values" do
|
12
|
+
headers = {
|
13
|
+
:x => '1',
|
14
|
+
:y => '2',
|
15
|
+
:reply_to => 'q.random.reply',
|
16
|
+
:published_on => '12345',
|
17
|
+
:event_hash => 'x123'
|
18
|
+
}
|
19
|
+
|
20
|
+
h = Droid::Utils.parse_custom_headers(headers)
|
21
|
+
|
22
|
+
h.size.should == 6
|
23
|
+
h[:ttl].should == -1
|
24
|
+
h[:reply_to].should == 'q.random.reply'
|
25
|
+
h[:published_on].should == 12345
|
26
|
+
h[:event_hash].should == 'x123'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "raises an exception if the data to format for publish is not a hash" do
|
30
|
+
lambda { Droid::Utils.format_publish('bad payload') }.should.raise Droid::BadPayload
|
31
|
+
end
|
32
|
+
|
33
|
+
it "generates a name for the instance" do
|
34
|
+
Socket.stubs(:gethostname).returns('deepblue.123')
|
35
|
+
Droid::Utils.generate_name_for_instance('woo').should == 'woo.deepblue.123'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "generates queue name, really more like a generic identifier" do
|
39
|
+
Socket.stubs(:gethostname).returns('deepblue.123')
|
40
|
+
Droid::Utils.generate_queue('topic').should == "topic.deepblue.123.#{$$}"
|
41
|
+
Droid::Utils.generate_queue('topic', 'local').should == "topic.deepblue.123.local"
|
42
|
+
end
|
43
43
|
end
|
data/spec/wait_for_port_spec.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
Thread.new do
|
4
|
-
|
5
|
-
|
4
|
+
sleep 2
|
5
|
+
TCPServer.new('localhost', 20_001).accept.close
|
6
6
|
end
|
7
7
|
|
8
8
|
describe 'Connectivity' do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
it "waits for the rabbitmq server to come up before opening the amqp connection" do
|
10
|
+
start = Time.now
|
11
|
+
Droid.wait_for_tcp_port('localhost', 20_001)
|
12
|
+
finish = Time.now
|
13
|
+
(finish - start).should > 1
|
14
|
+
(finish - start).should < 3
|
15
|
+
end
|
16
16
|
end
|