amqp 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -2
- data/CHANGELOG +25 -0
- data/Gemfile +4 -2
- data/README.md +2 -0
- data/{amqp.todo → TODO} +1 -3
- data/amqp.gemspec +3 -3
- data/bin/irb +2 -2
- data/bin/jenkins.sh +25 -0
- data/bin/set_test_suite_realms_up.sh +21 -0
- data/doc/EXAMPLE_01_PINGPONG +1 -1
- data/doc/EXAMPLE_02_CLOCK +1 -1
- data/doc/EXAMPLE_03_STOCKS +1 -1
- data/doc/EXAMPLE_04_MULTICLOCK +1 -1
- data/doc/EXAMPLE_05_ACK +1 -1
- data/doc/EXAMPLE_05_POP +1 -1
- data/doc/EXAMPLE_06_HASHTABLE +1 -1
- data/examples/{mq/ack.rb → ack.rb} +6 -6
- data/examples/{mq/automatic_binding_for_default_direct_exchange.rb → automatic_binding_for_default_direct_exchange.rb} +4 -4
- data/examples/{mq/callbacks.rb → callbacks.rb} +2 -2
- data/examples/{mq/clock.rb → clock.rb} +5 -5
- data/examples/{mq/hashtable.rb → hashtable.rb} +4 -4
- data/examples/{mq/internal.rb → internal.rb} +5 -5
- data/examples/{mq/logger.rb → logger.rb} +5 -5
- data/examples/{mq/multiclock.rb → multiclock.rb} +4 -4
- data/examples/{mq/pingpong.rb → pingpong.rb} +5 -5
- data/examples/{mq/pop.rb → pop.rb} +3 -3
- data/examples/{mq/primes-simple.rb → primes-simple.rb} +0 -0
- data/examples/{mq/primes.rb → primes.rb} +6 -6
- data/examples/{amqp/simple.rb → simple.rb} +1 -1
- data/examples/{mq/stocks.rb → stocks.rb} +5 -5
- data/lib/amqp.rb +8 -112
- data/lib/amqp/basic_client.rb +58 -0
- data/lib/amqp/channel.rb +937 -0
- data/lib/amqp/client.rb +72 -79
- data/lib/{mq → amqp}/collection.rb +12 -2
- data/lib/amqp/connection.rb +115 -0
- data/lib/amqp/exceptions.rb +18 -0
- data/lib/{mq → amqp}/exchange.rb +32 -34
- data/lib/{ext → amqp/ext}/em.rb +1 -1
- data/lib/{ext → amqp/ext}/emfork.rb +0 -0
- data/lib/amqp/frame.rb +3 -3
- data/lib/{mq → amqp}/header.rb +5 -11
- data/lib/{mq → amqp}/logger.rb +2 -2
- data/lib/amqp/protocol.rb +2 -2
- data/lib/{mq → amqp}/queue.rb +20 -17
- data/lib/{mq → amqp}/rpc.rb +20 -8
- data/lib/amqp/server.rb +1 -1
- data/lib/amqp/version.rb +1 -1
- data/lib/mq.rb +20 -964
- data/protocol/codegen.rb +1 -1
- data/research/api.rb +3 -3
- data/research/primes-forked.rb +5 -5
- data/research/primes-processes.rb +5 -5
- data/research/primes-threaded.rb +5 -5
- data/spec/integration/authentication_spec.rb +114 -0
- data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +13 -12
- data/spec/{unit/mq → integration}/channel_close_spec.rb +2 -2
- data/spec/{unit/mq → integration}/exchange_declaration_spec.rb +26 -14
- data/spec/{unit/mq → integration}/queue_declaration_spec.rb +4 -4
- data/spec/integration/queue_exclusivity_spec.rb +95 -0
- data/spec/integration/reply_queue_communication_spec.rb +63 -0
- data/spec/integration/store_and_forward_spec.rb +121 -0
- data/spec/integration/topic_subscription_spec.rb +193 -0
- data/spec/integration/workload_distribution_spec.rb +245 -0
- data/spec/spec_helper.rb +16 -32
- data/spec/unit/{mq/mq_basic_spec.rb → amqp/basic_spec.rb} +4 -4
- data/spec/unit/{mq → amqp}/collection_spec.rb +22 -7
- data/spec/unit/amqp/connection_spec.rb +116 -0
- data/spec/unit/amqp/frame_spec.rb +18 -18
- data/spec/unit/amqp/protocol_spec.rb +9 -11
- metadata +54 -49
- data/lib/ext/blankslate.rb +0 -9
- data/spec/mq_helper.rb +0 -70
- data/spec/unit/amqp/client_spec.rb +0 -472
- data/spec/unit/amqp/misc_spec.rb +0 -123
- data/spec/unit/mq/misc_spec.rb +0 -228
- data/spec/unit/mq/queue_spec.rb +0 -71
data/spec/unit/amqp/misc_spec.rb
DELETED
@@ -1,123 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
require 'amqp'
|
4
|
-
|
5
|
-
module MockClientModule
|
6
|
-
def my_mock_method
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class MockClient
|
11
|
-
include AMQP::Client
|
12
|
-
end
|
13
|
-
|
14
|
-
describe AMQP, 'class object' do
|
15
|
-
context 'has class accessors, with default values' do
|
16
|
-
subject { AMQP }
|
17
|
-
|
18
|
-
its(:logging) { should be_false }
|
19
|
-
its(:connection) { should be_nil }
|
20
|
-
its(:conn) { should be_nil } # Alias for #connection
|
21
|
-
its(:closing) { should be_false }
|
22
|
-
its(:settings) { should == {:host => "127.0.0.1",
|
23
|
-
:port => 5672,
|
24
|
-
:user => "guest",
|
25
|
-
:pass => "guest",
|
26
|
-
:vhost => "/",
|
27
|
-
:timeout => nil,
|
28
|
-
:logging => false,
|
29
|
-
:ssl => false} }
|
30
|
-
|
31
|
-
its(:client) { should == AMQP::BasicClient }
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
describe '.client=' do
|
37
|
-
after(:all) { AMQP.client = AMQP::BasicClient }
|
38
|
-
|
39
|
-
it 'is used to change default client module' do
|
40
|
-
AMQP.client = MockClientModule
|
41
|
-
AMQP.client.should == MockClientModule
|
42
|
-
MockClientModule.ancestors.should include AMQP
|
43
|
-
end
|
44
|
-
|
45
|
-
describe 'new default client module' do
|
46
|
-
it 'sticks around after being assigned' do
|
47
|
-
AMQP.client.should == MockClientModule
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'extends any object that includes AMQP::Client' do
|
51
|
-
@client = MockClient.new
|
52
|
-
@client.should respond_to :my_mock_method
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end # .client
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
describe 'logging' do
|
61
|
-
after(:all) do
|
62
|
-
AMQP.logging = false
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'is silent by default' do
|
66
|
-
AMQP.logging.should be_false
|
67
|
-
end
|
68
|
-
end # .logging=
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
describe '.start' do
|
74
|
-
context 'inside EM loop' do
|
75
|
-
include AMQP::SpecHelper
|
76
|
-
em_after { AMQP.cleanup_state }
|
77
|
-
|
78
|
-
it 'yields to given block AFTER connection is established' do
|
79
|
-
em do
|
80
|
-
AMQP.start AMQP_OPTS do
|
81
|
-
@block_fired = true
|
82
|
-
AMQP.connection.should be_connected
|
83
|
-
end
|
84
|
-
done(0.1) { @block_fired.should be_true }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end # context 'inside EM loop'
|
88
|
-
end # .start
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
describe '.stop' do
|
94
|
-
it 'is noop if connection is not established' do
|
95
|
-
expect { @res = AMQP.stop }.to_not raise_error
|
96
|
-
@res.should be_nil
|
97
|
-
end
|
98
|
-
|
99
|
-
context 'with established AMQP connection' do
|
100
|
-
include AMQP::Spec
|
101
|
-
after { AMQP.cleanup_state; done }
|
102
|
-
default_options AMQP_OPTS
|
103
|
-
|
104
|
-
it 'unsets AMQP.connection property. Mind the delay!' do
|
105
|
-
AMQP.start(AMQP_OPTS)
|
106
|
-
AMQP.connection.should be_connected
|
107
|
-
|
108
|
-
AMQP.stop
|
109
|
-
AMQP.connection.should_not be_nil
|
110
|
-
done(0.1) { AMQP.connection.should be_nil }
|
111
|
-
end
|
112
|
-
|
113
|
-
it 'yields to given block AFTER disconnect (BUT before AMQP.conn is cleared!)' do
|
114
|
-
AMQP.stop do
|
115
|
-
@block_fired = true
|
116
|
-
AMQP.connection.should_not be_nil
|
117
|
-
AMQP.instance_variable_get(:@closing).should be_true
|
118
|
-
end
|
119
|
-
done(0.1) { @block_fired.should be_true }
|
120
|
-
end
|
121
|
-
end # context 'with established AMQP connection'
|
122
|
-
end # .stop
|
123
|
-
end
|
data/spec/unit/mq/misc_spec.rb
DELETED
@@ -1,228 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'mq_helper'
|
3
|
-
|
4
|
-
describe 'MQ', 'class object' do
|
5
|
-
# after{AMQP.cleanup_state} # Tips off Thread.current[:mq] call
|
6
|
-
subject { MQ }
|
7
|
-
|
8
|
-
its(:logging) { should be_false }
|
9
|
-
|
10
|
-
|
11
|
-
describe '.logging=' do
|
12
|
-
it 'is independent from AMQP.logging' do
|
13
|
-
#
|
14
|
-
AMQP.logging = true
|
15
|
-
MQ.logging.should be_false
|
16
|
-
MQ.logging = false
|
17
|
-
AMQP.logging.should == true
|
18
|
-
|
19
|
-
#
|
20
|
-
AMQP.logging = false
|
21
|
-
MQ.logging = true
|
22
|
-
AMQP.logging.should be_false
|
23
|
-
MQ.logging = false
|
24
|
-
end
|
25
|
-
end # .logging=
|
26
|
-
|
27
|
-
|
28
|
-
describe '.default' do
|
29
|
-
it 'creates MQ object and stashes it in a Thread-local Hash' do
|
30
|
-
MQ.should_receive(:new).with(no_args).and_return('MQ mock')
|
31
|
-
Thread.current.should_receive(:[]).with(:mq)
|
32
|
-
Thread.current.should_receive(:[]=).with(:mq, 'MQ mock')
|
33
|
-
MQ.default.should == 'MQ mock'
|
34
|
-
end
|
35
|
-
end # .default
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
describe '.id' do
|
40
|
-
it 'returns Thread-local uuid for mq' do
|
41
|
-
uuid_pattern = /.*-[0-9]*-[0-9]*/
|
42
|
-
Thread.current.should_receive(:[]).with(:mq_id)
|
43
|
-
Thread.current.should_receive(:[]=).with(:mq_id, uuid_pattern)
|
44
|
-
MQ.id.should =~ uuid_pattern
|
45
|
-
end
|
46
|
-
end # .id
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
describe '.error' do
|
51
|
-
after(:all) { MQ.instance_variable_set(:@error_callback, nil) } # clear error callback
|
52
|
-
|
53
|
-
it 'is noop unless @error_callback was previously set' do
|
54
|
-
MQ.instance_variable_get(:@error_callback).should be_nil
|
55
|
-
MQ.error("Msg").should be_nil
|
56
|
-
end
|
57
|
-
|
58
|
-
context 'when @error_callback is set' do
|
59
|
-
before { MQ.error { |message| message.should == "Msg"; @callback_fired = true } }
|
60
|
-
|
61
|
-
it 'is setting @error_callback to given block' do
|
62
|
-
MQ.instance_variable_get(:@error_callback).should_not be_nil
|
63
|
-
@callback_fired.should be_false
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'does not fire @error_callback immediately' do
|
67
|
-
@callback_fired.should be_false
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'fires @error_callback on subsequent .error calls' do
|
71
|
-
MQ.error("Msg")
|
72
|
-
@callback_fired.should be_true
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end # .error
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
describe '.method_missing' do
|
80
|
-
it 'routes all unrecognized methods to MQ.default' do
|
81
|
-
MQ.should_receive(:new).with(no_args).and_return(mock('channel'))
|
82
|
-
MQ.default.should_receive(:foo).with("Msg")
|
83
|
-
MQ.foo("Msg")
|
84
|
-
end
|
85
|
-
end # .method_missing
|
86
|
-
end # describe 'MQ as a class'
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
describe 'Channel object' do
|
92
|
-
context 'when initialized with a mock connection' do
|
93
|
-
before { @client = MockConnection.new }
|
94
|
-
after { AMQP.cleanup_state }
|
95
|
-
subject { MQ.new(@client).tap { |mq| mq.succeed } } # Indicates that channel is connected
|
96
|
-
|
97
|
-
it 'should have a channel' do
|
98
|
-
subject.channel.should be_kind_of Fixnum
|
99
|
-
subject.channel.should == 1 # Essentially, this channel (mq) number
|
100
|
-
end
|
101
|
-
|
102
|
-
it 'has publicly accessible collections' do
|
103
|
-
subject.exchanges.should be_empty
|
104
|
-
subject.queues.should be_empty
|
105
|
-
subject.consumers.should be_empty
|
106
|
-
subject.rpcs.should be_empty
|
107
|
-
end
|
108
|
-
|
109
|
-
describe '#send' do
|
110
|
-
it 'sends given data through its connection' do
|
111
|
-
args = [ 'data1', 'data2', 'data3']
|
112
|
-
subject.send *args
|
113
|
-
@client.messages[-3..-1].map { |message| message[:data] }.should == args
|
114
|
-
end #send
|
115
|
-
|
116
|
-
it 'sets data`s ticket property if @ticket is set for MQ object' do
|
117
|
-
ticket = subject_mock(:@ticket)
|
118
|
-
data = mock('data')
|
119
|
-
data.should_receive(:ticket=).with(ticket)
|
120
|
-
subject.send data
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
describe '#reset' do
|
127
|
-
it 'resets and reinitializes the channel, clears and resets its queues/exchanges' do
|
128
|
-
subject.queue('test').should_receive(:reset)
|
129
|
-
subject.fanout('fanout').should_receive(:reset)
|
130
|
-
subject.should_receive(:initialize).with(@client)
|
131
|
-
|
132
|
-
subject.reset
|
133
|
-
subject.queues.should be_empty
|
134
|
-
subject.exchanges.should be_empty
|
135
|
-
subject.consumers.should be_empty
|
136
|
-
end
|
137
|
-
end #reset
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
describe '#prefetch', 'setting :prefetch_count for broker' do
|
142
|
-
it 'sends Protocol::Basic::Qos' do
|
143
|
-
subject.prefetch 13
|
144
|
-
@client.messages.last[:data].should be_an AMQP::Protocol::Basic::Qos
|
145
|
-
@client.messages.last[:data].instance_variable_get(:@prefetch_count).should == 13
|
146
|
-
end
|
147
|
-
|
148
|
-
it 'returns MQ object itself, allowing for method chains' do
|
149
|
-
subject.prefetch(1).should == subject
|
150
|
-
end
|
151
|
-
end #prefetch
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
describe '#recover', "asks broker to redeliver unack'ed messages on this channel" do
|
156
|
-
it "sends Basic::Recover through @connection" do
|
157
|
-
subject.recover
|
158
|
-
@client.messages.last[:data].should be_an AMQP::Protocol::Basic::Recover
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'defaults to :requeue=nil, redeliver messages to the original recipient' do
|
162
|
-
subject.recover
|
163
|
-
@client.messages.last[:data].instance_variable_get(:@requeue).should == nil
|
164
|
-
end #prefetch
|
165
|
-
|
166
|
-
it 'prompts broker to requeue messages (to other subs) if :requeue set to true' do
|
167
|
-
subject.recover true
|
168
|
-
@client.messages.last[:data].instance_variable_get(:@requeue).should == true
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'returns MQ object itself, allowing for method chains' do
|
172
|
-
subject.recover.should == subject
|
173
|
-
end
|
174
|
-
end #recover
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
describe '#close', 'closes channel' do
|
179
|
-
it 'can be simplified, getting rid of @closing ivar?'
|
180
|
-
# TODO: Just set a callback sending Protocol::Channel::Close...'
|
181
|
-
|
182
|
-
it 'sends Channel::Close through @connection' do
|
183
|
-
subject.close
|
184
|
-
@client.messages.last[:data].should be_an AMQP::Protocol::Channel::Close
|
185
|
-
end
|
186
|
-
|
187
|
-
it 'does not actually delete the channel or close connection' do
|
188
|
-
@client.channels.should_not_receive(:delete)
|
189
|
-
@client.should_not_receive(:close)
|
190
|
-
subject.close
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'actual channel closing happens ONLY when Channel::CloseOk is received' do
|
194
|
-
@client.channels.should_receive(:delete) do |key|
|
195
|
-
key.should == 1
|
196
|
-
@client.channels.clear
|
197
|
-
end
|
198
|
-
@client.should_receive(:close)
|
199
|
-
subject.process_frame AMQP::Frame::Method.new(AMQP::Protocol::Channel::CloseOk.new)
|
200
|
-
end
|
201
|
-
end #close
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
describe '#get_queue', 'supports #pop (Basic::Get) operation on queues' do
|
207
|
-
it 'yields a FIFO queue to a given block' do
|
208
|
-
subject.get_queue do |fifo|
|
209
|
-
@block_called = true
|
210
|
-
fifo.should == []
|
211
|
-
end
|
212
|
-
@block_called.should be_true
|
213
|
-
end
|
214
|
-
|
215
|
-
it 'FIFO queue contains consumers that called Queue#pop' do
|
216
|
-
queue1 = subject.queue('test1')
|
217
|
-
queue2 = subject.queue('test2')
|
218
|
-
queue1.pop
|
219
|
-
queue2.pop
|
220
|
-
subject.get_queue do |fifo|
|
221
|
-
fifo.should have(2).consumers
|
222
|
-
fifo.first.should == queue1
|
223
|
-
fifo.last.should == queue2
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end #get_queue
|
227
|
-
end # context 'when initialized with a mock connection'
|
228
|
-
end # describe MQ object, also vaguely known as "channel"
|
data/spec/unit/mq/queue_spec.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'mq_helper'
|
3
|
-
|
4
|
-
describe MQ::Queue, :broker => true do
|
5
|
-
|
6
|
-
#
|
7
|
-
# Environment
|
8
|
-
#
|
9
|
-
|
10
|
-
include AMQP::Spec
|
11
|
-
|
12
|
-
default_options AMQP_OPTS
|
13
|
-
default_timeout 3
|
14
|
-
|
15
|
-
amqp_before do
|
16
|
-
@channel = MQ.new
|
17
|
-
@queue = MQ::Queue.new(@channel, 'test_queue', :option => 'useless')
|
18
|
-
end
|
19
|
-
|
20
|
-
amqp_after { @queue.purge; AMQP.cleanup_state }
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
# Examples
|
25
|
-
#
|
26
|
-
|
27
|
-
context 'declared with name of "test_queue"' do
|
28
|
-
amqp_after {@queue.unsubscribe; @queue.delete}
|
29
|
-
# TODO: Fix amqp_after: it is NOT run in case of raised exceptions, it seems
|
30
|
-
|
31
|
-
it 'supports asynchronous subscription to broker-predefined amq.direct exchange' do
|
32
|
-
data = "data sent via amq.direct exchange"
|
33
|
-
|
34
|
-
@queue.subscribe do |header, message|
|
35
|
-
header.should be_an MQ::Header
|
36
|
-
message.should == data
|
37
|
-
done
|
38
|
-
end
|
39
|
-
@queue.publish(data)
|
40
|
-
end # it
|
41
|
-
|
42
|
-
it 'supports synchronous message fetching via #pop (Basic.Get)' do
|
43
|
-
@queue.publish('send some data down the pipe')
|
44
|
-
|
45
|
-
@queue.pop do |header, message|
|
46
|
-
header.should be_an MQ::Header
|
47
|
-
message.should == 'send some data down the pipe'
|
48
|
-
done
|
49
|
-
end
|
50
|
-
end # it
|
51
|
-
|
52
|
-
xit 'asynchronously receives own status from the broker' do
|
53
|
-
pending "TODO: this example currently fails; do investigate why. MK."
|
54
|
-
total_number_of_messages = 123
|
55
|
-
|
56
|
-
total_number_of_messages.times do |n|
|
57
|
-
@queue.publish('message # #{n}')
|
58
|
-
end
|
59
|
-
|
60
|
-
@on_status_fired = false
|
61
|
-
@queue.status do |messages, consumers|
|
62
|
-
@on_status_fired = true
|
63
|
-
messages.should == total_number_of_messages
|
64
|
-
consumers.should == 0
|
65
|
-
end # it
|
66
|
-
|
67
|
-
@on_status_fired.should be_true
|
68
|
-
done(1)
|
69
|
-
end # it
|
70
|
-
end # context
|
71
|
-
end # MQ::Queue, 'with real connection
|