sparqcode_bunny 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +15 -0
  4. data/.yardopts +9 -0
  5. data/CHANGELOG +27 -0
  6. data/Gemfile +39 -0
  7. data/LICENSE +21 -0
  8. data/README.textile +82 -0
  9. data/Rakefile +14 -0
  10. data/bunny.gemspec +43 -0
  11. data/examples/simple_08.rb +32 -0
  12. data/examples/simple_09.rb +32 -0
  13. data/examples/simple_ack_08.rb +35 -0
  14. data/examples/simple_ack_09.rb +35 -0
  15. data/examples/simple_consumer_08.rb +55 -0
  16. data/examples/simple_consumer_09.rb +55 -0
  17. data/examples/simple_fanout_08.rb +41 -0
  18. data/examples/simple_fanout_09.rb +41 -0
  19. data/examples/simple_headers_08.rb +42 -0
  20. data/examples/simple_headers_09.rb +42 -0
  21. data/examples/simple_publisher_08.rb +29 -0
  22. data/examples/simple_publisher_09.rb +29 -0
  23. data/examples/simple_topic_08.rb +61 -0
  24. data/examples/simple_topic_09.rb +61 -0
  25. data/ext/amqp-0.8.json +616 -0
  26. data/ext/amqp-0.9.1.json +388 -0
  27. data/ext/config.yml +4 -0
  28. data/ext/qparser.rb +469 -0
  29. data/lib/bunny/channel08.rb +39 -0
  30. data/lib/bunny/channel09.rb +39 -0
  31. data/lib/bunny/client08.rb +472 -0
  32. data/lib/bunny/client09.rb +374 -0
  33. data/lib/bunny/consumer.rb +35 -0
  34. data/lib/bunny/exchange08.rb +171 -0
  35. data/lib/bunny/exchange09.rb +159 -0
  36. data/lib/bunny/queue08.rb +403 -0
  37. data/lib/bunny/queue09.rb +325 -0
  38. data/lib/bunny/subscription08.rb +87 -0
  39. data/lib/bunny/subscription09.rb +87 -0
  40. data/lib/bunny/system_timer.rb +14 -0
  41. data/lib/bunny/version.rb +5 -0
  42. data/lib/bunny.rb +109 -0
  43. data/lib/qrack/amq-client-url.rb +165 -0
  44. data/lib/qrack/channel.rb +20 -0
  45. data/lib/qrack/client.rb +235 -0
  46. data/lib/qrack/errors.rb +5 -0
  47. data/lib/qrack/protocol/protocol08.rb +134 -0
  48. data/lib/qrack/protocol/protocol09.rb +135 -0
  49. data/lib/qrack/protocol/spec08.rb +828 -0
  50. data/lib/qrack/protocol/spec09.rb +524 -0
  51. data/lib/qrack/qrack08.rb +20 -0
  52. data/lib/qrack/qrack09.rb +20 -0
  53. data/lib/qrack/queue.rb +40 -0
  54. data/lib/qrack/subscription.rb +112 -0
  55. data/lib/qrack/transport/buffer08.rb +278 -0
  56. data/lib/qrack/transport/buffer09.rb +280 -0
  57. data/lib/qrack/transport/frame08.rb +117 -0
  58. data/lib/qrack/transport/frame09.rb +97 -0
  59. data/spec/spec_08/bunny_spec.rb +77 -0
  60. data/spec/spec_08/connection_spec.rb +25 -0
  61. data/spec/spec_08/exchange_spec.rb +173 -0
  62. data/spec/spec_08/queue_spec.rb +235 -0
  63. data/spec/spec_09/amqp_url_spec.rb +19 -0
  64. data/spec/spec_09/bunny_spec.rb +76 -0
  65. data/spec/spec_09/connection_spec.rb +29 -0
  66. data/spec/spec_09/exchange_spec.rb +173 -0
  67. data/spec/spec_09/queue_spec.rb +248 -0
  68. metadata +151 -0
data/ext/qparser.rb ADDED
@@ -0,0 +1,469 @@
1
+ # encoding: utf-8
2
+
3
+ require 'json'
4
+ require 'erb'
5
+ require 'pathname'
6
+ require 'yaml'
7
+
8
+ def spec_v8_0_0?(spec)
9
+ spec['major'] == '8' && spec['minor'] == '0' && spec['revision'] == '0'
10
+ end
11
+
12
+ def spec_details(spec)
13
+ meta = {}
14
+
15
+ meta['major'] = spec['major-version']
16
+ meta['minor'] = spec['minor-version']
17
+ meta['revision'] = spec['revision'] || '0'
18
+ meta['port'] = spec['port']
19
+ meta['comment'] = "AMQ Protocol version #{meta['major']}.#{meta['minor']}.#{meta['revision']}"
20
+
21
+ meta
22
+ end
23
+
24
+ def process_constants(spec)
25
+ # AMQP constants
26
+
27
+ frame_constants = {}
28
+ other_constants = {}
29
+
30
+ spec['constants'].each do |constant|
31
+ if constant['name'].match(/^frame/i)
32
+ frame_constants[constant['value'].to_i] =
33
+ constant['name'].sub(/^frame./i,'').split(/\s|-/).map{|w| w.downcase.capitalize}.join
34
+ else
35
+ other_constants[constant['value']] = constant['name']
36
+ end
37
+ end
38
+
39
+ [frame_constants.sort, other_constants.sort]
40
+ end
41
+
42
+ def domain_types(spec, major, minor, revision)
43
+ # AMQP domain types
44
+
45
+ # add types that may be missing in the spec version
46
+ dt_arr = add_types(spec)
47
+ spec["domains"].each do |domain|
48
+ # JSON spec gives domain types as two element arrays like ["channel-id", "longstr"]
49
+ dt_arr << domain.last
50
+ end
51
+
52
+ # Return sorted array
53
+ dt_arr.uniq.sort
54
+ end
55
+
56
+ def classes(spec, major, minor, revision)
57
+ # AMQP classes
58
+ spec['classes'].map do |amqp_class|
59
+ cls_hash = {}
60
+ cls_hash[:name] = amqp_class['name']
61
+ cls_hash[:index] = amqp_class['id']
62
+ # Get fields for class
63
+ cls_hash[:fields] = fields(amqp_class) # are these amqp_class["properties"] ?
64
+ # Get methods for class
65
+ meth_arr = class_methods(amqp_class)
66
+ # Add missing methods
67
+ add_arr =[]
68
+ add_arr = add_methods(spec) if cls_hash[:name] == 'queue'
69
+ method_arr = meth_arr + add_arr
70
+ # Add array to class hash
71
+ cls_hash[:methods] = method_arr
72
+ cls_hash
73
+ end
74
+ end
75
+
76
+ # Get methods for class
77
+ def class_methods(amqp_class)
78
+ amqp_class['methods'].map do |method|
79
+ meth_hash = {}
80
+ meth_hash[:name] = method['name']
81
+ meth_hash[:index] = method['id']
82
+ # Get fields for method
83
+ meth_hash[:fields] = fields(method)
84
+ meth_hash
85
+ end
86
+ end
87
+
88
+ # Get the fields for a class or method
89
+ def fields(element)
90
+ # The JSON spec puts these in "properties" for a class and "arguments" for a
91
+ # method
92
+ (element['arguments'] || element['properties'] || []).map do |field|
93
+ field_hash = {}
94
+ field_hash[:name] = field['name'].tr(' ', '-')
95
+ field_hash[:domain] = field['type'] || field['domain']
96
+
97
+ # Convert domain type if necessary
98
+ conv_arr = convert_type(field_hash[:domain])
99
+ field_hash[:domain] = conv_arr.last unless conv_arr.empty?
100
+
101
+ field_hash
102
+ end
103
+ end
104
+
105
+ def add_types(spec)
106
+ spec_v8_0_0?(spec) ? ['long', 'longstr', 'octet', 'timestamp'] : []
107
+ end
108
+
109
+ def add_methods(spec)
110
+ meth_arr = []
111
+
112
+ if spec_v8_0_0?(spec)
113
+ # Add Queue Unbind method
114
+ meth_hash = {:name => 'unbind',
115
+ :index => '50',
116
+ :fields => [{:name => 'ticket', :domain => 'short'},
117
+ {:name => 'queue', :domain => 'shortstr'},
118
+ {:name => 'exchange', :domain => 'shortstr'},
119
+ {:name => 'routing_key', :domain => 'shortstr'},
120
+ {:name => 'arguments', :domain => 'table'}
121
+ ]
122
+ }
123
+
124
+ meth_arr << meth_hash
125
+
126
+ # Add Queue Unbind-ok method
127
+ meth_hash = {:name => 'unbind-ok',
128
+ :index => '51',
129
+ :fields => []
130
+ }
131
+
132
+ meth_arr << meth_hash
133
+ end
134
+
135
+ # Return methods
136
+ meth_arr
137
+
138
+ end
139
+
140
+ def convert_type(name)
141
+ type_arr = @type_conversion.select {|k,v| k == name}.flatten
142
+ end
143
+
144
+ # Start of Main program
145
+
146
+ # Read in config options
147
+ CONFIG = YAML::load(File.read('config.yml'))
148
+
149
+ # Get path to the spec file and the spec file name on its own
150
+ specpath = CONFIG[:spec_in]
151
+ path = Pathname.new(specpath)
152
+ specfile = path.basename.to_s
153
+
154
+ # Read in the spec file
155
+ spec = JSON.parse(IO.read(specpath))
156
+
157
+ # Declare type conversion hash
158
+ @type_conversion = {'path' => 'shortstr',
159
+ 'known hosts' => 'shortstr',
160
+ 'known-hosts' => 'shortstr',
161
+ 'reply code' => 'short',
162
+ 'reply-code' => 'short',
163
+ 'reply text' => 'shortstr',
164
+ 'reply-text' => 'shortstr',
165
+ 'class id' => 'short',
166
+ 'class-id' => 'short',
167
+ 'method id' => 'short',
168
+ 'method-id' => 'short',
169
+ 'channel-id' => 'longstr',
170
+ 'access ticket' => 'short',
171
+ 'access-ticket' => 'short',
172
+ 'exchange name' => 'shortstr',
173
+ 'exchange-name' => 'shortstr',
174
+ 'queue name' => 'shortstr',
175
+ 'queue-name' => 'shortstr',
176
+ 'consumer tag' => 'shortstr',
177
+ 'consumer-tag' => 'shortstr',
178
+ 'delivery tag' => 'longlong',
179
+ 'delivery-tag' => 'longlong',
180
+ 'redelivered' => 'bit',
181
+ 'no ack' => 'bit',
182
+ 'no-ack' => 'bit',
183
+ 'no local' => 'bit',
184
+ 'no-local' => 'bit',
185
+ 'peer properties' => 'table',
186
+ 'peer-properties' => 'table',
187
+ 'destination' => 'shortstr',
188
+ 'duration' => 'longlong',
189
+ 'security-token' => 'longstr',
190
+ 'reject-code' => 'short',
191
+ 'reject-text' => 'shortstr',
192
+ 'offset' => 'longlong',
193
+ 'no-wait' => 'bit',
194
+ 'message-count' => 'long'
195
+ }
196
+
197
+ # Spec details
198
+ spec_info = spec_details(spec)
199
+
200
+ # Constants
201
+ constants = process_constants(spec)
202
+
203
+ # Frame constants
204
+ frame_constants = constants[0].select {|k,v| k <= 8}
205
+ frame_footer = constants[0].select {|k,v| v == 'End'}[0][0]
206
+
207
+ # Other constants
208
+ other_constants = constants[1]
209
+
210
+ # Domain types
211
+ data_types = domain_types(spec, spec_info['major'], spec_info['minor'], spec_info['revision'])
212
+
213
+ # Classes
214
+ class_defs = classes(spec, spec_info['major'], spec_info['minor'], spec_info['revision'])
215
+
216
+ # Generate spec.rb
217
+ spec_rb = File.open(CONFIG[:spec_out], 'w')
218
+ spec_rb.puts(
219
+ ERB.new(%q[
220
+ # encoding: utf-8
221
+
222
+
223
+ #:stopdoc:
224
+ # this file was autogenerated on <%= Time.now.to_s %>
225
+ # using <%= specfile.ljust(16) %> (mtime: <%= File.mtime(specpath) %>)
226
+ #
227
+ # DO NOT EDIT! (edit ext/qparser.rb and config.yml instead, and run 'ruby qparser.rb')
228
+
229
+ module Qrack
230
+ module Protocol
231
+ HEADER = "AMQP".freeze
232
+ VERSION_MAJOR = <%= spec_info['major'] %>
233
+ VERSION_MINOR = <%= spec_info['minor'] %>
234
+ REVISION = <%= spec_info['revision'] %>
235
+ PORT = <%= spec_info['port'] %>
236
+
237
+ RESPONSES = {
238
+ <%- other_constants.each do |value, name| -%>
239
+ <%= value %> => :<%= name.gsub(/\s|-/, '_').upcase -%>,
240
+ <%- end -%>
241
+ }
242
+
243
+ FIELDS = [
244
+ <%- data_types.each do |d| -%>
245
+ :<%= d -%>,
246
+ <%- end -%>
247
+ ]
248
+
249
+ class Class
250
+ class << self
251
+ FIELDS.each do |f|
252
+ class_eval %[
253
+ def #{f} name
254
+ properties << [ :#{f}, name ] unless properties.include?([:#{f}, name])
255
+ attr_accessor name
256
+ end
257
+ ]
258
+ end
259
+
260
+ def properties() @properties ||= [] end
261
+
262
+ def id() self::ID end
263
+ def name() self::NAME.to_s end
264
+ end
265
+
266
+ class Method
267
+ class << self
268
+ FIELDS.each do |f|
269
+ class_eval %[
270
+ def #{f} name
271
+ arguments << [ :#{f}, name ] unless arguments.include?([:#{f}, name])
272
+ attr_accessor name
273
+ end
274
+ ]
275
+ end
276
+
277
+ def arguments() @arguments ||= [] end
278
+
279
+ def parent() Protocol.const_get(self.to_s[/Protocol::(.+?)::/,1]) end
280
+ def id() self::ID end
281
+ def name() self::NAME.to_s end
282
+ end
283
+
284
+ def == b
285
+ self.class.arguments.inject(true) do |eql, (type, name)|
286
+ eql and __send__("#{name}") == b.__send__("#{name}")
287
+ end
288
+ end
289
+ end
290
+
291
+ def self.methods() @methods ||= {} end
292
+
293
+ def self.Method(id, name)
294
+ @_base_methods ||= {}
295
+ @_base_methods[id] ||= ::Class.new(Method) do
296
+ class_eval %[
297
+ def self.inherited klass
298
+ klass.const_set(:ID, #{id})
299
+ klass.const_set(:NAME, :#{name.to_s})
300
+ klass.parent.methods[#{id}] = klass
301
+ klass.parent.methods[klass::NAME] = klass
302
+ end
303
+ ]
304
+ end
305
+ end
306
+ end
307
+
308
+ def self.classes() @classes ||= {} end
309
+
310
+ def self.Class(id, name)
311
+ @_base_classes ||= {}
312
+ @_base_classes[id] ||= ::Class.new(Class) do
313
+ class_eval %[
314
+ def self.inherited klass
315
+ klass.const_set(:ID, #{id})
316
+ klass.const_set(:NAME, :#{name.to_s})
317
+ Protocol.classes[#{id}] = klass
318
+ Protocol.classes[klass::NAME] = klass
319
+ end
320
+ ]
321
+ end
322
+ end
323
+ end
324
+ end
325
+
326
+ module Qrack
327
+ module Protocol
328
+ <%- class_defs.each do |h| -%>
329
+ class <%= h[:name].capitalize.ljust(12) %> < Class( <%= h[:index].to_s.rjust(3) %>, :<%= h[:name].ljust(12) %> ); end
330
+ <%- end -%>
331
+
332
+ <%- class_defs.each do |c| -%>
333
+ class <%= c[:name].capitalize %>
334
+ <%- c[:fields].each do |p| -%>
335
+ <%= p[:domain].ljust(10) %> :<%= p[:name].tr('-','_') %>
336
+ <%- end if c[:fields] -%>
337
+
338
+ <%- c[:methods].each do |m| -%>
339
+ class <%= m[:name].capitalize.gsub(/-(.)/){ "#{$1.upcase}"}.ljust(12) %> < Method( <%= m[:index].to_s.rjust(3) %>, :<%= m[:name].tr('- ','_').ljust(14) %> ); end
340
+ <%- end -%>
341
+
342
+ <%- c[:methods].each do |m| -%>
343
+
344
+ class <%= m[:name].capitalize.gsub(/-(.)/){ "#{$1.upcase}"} %>
345
+ <%- m[:fields].each do |a| -%>
346
+ <%- if a[:domain] -%>
347
+ <%= a[:domain].ljust(16) %> :<%= a[:name].tr('- ','_') %>
348
+ <%- end -%>
349
+ <%- end -%>
350
+ end
351
+ <%- end -%>
352
+
353
+ end
354
+
355
+ <%- end -%>
356
+ end
357
+
358
+ end
359
+ ].gsub!(/^ /,''), nil, '>-%').result(binding)
360
+ )
361
+
362
+ # Close spec.rb file
363
+ spec_rb.close
364
+
365
+ # Generate frame.rb file
366
+
367
+ frame_rb = File.open(CONFIG[:frame_out], 'w')
368
+ frame_rb.puts(
369
+ ERB.new(%q[
370
+ # encoding: utf-8
371
+
372
+
373
+ #:stopdoc:
374
+ # this file was autogenerated on <%= Time.now.to_s %>
375
+ #
376
+ # DO NOT EDIT! (edit ext/qparser.rb and config.yml instead, and run 'ruby qparser.rb')
377
+
378
+ module Qrack
379
+ module Transport
380
+ class Frame
381
+
382
+ FOOTER = <%= frame_footer %>
383
+ ID = 0
384
+
385
+ @types = {
386
+ <%- frame_constants.each do |value, name| -%>
387
+ <%= value %> => '<%= name %>',
388
+ <%- end -%>
389
+ }
390
+
391
+ attr_accessor :channel, :payload
392
+
393
+ def initialize payload = nil, channel = 0
394
+ @channel, @payload = channel, payload
395
+ end
396
+
397
+ def id
398
+ self.class::ID
399
+ end
400
+
401
+ def to_binary
402
+ buf = Transport::Buffer.new
403
+ buf.write :octet, id
404
+ buf.write :short, channel
405
+ buf.write :longstr, payload
406
+ buf.write :octet, FOOTER
407
+ buf.rewind
408
+ buf
409
+ end
410
+
411
+ def to_s
412
+ to_binary.to_s
413
+ end
414
+
415
+ def == frame
416
+ [ :id, :channel, :payload ].inject(true) do |eql, field|
417
+ eql and __send__(field) == frame.__send__(field)
418
+ end
419
+ end
420
+
421
+ def self.parse buf
422
+ buf = Transport::Buffer.new(buf) unless buf.is_a? Transport::Buffer
423
+ buf.extract do
424
+ id, channel, payload, footer = buf.read(:octet, :short, :longstr, :octet)
425
+ Qrack::Transport.const_get(@types[id]).new(payload, channel) if footer == FOOTER
426
+ end
427
+ end
428
+
429
+ end
430
+
431
+ class Method < Frame
432
+
433
+ ID = 1
434
+
435
+ def initialize payload = nil, channel = 0
436
+ super
437
+ unless @payload.is_a? Protocol::Class::Method or @payload.nil?
438
+ @payload = Protocol.parse(@payload)
439
+ end
440
+ end
441
+ end
442
+
443
+ class Header < Frame
444
+
445
+ ID = 2
446
+
447
+ def initialize payload = nil, channel = 0
448
+ super
449
+ unless @payload.is_a? Protocol::Header or @payload.nil?
450
+ @payload = Protocol::Header.new(@payload)
451
+ end
452
+ end
453
+ end
454
+
455
+ <%- frame_constants.each do |value, name| -%>
456
+ <%- if value > 2 -%>
457
+ class <%= name %> < Frame
458
+ ID = <%= value %>
459
+ end
460
+
461
+ <%- end -%>
462
+ <%- end -%>
463
+ end
464
+ end
465
+ ].gsub!(/^ /,''), nil, '>-%').result(binding)
466
+ )
467
+
468
+ # Close frame.rb file
469
+ frame_rb.close
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ module Bunny
4
+ class Channel < Qrack::Channel
5
+
6
+ def initialize(client)
7
+ super
8
+ end
9
+
10
+ def open
11
+ client.channel = self
12
+ client.send_frame(Qrack::Protocol::Channel::Open.new)
13
+
14
+ method = client.next_method
15
+
16
+ client.check_response(method, Qrack::Protocol::Channel::OpenOk, "Cannot open channel #{number}")
17
+
18
+ @active = true
19
+ :open_ok
20
+ end
21
+
22
+ def close
23
+ client.channel = self
24
+ client.send_frame(Qrack::Protocol::Channel::Close.new(:reply_code => 200, :reply_text => 'bye', :method_id => 0, :class_id => 0))
25
+
26
+ method = client.next_method
27
+
28
+ client.check_response(method, Qrack::Protocol::Channel::CloseOk, "Error closing channel #{number}")
29
+
30
+ @active = false
31
+ :close_ok
32
+ end
33
+
34
+ def open?
35
+ active
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+
3
+ module Bunny
4
+ class Channel09 < Qrack::Channel
5
+
6
+ def initialize(client)
7
+ super
8
+ end
9
+
10
+ def open
11
+ client.channel = self
12
+ client.send_frame(Qrack::Protocol09::Channel::Open.new)
13
+
14
+ method = client.next_method
15
+
16
+ client.check_response(method, Qrack::Protocol09::Channel::OpenOk, "Cannot open channel #{number}")
17
+
18
+ @active = true
19
+ :open_ok
20
+ end
21
+
22
+ def close
23
+ client.channel = self
24
+ client.send_frame(Qrack::Protocol09::Channel::Close.new(:reply_code => 200, :reply_text => 'bye', :method_id => 0, :class_id => 0))
25
+
26
+ method = client.next_method
27
+
28
+ client.check_response(method, Qrack::Protocol09::Channel::CloseOk, "Error closing channel #{number}")
29
+
30
+ @active = false
31
+ :close_ok
32
+ end
33
+
34
+ def open?
35
+ active
36
+ end
37
+
38
+ end
39
+ end