droid 1.0.2pre → 1.0.2
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/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/Rakefile
CHANGED
@@ -2,35 +2,35 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
|
4
4
|
begin
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "droid"
|
8
|
+
gem.summary = %Q{AMQP Wrapper Library}
|
9
|
+
gem.description = %Q{Easy to use AMQP Library with constructs for typical usage patterns}
|
10
|
+
gem.email = "ricardo@heroku.com"
|
11
|
+
gem.homepage = "http://heroku.com"
|
12
|
+
gem.authors = ["Ricardo Chimal, Jr."]
|
13
13
|
|
14
|
-
|
14
|
+
gem.add_development_dependency "baconmocha", ">= 0"
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
gem.add_dependency 'json_pure', '>= 1.2.0'
|
18
|
+
gem.add_dependency 'rest-client', '>= 1.2.0'
|
19
|
+
gem.add_dependency 'amqp', '0.6.7'
|
20
|
+
gem.add_dependency 'bunny', '~> 0.6.0'
|
21
|
+
gem.add_dependency 'SystemTimer', '~> 1.2.0'
|
22
|
+
gem.add_dependency 'eventmachine_httpserver', '0.2.0'
|
23
|
+
end
|
24
|
+
Jeweler::GemcutterTasks.new
|
25
25
|
rescue LoadError
|
26
|
-
|
26
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
27
27
|
end
|
28
28
|
|
29
29
|
require 'rake/testtask'
|
30
30
|
Rake::TestTask.new(:spec) do |spec|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
spec.libs << 'lib' << 'spec'
|
32
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
33
|
+
spec.verbose = true
|
34
34
|
end
|
35
35
|
|
36
36
|
task :spec => :check_dependencies
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.2
|
data/bin/bleedq
CHANGED
data/droid.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{droid}
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.0.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ricardo Chimal, Jr."]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-07-29}
|
13
13
|
s.default_executable = %q{bleedq}
|
14
14
|
s.description = %q{Easy to use AMQP Library with constructs for typical usage patterns}
|
15
15
|
s.email = %q{ricardo@heroku.com}
|
data/examples/async_reply.rb
CHANGED
@@ -2,24 +2,24 @@ $:.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
2
|
require 'droid'
|
3
3
|
|
4
4
|
def log
|
5
|
-
|
5
|
+
Droid.log
|
6
6
|
end
|
7
7
|
|
8
8
|
Droid.new('Example Reply') do |droid|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
droid.worker('example.target').subscribe do |req|
|
10
|
+
log.debug "headers: #{req.header.headers.inspect}"
|
11
|
+
log.debug "event_hash should be woot -> #{req.droid_headers[:event_hash]}"
|
12
|
+
req.reply(:target_received_at => Time.now.to_i)
|
13
|
+
req.ack
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
droid.listener('example.check.target').subscribe do |req|
|
17
|
+
req.publish('example.target', { :checking => Time.now.to_i }) do |req2|
|
18
|
+
log.debug "event_hash should be woot -> #{req2.droid_headers[:event_hash]}"
|
19
|
+
log.info "We're done checking!"
|
20
|
+
Droid.stop_safe
|
21
|
+
end
|
22
|
+
end
|
23
23
|
|
24
|
-
|
24
|
+
droid.timer(2) { Droid.publish('example.check.target', {:sent_at => Time.now.to_i}, {:event_hash => "woot"}) }
|
25
25
|
end
|
@@ -2,21 +2,21 @@ $:.unshift File.dirname(__FILE__) + '/../lib/'
|
|
2
2
|
require 'heroku_droid'
|
3
3
|
|
4
4
|
def log
|
5
|
-
|
5
|
+
Droid.log
|
6
6
|
end
|
7
7
|
|
8
8
|
HerokuDroid.new('Example Reply') do |droid|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
droid.worker('example.target').subscribe do |req|
|
10
|
+
req.reply(:target_received_at => Time.now.to_i)
|
11
|
+
req.ack
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
droid.listener('example.check.target').subscribe do |req|
|
15
|
+
req.publish('example.target', { :checking => Time.now.to_i }) do |req2|
|
16
|
+
log.info "We're done checking!"
|
17
|
+
Droid.stop_safe
|
18
|
+
end
|
19
|
+
end
|
20
20
|
|
21
|
-
|
21
|
+
EM.add_timer(2) { Droid.publish('example.check.target', :sent_at => Time.now.to_i ) }
|
22
22
|
end
|
data/examples/sync.rb
CHANGED
@@ -2,22 +2,22 @@ $:.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
2
|
require 'droid'
|
3
3
|
|
4
4
|
def log
|
5
|
-
|
5
|
+
Droid.log
|
6
6
|
end
|
7
7
|
|
8
8
|
if ARGV[0] == 'listen'
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
Droid.new('Example Worker') do |droid|
|
10
|
+
droid.worker('example.bunny.worker').subscribe do |req|
|
11
|
+
req.ack
|
12
|
+
log.info "Work done, replying..."
|
13
|
+
req.reply(:t => Time.now.to_i)
|
14
|
+
end
|
15
|
+
|
16
|
+
droid.listener('example.bunny.listener').subscribe do |req|
|
17
|
+
log.info "I heard #{req['t']}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
exit(0)
|
21
21
|
end
|
22
22
|
|
23
23
|
|
data/examples/worker.rb
CHANGED
@@ -2,7 +2,7 @@ $:.unshift File.dirname(__FILE__) + '/../lib'
|
|
2
2
|
require 'droid'
|
3
3
|
|
4
4
|
def log
|
5
|
-
|
5
|
+
Droid.log
|
6
6
|
end
|
7
7
|
|
8
8
|
@gum_chews = 0
|
@@ -10,49 +10,49 @@ end
|
|
10
10
|
|
11
11
|
Droid.new('Example Worker') do |droid|
|
12
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
|
-
|
13
|
+
# auto acks the message
|
14
|
+
@gum = droid.worker('example.chew.gum').subscribe do |req|
|
15
|
+
log.info "flavor: #{req['flavor']}, packs: #{req['packs']}"
|
16
|
+
log.info req.msg.inspect
|
17
|
+
log.info req.droid_headers
|
18
|
+
|
19
|
+
@gum_chews += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
# explicit ack
|
23
|
+
@toffee = droid.worker('example.chew.toffee').subscribe(:auto_ack => false) do |req|
|
24
|
+
log.info "flavor: #{req['flavor']}"
|
25
|
+
log.info req.msg.inspect
|
26
|
+
log.info req.droid_headers
|
27
|
+
|
28
|
+
req.ack
|
29
|
+
|
30
|
+
@toffee_chews += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
droid.periodic_timer(2) do
|
34
|
+
log.debug "checking gum & toffee chews"
|
35
|
+
|
36
|
+
if @gum_chews == 3 && @gum
|
37
|
+
@gum.destroy
|
38
|
+
@gum = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
if @toffee_chews == 2 && @toffee
|
42
|
+
@toffee.destroy
|
43
|
+
@toffee = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
if @toffee.nil? && @gum.nil?
|
47
|
+
Droid.stop_safe
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
droid.timer(2) do
|
52
|
+
droid.publish('example.chew.gum', :flavor => 'spearmint', :packs => 2)
|
53
|
+
droid.publish('example.chew.gum', :flavor => 'bubblegum', :packs => 3)
|
54
|
+
droid.publish('example.chew.gum', :flavor => 'peppermint', :packs => 1)
|
55
|
+
droid.publish('example.chew.toffee', :flavor => 'caramel')
|
56
|
+
droid.publish('example.chew.toffee', :flavor => 'licorish')
|
57
|
+
end
|
58
58
|
end
|
data/lib/droid.rb
CHANGED
@@ -3,7 +3,7 @@ require 'amqp'
|
|
3
3
|
require 'mq'
|
4
4
|
|
5
5
|
if !defined?(JSON) && !defined?(JSON_LOADED)
|
6
|
-
|
6
|
+
require 'json/pure'
|
7
7
|
end
|
8
8
|
|
9
9
|
require 'droid/monkey'
|
@@ -16,126 +16,126 @@ require 'droid/queue'
|
|
16
16
|
require 'droid/em'
|
17
17
|
|
18
18
|
class Droid
|
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
|
-
|
19
|
+
def self.version
|
20
|
+
@@version ||= File.read(File.dirname(__FILE__) + '/../VERSION').strip
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.name
|
24
|
+
@@name
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.name=(name)
|
28
|
+
@@name = name
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.log=(log)
|
32
|
+
@@log = log
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.log
|
36
|
+
@@log ||= begin
|
37
|
+
require 'logger'
|
38
|
+
Logger.class_eval <<EORUBY
|
39
|
+
alias_method :notice, :info
|
40
|
+
|
41
|
+
alias_method :error_og, :error
|
42
|
+
def error(err, opts={})
|
43
|
+
e = opts[:exception]
|
44
|
+
if e.respond_to?(:backtrace)
|
45
|
+
err += "\n" + e.backtrace.join("\n ")
|
46
|
+
end
|
47
|
+
error_og(err)
|
48
|
+
end
|
49
49
|
EORUBY
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
50
|
+
Logger.new($stderr)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.default_config
|
55
|
+
uri = URI.parse(ENV["AMQP_URL"] || 'amqp://guest:guest@localhost:5672/')
|
56
|
+
{
|
57
|
+
:vhost => uri.path,
|
58
|
+
:host => uri.host,
|
59
|
+
:user => uri.user,
|
60
|
+
:port => uri.port || 5672,
|
61
|
+
:pass => uri.password
|
62
|
+
}
|
63
|
+
rescue Object => e
|
64
|
+
raise "invalid AMQP_URL: (#{uri.inspect}) #{e.class} -> #{e.message}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.start(opts={})
|
68
|
+
config = opts[:config] || self.default_config
|
69
|
+
|
70
|
+
wait_for_tcp_port(config[:host], config[:port])
|
71
|
+
|
72
|
+
begin
|
73
|
+
::Signal.trap('INT') { ::AMQP.stop{ ::EM.stop } }
|
74
|
+
::Signal.trap('TERM'){ ::AMQP.stop{ ::EM.stop } }
|
75
|
+
|
76
|
+
::AMQP.start(config) do
|
77
|
+
yield if block_given?
|
78
|
+
end
|
79
|
+
rescue ::AMQP::Error => e
|
80
|
+
log.debug "Caught #{e.class}, sleeping to avoid inittab thrashing"
|
81
|
+
sleep 5
|
82
|
+
log.debug "Done."
|
83
|
+
raise
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.stop_safe
|
88
|
+
::EM.add_timer(0.2) { ::AMQP.stop { ::EM.stop } }
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.closing?
|
92
|
+
::AMQP.closing?
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.handle_error(err)
|
96
|
+
log.error "#{err.class}: #{err.message}", :exception => err
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.wait_for_tcp_port(host, port, opts={})
|
100
|
+
require 'system_timer'
|
101
|
+
require 'socket'
|
102
|
+
|
103
|
+
opts[:retries] ||= 6
|
104
|
+
opts[:timeout] ||= 5
|
105
|
+
|
106
|
+
opts[:retries].times do
|
107
|
+
begin
|
108
|
+
SystemTimer::timeout(opts[:timeout]) do
|
109
|
+
TCPSocket.new(host.to_s, port).close
|
110
|
+
end
|
111
|
+
return
|
112
|
+
rescue Object => e
|
113
|
+
log.info "#{host}:#{port} not available, waiting... #{e.class}: #{e.message}"
|
114
|
+
sleep 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
raise "#{host}:#{port} did not come up after #{opts[:retries]} retries"
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(name, opts={})
|
122
|
+
log.info "=== #{name} droid initializing"
|
123
|
+
|
124
|
+
self.class.name = name
|
125
|
+
self.class.start do
|
126
|
+
yield self if block_given?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def publish(*args)
|
131
|
+
Droid.publish(*args)
|
132
|
+
end
|
133
|
+
|
134
|
+
def log
|
135
|
+
self.class.log
|
136
|
+
end
|
137
|
+
|
138
|
+
include Droid::QueueMethods
|
139
|
+
include Droid::BackwardsCompatibleMethods
|
140
|
+
include Droid::EMTimerUtils
|
141
141
|
end
|