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,245 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ describe "Workload distribution" 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 5
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
+ amqp_before :each do
36
+ @exchange = @channel.fanout("amqpgem.integration.multicast.fanout", :auto_delete => true)
37
+ end
38
+
39
+ context "with three bound queues" do
40
+ amqp_before :each do
41
+ @queue1 = @channel.queue("amqpgem.integration.multicast.queue1", :auto_delete => true)
42
+ @queue2 = @channel.queue("amqpgem.integration.multicast.queue2", :auto_delete => true)
43
+ @queue3 = @channel.queue("amqpgem.integration.multicast.queue3", :auto_delete => true)
44
+
45
+ @queues = [@queue1, @queue2, @queue3]
46
+
47
+ @sent_values = Array.new
48
+
49
+ @queue1.bind(@exchange).subscribe do |payload|
50
+ @received_messages[@queue1.name].push(payload.to_i)
51
+ end # subscribe
52
+
53
+ @queue2.bind(@exchange).subscribe do |payload|
54
+ @received_messages[@queue2.name].push(payload.to_i)
55
+ end # subscribe
56
+
57
+ @queue3.bind(@exchange).subscribe do |payload|
58
+ @received_messages[@queue3.name].push(payload.to_i)
59
+ end # subscribe
60
+ end
61
+
62
+ amqp_before :each do
63
+ @received_messages = {
64
+ @queue1.name => [],
65
+ @queue2.name => [],
66
+ @queue3.name => []
67
+ }
68
+
69
+ @expected_number_of_messages = {
70
+ @queue1.name => 100,
71
+ @queue2.name => 100,
72
+ @queue3.name => 100
73
+ }
74
+ end
75
+
76
+ amqp_after :each do
77
+ @sent_values.clear
78
+ end
79
+
80
+
81
+ context "and messages are published as non-mandatory" do
82
+ it "routes all messages to all bound queues" do
83
+ 100.times do
84
+ dispatched_data = rand(5_000_000)
85
+ @sent_values.push(dispatched_data)
86
+
87
+ @exchange.publish(dispatched_data, :mandatory => false)
88
+ end
89
+
90
+ # for Rubinius, it is surprisingly slow on this workload
91
+ done(1.5) {
92
+ [@queue1, @queue2, @queue3].each do |q|
93
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
94
+
95
+ # this one is ordering assertion
96
+ @received_messages[q.name].should == @sent_values
97
+ end
98
+ }
99
+ end # it
100
+ end # context
101
+
102
+
103
+
104
+ context "and messages are published as mandatory" do
105
+ it "routes all messages to all bound queues" do
106
+ 100.times do
107
+ dispatched_data = rand(5_000_000)
108
+ @sent_values.push(dispatched_data)
109
+
110
+ @exchange.publish(dispatched_data, :mandatory => true)
111
+ end
112
+
113
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
114
+ done(1.5) {
115
+ [@queue1, @queue2, @queue3].each do |q|
116
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
117
+
118
+ # this one is ordering assertion
119
+ @received_messages[q.name].should == @sent_values
120
+ end
121
+ }
122
+ end # it
123
+ end # context
124
+
125
+
126
+
127
+ context "and messages are published as non-persistent" do
128
+ it "routes all messages to all bound queues" do
129
+ 100.times do
130
+ dispatched_data = rand(5_000_000)
131
+ @sent_values.push(dispatched_data)
132
+
133
+ @exchange.publish(dispatched_data, :persistent => false)
134
+ end
135
+
136
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
137
+ done(1.5) {
138
+ [@queue1, @queue2, @queue3].each do |q|
139
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
140
+
141
+ # this one is ordering assertion
142
+ @received_messages[q.name].should == @sent_values
143
+ end
144
+ }
145
+ end # it
146
+ end # context
147
+
148
+
149
+
150
+ context "and messages are published as persistent" do
151
+ it "routes all messages to all bound queues" do
152
+ 100.times do
153
+ dispatched_data = rand(5_000_000)
154
+ @sent_values.push(dispatched_data)
155
+
156
+ @exchange.publish(dispatched_data, :persistent => true)
157
+ end
158
+
159
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
160
+ done(1.5) {
161
+ [@queue1, @queue2, @queue3].each do |q|
162
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
163
+
164
+ # this one is ordering assertion
165
+ @received_messages[q.name].should == @sent_values
166
+ end
167
+ }
168
+ end # it
169
+ end # context
170
+
171
+
172
+ context "and messages are published as non-immediate" do
173
+ it "routes all messages to all bound queues" do
174
+ 100.times do
175
+ dispatched_data = rand(5_000_000)
176
+ @sent_values.push(dispatched_data)
177
+
178
+ @exchange.publish(dispatched_data, :immediate => false)
179
+ end
180
+
181
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
182
+ done(1.5) {
183
+ [@queue1, @queue2, @queue3].each do |q|
184
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
185
+
186
+ # this one is ordering assertion
187
+ @received_messages[q.name].should == @sent_values
188
+ end
189
+ }
190
+ end # it
191
+ end # context
192
+
193
+
194
+
195
+ context "and messages are published as immediate" do
196
+ it "may get a Basic.Return back"
197
+ end # context
198
+
199
+
200
+
201
+ context "and messages are published WITHOUT routing key" do
202
+ it "routes all messages to all bound queues" do
203
+ 100.times do
204
+ dispatched_data = rand(5_000_000)
205
+ @sent_values.push(dispatched_data)
206
+
207
+ @exchange.publish(dispatched_data)
208
+ end
209
+
210
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
211
+ done(1.5) {
212
+ [@queue1, @queue2, @queue3].each do |q|
213
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
214
+
215
+ # this one is ordering assertion
216
+ @received_messages[q.name].should == @sent_values
217
+ end
218
+ }
219
+ end # it
220
+ end # context
221
+
222
+
223
+ context "and messages are published WITH routing key that matches name of one of the queues" do
224
+ it "routes all messages to all bound queues" do
225
+ 100.times do
226
+ dispatched_data = rand(5_000_000)
227
+ @sent_values.push(dispatched_data)
228
+
229
+ @exchange.publish(dispatched_data, :routing_key => @queues.sample.name)
230
+ end
231
+
232
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
233
+ done(1.5) {
234
+ [@queue1, @queue2, @queue3].each do |q|
235
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
236
+
237
+ # this one is ordering assertion
238
+ @received_messages[q.name].should == @sent_values
239
+ end
240
+ }
241
+ end # it
242
+ end # context
243
+ end # context
244
+ end # context
245
+ end # describe
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # -*- coding: utf-8 -*-
2
2
 
3
3
  $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
4
4
 
@@ -6,7 +6,8 @@ require 'bundler'
6
6
  Bundler.setup
7
7
  Bundler.require :default, :test
8
8
 
9
- require "mq"
9
+ require "amqp-spec"
10
+ require "amqp"
10
11
 
11
12
  amqp_config = File.dirname(__FILE__) + '/amqp.yml'
12
13
 
@@ -26,8 +27,12 @@ else
26
27
  AMQP_OPTS = {:host => 'localhost', :port => 5672}
27
28
  end
28
29
 
30
+ #
31
+ # Ruby version-specific
32
+ #
29
33
 
30
- if RUBY_VERSION == "1.8.7"
34
+ case RUBY_VERSION
35
+ when "1.8.7" then
31
36
  module ArrayExtensions
32
37
  def sample
33
38
  self.choice
@@ -37,18 +42,17 @@ if RUBY_VERSION == "1.8.7"
37
42
  class Array
38
43
  include ArrayExtensions
39
44
  end
45
+ when "1.8.6" then
46
+ raise "Ruby 1.8.6 is not supported. Sorry, pal. Time to move on beyond One True Ruby. Yes, time flies by."
47
+ when /^1.9/ then
48
+ puts "Encoding.default_internal was #{Encoding.default_internal || 'not set'}, switching to #{Encoding::UTF_8}"
49
+ Encoding.default_internal = Encoding::UTF_8
50
+
51
+ puts "Encoding.default_external was #{Encoding.default_internal || 'not set'}, switching to #{Encoding::UTF_8}"
52
+ Encoding.default_external = Encoding::UTF_8
40
53
  end
41
54
 
42
55
 
43
-
44
- # Shorthand for mocking subject's instance variable
45
- def subject_mock(name, as_null = false)
46
- mock = mock(name)
47
- mock.as_null_object if as_null
48
- subject.instance_variable_set(name.to_sym, mock)
49
- mock
50
- end
51
-
52
56
  # Returns Header that should be correctly parsed
53
57
  def basic_header(opts = {})
54
58
  AMQP::Frame::Header.new(
@@ -74,23 +78,3 @@ def test_method_deliver opts = {}
74
78
  AMQP::Protocol::Basic::Deliver.new(
75
79
  :consumer_tag => opts[:consumer_tag] || 'test_consumer'))
76
80
  end
77
-
78
- require "stringio"
79
-
80
- def capture_stdout(&block)
81
- $stdout = StringIO.new
82
- block.call
83
- $stdout.rewind
84
- result = $stdout.read
85
- $stdout = STDOUT
86
- return result
87
- end
88
-
89
- def capture_stderr(&block)
90
- $stderr = StringIO.new
91
- block.call
92
- $stderr.rewind
93
- result = $stderr.read
94
- $stderr = STDOUT
95
- return result
96
- end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe MQ do
5
+ describe AMQP do
6
6
 
7
7
  #
8
8
  # Environment
@@ -13,7 +13,7 @@ describe MQ do
13
13
  default_timeout 5
14
14
 
15
15
  amqp_before do
16
- @channel = MQ.new
16
+ @channel = AMQP::Channel.new
17
17
  end
18
18
 
19
19
 
@@ -25,7 +25,7 @@ describe MQ do
25
25
  describe ".channel" do
26
26
  it 'gives each thread a separate channel' do
27
27
  pending 'This is not implemented in current lib'
28
- class MQ
28
+ module AMQP
29
29
  @@cur_channel = 0
30
30
  end
31
31
 
@@ -36,4 +36,4 @@ describe MQ do
36
36
  done
37
37
  end
38
38
  end
39
- end # describe MQ
39
+ end # describe AMQP
@@ -1,14 +1,14 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require "spec_helper"
4
- require "mq/collection"
4
+ require "amqp/collection"
5
5
 
6
6
  Item = Struct.new(:name)
7
7
 
8
- describe MQ::Collection do
8
+ describe AMQP::Collection do
9
9
  before do
10
10
  @items = 3.times.map { |int| Item.new("name-#{int}") }
11
- @collection = MQ::Collection.new(@items)
11
+ @collection = AMQP::Collection.new(@items)
12
12
  end
13
13
 
14
14
  it "provides access to items by name" do
@@ -23,7 +23,7 @@ describe MQ::Collection do
23
23
 
24
24
  describe "#<<" do
25
25
  it "should raise IncompatibleItemError if the argument doesn't have method :name" do
26
- lambda { @collection << nil }.should raise_error(MQ::Collection::IncompatibleItemError)
26
+ lambda { @collection << nil }.should raise_error(AMQP::Collection::IncompatibleItemError)
27
27
  end
28
28
 
29
29
  it "should add an item into the collection" do
@@ -88,7 +88,7 @@ describe MQ::Collection do
88
88
  describe "#add!" do
89
89
  context "when the argument doesn't respond to :name" do
90
90
  it "should raise IncompatibleItemError " do
91
- lambda { @collection.add!(nil) }.should raise_error(MQ::Collection::IncompatibleItemError)
91
+ lambda { @collection.add!(nil) }.should raise_error(AMQP::Collection::IncompatibleItemError)
92
92
  end # it
93
93
  end
94
94
 
@@ -114,7 +114,7 @@ describe MQ::Collection do
114
114
  describe "#<<" do
115
115
  context "when the argument doesn't respond to :name" do
116
116
  it "should raise IncompatibleItemError " do
117
- lambda { @collection << nil }.should raise_error(MQ::Collection::IncompatibleItemError)
117
+ lambda { @collection << nil }.should raise_error(AMQP::Collection::IncompatibleItemError)
118
118
  end # it
119
119
  end # context
120
120
 
@@ -126,4 +126,19 @@ describe MQ::Collection do
126
126
  end
127
127
  end # context
128
128
  end # describe
129
- end # describe MQ::Collection
129
+
130
+
131
+
132
+
133
+
134
+ describe "#delete" do
135
+ it "should remove item with the given name from the collection" do
136
+ items = [Item.new("test-0"), Item.new("test-1")]
137
+ collection = AMQP::Collection.new(items)
138
+ lambda {
139
+ collection.delete("test-1")
140
+ }.should change(collection, :length).by(-1)
141
+ collection.should_not include("test-1")
142
+ end
143
+ end # describe
144
+ end # describe AMQP::Collection
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+ require 'amqp'
3
+
4
+ describe AMQP, 'class object' do
5
+
6
+ #
7
+ # Environment
8
+ #
9
+
10
+ subject { AMQP }
11
+
12
+ #
13
+ # Examples
14
+ #
15
+
16
+ its(:settings) do
17
+ should == {
18
+ :host => "127.0.0.1",
19
+ :port => 5672,
20
+ :user => "guest",
21
+ :pass => "guest",
22
+ :vhost => "/",
23
+ :timeout => nil,
24
+ :logging => false,
25
+ :ssl => false
26
+ }
27
+ end
28
+
29
+ its(:client) { should == AMQP::BasicClient }
30
+
31
+
32
+
33
+
34
+ describe 'logging' do
35
+ after(:all) do
36
+ AMQP.logging = false
37
+ end
38
+
39
+ it 'is silent by default' do
40
+ AMQP.logging.should be_false
41
+ end
42
+ end # .logging=
43
+
44
+
45
+
46
+
47
+ describe '.start' do
48
+
49
+ #
50
+ # Environment
51
+ #
52
+
53
+ include AMQP::SpecHelper
54
+
55
+ em_before { AMQP.cleanup_state }
56
+ em_after { AMQP.cleanup_state }
57
+
58
+ #
59
+ # Examples
60
+ #
61
+
62
+ it 'yields to given block AFTER connection is established' do
63
+ em do
64
+ AMQP.start AMQP_OPTS do
65
+ @block_fired = true
66
+
67
+ AMQP.connection.should be_connected
68
+ end
69
+ done(0.1) { @block_fired.should be_true }
70
+ end
71
+ end
72
+ end # .start
73
+
74
+
75
+
76
+
77
+ describe '.stop' do
78
+ context "when connection is not established" do
79
+ it 'is a no-op' do
80
+ expect { @res = AMQP.stop }.to_not raise_error
81
+ @res.should be_nil
82
+ end # it
83
+ end # context
84
+
85
+
86
+ context 'with established AMQP connection' do
87
+
88
+ #
89
+ #
90
+ #
91
+
92
+ include AMQP::Spec
93
+ after { AMQP.cleanup_state; done }
94
+ default_options AMQP_OPTS
95
+
96
+ #
97
+ # Examples
98
+ #
99
+
100
+ it 'properly closes AMQP broker connection and fires a callback. Mind the delay!' do
101
+ AMQP.start(AMQP_OPTS)
102
+ AMQP.connection.should be_connected
103
+
104
+ @block_has_fired = false
105
+
106
+ AMQP.stop do
107
+ @block_has_fired = true
108
+ end
109
+ AMQP.connection.should_not be_nil
110
+ done(0.1) do
111
+ @block_has_fired.should be_true
112
+ end
113
+ end # it
114
+ end # context
115
+ end # describe
116
+ end # describe AMQP