amqp 0.7.0.pre → 0.7.0

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 (71) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG +8 -2
  4. data/CONTRIBUTORS +22 -0
  5. data/Gemfile +3 -3
  6. data/README.md +20 -11
  7. data/Rakefile +30 -6
  8. data/amqp.gemspec +1 -1
  9. data/bin/cleanify.rb +50 -0
  10. data/examples/amqp/simple.rb +6 -4
  11. data/examples/mq/ack.rb +8 -6
  12. data/examples/mq/automatic_binding_for_default_direct_exchange.rb +65 -0
  13. data/examples/mq/callbacks.rb +9 -1
  14. data/examples/mq/clock.rb +17 -17
  15. data/examples/mq/hashtable.rb +19 -10
  16. data/examples/mq/internal.rb +13 -11
  17. data/examples/mq/logger.rb +38 -36
  18. data/examples/mq/multiclock.rb +16 -7
  19. data/examples/mq/pingpong.rb +16 -7
  20. data/examples/mq/pop.rb +8 -6
  21. data/examples/mq/primes-simple.rb +2 -0
  22. data/examples/mq/primes.rb +7 -5
  23. data/examples/mq/stocks.rb +14 -5
  24. data/lib/amqp.rb +12 -8
  25. data/lib/amqp/buffer.rb +35 -158
  26. data/lib/amqp/client.rb +34 -22
  27. data/lib/amqp/frame.rb +8 -64
  28. data/lib/amqp/protocol.rb +21 -70
  29. data/lib/amqp/server.rb +11 -9
  30. data/lib/amqp/spec.rb +8 -6
  31. data/lib/amqp/version.rb +2 -0
  32. data/lib/ext/blankslate.rb +3 -1
  33. data/lib/ext/em.rb +2 -0
  34. data/lib/ext/emfork.rb +13 -11
  35. data/lib/mq.rb +253 -156
  36. data/lib/mq/collection.rb +6 -88
  37. data/lib/mq/exchange.rb +70 -13
  38. data/lib/mq/header.rb +12 -6
  39. data/lib/mq/logger.rb +9 -7
  40. data/lib/mq/queue.rb +42 -30
  41. data/lib/mq/rpc.rb +6 -4
  42. data/protocol/codegen.rb +20 -18
  43. data/research/api.rb +10 -46
  44. data/research/primes-forked.rb +9 -7
  45. data/research/primes-processes.rb +74 -72
  46. data/research/primes-threaded.rb +9 -7
  47. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +61 -0
  48. data/spec/mq_helper.rb +70 -0
  49. data/spec/spec_helper.rb +84 -29
  50. data/spec/unit/amqp/buffer_spec.rb +178 -0
  51. data/spec/unit/amqp/client_spec.rb +472 -0
  52. data/spec/unit/amqp/frame_spec.rb +60 -0
  53. data/spec/unit/amqp/misc_spec.rb +123 -0
  54. data/spec/unit/amqp/protocol_spec.rb +53 -0
  55. data/spec/unit/mq/channel_close_spec.rb +15 -0
  56. data/spec/unit/mq/collection_spec.rb +129 -0
  57. data/spec/unit/mq/exchange_declaration_spec.rb +524 -0
  58. data/spec/unit/mq/misc_spec.rb +228 -0
  59. data/spec/unit/mq/mq_basic_spec.rb +39 -0
  60. data/spec/unit/mq/queue_declaration_spec.rb +97 -0
  61. data/spec/unit/mq/queue_spec.rb +71 -0
  62. metadata +33 -21
  63. data/Gemfile.lock +0 -16
  64. data/old/README +0 -30
  65. data/old/Rakefile +0 -12
  66. data/old/amqp-0.8.json +0 -606
  67. data/old/amqp_spec.rb +0 -796
  68. data/old/amqpc.rb +0 -695
  69. data/old/codegen.rb +0 -148
  70. data/spec/channel_close_spec.rb +0 -13
  71. data/spec/sync_async_spec.rb +0 -52
@@ -0,0 +1,228 @@
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"
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe MQ do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include AMQP::Spec
12
+
13
+ default_timeout 5
14
+
15
+ amqp_before do
16
+ @channel = MQ.new
17
+ end
18
+
19
+
20
+ #
21
+ # Examples
22
+ #
23
+
24
+
25
+ describe ".channel" do
26
+ it 'gives each thread a separate channel' do
27
+ pending 'This is not implemented in current lib'
28
+ class MQ
29
+ @@cur_channel = 0
30
+ end
31
+
32
+ described_class.channel.should == 1
33
+
34
+ Thread.new { described_class.channel }.value.should == 2
35
+ Thread.new { described_class.channel }.value.should == 3
36
+ done
37
+ end
38
+ end
39
+ end # describe MQ
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe MQ do
6
+
7
+ #
8
+ # Environment
9
+ #
10
+
11
+ include AMQP::Spec
12
+
13
+ default_timeout 5
14
+
15
+ amqp_before do
16
+ @channel = MQ.new
17
+ end
18
+
19
+
20
+ #
21
+ # Examples
22
+ #
23
+
24
+ describe "#queue" do
25
+ context "when queue name is specified" do
26
+ let(:name) { "a queue declared at #{Time.now.to_i}" }
27
+
28
+ it "declares a new queue with that name" do
29
+ queue = @channel.queue(name)
30
+ queue.name.should == name
31
+
32
+ done
33
+ end
34
+
35
+ it "caches that queue" do
36
+ queue = @channel.queue(name)
37
+ @channel.queue(name).object_id.should == queue.object_id
38
+
39
+ done
40
+ end
41
+ end # context
42
+
43
+ context "when queue name is passed on as an empty string" do
44
+ it "uses server-assigned queue name" do
45
+ @channel.queue("") do |queue, *args|
46
+ queue.name.should_not be_empty
47
+ done
48
+ end
49
+ end
50
+ end # context
51
+
52
+
53
+
54
+
55
+ context "when passive option is used" do
56
+ context "and exchange with given name already exists" do
57
+ it "silently returns" do
58
+ name = "a_new_queue declared at #{Time.now.to_i}"
59
+
60
+ original_exchange = @channel.queue(name)
61
+ exchange = @channel.queue(name, :passive => true)
62
+
63
+ exchange.should == original_exchange
64
+
65
+ done
66
+ end # it
67
+ end
68
+
69
+ context "and exchange with given name DOES NOT exist" do
70
+ it "raises an exception" do
71
+ pending "Not yet supported"
72
+
73
+ expect {
74
+ exchange = @channel.queue("queue declared at #{Time.now.to_i}", :passive => true)
75
+ }.to raise_error
76
+
77
+ done
78
+ end # it
79
+ end # context
80
+ end # context
81
+
82
+
83
+
84
+
85
+ context "when queue is re-declared with parameters different from original declaration" do
86
+ it "raises an exception" do
87
+ @channel.queue("previously.declared.durable.queue", :durable => true)
88
+
89
+ expect {
90
+ @channel.queue("previously.declared.durable.queue", :durable => false)
91
+ }.to raise_error(MQ::IncompatibleOptionsError)
92
+
93
+ done
94
+ end # it
95
+ end # context
96
+ end # describe
97
+ end # describe MQ
@@ -0,0 +1,71 @@
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
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amqp
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: true
4
+ prerelease: false
5
5
  segments:
6
6
  - 0
7
7
  - 7
8
8
  - 0
9
- - pre
10
- version: 0.7.0.pre
9
+ version: 0.7.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Aman Gupta
@@ -15,7 +14,7 @@ authors:
15
14
  autorequire:
16
15
  bindir: bin
17
16
  cert_chain:
18
- date: 2011-01-05 00:00:00 +00:00
17
+ date: 2011-01-18 00:00:00 +00:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -50,14 +49,16 @@ extra_rdoc_files:
50
49
  - doc/EXAMPLE_06_HASHTABLE
51
50
  files:
52
51
  - .gitignore
52
+ - .rspec
53
53
  - CHANGELOG
54
+ - CONTRIBUTORS
54
55
  - Gemfile
55
- - Gemfile.lock
56
56
  - README.md
57
57
  - Rakefile
58
58
  - amqp.gemspec
59
59
  - amqp.pre.gemspec
60
60
  - amqp.todo
61
+ - bin/cleanify.rb
61
62
  - bin/irb
62
63
  - doc/EXAMPLE_01_PINGPONG
63
64
  - doc/EXAMPLE_02_CLOCK
@@ -68,6 +69,7 @@ files:
68
69
  - doc/EXAMPLE_06_HASHTABLE
69
70
  - examples/amqp/simple.rb
70
71
  - examples/mq/ack.rb
72
+ - examples/mq/automatic_binding_for_default_direct_exchange.rb
71
73
  - examples/mq/callbacks.rb
72
74
  - examples/mq/clock.rb
73
75
  - examples/mq/hashtable.rb
@@ -97,12 +99,6 @@ files:
97
99
  - lib/mq/logger.rb
98
100
  - lib/mq/queue.rb
99
101
  - lib/mq/rpc.rb
100
- - old/README
101
- - old/Rakefile
102
- - old/amqp-0.8.json
103
- - old/amqp_spec.rb
104
- - old/amqpc.rb
105
- - old/codegen.rb
106
102
  - protocol/amqp-0.8.json
107
103
  - protocol/amqp-0.8.xml
108
104
  - protocol/codegen.rb
@@ -111,17 +107,35 @@ files:
111
107
  - research/primes-forked.rb
112
108
  - research/primes-processes.rb
113
109
  - research/primes-threaded.rb
114
- - spec/channel_close_spec.rb
110
+ - spec/integration/automatic_binding_for_default_direct_exchange_spec.rb
111
+ - spec/mq_helper.rb
115
112
  - spec/spec_helper.rb
116
- - spec/sync_async_spec.rb
113
+ - spec/unit/amqp/buffer_spec.rb
114
+ - spec/unit/amqp/client_spec.rb
115
+ - spec/unit/amqp/frame_spec.rb
116
+ - spec/unit/amqp/misc_spec.rb
117
+ - spec/unit/amqp/protocol_spec.rb
118
+ - spec/unit/mq/channel_close_spec.rb
119
+ - spec/unit/mq/collection_spec.rb
120
+ - spec/unit/mq/exchange_declaration_spec.rb
121
+ - spec/unit/mq/misc_spec.rb
122
+ - spec/unit/mq/mq_basic_spec.rb
123
+ - spec/unit/mq/queue_declaration_spec.rb
124
+ - spec/unit/mq/queue_spec.rb
117
125
  has_rdoc: true
118
- homepage: http://github.com/tmm1/amqp
126
+ homepage: http://github.com/ruby-amqp/amqp
119
127
  licenses: []
120
128
 
121
129
  post_install_message: "[\e[32mVersion 0.7\e[0m] [BUG] Sync API for queues and exchanges, support for server-generated queues & exchange names (via semi-lazy collection).\n\
122
130
  [\e[32mVersion 0.7\e[0m] [BUG] Sync API for MQ#close (Channel.Close) [issue #34].\n\
123
- [\e[32mVersion 0.7\e[0m] [FEATURE] From majek's fork, with some fixes. Example: AMQP.start(\"amqps://\")\n\
124
- [\e[32mVersion 0.7\e[0m] [DEVELOP] some em-spec-based specs, bin/irb, Gemfile.\n"
131
+ [\e[32mVersion 0.7\e[0m] [FEATURE] AMQP URL from majek's fork, with some fixes. Example: AMQP.start(\"amqps://\")\n\
132
+ [\e[32mVersion 0.7\e[0m] [DEVELOP] Added some em-spec-based specs, bin/irb, Gemfile.\n\
133
+ [\e[32mVersion 0.7\e[0m] [FEATURE] Added MQ::Exchange.default for the default exchange.\n\
134
+ [\e[32mVersion 0.7\e[0m] [FEATURE] Raise an exception if we're trying to use Basic.Reject with RabbitMQ.\n\
135
+ [\e[32mVersion 0.7\e[0m] [FEATURE] Fail if an entity is re-declared with different options.\n\
136
+ [\e[32mVersion 0.7\e[0m] [BUG] Don't reconnect if the credentials are wrong.\n\
137
+ [\e[32mVersion 0.7\e[0m] [BUG] Fixed an exception which occurred when Queue#bind was called synchronously with a callback.\n\
138
+ [\e[32mVersion 0.7\e[0m] [DEVELOPMENT] Added a lot of specs (Bacon replaced by rSpec 2).\n"
125
139
  rdoc_options:
126
140
  - --include=examples
127
141
  require_paths:
@@ -137,13 +151,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
137
151
  required_rubygems_version: !ruby/object:Gem::Requirement
138
152
  none: false
139
153
  requirements:
140
- - - ">"
154
+ - - ">="
141
155
  - !ruby/object:Gem::Version
142
156
  segments:
143
- - 1
144
- - 3
145
- - 1
146
- version: 1.3.1
157
+ - 0
158
+ version: "0"
147
159
  requirements: []
148
160
 
149
161
  rubyforge_project: amqp