amq-client 0.7.0.alpha34 → 0.7.0.alpha35
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.
- data/.travis.yml +4 -0
- data/Gemfile +1 -1
- data/README.textile +1 -1
- data/bin/ci/before_build.sh +24 -0
- data/examples/eventmachine_adapter/extensions/rabbitmq/handling_confirm_select_ok.rb +2 -2
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +1 -1
- data/examples/eventmachine_adapter/extensions/rabbitmq/publisher_confirmations_with_unroutable_message.rb +1 -1
- data/lib/amq/client.rb +29 -17
- data/lib/amq/client/adapter.rb +8 -504
- data/lib/amq/client/adapters/coolio.rb +4 -282
- data/lib/amq/client/adapters/event_machine.rb +4 -382
- data/lib/amq/client/async/adapter.rb +517 -0
- data/lib/amq/client/async/adapters/coolio.rb +291 -0
- data/lib/amq/client/async/adapters/event_machine.rb +392 -0
- data/lib/amq/client/async/adapters/eventmachine.rb +1 -0
- data/lib/amq/client/async/callbacks.rb +71 -0
- data/lib/amq/client/async/channel.rb +385 -0
- data/lib/amq/client/async/entity.rb +66 -0
- data/lib/amq/client/async/exchange.rb +157 -0
- data/lib/amq/client/async/extensions/rabbitmq/basic.rb +38 -0
- data/lib/amq/client/async/extensions/rabbitmq/confirm.rb +248 -0
- data/lib/amq/client/async/queue.rb +455 -0
- data/lib/amq/client/callbacks.rb +6 -65
- data/lib/amq/client/channel.rb +4 -376
- data/lib/amq/client/entity.rb +6 -57
- data/lib/amq/client/exchange.rb +4 -148
- data/lib/amq/client/extensions/rabbitmq/basic.rb +4 -28
- data/lib/amq/client/extensions/rabbitmq/confirm.rb +5 -240
- data/lib/amq/client/queue.rb +5 -450
- data/lib/amq/client/version.rb +1 -1
- data/spec/unit/client_spec.rb +10 -30
- metadata +16 -22
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "amq/client/openable"
|
4
|
+
require "amq/client/async/callbacks"
|
5
|
+
|
6
|
+
module AMQ
|
7
|
+
module Client
|
8
|
+
module Async
|
9
|
+
module RegisterEntityMixin
|
10
|
+
# @example Registering Channel implementation
|
11
|
+
# Adapter.register_entity(:channel, Channel)
|
12
|
+
# # ... so then I can do:
|
13
|
+
# channel = client.channel(1)
|
14
|
+
# # instead of:
|
15
|
+
# channel = Channel.new(client, 1)
|
16
|
+
def register_entity(name, klass)
|
17
|
+
define_method(name) do |*args, &block|
|
18
|
+
klass.new(self, *args, &block)
|
19
|
+
end # define_method
|
20
|
+
end # register_entity
|
21
|
+
end # RegisterEntityMixin
|
22
|
+
|
23
|
+
module ProtocolMethodHandlers
|
24
|
+
def handle(klass, &block)
|
25
|
+
AMQ::Client::HandlersRegistry.register(klass, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def handlers
|
29
|
+
AMQ::Client::HandlersRegistry.handlers
|
30
|
+
end
|
31
|
+
end # ProtocolMethodHandlers
|
32
|
+
|
33
|
+
|
34
|
+
# AMQ entities, as implemented by AMQ::Client, have callbacks and can run them
|
35
|
+
# when necessary.
|
36
|
+
#
|
37
|
+
# @note Exchanges and queues implementation is based on this class.
|
38
|
+
#
|
39
|
+
# @abstract
|
40
|
+
module Entity
|
41
|
+
|
42
|
+
#
|
43
|
+
# Behaviors
|
44
|
+
#
|
45
|
+
|
46
|
+
include Openable
|
47
|
+
include Async::Callbacks
|
48
|
+
|
49
|
+
#
|
50
|
+
# API
|
51
|
+
#
|
52
|
+
|
53
|
+
# @return [Array<#call>]
|
54
|
+
attr_reader :callbacks
|
55
|
+
|
56
|
+
|
57
|
+
def initialize(connection)
|
58
|
+
@connection = connection
|
59
|
+
# Be careful with default values for #ruby hashes: h = Hash.new(Array.new); h[:key] ||= 1
|
60
|
+
# won't assign anything to :key. MK.
|
61
|
+
@callbacks = Hash.new
|
62
|
+
end # initialize
|
63
|
+
end # Entity
|
64
|
+
end # Async
|
65
|
+
end # Client
|
66
|
+
end # AMQ
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "amq/client/entity"
|
4
|
+
require "amq/client/server_named_entity"
|
5
|
+
|
6
|
+
module AMQ
|
7
|
+
module Client
|
8
|
+
module Async
|
9
|
+
class Exchange
|
10
|
+
|
11
|
+
|
12
|
+
include Entity
|
13
|
+
include ServerNamedEntity
|
14
|
+
extend ProtocolMethodHandlers
|
15
|
+
|
16
|
+
TYPES = [:fanout, :direct, :topic, :headers].freeze
|
17
|
+
|
18
|
+
class IncompatibleExchangeTypeError < StandardError
|
19
|
+
def initialize(types, given)
|
20
|
+
super("#{given.inspect} exchange type is unknown. Standard types are #{TYPES.inspect}, custom exchange types must begin with x-, for example: x-recent-history")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
#
|
26
|
+
# API
|
27
|
+
#
|
28
|
+
|
29
|
+
# Channel this exchange belongs to.
|
30
|
+
attr_reader :channel
|
31
|
+
|
32
|
+
# Exchange name. May be server-generated or assigned directly.
|
33
|
+
attr_reader :name
|
34
|
+
|
35
|
+
# @return [Symbol] One of :direct, :fanout, :topic, :headers
|
36
|
+
attr_reader :type
|
37
|
+
|
38
|
+
def initialize(connection, channel, name, type = :fanout)
|
39
|
+
if !(TYPES.include?(type.to_sym) || type.to_s =~ /^x-.+/i)
|
40
|
+
raise IncompatibleExchangeTypeError.new(TYPES, type)
|
41
|
+
end
|
42
|
+
|
43
|
+
@connection = connection
|
44
|
+
@channel = channel
|
45
|
+
@name = name
|
46
|
+
@type = type
|
47
|
+
|
48
|
+
# register pre-declared exchanges
|
49
|
+
if @name == AMQ::Protocol::EMPTY_STRING || @name =~ /^amq\.(fanout|topic)/
|
50
|
+
@channel.register_exchange(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
super(connection)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def fanout?
|
58
|
+
@type == :fanout
|
59
|
+
end
|
60
|
+
|
61
|
+
def direct?
|
62
|
+
@type == :direct
|
63
|
+
end
|
64
|
+
|
65
|
+
def topic?
|
66
|
+
@type == :topic
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
def declare(passive = false, durable = false, auto_delete = false, nowait = false, arguments = nil, &block)
|
72
|
+
@connection.send_frame(Protocol::Exchange::Declare.encode(@channel.id, @name, @type.to_s, passive, durable, auto_delete, false, nowait, arguments))
|
73
|
+
|
74
|
+
unless nowait
|
75
|
+
self.define_callback(:declare, &block)
|
76
|
+
@channel.exchanges_awaiting_declare_ok.push(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def delete(if_unused = false, nowait = false, &block)
|
84
|
+
@connection.send_frame(Protocol::Exchange::Delete.encode(@channel.id, @name, if_unused, nowait))
|
85
|
+
|
86
|
+
unless nowait
|
87
|
+
self.define_callback(:delete, &block)
|
88
|
+
|
89
|
+
# TODO: delete itself from exchanges cache
|
90
|
+
@channel.exchanges_awaiting_delete_ok.push(self)
|
91
|
+
end
|
92
|
+
|
93
|
+
self
|
94
|
+
end # delete(if_unused = false, nowait = false)
|
95
|
+
|
96
|
+
|
97
|
+
def publish(payload, routing_key = AMQ::Protocol::EMPTY_STRING, user_headers = {}, mandatory = false, immediate = false, frame_size = nil)
|
98
|
+
headers = { :priority => 0, :delivery_mode => 2, :content_type => "application/octet-stream" }.merge(user_headers)
|
99
|
+
@connection.send_frameset(Protocol::Basic::Publish.encode(@channel.id, payload, headers, @name, routing_key, mandatory, immediate, (frame_size || @connection.frame_max)))
|
100
|
+
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def on_return(&block)
|
106
|
+
self.redefine_callback(:return, &block)
|
107
|
+
|
108
|
+
self
|
109
|
+
end # on_return(&block)
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
def handle_declare_ok(method)
|
115
|
+
@name = method.exchange if self.anonymous?
|
116
|
+
@channel.register_exchange(self)
|
117
|
+
|
118
|
+
self.exec_callback_once_yielding_self(:declare, method)
|
119
|
+
end
|
120
|
+
|
121
|
+
def handle_delete_ok(method)
|
122
|
+
self.exec_callback_once(:delete, method)
|
123
|
+
end # handle_delete_ok(method)
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
self.handle(Protocol::Exchange::DeclareOk) do |connection, frame|
|
128
|
+
method = frame.decode_payload
|
129
|
+
channel = connection.channels[frame.channel]
|
130
|
+
exchange = channel.exchanges_awaiting_declare_ok.shift
|
131
|
+
|
132
|
+
exchange.handle_declare_ok(method)
|
133
|
+
end # handle
|
134
|
+
|
135
|
+
|
136
|
+
self.handle(Protocol::Exchange::DeleteOk) do |connection, frame|
|
137
|
+
channel = connection.channels[frame.channel]
|
138
|
+
exchange = channel.exchanges_awaiting_delete_ok.shift
|
139
|
+
exchange.handle_delete_ok(frame.decode_payload)
|
140
|
+
end # handle
|
141
|
+
|
142
|
+
|
143
|
+
self.handle(Protocol::Basic::Return) do |connection, frame, content_frames|
|
144
|
+
channel = connection.channels[frame.channel]
|
145
|
+
method = frame.decode_payload
|
146
|
+
exchange = channel.find_exchange(method.exchange)
|
147
|
+
|
148
|
+
header = content_frames.shift
|
149
|
+
body = content_frames.map { |frame| frame.payload }.join
|
150
|
+
|
151
|
+
exchange.exec_callback(:return, method, header, body)
|
152
|
+
end
|
153
|
+
|
154
|
+
end # Exchange
|
155
|
+
end # Async
|
156
|
+
end # Client
|
157
|
+
end # AMQ
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "amq/client/async/channel"
|
4
|
+
|
5
|
+
# Basic.Nack
|
6
|
+
module AMQ
|
7
|
+
module Client
|
8
|
+
module Async
|
9
|
+
module Extensions
|
10
|
+
module RabbitMQ
|
11
|
+
module Basic
|
12
|
+
module ChannelMixin
|
13
|
+
|
14
|
+
# Overrides {AMQ::Client::Channel#reject} behavior to use basic.nack.
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
# @see http://www.rabbitmq.com/amqp-0-9-1-quickref.html#basic.nack
|
18
|
+
def reject(delivery_tag, requeue = true, multi = false)
|
19
|
+
if multi
|
20
|
+
@connection.send_frame(Protocol::Basic::Nack.encode(self.id, delivery_tag, multi, requeue))
|
21
|
+
else
|
22
|
+
super(delivery_tag, requeue)
|
23
|
+
end
|
24
|
+
end # reject
|
25
|
+
|
26
|
+
end # ChannelMixin
|
27
|
+
end # Basic
|
28
|
+
end # RabbitMQ
|
29
|
+
end # Extensions
|
30
|
+
|
31
|
+
class Channel
|
32
|
+
# use modules, the native Ruby way of extension of existing classes,
|
33
|
+
# instead of reckless monkey-patching. MK.
|
34
|
+
include Extensions::RabbitMQ::Basic::ChannelMixin
|
35
|
+
end # Channel
|
36
|
+
end # Async
|
37
|
+
end # Client
|
38
|
+
end # AMQ
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQ
|
4
|
+
module Client
|
5
|
+
module Async
|
6
|
+
module Extensions
|
7
|
+
module RabbitMQ
|
8
|
+
# h2. Purpose
|
9
|
+
# In case that the broker crashes, some messages can get lost.
|
10
|
+
# Thanks to this extension, broker sends Basic.Ack when the message
|
11
|
+
# is processed by the broker. In case of persistent messages, it must
|
12
|
+
# be written to disk or ack'd on all the queues it was delivered to.
|
13
|
+
# However it doesn't have to be necessarily 1:1, because the broker
|
14
|
+
# can send Basic.Ack with multi flag to acknowledge multiple messages.
|
15
|
+
#
|
16
|
+
# So it provides clients a lightweight way of keeping track of which
|
17
|
+
# messages have been processed by the broker and which would need
|
18
|
+
# re-publishing in case of broker shutdown or network failure.
|
19
|
+
#
|
20
|
+
# Transactions are solving the same problem, but they are very slow:
|
21
|
+
# confirmations are more than 100 times faster.
|
22
|
+
#
|
23
|
+
# h2. Workflow
|
24
|
+
# * Client asks broker to confirm messages on given channel (Confirm.Select).
|
25
|
+
# * Broker sends back Confirm.Select-Ok, unless we sent Confirm.Select with nowait=true.
|
26
|
+
# * After each published message, the client receives Basic.Ack from the broker.
|
27
|
+
# * If something bad happens inside the broker, it sends Basic.Nack.
|
28
|
+
#
|
29
|
+
# h2. Gotchas
|
30
|
+
# Note that we don't keep track of messages awaiting confirmation.
|
31
|
+
# It'd add a huge overhead and it's impossible to come up with one-suits-all solution.
|
32
|
+
# If you want to create such module, you'll probably want to redefine Channel#after_publish,
|
33
|
+
# so it will put messages into a queue and then handlers for Basic.Ack and Basic.Nack.
|
34
|
+
# This is the reason why we pass every argument from Exchange#publish to Channel#after_publish.
|
35
|
+
# You should not forget though, that both of these methods can have multi flag!
|
36
|
+
#
|
37
|
+
# Transactional channel cannot be put into confirm mode and a confirm
|
38
|
+
# mode channel cannot be made transactional.
|
39
|
+
#
|
40
|
+
# If the connection between the publisher and broker drops with outstanding
|
41
|
+
# confirms, it does not necessarily mean that the messages were lost, so
|
42
|
+
# republishing may result in duplicate messages.
|
43
|
+
|
44
|
+
# h2. Learn more
|
45
|
+
# @see http://www.rabbitmq.com/blog/2011/02/10/introducing-publisher-confirms
|
46
|
+
# @see http://www.rabbitmq.com/amqp-0-9-1-quickref.html#class.confirm
|
47
|
+
# @see http://www.rabbitmq.com/amqp-0-9-1-reference.html#basic.ack
|
48
|
+
module Confirm
|
49
|
+
module ChannelMixin
|
50
|
+
|
51
|
+
# Change publisher index. Publisher index is incremented
|
52
|
+
# by 1 after each Basic.Publish starting at 1. This is done
|
53
|
+
# on both client and server, hence this acknowledged messages
|
54
|
+
# can be matched via its delivery-tag.
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
attr_writer :publisher_index
|
58
|
+
|
59
|
+
# Publisher index is an index of the last message since
|
60
|
+
# the confirmations were activated, started with 1. It's
|
61
|
+
# incremented by 1 after each Basic.Publish starting at 1.
|
62
|
+
# This is done on both client and server, hence this
|
63
|
+
# acknowledged messages can be matched via its delivery-tag.
|
64
|
+
#
|
65
|
+
# @return [Integer] Current publisher index.
|
66
|
+
# @api public
|
67
|
+
def publisher_index
|
68
|
+
@publisher_index ||= 1
|
69
|
+
end
|
70
|
+
|
71
|
+
# Resets publisher index to 0
|
72
|
+
#
|
73
|
+
# @api plugin
|
74
|
+
def reset_publisher_index!
|
75
|
+
@publisher_index = 0
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# This method is executed after publishing of each message via {Exchage#publish}.
|
80
|
+
# Currently it just increments publisher index by 1, so messages
|
81
|
+
# can be actually matched.
|
82
|
+
#
|
83
|
+
# @api plugin
|
84
|
+
def after_publish(*args)
|
85
|
+
self.publisher_index += 1
|
86
|
+
end
|
87
|
+
|
88
|
+
# Turn on confirmations for this channel and, if given,
|
89
|
+
# register callback for Confirm.Select-Ok.
|
90
|
+
#
|
91
|
+
# @raise [RuntimeError] Occurs when confirmations are already activated.
|
92
|
+
# @raise [RuntimeError] Occurs when nowait is true and block is given.
|
93
|
+
#
|
94
|
+
# @param [Boolean] nowait Whether we expect Confirm.Select-Ok to be returned by the broker or not.
|
95
|
+
# @yield [method] Callback which will be executed once we receive Confirm.Select-Ok.
|
96
|
+
# @yieldparam [AMQ::Protocol::Confirm::SelectOk] method Protocol method class instance.
|
97
|
+
#
|
98
|
+
# @return [self] self.
|
99
|
+
#
|
100
|
+
# @see #confirm
|
101
|
+
def confirm_select(nowait = false, &block)
|
102
|
+
if nowait && block
|
103
|
+
raise "You can't use Confirm.Select with nowait=true and a callback at the same time."
|
104
|
+
end
|
105
|
+
|
106
|
+
@uses_publisher_confirmations = true
|
107
|
+
self.redefine_callback(:confirm_select, &block)
|
108
|
+
@connection.send_frame(Protocol::Confirm::Select.encode(@id, nowait))
|
109
|
+
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# @return [Boolean]
|
114
|
+
def uses_publisher_confirmations?
|
115
|
+
@uses_publisher_confirmations
|
116
|
+
end # uses_publisher_confirmations?
|
117
|
+
|
118
|
+
|
119
|
+
# Turn on confirmations for this channel and, if given,
|
120
|
+
# register callback for basic.ack from the broker.
|
121
|
+
#
|
122
|
+
# @raise [RuntimeError] Occurs when confirmations are already activated.
|
123
|
+
# @raise [RuntimeError] Occurs when nowait is true and block is given.
|
124
|
+
# @param [Boolean] nowait Whether we expect Confirm.Select-Ok to be returned by the broker or not.
|
125
|
+
#
|
126
|
+
# @yield [basick_ack] Callback which will be executed every time we receive Basic.Ack from the broker.
|
127
|
+
# @yieldparam [AMQ::Protocol::Basic::Ack] basick_ack Protocol method class instance.
|
128
|
+
#
|
129
|
+
# @return [self] self.
|
130
|
+
def on_ack(nowait = false, &block)
|
131
|
+
self.use_publisher_confirmations! unless self.uses_publisher_confirmations?
|
132
|
+
|
133
|
+
self.define_callback(:ack, &block) if block
|
134
|
+
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
# Register error callback for Basic.Nack. It's called
|
140
|
+
# when message(s) is rejected.
|
141
|
+
#
|
142
|
+
# @return [self] self
|
143
|
+
def on_nack(&block)
|
144
|
+
self.define_callback(:nack, &block) if block
|
145
|
+
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
|
152
|
+
# Handler for Confirm.Select-Ok. By default, it just
|
153
|
+
# executes hook specified via the #confirmations method
|
154
|
+
# with a single argument, a protocol method class
|
155
|
+
# instance (an instance of AMQ::Protocol::Confirm::SelectOk)
|
156
|
+
# and then it deletes the callback, since Confirm.Select
|
157
|
+
# is supposed to be sent just once.
|
158
|
+
#
|
159
|
+
# @api plugin
|
160
|
+
def handle_select_ok(method)
|
161
|
+
self.exec_callback_once(:confirm_select, method)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Handler for Basic.Ack. By default, it just
|
165
|
+
# executes hook specified via the #confirm method
|
166
|
+
# with a single argument, a protocol method class
|
167
|
+
# instance (an instance of AMQ::Protocol::Basic::Ack).
|
168
|
+
#
|
169
|
+
# @api plugin
|
170
|
+
def handle_basic_ack(method)
|
171
|
+
self.exec_callback(:ack, method)
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
# Handler for Basic.Nack. By default, it just
|
176
|
+
# executes hook specified via the #confirm_failed method
|
177
|
+
# with a single argument, a protocol method class
|
178
|
+
# instance (an instance of AMQ::Protocol::Basic::Nack).
|
179
|
+
#
|
180
|
+
# @api plugin
|
181
|
+
def handle_basic_nack(method)
|
182
|
+
self.exec_callback(:nack, method)
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def reset_state!
|
187
|
+
super
|
188
|
+
|
189
|
+
@uses_publisher_confirmations = false
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
def self.included(host)
|
194
|
+
host.handle(Protocol::Confirm::SelectOk) do |connection, frame|
|
195
|
+
method = frame.decode_payload
|
196
|
+
channel = connection.channels[frame.channel]
|
197
|
+
channel.handle_select_ok(method)
|
198
|
+
end
|
199
|
+
|
200
|
+
host.handle(Protocol::Basic::Ack) do |connection, frame|
|
201
|
+
method = frame.decode_payload
|
202
|
+
channel = connection.channels[frame.channel]
|
203
|
+
channel.handle_basic_ack(method)
|
204
|
+
end
|
205
|
+
|
206
|
+
host.handle(Protocol::Basic::Nack) do |connection, frame|
|
207
|
+
method = frame.decode_payload
|
208
|
+
channel = connection.channels[frame.channel]
|
209
|
+
channel.handle_basic_nack(method)
|
210
|
+
end
|
211
|
+
end # self.included(host)
|
212
|
+
end # ChannelMixin
|
213
|
+
|
214
|
+
|
215
|
+
module ExchangeMixin
|
216
|
+
# Publish message and then run #after_publish on channel belonging
|
217
|
+
# to the exchange. This is used for incrementing the publisher index.
|
218
|
+
#
|
219
|
+
# @api public
|
220
|
+
# @see AMQ::Client::Exchange#publish
|
221
|
+
# @see AMQ::Client::Extensions::RabbitMQ::Channel#publisher_index
|
222
|
+
# @return [self] self
|
223
|
+
def publish(*args)
|
224
|
+
super(*args)
|
225
|
+
@channel.after_publish(*args)
|
226
|
+
|
227
|
+
self
|
228
|
+
end # publish
|
229
|
+
end # ExchangeMixin
|
230
|
+
end # Confirm
|
231
|
+
end # RabbitMQ
|
232
|
+
end # Extensions
|
233
|
+
|
234
|
+
|
235
|
+
class Channel
|
236
|
+
# use modules, a native Ruby way of extension of existing classes,
|
237
|
+
# instead of reckless monkey-patching. MK.
|
238
|
+
include Extensions::RabbitMQ::Confirm::ChannelMixin
|
239
|
+
end # Channel
|
240
|
+
|
241
|
+
class Exchange
|
242
|
+
# use modules, a native Ruby way of extension of existing classes,
|
243
|
+
# instead of reckless monkey-patching. MK.
|
244
|
+
include Extensions::RabbitMQ::Confirm::ExchangeMixin
|
245
|
+
end # Exchange
|
246
|
+
end # Async
|
247
|
+
end # Client
|
248
|
+
end # AMQ
|