amqp 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +3 -2
  2. data/CHANGELOG +25 -0
  3. data/Gemfile +4 -2
  4. data/README.md +2 -0
  5. data/{amqp.todo → TODO} +1 -3
  6. data/amqp.gemspec +3 -3
  7. data/bin/irb +2 -2
  8. data/bin/jenkins.sh +25 -0
  9. data/bin/set_test_suite_realms_up.sh +21 -0
  10. data/doc/EXAMPLE_01_PINGPONG +1 -1
  11. data/doc/EXAMPLE_02_CLOCK +1 -1
  12. data/doc/EXAMPLE_03_STOCKS +1 -1
  13. data/doc/EXAMPLE_04_MULTICLOCK +1 -1
  14. data/doc/EXAMPLE_05_ACK +1 -1
  15. data/doc/EXAMPLE_05_POP +1 -1
  16. data/doc/EXAMPLE_06_HASHTABLE +1 -1
  17. data/examples/{mq/ack.rb → ack.rb} +6 -6
  18. data/examples/{mq/automatic_binding_for_default_direct_exchange.rb → automatic_binding_for_default_direct_exchange.rb} +4 -4
  19. data/examples/{mq/callbacks.rb → callbacks.rb} +2 -2
  20. data/examples/{mq/clock.rb → clock.rb} +5 -5
  21. data/examples/{mq/hashtable.rb → hashtable.rb} +4 -4
  22. data/examples/{mq/internal.rb → internal.rb} +5 -5
  23. data/examples/{mq/logger.rb → logger.rb} +5 -5
  24. data/examples/{mq/multiclock.rb → multiclock.rb} +4 -4
  25. data/examples/{mq/pingpong.rb → pingpong.rb} +5 -5
  26. data/examples/{mq/pop.rb → pop.rb} +3 -3
  27. data/examples/{mq/primes-simple.rb → primes-simple.rb} +0 -0
  28. data/examples/{mq/primes.rb → primes.rb} +6 -6
  29. data/examples/{amqp/simple.rb → simple.rb} +1 -1
  30. data/examples/{mq/stocks.rb → stocks.rb} +5 -5
  31. data/lib/amqp.rb +8 -112
  32. data/lib/amqp/basic_client.rb +58 -0
  33. data/lib/amqp/channel.rb +937 -0
  34. data/lib/amqp/client.rb +72 -79
  35. data/lib/{mq → amqp}/collection.rb +12 -2
  36. data/lib/amqp/connection.rb +115 -0
  37. data/lib/amqp/exceptions.rb +18 -0
  38. data/lib/{mq → amqp}/exchange.rb +32 -34
  39. data/lib/{ext → amqp/ext}/em.rb +1 -1
  40. data/lib/{ext → amqp/ext}/emfork.rb +0 -0
  41. data/lib/amqp/frame.rb +3 -3
  42. data/lib/{mq → amqp}/header.rb +5 -11
  43. data/lib/{mq → amqp}/logger.rb +2 -2
  44. data/lib/amqp/protocol.rb +2 -2
  45. data/lib/{mq → amqp}/queue.rb +20 -17
  46. data/lib/{mq → amqp}/rpc.rb +20 -8
  47. data/lib/amqp/server.rb +1 -1
  48. data/lib/amqp/version.rb +1 -1
  49. data/lib/mq.rb +20 -964
  50. data/protocol/codegen.rb +1 -1
  51. data/research/api.rb +3 -3
  52. data/research/primes-forked.rb +5 -5
  53. data/research/primes-processes.rb +5 -5
  54. data/research/primes-threaded.rb +5 -5
  55. data/spec/integration/authentication_spec.rb +114 -0
  56. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +13 -12
  57. data/spec/{unit/mq → integration}/channel_close_spec.rb +2 -2
  58. data/spec/{unit/mq → integration}/exchange_declaration_spec.rb +26 -14
  59. data/spec/{unit/mq → integration}/queue_declaration_spec.rb +4 -4
  60. data/spec/integration/queue_exclusivity_spec.rb +95 -0
  61. data/spec/integration/reply_queue_communication_spec.rb +63 -0
  62. data/spec/integration/store_and_forward_spec.rb +121 -0
  63. data/spec/integration/topic_subscription_spec.rb +193 -0
  64. data/spec/integration/workload_distribution_spec.rb +245 -0
  65. data/spec/spec_helper.rb +16 -32
  66. data/spec/unit/{mq/mq_basic_spec.rb → amqp/basic_spec.rb} +4 -4
  67. data/spec/unit/{mq → amqp}/collection_spec.rb +22 -7
  68. data/spec/unit/amqp/connection_spec.rb +116 -0
  69. data/spec/unit/amqp/frame_spec.rb +18 -18
  70. data/spec/unit/amqp/protocol_spec.rb +9 -11
  71. metadata +54 -49
  72. data/lib/ext/blankslate.rb +0 -9
  73. data/spec/mq_helper.rb +0 -70
  74. data/spec/unit/amqp/client_spec.rb +0 -472
  75. data/spec/unit/amqp/misc_spec.rb +0 -123
  76. data/spec/unit/mq/misc_spec.rb +0 -228
  77. data/spec/unit/mq/queue_spec.rb +0 -71
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe "Non-exclusive queue" do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include AMQP::Spec
12
+
13
+ default_timeout 5
14
+
15
+ amqp_before do
16
+ @connection1 = AMQP.connect
17
+ @connection2 = AMQP.connect
18
+ end
19
+
20
+
21
+
22
+ #
23
+ # Examples
24
+ #
25
+
26
+ it "can be used across multiple connections" do
27
+ @connection1.should_not == @connection2
28
+
29
+ channel1 = AMQP::Channel.new(@connection1)
30
+ channel2 = AMQP::Channel.new(@connection2)
31
+
32
+ instance1 = AMQP::Queue.new(channel1, "amqpgem.integration.queues.non-exclusive", :exclusive => false, :auto_delete => true)
33
+ instance2 = AMQP::Queue.new(channel2, "amqpgem.integration.queues.non-exclusive", :exclusive => false, :auto_delete => true)
34
+
35
+ exchange1 = channel1.fanout("amqpgem.integration.exchanges.fanout1", :auto_delete => true)
36
+ exchange2 = channel2.fanout("amqpgem.integration.exchanges.fanout2", :auto_delete => true)
37
+
38
+
39
+ instance1.bind(exchange1).subscribe do |payload|
40
+ end
41
+
42
+ instance2.bind(exchange2).subscribe do |payload|
43
+ end
44
+
45
+ done(0.2) {
46
+ channel1.should be_open
47
+ channel1.close
48
+
49
+ channel2.should be_open
50
+ channel2.close
51
+ }
52
+ end
53
+ end
54
+
55
+
56
+
57
+ describe "Exclusive queue" do
58
+
59
+ #
60
+ # Environment
61
+ #
62
+
63
+ include AMQP::Spec
64
+
65
+ default_timeout 1
66
+
67
+ amqp_before do
68
+ @connection1 = AMQP.connect
69
+ @connection2 = AMQP.connect
70
+ end
71
+
72
+
73
+ #
74
+ # Examples
75
+ #
76
+
77
+ it "can ONLY be used by ONE connection" do
78
+ @connection1.should_not == @connection2
79
+
80
+ channel1 = AMQP::Channel.new(@connection1)
81
+ channel2 = AMQP::Channel.new(@connection2)
82
+
83
+ AMQP::Queue.new(channel1, "amqpgem.integration.queues.exclusive", :exclusive => true)
84
+ sleep 0.1
85
+
86
+ AMQP::Queue.new(channel2, "amqpgem.integration.queues.exclusive", :exclusive => true)
87
+
88
+
89
+ done(0.2) {
90
+ channel1.should_not be_closed
91
+ # because it is a channel-level exception
92
+ channel2.should be_closed
93
+ }
94
+ end
95
+ end
@@ -0,0 +1,63 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ describe "Exclusive server-named queue" do
5
+
6
+ #
7
+ # Environment
8
+ #
9
+
10
+ include AMQP::Spec
11
+
12
+ default_timeout 1
13
+
14
+ amqp_before do
15
+ @channel = AMQP::Channel.new
16
+ @channel.should be_open
17
+
18
+ @exchange = AMQP::Exchange.default(@channel)
19
+ end
20
+
21
+
22
+
23
+ it "can be used for temporary point-to-point communication" do
24
+ @exchange.channel.should == @channel
25
+
26
+ @channel.queue("", :exclusive => true) do |queue1|
27
+ @channel.queue("", :exclusive => true) do |queue2|
28
+ request_timestamp = Time.now
29
+ reply_timestamp = nil
30
+
31
+ queue1.subscribe do |header, body|
32
+ header.timestamp.to_i.should == request_timestamp.to_i
33
+ header.app_id.should == "Client"
34
+ header.reply_to.should == queue2.name
35
+
36
+ reply_timestamp = Time.now
37
+ @exchange.publish(rand(1000), :routing_key => header.reply_to, :reply_to => queue1.name, :app_id => "Server", :timestamp => reply_timestamp)
38
+ end
39
+
40
+ queue2.subscribe do |header, body|
41
+ header.timestamp.to_i.should == reply_timestamp.to_i
42
+ header.app_id.should == "Server"
43
+ header.reply_to.should == queue1.name
44
+ end
45
+
46
+
47
+ # publish the request
48
+ @exchange.publish(rand(1000),
49
+ :routing_key => queue1.name,
50
+ :reply_to => queue2.name,
51
+ :app_id => "Client",
52
+ :timestamp => request_timestamp,
53
+ :mandatory => true,
54
+ :immediate => true)
55
+
56
+ done(0.1) {
57
+ queue1.unsubscribe
58
+ queue2.unsubscribe
59
+ }
60
+ end # do
61
+ end # do
62
+ end # it
63
+ end # describe
@@ -0,0 +1,121 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ describe "Store-and-forward routing" do
5
+
6
+ #
7
+ # Environment
8
+ #
9
+
10
+ include AMQP::Spec
11
+ include AMQP::SpecHelper
12
+
13
+ em_before { AMQP.cleanup_state }
14
+ em_after { AMQP.cleanup_state }
15
+
16
+ default_options AMQP_OPTS
17
+ default_timeout 10
18
+
19
+ amqp_before do
20
+ @channel = AMQP::Channel.new
21
+ @channel.should be_open
22
+ end
23
+
24
+ after(:all) do
25
+ AMQP.cleanup_state
26
+ done
27
+ end
28
+
29
+
30
+ #
31
+ # Examples
32
+ #
33
+
34
+ context "that uses fanout exchange" do
35
+ context "with a single bound queue" do
36
+ amqp_before do
37
+ @exchange = @channel.fanout("amqpgem.integration.snf.fanout", :auto_delete => true)
38
+ @queue = @channel.queue("amqpgem.integration.snf.queue1", :auto_delete => true)
39
+
40
+ @queue.bind(@exchange)
41
+ end
42
+
43
+ it "allows asynchronous subscription to messages WITHOUT acknowledgements" do
44
+ number_of_received_messages = 0
45
+ # put a little pressure
46
+ expected_number_of_messages = 300
47
+ # It is always a good idea to use non-ASCII charachters in
48
+ # various test suites. MK.
49
+ dispatched_data = "libertà è participazione (inviato a #{Time.now.to_i})"
50
+
51
+ @queue.subscribe(:ack => false) do |payload|
52
+ number_of_received_messages += 1
53
+ if RUBY_VERSION =~ /^1.9/
54
+ payload.force_encoding("UTF-8").should == dispatched_data
55
+ else
56
+ payload.should == dispatched_data
57
+ end
58
+ end # subscribe
59
+
60
+ expected_number_of_messages.times do
61
+ @exchange.publish(dispatched_data)
62
+ end
63
+
64
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
65
+ done(4.0) {
66
+ number_of_received_messages.should == expected_number_of_messages
67
+ @queue.unsubscribe
68
+ }
69
+ end # it
70
+
71
+
72
+ it "allows asynchronous subscription to messages WITH acknowledgements" do
73
+ number_of_received_messages = 0
74
+ expected_number_of_messages = 500
75
+
76
+ @queue.subscribe(:ack => true) do |payload|
77
+ number_of_received_messages += 1
78
+ end # subscribe
79
+
80
+ expected_number_of_messages.times do
81
+ @exchange.publish(rand)
82
+ end
83
+
84
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
85
+ done(3.0) {
86
+ number_of_received_messages.should == expected_number_of_messages
87
+ @queue.unsubscribe
88
+ }
89
+ end # it
90
+
91
+
92
+
93
+ it "allows synchronous fetching of messages" do
94
+ number_of_received_messages = 0
95
+ expected_number_of_messages = 300
96
+
97
+ dispatched_data = "fetch me synchronously"
98
+
99
+ expected_number_of_messages.times do
100
+ @exchange.publish(dispatched_data)
101
+ end
102
+
103
+ expected_number_of_messages.times do
104
+ @queue.pop do |payload|
105
+ number_of_received_messages += 1
106
+
107
+ if RUBY_VERSION =~ /^1.9/
108
+ payload.force_encoding("UTF-8").should == dispatched_data
109
+ else
110
+ payload.should == dispatched_data
111
+ end
112
+ end # pop
113
+ end # do
114
+
115
+ done(0.5) {
116
+ number_of_received_messages.should == expected_number_of_messages
117
+ }
118
+ end # it
119
+ end # context
120
+ end # context
121
+ end # describe
@@ -0,0 +1,193 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ describe "Topic-based subscription" do
5
+
6
+ #
7
+ # Environment
8
+ #
9
+
10
+ include AMQP::Spec
11
+ include AMQP::SpecHelper
12
+
13
+ em_before { AMQP.cleanup_state }
14
+ em_after { AMQP.cleanup_state }
15
+
16
+ default_options AMQP_OPTS
17
+ default_timeout 10
18
+
19
+ amqp_before do
20
+ @channel = AMQP::Channel.new
21
+ @channel.should be_open
22
+
23
+ @exchange = @channel.topic
24
+ end
25
+
26
+ after(:all) do
27
+ AMQP.cleanup_state
28
+ done
29
+ end
30
+
31
+
32
+ #
33
+ # Examples
34
+ #
35
+
36
+ context "when keys match exactly" do
37
+ it "routes messages to bound queues" do
38
+ @aapl_queue = @channel.queue("AAPL queue", :auto_delete => true)
39
+ @amzn_queue = @channel.queue("AMZN queue", :auto_delete => true)
40
+
41
+ received_messages = {
42
+ @aapl_queue.name => 0,
43
+ @amzn_queue.name => 0
44
+ }
45
+
46
+ expected_messages = {
47
+ @aapl_queue.name => 4,
48
+ @amzn_queue.name => 2
49
+ }
50
+
51
+ @aapl_queue.bind(@exchange, :key => "nasdaq.aapl").subscribe do |payload|
52
+ received_messages[@aapl_queue.name] += 1
53
+ end
54
+ @amzn_queue.bind(@exchange, :key => "nasdaq.amzn").subscribe do |payload|
55
+ received_messages[@amzn_queue.name] += 1
56
+ end
57
+
58
+ 4.times do
59
+ @exchange.publish(332 + rand(1000)/400.0, :key => "nasdaq.aapl")
60
+ end
61
+
62
+ 2.times do
63
+ @exchange.publish(181 + rand(1000)/400.0, :key => "nasdaq.amzn")
64
+ end
65
+
66
+ # publish some messages none of our queues should be receiving
67
+ 3.times do
68
+ @exchange.publish(626 + rand(1000)/400.0, :key => "nasdaq.goog")
69
+ end # do
70
+
71
+ done(0.2) {
72
+ received_messages.should == expected_messages
73
+ @aapl_queue.unsubscribe
74
+ @amzn_queue.unsubscribe
75
+ }
76
+ end # it
77
+ end # context
78
+
79
+
80
+ context "when key matches on * (single word globbing)" do
81
+ it "routes messages to bound queues" do
82
+ @nba_queue = @channel.queue("NBA queue", :auto_delete => true)
83
+ @knicks_queue = @channel.queue("New York Knicks queue", :auto_delete => true)
84
+ @celtics_queue = @channel.queue("Boston Celtics queue", :auto_delete => true)
85
+
86
+ received_messages = {
87
+ @nba_queue.name => 0,
88
+ @knicks_queue.name => 0,
89
+ @celtics_queue.name => 0
90
+ }
91
+
92
+ expected_messages = {
93
+ @nba_queue.name => 7,
94
+ @knicks_queue.name => 2,
95
+ @celtics_queue.name => 3
96
+ }
97
+
98
+ @nba_queue.bind(@exchange, :key => "sports.nba.*").subscribe do |payload|
99
+ received_messages[@nba_queue.name] += 1
100
+ end
101
+
102
+ @knicks_queue.bind(@exchange, :key => "sports.nba.knicks").subscribe do |payload|
103
+ received_messages[@knicks_queue.name] += 1
104
+ end
105
+
106
+ @celtics_queue.bind(@exchange, :key => "sports.nba.celtics").subscribe do |payload|
107
+ received_messages[@celtics_queue.name] += 1
108
+ end
109
+
110
+ @exchange.publish("Houston Rockets 104 : New York Knicks 89", :key => "sports.nba.knicks")
111
+ @exchange.publish("Phoenix Suns 129 : New York Knicks 121", :key => "sports.nba.knicks")
112
+
113
+ @exchange.publish("Ray Allen hit a 21-foot jumper with 24.5 seconds remaining on the clock to give Boston a win over Detroit last night in the TD Garden", :key => "sports.nba.celtics")
114
+ @exchange.publish("Garnett's Return Sparks Celtics Over Magic at Garden", :key => "sports.nba.celtics")
115
+ @exchange.publish("Tricks of the Trade: Allen Reveals Magic of Big Shots", :key => "sports.nba.celtics")
116
+
117
+ @exchange.publish("Blatche, Wall lead Wizards over Jazz 108-101", :key => "sports.nba.jazz")
118
+ @exchange.publish("Deron Williams Receives NBA Cares Community Assist Award", :key => "sports.nba.jazz")
119
+
120
+ done(0.2) {
121
+ received_messages.should == expected_messages
122
+
123
+ @nba_queue.unsubscribe
124
+ @knicks_queue.unsubscribe
125
+ @celtics_queue.unsubscribe
126
+ }
127
+ end # it
128
+ end # context
129
+
130
+
131
+
132
+
133
+ context "when key matches on # (multiple words globbing)" do
134
+ it "routes messages to bound queues" do
135
+ @sports_queue = @channel.queue("Sports queue", :auto_delete => true)
136
+ @nba_queue = @channel.queue("NBA queue", :auto_delete => true)
137
+ @knicks_queue = @channel.queue("New York Knicks queue", :auto_delete => true)
138
+ @celtics_queue = @channel.queue("Boston Celtics queue", :auto_delete => true)
139
+
140
+ received_messages = {
141
+ @sports_queue.name => 0,
142
+ @nba_queue.name => 0,
143
+ @knicks_queue.name => 0,
144
+ @celtics_queue.name => 0
145
+ }
146
+
147
+ expected_messages = {
148
+ @sports_queue.name => 9,
149
+ @nba_queue.name => 7,
150
+ @knicks_queue.name => 2,
151
+ @celtics_queue.name => 3
152
+ }
153
+
154
+ @sports_queue.bind(@exchange, :key => "sports.#").subscribe do |payload|
155
+ received_messages[@sports_queue.name] += 1
156
+ end
157
+
158
+ @nba_queue.bind(@exchange, :key => "*.nba.*").subscribe do |payload|
159
+ received_messages[@nba_queue.name] += 1
160
+ end
161
+
162
+ @knicks_queue.bind(@exchange, :key => "sports.nba.knicks").subscribe do |payload|
163
+ received_messages[@knicks_queue.name] += 1
164
+ end
165
+
166
+ @celtics_queue.bind(@exchange, :key => "#.celtics").subscribe do |payload|
167
+ received_messages[@celtics_queue.name] += 1
168
+ end
169
+
170
+ @exchange.publish("Houston Rockets 104 : New York Knicks 89", :key => "sports.nba.knicks")
171
+ @exchange.publish("Phoenix Suns 129 : New York Knicks 121", :key => "sports.nba.knicks")
172
+
173
+ @exchange.publish("Ray Allen hit a 21-foot jumper with 24.5 seconds remaining on the clock to give Boston a win over Detroit last night in the TD Garden", :key => "sports.nba.celtics")
174
+ @exchange.publish("Garnett's Return Sparks Celtics Over Magic at Garden", :key => "sports.nba.celtics")
175
+ @exchange.publish("Tricks of the Trade: Allen Reveals Magic of Big Shots", :key => "sports.nba.celtics")
176
+
177
+ @exchange.publish("Blatche, Wall lead Wizards over Jazz 108-101", :key => "sports.nba.jazz")
178
+ @exchange.publish("Deron Williams Receives NBA Cares Community Assist Award", :key => "sports.nba.jazz")
179
+
180
+ @exchange.publish("Philadelphia's Daniel Briere has been named as an All-Star replacement for Jarome Iginla.", :key => "sports.nhl.allstargame")
181
+ @exchange.publish("Devils blank Sid- and Malkin-less Penguins 2-0", :key => "sports.nhl.penguins")
182
+
183
+ done(0.2) {
184
+ received_messages.should == expected_messages
185
+
186
+ @sports_queue.unsubscribe
187
+ @nba_queue.unsubscribe
188
+ @knicks_queue.unsubscribe
189
+ @celtics_queue.unsubscribe
190
+ }
191
+ end # it
192
+ end # context
193
+ end # describe