sparqcode_bunny 0.0.1

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