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
@@ -1,60 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.expand_path('../frame', __FILE__)
3
+ require "amqp/basic_client"
4
4
 
5
5
  require 'uri'
6
6
 
7
7
  module AMQP
8
- class Error < StandardError; end
9
-
10
- module BasicClient
11
- attr_reader :broker
12
-
13
- def process_frame(frame)
14
- if mq = channels[frame.channel]
15
- mq.process_frame(frame)
16
- return
17
- end
18
-
19
- case frame
20
- when Frame::Method
21
- case method = frame.payload
22
- when Protocol::Connection::Start
23
- @broker = method
24
- send Protocol::Connection::StartOk.new({:platform => 'Ruby/EventMachine',
25
- :product => 'AMQP',
26
- :information => 'http://github.com/ruby-amqp/amqp',
27
- :version => VERSION},
28
- 'AMQPLAIN',
29
- {:LOGIN => @settings[:user],
30
- :PASSWORD => @settings[:pass]},
31
- 'en_US')
32
-
33
- when Protocol::Connection::Tune
34
- send Protocol::Connection::TuneOk.new(:channel_max => 0,
35
- :frame_max => 131072,
36
- :heartbeat => 0)
37
-
38
- send Protocol::Connection::Open.new(:virtual_host => @settings[:vhost],
39
- :capabilities => '',
40
- :insist => @settings[:insist])
41
-
42
- @on_disconnect = method(:disconnected)
43
-
44
- when Protocol::Connection::OpenOk
45
- succeed(self)
46
-
47
- when Protocol::Connection::Close
48
- # raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
49
- STDERR.puts "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
50
-
51
- when Protocol::Connection::CloseOk
52
- @on_disconnect.call if @on_disconnect
53
- end
54
- end
55
- end
56
- end
57
-
58
8
  def self.client
59
9
  @client ||= BasicClient
60
10
  end
@@ -78,25 +28,55 @@ module AMQP
78
28
  timeout @settings[:timeout] if @settings[:timeout]
79
29
  errback { @on_disconnect.call } unless @reconnecting
80
30
 
81
- @connected = false
31
+ # TCP connection "openness"
32
+ @tcp_connection_established = false
33
+ # AMQP connection "openness"
34
+ @connected = false
82
35
  end
83
36
 
84
37
  def connection_completed
85
- start_tls if @settings[:ssl]
38
+ if @settings[:ssl].is_a? Hash
39
+ start_tls @settings[:ssl]
40
+ elsif @settings[:ssl]
41
+ start_tls
42
+ end
43
+
86
44
  log 'connected'
87
45
  # @on_disconnect = proc { raise Error, 'Disconnected from server' }
88
46
  unless @closing
89
47
  @reconnecting = false
90
48
  end
91
49
 
92
- @connected = true
93
- @connection_status.call(:connected) if @connection_status
50
+ @tcp_connection_established = true
94
51
 
95
52
  @buf = Buffer.new
96
53
  send_data HEADER
97
54
  send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
55
+
56
+ if heartbeat = @settings[:heartbeat]
57
+ init_heartbeat if (@settings[:heartbeat] = heartbeat.to_i) > 0
58
+ end
98
59
  end
99
60
 
61
+ def init_heartbeat
62
+ @last_server_heartbeat = Time.now
63
+
64
+ @timer ||= EM::PeriodicTimer.new(@settings[:heartbeat]) do
65
+ if connected?
66
+ if @last_server_heartbeat < (Time.now - (@settings[:heartbeat] * 2))
67
+ log "Reconnecting due to missing server heartbeats"
68
+ reconnect(true)
69
+ else
70
+ send AMQP::Frame::Heartbeat.new
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def tcp_connection_established?
77
+ @tcp_connection_established
78
+ end # tcp_connection_established?
79
+
100
80
  def connected?
101
81
  @connected
102
82
  end
@@ -104,7 +84,7 @@ module AMQP
104
84
  def unbind
105
85
  log 'disconnected'
106
86
  @connected = false
107
- EM.next_tick { @on_disconnect.call }
87
+ EM.next_tick { @on_disconnect.call; @tcp_connection_established = false }
108
88
  end
109
89
 
110
90
  def add_channel(mq)
@@ -198,17 +178,27 @@ module AMQP
198
178
  EM.reconnect @settings[:host], @settings[:port], self
199
179
  end
200
180
 
201
- def self.connect amqp_url_or_opts = nil
202
- if amqp_url_or_opts.is_a?(String)
203
- opts = parse_amqp_url(amqp_url_or_opts)
204
- elsif amqp_url_or_opts.is_a?(Hash)
205
- opts = amqp_url_or_opts
206
- elsif amqp_url_or_opts.nil?
207
- opts = Hash.new
181
+ def self.connect(arg = nil)
182
+ opts = case arg
183
+ when String then
184
+ opts = parse_connection_uri(arg)
185
+ when Hash then
186
+ arg
187
+ else
188
+ Hash.new
189
+ end
190
+
191
+ options = AMQP.settings.merge(opts)
192
+
193
+ if options[:username]
194
+ options[:user] = options.delete(:username)
208
195
  end
209
196
 
210
- opts = AMQP.settings.merge(opts)
211
- EM.connect opts[:host], opts[:port], self, opts
197
+ if options[:password]
198
+ options[:pass] = options.delete(:password)
199
+ end
200
+
201
+ EM.connect options[:host], options[:port], self, options
212
202
  end
213
203
 
214
204
  def connection_status(&blk)
@@ -217,6 +207,23 @@ module AMQP
217
207
 
218
208
  private
219
209
 
210
+ def self.parse_connection_uri(connection_string)
211
+ uri = URI.parse(connection_string)
212
+ raise("amqp:// uri required!") unless %w{amqp amqps}.include?(uri.scheme)
213
+
214
+ opts = {}
215
+
216
+ opts[:user] = URI.unescape(uri.user) if uri.user
217
+ opts[:pass] = URI.unescape(uri.password) if uri.password
218
+ opts[:vhost] = URI.unescape(uri.path) if uri.path
219
+ opts[:host] = uri.host if uri.host
220
+ opts[:port] = uri.port || Hash["amqp" => 5672, "amqps" => 5671][uri.scheme]
221
+ opts[:ssl] = uri.scheme == "amqps"
222
+
223
+ opts
224
+ end
225
+
226
+
220
227
  def disconnected
221
228
  @connection_status.call(:disconnected) if @connection_status
222
229
  reconnect
@@ -228,19 +235,5 @@ module AMQP
228
235
  pp args
229
236
  puts
230
237
  end
231
-
232
- def self.parse_amqp_url(amqp_url)
233
- uri = URI.parse(amqp_url)
234
- raise("amqp:// uri required!") unless %w{amqp amqps}.include? uri.scheme
235
- opts = {}
236
- opts[:user] = URI.unescape(uri.user) if uri.user
237
- opts[:pass] = URI.unescape(uri.password) if uri.password
238
- opts[:vhost] = URI.unescape(uri.path) if uri.path
239
- opts[:host] = uri.host if uri.host
240
- opts[:port] = uri.port ? uri.port :
241
- {"amqp" => 5672, "amqps" => 5671}[uri.scheme]
242
- opts[:ssl] = uri.scheme == "amqps"
243
- return opts
244
- end
245
238
  end
246
239
  end
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
- class MQ
4
- # MQ::Collection is used to store named AMQ model entities (exchanges, queues)
3
+ module AMQP
4
+ # AMQP::Collection is used to store named AMQ model entities (exchanges, queues)
5
5
  class Collection < ::Array
6
6
  class IncompatibleItemError < ArgumentError
7
7
  def initialize(item)
@@ -46,5 +46,15 @@ class MQ
46
46
  __push__(item)
47
47
  return item
48
48
  end
49
+
50
+ def include?(name)
51
+ not self[name].nil?
52
+ end
53
+
54
+ def delete(name)
55
+ if self.include?(name)
56
+ self.delete_at(self.index(self[name]))
57
+ end
58
+ end
49
59
  end
50
60
  end
@@ -0,0 +1,115 @@
1
+ # encoding: utf-8
2
+
3
+ require "amqp/ext/em"
4
+
5
+ require "amqp/buffer"
6
+ require "amqp/spec"
7
+ require "amqp/protocol"
8
+ require "amqp/frame"
9
+ require "amqp/client"
10
+
11
+ module AMQP
12
+ class << self
13
+ @logging = false
14
+ attr_accessor :logging
15
+ attr_reader :conn, :closing
16
+ alias :closing? :closing
17
+ alias :connection :conn
18
+ end
19
+
20
+ def self.connect *args
21
+ Client.connect *args
22
+ end
23
+
24
+ def self.settings
25
+ @settings ||= {
26
+ # server address
27
+ :host => '127.0.0.1',
28
+ :port => PORT,
29
+
30
+ # login details
31
+ :user => 'guest',
32
+ :pass => 'guest',
33
+ :vhost => '/',
34
+
35
+ # connection timeout
36
+ :timeout => nil,
37
+
38
+ # logging
39
+ :logging => false,
40
+
41
+ # ssl
42
+ :ssl => false
43
+ }
44
+ end
45
+
46
+ # Must be called to startup the connection to the AMQP server.
47
+ #
48
+ # The method takes several arguments and an optional block.
49
+ #
50
+ # This takes any option that is also accepted by EventMachine::connect.
51
+ # Additionally, there are several AMQP-specific options.
52
+ #
53
+ # * :user => String (default 'guest')
54
+ # The username as defined by the AMQP server.
55
+ # * :pass => String (default 'guest')
56
+ # The password for the associated :user as defined by the AMQP server.
57
+ # * :vhost => String (default '/')
58
+ # The virtual host as defined by the AMQP server.
59
+ # * :timeout => Numeric (default nil)
60
+ # Measured in seconds.
61
+ # * :logging => true | false (default false)
62
+ # Toggle the extremely verbose logging of all protocol communications
63
+ # between the client and the server. Extremely useful for debugging.
64
+ #
65
+ # AMQP.start do
66
+ # # default is to connect to localhost:5672
67
+ #
68
+ # # define queues, exchanges and bindings here.
69
+ # # also define all subscriptions and/or publishers
70
+ # # here.
71
+ #
72
+ # # this block never exits unless EM.stop_event_loop
73
+ # # is called.
74
+ # end
75
+ #
76
+ # Most code will use the MQ api. Any calls to AMQP::Channel.direct / AMQP::Channel.fanout /
77
+ # AMQP::Channel.topic / AMQP::Channel.queue will implicitly call #start. In those cases,
78
+ # it is sufficient to put your code inside of an EventMachine.run
79
+ # block. See the code examples in AMQP for details.
80
+ #
81
+ def self.start *args, &blk
82
+ EM.run {
83
+ @conn ||= connect *args
84
+ @conn.callback(&blk) if blk
85
+ @conn
86
+ }
87
+ end
88
+
89
+ class << self
90
+ alias :run :start
91
+ end
92
+
93
+ def self.stop
94
+ if @conn and not @closing
95
+ @closing = true
96
+ EM.next_tick do
97
+ @conn.close {
98
+ yield if block_given?
99
+ @conn = nil
100
+ @closing = false
101
+ }
102
+ end
103
+ end
104
+ end
105
+
106
+ def self.fork workers
107
+ EM.fork(workers) do
108
+ # clean up globals in the fork
109
+ Thread.current[:mq] = nil
110
+ AMQP.instance_variable_set('@conn', nil)
111
+
112
+ yield
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ module AMQP
4
+ # Raised whenever an illegal operation is attempted.
5
+ class Error < StandardError; end
6
+
7
+ class IncompatibleOptionsError < Error
8
+ def initialize(name, opts_1, opts_2)
9
+ super("There is already an instance called #{name} with options #{opts_1.inspect}, you can't define the same instance with different options (#{opts_2.inspect})!")
10
+ end
11
+ end # IncompatibleOptionsError
12
+
13
+ class ChannelClosedError < Error
14
+ def initialize(instance)
15
+ super("The channel #{instance.channel} was closed, you can't use it anymore!")
16
+ end
17
+ end # ChannelClosedError
18
+ end # AMQP
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- class MQ
3
+ module AMQP
4
4
  # An Exchange acts as an ingress point for all published messages. An
5
5
  # exchange may also be described as a router or a matcher. Every
6
6
  # published message is received by an exchange which, depending on its
@@ -14,7 +14,7 @@ class MQ
14
14
  # As part of the standard, the server _must_ predeclare the direct exchange
15
15
  # 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
16
16
  # starting with 'amq.' are reserved). Attempts to declare an exchange using
17
- # 'amq.' as the name will raise an MQ:Error and fail. In practice these
17
+ # 'amq.' as the name will raise an AMQP::Error and fail. In practice these
18
18
  # default exchanges are never used directly by client code.
19
19
  #
20
20
  # These predececlared exchanges are used when the client code declares
@@ -23,14 +23,6 @@ class MQ
23
23
  #
24
24
  class Exchange
25
25
 
26
- #
27
- # Behaviors
28
- #
29
-
30
- include AMQP
31
-
32
-
33
-
34
26
  #
35
27
  # API
36
28
  #
@@ -45,12 +37,12 @@ class MQ
45
37
  # predefined in the system, so you can use it straightaway.
46
38
 
47
39
  # Example:
48
- # MQ.new.queue("tasks")
49
- # MQ::Exchange.default.publish("make clean", routing_key: "tasks")
40
+ # AMQP::Channel.new.queue("tasks")
41
+ # AMQP::Channel::Exchange.default.publish("make clean", routing_key: "tasks")
50
42
 
51
43
  # For more info see section 2.1.2.4 Automatic Mode of the AMQP 0.9.1 spec.
52
- def self.default
53
- @@default ||= self.new(MQ.new, :direct, "", :no_declare => true)
44
+ def self.default(channel = nil)
45
+ self.new(channel || AMQP::Channel.new, :direct, "", :no_declare => true)
54
46
  end
55
47
 
56
48
  def self.add_default_options(type, name, opts, block)
@@ -65,7 +57,7 @@ class MQ
65
57
  # As part of the standard, the server _must_ predeclare the direct exchange
66
58
  # 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
67
59
  # starting with 'amq.' are reserved). Attempts to declare an exchange using
68
- # 'amq.' as the name will raise an MQ:Error and fail. In practice these
60
+ # 'amq.' as the name will raise an AMQP::Error and fail. In practice these
69
61
  # default exchanges are never used directly by client code.
70
62
  #
71
63
  # == Direct
@@ -80,14 +72,14 @@ class MQ
80
72
  # called 'amq.direct'. In this case it needs to use :key to do its matching.
81
73
  #
82
74
  # # exchange is named 'foo'
83
- # exchange = MQ::Exchange.new(MQ.new, :direct, 'foo')
75
+ # exchange = AMQP::Channel::Exchange.new(AMQP::Channel.new, :direct, 'foo')
84
76
  #
85
77
  # # or, the exchange can use the default name (amq.direct) and perform
86
78
  # # routing comparisons using the :key
87
- # exchange = MQ::Exchange.new(MQ.new, :direct, "", :key => 'foo')
79
+ # exchange = AMQP::Channel::Exchange.new(AMQP::Channel.new, :direct, "", :key => 'foo')
88
80
  # exchange.publish('some data') # will be delivered to queue bound to 'foo'
89
81
  #
90
- # queue = MQ::Queue.new(MQ.new, 'foo')
82
+ # queue = AMQP::Channel::Queue.new(AMQP::Channel.new, 'foo')
91
83
  # # can receive data since the queue name and the exchange key match exactly
92
84
  # queue.pop { |data| puts "received data [#{data}]" }
93
85
  #
@@ -105,21 +97,21 @@ class MQ
105
97
  # In this case it needs to use :key to do its matching.
106
98
  #
107
99
  # EM.run do
108
- # clock = MQ::Exchange.new(MQ.new, :fanout, 'clock')
100
+ # clock = AMQP::Channel::Exchange.new(AMQP::Channel.new, :fanout, 'clock')
109
101
  # EM.add_periodic_timer(1) do
110
102
  # puts "\npublishing #{time = Time.now}"
111
103
  # clock.publish(Marshal.dump(time))
112
104
  # end
113
105
  #
114
106
  # # one way of defining a queue
115
- # amq = MQ::Queue.new(MQ.new, 'every second')
116
- # amq.bind(MQ.fanout('clock')).subscribe do |time|
107
+ # amq = AMQP::Channel::Queue.new(AMQP::Channel.new, 'every second')
108
+ # amq.bind(AMQP::Channel.fanout('clock')).subscribe do |time|
117
109
  # puts "every second received #{Marshal.load(time)}"
118
110
  # end
119
111
  #
120
112
  # # defining a queue using the convenience method
121
113
  # # note the string passed to #bind
122
- # MQ.queue('every 5 seconds').bind('clock').subscribe do |time|
114
+ # AMQP::Channel.queue('every 5 seconds').bind('clock').subscribe do |time|
123
115
  # time = Marshal.load(time)
124
116
  # puts "every 5 seconds received #{time}" if time.strftime('%S').to_i%5 == 0
125
117
  # end
@@ -150,7 +142,7 @@ class MQ
150
142
  # for matching against the published routing key.
151
143
  #
152
144
  # EM.run do
153
- # exch = MQ::Exchange.new(MQ.new, :topic, "stocks")
145
+ # exch = AMQP::Channel::Exchange.new(AMQP::Channel.new, :topic, "stocks")
154
146
  # keys = ['stock.us.aapl', 'stock.de.dax']
155
147
  #
156
148
  # EM.add_periodic_timer(1) do # every second
@@ -159,17 +151,17 @@ class MQ
159
151
  # end
160
152
  #
161
153
  # # match against one dot-separated item
162
- # MQ.queue('us stocks').bind(exch, :key => 'stock.us.*').subscribe do |price|
154
+ # AMQP::Channel.queue('us stocks').bind(exch, :key => 'stock.us.*').subscribe do |price|
163
155
  # puts "us stock price [#{price}]"
164
156
  # end
165
157
  #
166
158
  # # match against multiple dot-separated items
167
- # MQ.queue('all stocks').bind(exch, :key => 'stock.#').subscribe do |price|
159
+ # AMQP::Channel.queue('all stocks').bind(exch, :key => 'stock.#').subscribe do |price|
168
160
  # puts "all stocks: price [#{price}]"
169
161
  # end
170
162
  #
171
163
  # # require exact match
172
- # MQ.queue('only dax').bind(exch, :key => 'stock.de.dax').subscribe do |price|
164
+ # AMQP::Channel.queue('only dax').bind(exch, :key => 'stock.de.dax').subscribe do |price|
173
165
  # puts "dax price [#{price}]"
174
166
  # end
175
167
  # end
@@ -230,8 +222,8 @@ class MQ
230
222
  #
231
223
  # == Exceptions
232
224
  # Doing any of these activities are illegal and will raise exceptions:
233
- #
234
- # * redeclare an already-declared exchange to a different type (raises MQ::IncompatibleOptionsError)
225
+ #
226
+ # * redeclare an already-declared exchange to a different type (raises AMQP::Channel::IncompatibleOptionsError)
235
227
  # * :passive => true and the exchange does not exist (NOT_FOUND)
236
228
  #
237
229
  def initialize(mq, type, name, opts = {}, &block)
@@ -270,12 +262,16 @@ class MQ
270
262
  attr_reader :name, :type, :key, :status
271
263
  attr_accessor :opts, :callback
272
264
 
265
+ def channel
266
+ @mq
267
+ end # channel
268
+
273
269
  # This method publishes a staged file message to a specific exchange.
274
270
  # The file message will be routed to queues as defined by the exchange
275
271
  # configuration and distributed to any active consumers when the
276
272
  # transaction, if any, is committed.
277
273
  #
278
- # exchange = MQ.direct('name', :key => 'foo.bar')
274
+ # exchange = AMQP::Channel.direct('name', :key => 'foo.bar')
279
275
  # exchange.publish("some data")
280
276
  #
281
277
  # The method takes several hash key options which modify the behavior or
@@ -310,6 +306,8 @@ class MQ
310
306
  # message stays in memory and is never persisted to non-volatile (slow)
311
307
  # storage.
312
308
  #
309
+ # * :content_type
310
+ # Content type you want to send the message with. It defaults to "application/octet-stream".
313
311
  def publish(data, opts = {})
314
312
  @mq.callback {
315
313
  out = []
@@ -320,7 +318,7 @@ class MQ
320
318
  data = data.to_s
321
319
 
322
320
  out << Protocol::Header.new(Protocol::Basic,
323
- data.bytesize, { :content_type => 'application/octet-stream',
321
+ data.bytesize, { opts[:content_type] || :content_type => "application/octet-stream",
324
322
  :delivery_mode => (opts[:persistent] ? 2 : 1),
325
323
  :priority => 0 }.merge(opts))
326
324
 
@@ -335,9 +333,9 @@ class MQ
335
333
  # bindings on the exchange are cancelled.
336
334
  #
337
335
  # Further attempts to publish messages to a deleted exchange will raise
338
- # an MQ::Error due to a channel close exception.
336
+ # an AMQP::Channel::Error due to a channel close exception.
339
337
  #
340
- # exchange = MQ.direct('name', :key => 'foo.bar')
338
+ # exchange = AMQP::Channel.direct('name', :key => 'foo.bar')
341
339
  # exchange.delete
342
340
  #
343
341
  # == Options
@@ -351,7 +349,7 @@ class MQ
351
349
  # * :if_unused => true | false (default false)
352
350
  # If set, the server will only delete the exchange if it has no queue
353
351
  # bindings. If the exchange has queue bindings the server does not
354
- # delete it but raises a channel exception instead (MQ:Error).
352
+ # delete it but raises a channel exception instead (AMQP::Error).
355
353
  #
356
354
  def delete(opts = {})
357
355
  @mq.callback {
@@ -392,4 +390,4 @@ class MQ
392
390
  self.callback && self.callback.call(self)
393
391
  end # receive_response
394
392
  end # Exchange
395
- end # MQ
393
+ end # AMQP