rabbitmq 0.2.5 → 1.0.0.pre.pre

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/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