amqp 0.7.0 → 0.7.1

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.
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