rabbitmq 0.2.5 → 1.0.0.pre.pre

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rabbitmq/util.rb CHANGED
@@ -1,7 +1,10 @@
1
1
 
2
2
  module RabbitMQ
3
- module Util; end
4
- class << Util
3
+
4
+ # Helper functions for this library.
5
+ # @api private
6
+ module Util
7
+ module_function
5
8
 
6
9
  def const_name(lowercase_name)
7
10
  lowercase_name.to_s.gsub(/((?:\A\w)|(?:_\w))/) { |x| x[-1].upcase }
@@ -47,6 +50,6 @@ module RabbitMQ
47
50
 
48
51
  result.merge(overrides)
49
52
  end
50
-
51
53
  end
54
+
52
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rabbitmq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 1.0.0.pre.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe McIlvain
@@ -126,10 +126,8 @@ files:
126
126
  - ext/rabbitmq/Rakefile
127
127
  - lib/rabbitmq.rb
128
128
  - lib/rabbitmq/channel.rb
129
- - lib/rabbitmq/connection.rb
130
- - lib/rabbitmq/connection/channel_manager.rb
131
- - lib/rabbitmq/connection/event_manager.rb
132
- - lib/rabbitmq/connection/transport.rb
129
+ - lib/rabbitmq/client.rb
130
+ - lib/rabbitmq/client/connection.rb
133
131
  - lib/rabbitmq/ffi.rb
134
132
  - lib/rabbitmq/ffi/error.rb
135
133
  - lib/rabbitmq/ffi/ext.rb
@@ -224,9 +222,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
224
222
  version: '0'
225
223
  required_rubygems_version: !ruby/object:Gem::Requirement
226
224
  requirements:
227
- - - ">="
225
+ - - ">"
228
226
  - !ruby/object:Gem::Version
229
- version: '0'
227
+ version: 1.3.1
230
228
  requirements: []
231
229
  rubyforge_project:
232
230
  rubygems_version: 2.2.2
@@ -234,3 +232,4 @@ signing_key:
234
232
  specification_version: 4
235
233
  summary: rabbitmq
236
234
  test_files: []
235
+ has_rdoc:
@@ -1,382 +0,0 @@
1
-
2
- require 'socket'
3
-
4
- module RabbitMQ
5
- class Connection
6
- def initialize *args
7
- @ptr = FFI.amqp_new_connection
8
- @info = Util.connection_info(*args)
9
-
10
- @open_channels = {}
11
- @released_channels = {}
12
- @event_handlers = Hash.new { |h,k| h[k] = {} }
13
- @incoming_events = Hash.new { |h,k| h[k] = {} }
14
-
15
- @frame = FFI::Frame.new
16
-
17
- create_socket!
18
-
19
- @finalizer = self.class.send :create_finalizer_for, @ptr
20
- ObjectSpace.define_finalizer self, @finalizer
21
- end
22
-
23
- def destroy
24
- if @finalizer
25
- @finalizer.call
26
- ObjectSpace.undefine_finalizer self
27
- end
28
- @ptr = @socket = @finalizer = nil
29
- end
30
-
31
- class DestroyedError < RuntimeError; end
32
-
33
- # @private
34
- def self.create_finalizer_for(ptr)
35
- Proc.new do
36
- FFI.amqp_connection_close(ptr, 200)
37
- FFI.amqp_destroy_connection(ptr)
38
- end
39
- end
40
-
41
- def user; @info[:user]; end
42
- def password; @info[:password]; end
43
- def host; @info[:host]; end
44
- def vhost; @info[:vhost]; end
45
- def port; @info[:port]; end
46
- def ssl?; @info[:ssl]; end
47
-
48
- def protocol_timeout
49
- @protocol_timeout ||= 30 # seconds
50
- end
51
- attr_writer :protocol_timeout
52
-
53
- def max_channels
54
- @max_channels ||= FFI::CHANNEL_MAX_ID
55
- end
56
- attr_writer :max_channels
57
-
58
- def max_frame_size
59
- @max_frame_size ||= 131072
60
- end
61
- attr_writer :max_frame_size
62
-
63
- def heartbeat_interval; 0; end # not fully implemented in librabbitmq
64
-
65
- def start
66
- close # Close if already open
67
- connect_socket!
68
- login!
69
-
70
- self
71
- end
72
-
73
- def close
74
- raise DestroyedError unless @ptr
75
- FFI.amqp_connection_close(@ptr, 200)
76
-
77
- release_all_channels
78
-
79
- self
80
- end
81
-
82
- # Send a request on the given channel with the given type and properties.
83
- #
84
- # @param channel_id [Integer] The channel number to send on.
85
- # @param method [Symbol] The type of protocol method to send.
86
- # @param properties [Hash] The properties to apply to the method.
87
- # @raise [RabbitMQ::FFI::Error] if a library exception occurs.
88
- #
89
- def send_request(channel_id, method, properties={})
90
- Util.error_check :"sending a request",
91
- send_request_internal(Integer(channel_id), method.to_sym, properties)
92
-
93
- nil
94
- end
95
-
96
- # Wait for a specific response on the given channel of the given type
97
- # and return the event data for the response when it is received.
98
- # Any other events received will be processed or stored internally.
99
- #
100
- # @param channel_id [Integer] The channel number to watch for.
101
- # @param method [Symbol,Array<Symbol>] The protocol method(s) to watch for.
102
- # @param timeout [Float] The maximum time to wait for a response in seconds;
103
- # uses the value of {#protocol_timeout} by default.
104
- # @raise [RabbitMQ::ServerError] if any error event is received.
105
- # @raise [RabbitMQ::FFI::Error::Timeout] if no event is received.
106
- # @raise [RabbitMQ::FFI::Error] if a library exception occurs.
107
- # @return [Hash] the response data received.
108
- #
109
- def fetch_response(channel_id, method, timeout=protocol_timeout)
110
- methods = Array(method).map(&:to_sym)
111
- fetch_response_internal(Integer(channel_id), methods, Float(timeout))
112
- end
113
-
114
- # Register a handler for events on the given channel of the given type.
115
- #
116
- # @param channel_id [Integer] The channel number to watch for.
117
- # @param method [Symbol] The type of protocol method to watch for.
118
- # @param callable [#call,nil] The callable handler if no block is given.
119
- # @param block [Proc,nil] The handler block to register.
120
- # @return [Proc,#call] The given block or callable.
121
- # @yieldparam event [Hash] The event passed to the handler.
122
- #
123
- def on_event(channel_id, method, callable=nil, &block)
124
- handler = block || callable
125
- raise ArgumentError, "expected block or callable as event handler" \
126
- unless handler.respond_to?(:call)
127
-
128
- @event_handlers[Integer(channel_id)][method.to_sym] = handler
129
- handler
130
- end
131
-
132
- # Fetch and handle events in a loop that blocks the calling thread.
133
- # The loop will continue until the {#break!} method is called from within
134
- # an event handler, or until the given timeout duration has elapsed.
135
- #
136
- # @param timeout [Float] the maximum time to run the loop, in seconds;
137
- # if none is given, the loop will block indefinitely or until {#break!}.
138
- #
139
- def run_loop!(timeout=nil)
140
- timeout = Float(timeout) if timeout
141
- @breaking = false
142
- fetch_events(timeout)
143
- nil
144
- end
145
-
146
- # Stop iterating from within an execution of the {#run_loop!} method.
147
- # Call this method only from within an event handler.
148
- # It will take effect only after the handler finishes running.
149
- #
150
- # @return [nil]
151
- #
152
- def break!
153
- @breaking = true
154
- nil
155
- end
156
-
157
- def channel(id=nil)
158
- Channel.new(self, allocate_channel(id), pre_allocated: true)
159
- end
160
-
161
- private def ptr
162
- raise DestroyedError unless @ptr
163
- @ptr
164
- end
165
-
166
- private def create_socket!
167
- raise DestroyedError unless @ptr
168
-
169
- @socket = FFI.amqp_tcp_socket_new(@ptr)
170
- Util.null_check :"creating a socket", @socket
171
- end
172
-
173
- private def connect_socket!
174
- raise DestroyedError unless @ptr
175
- raise NotImplementedError if ssl?
176
-
177
- create_socket!
178
- Util.error_check :"opening a socket",
179
- FFI.amqp_socket_open(@socket, host, port)
180
-
181
- @ruby_socket = Socket.for_fd(FFI.amqp_get_sockfd(@ptr))
182
- @ruby_socket.autoclose = false
183
- end
184
-
185
- private def login!
186
- raise DestroyedError unless @ptr
187
-
188
- res = FFI.amqp_login(@ptr, vhost, max_channels, max_frame_size,
189
- heartbeat_interval, :plain, :string, user, :string, password)
190
-
191
- case res[:reply_type]
192
- when :library_exception; Util.error_check :"logging in", res[:library_error]
193
- when :server_exception; raise NotImplementedError
194
- end
195
-
196
- @server_properties = FFI::Table.new(FFI.amqp_get_server_properties(@ptr)).to_h
197
- end
198
-
199
- private def open_channel(id)
200
- Util.error_check :"opening a new channel",
201
- send_request_internal(id, :channel_open)
202
-
203
- fetch_response(id, :channel_open_ok)
204
- end
205
-
206
- private def reopen_channel(id)
207
- Util.error_check :"acknowledging server-initated channel closure",
208
- send_request_internal(id, :channel_close_ok)
209
-
210
- Util.error_check :"reopening channel after server-initated closure",
211
- send_request_internal(id, :channel_open)
212
-
213
- fetch_response(id, :channel_open_ok)
214
- end
215
-
216
- private def allocate_channel(id=nil)
217
- if id
218
- raise ArgumentError, "channel #{id} is already in use" if @open_channels[id]
219
- elsif @released_channels.empty?
220
- id = (@open_channels.keys.sort.last || 0) + 1
221
- else
222
- id = @released_channels.keys.first
223
- end
224
- raise ArgumentError, "channel #{id} is too high" if id > max_channels
225
-
226
- already_open = @released_channels.delete(id)
227
- open_channel(id) unless already_open
228
-
229
- @open_channels[id] = true
230
- @event_handlers[id] ||= {}
231
-
232
- id
233
- end
234
-
235
- private def release_channel(id)
236
- @open_channels.delete(id)
237
- @event_handlers.delete(id)
238
- @released_channels[id] = true
239
- end
240
-
241
- private def release_all_channels
242
- @open_channels.clear
243
- @event_handlers.clear
244
- @released_channels.clear
245
- end
246
-
247
- # Block until there is readable data on the internal ruby socket,
248
- # returning true if there is readable data, or false if time expired.
249
- private def select(timeout=0, start=Time.now)
250
- if timeout
251
- timeout = timeout - (start-Time.now)
252
- timeout = 0 if timeout < 0
253
- end
254
-
255
- IO.select([@ruby_socket], [], [], timeout) ? true : false
256
- end
257
-
258
- # Return the next available frame, or nil if time expired.
259
- private def fetch_next_frame(timeout=0, start=Time.now)
260
- frame = @frame
261
-
262
- # Try fetching the next frame without a blocking call.
263
- status = FFI.amqp_simple_wait_frame_noblock(@ptr, frame, FFI::Timeval.zero)
264
- case status
265
- when :ok; return frame
266
- when :timeout; # do nothing and proceed to waiting on select below
267
- else Util.error_check :"fetching the next frame", status
268
- end
269
-
270
- # Otherwise, wait for the socket to be readable and try fetching again.
271
- return nil unless select(timeout, start)
272
- Util.error_check :"fetching the next frame",
273
- FFI.amqp_simple_wait_frame(@ptr, frame)
274
-
275
- frame
276
- end
277
-
278
- # Fetch the next one or more frames to form the next discrete event,
279
- # returning the event as a Hash, or nil if time expired.
280
- private def fetch_next_event(timeout=0, start=Time.now)
281
- frame = fetch_next_frame(timeout, start)
282
- return unless frame
283
- event = frame.as_method_to_h(false)
284
- return event unless FFI::Method.has_content?(event.fetch(:method))
285
-
286
- frame = fetch_next_frame(timeout, start)
287
- return unless frame
288
- event.merge!(frame.as_header_to_h)
289
-
290
- body = ""
291
- while body.size < event.fetch(:body_size)
292
- frame = fetch_next_frame(timeout, start)
293
- return unless frame
294
- body.concat frame.as_body_to_s
295
- end
296
-
297
- event[:body] = body
298
- event
299
- end
300
-
301
- # Execute the handler for this type of event, if any
302
- private def handle_incoming_event(event)
303
- if (handlers = @event_handlers[event.fetch(:channel)])
304
- if (handler = (handlers[event.fetch(:method)]))
305
- handler.call(event)
306
- end
307
- end
308
- end
309
-
310
- # Store the event in short-term storage for retrieval by fetch_response.
311
- # If another event is received with the same method name, it will
312
- # overwrite this one - fetch_response gets the latest or next by method.
313
- # Raises an exception if the incoming event is an error condition.
314
- private def store_incoming_event(event)
315
- method = event.fetch(:method)
316
-
317
- case method
318
- when :channel_close
319
- raise_if_server_error!(event)
320
- when :connection_close
321
- raise_if_server_error!(event)
322
- else
323
- @incoming_events[event.fetch(:channel)][method] = event
324
- end
325
- end
326
-
327
- # Raise an exception if the incoming event is an error condition.
328
- # Also takes action to reopen the channel or close the connection.
329
- private def raise_if_server_error!(event)
330
- if (exc = ServerError.from(event))
331
- if exc.is_a?(ServerError::ChannelError)
332
- reopen_channel(event.fetch(:channel)) # recover by reopening the channel
333
- elsif exc.is_a?(ServerError::ConnectionError)
334
- close # can't recover here - close and let the user recover manually
335
- end
336
- raise exc
337
- end
338
- end
339
-
340
- private def fetch_events(timeout=protocol_timeout, start=Time.now)
341
- raise DestroyedError unless @ptr
342
-
343
- FFI.amqp_maybe_release_buffers(@ptr)
344
-
345
- while (event = fetch_next_event(timeout, start))
346
- handle_incoming_event(event)
347
- store_incoming_event(event)
348
- break if @breaking
349
- end
350
- end
351
-
352
- private def fetch_response_internal(channel, methods, timeout=protocol_timeout, start=Time.now)
353
- raise DestroyedError unless @ptr
354
-
355
- methods.each { |method|
356
- found = @incoming_events[channel].delete(method)
357
- return found if found
358
- }
359
-
360
- FFI.amqp_maybe_release_buffers_on_channel(@ptr, channel)
361
-
362
- while (event = fetch_next_event(timeout, start))
363
- handle_incoming_event(event)
364
- return event if channel == event.fetch(:channel) \
365
- && methods.include?(event.fetch(:method))
366
- store_incoming_event(event)
367
- end
368
-
369
- raise FFI::Error::Timeout, "waiting for response"
370
- end
371
-
372
- private def send_request_internal(channel_id, method, properties={})
373
- raise DestroyedError unless @ptr
374
-
375
- req = FFI::Method.lookup_class(method).new.apply(properties)
376
- status = FFI.amqp_send_method(@ptr, channel_id, method, req.pointer)
377
-
378
- req.free!
379
- status
380
- end
381
- end
382
- end
@@ -1,61 +0,0 @@
1
-
2
- module RabbitMQ
3
- class Connection
4
-
5
- class ChannelManager
6
- def initialize(connection)
7
- @connection = connection
8
- @open_channels = {}
9
- @released_channels = {}
10
- end
11
-
12
- def allocate_channel(id=nil)
13
- if id
14
- raise ArgumentError, "channel #{id} is already in use" if @open_channels[id]
15
- elsif @released_channels.empty?
16
- id = (@open_channels.keys.sort.last || 0) + 1
17
- else
18
- id = @released_channels.keys.first
19
- end
20
- raise ArgumentError, "channel #{id} is too high" if id > @connection.max_channels
21
-
22
- already_open = @released_channels.delete(id)
23
- request_channel_open(id) unless already_open
24
-
25
- @open_channels[id] = true
26
- @event_handlers[id] ||= {}
27
-
28
- id
29
- end
30
-
31
- def release_channel(id)
32
- @open_channels.delete(id)
33
- @released_channels[id] = true
34
- end
35
-
36
- def release_all_channels
37
- @open_channels.clear
38
- @released_channels.clear
39
- end
40
-
41
- def request_channel_open(id)
42
- Util.error_check :"opening a new channel",
43
- @connection.send_request_internal(id, :channel_open)
44
-
45
- @connection.fetch_response(id, :channel_open_ok)
46
- end
47
-
48
- def request_channel_reopen(id)
49
- Util.error_check :"acknowledging server-initated channel closure",
50
- @connection.send_request_internal(id, :channel_close_ok)
51
-
52
- Util.error_check :"reopening channel after server-initated closure",
53
- @connection.send_request_internal(id, :channel_open)
54
-
55
- @connection.fetch_response(id, :channel_open_ok)
56
- end
57
-
58
- end
59
-
60
- end
61
- end