bunny 0.6.3.rc2 → 0.7

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 +8 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +15 -0
  4. data/.yardopts +9 -0
  5. data/CHANGELOG +3 -0
  6. data/Gemfile +39 -0
  7. data/Gemfile.lock +34 -0
  8. data/LICENSE +5 -4
  9. data/README.textile +54 -0
  10. data/Rakefile +15 -13
  11. data/bunny.gemspec +42 -61
  12. data/examples/simple_08.rb +4 -2
  13. data/examples/simple_09.rb +4 -2
  14. data/examples/simple_ack_08.rb +3 -1
  15. data/examples/simple_ack_09.rb +3 -1
  16. data/examples/simple_consumer_08.rb +4 -2
  17. data/examples/simple_consumer_09.rb +4 -2
  18. data/examples/simple_fanout_08.rb +3 -1
  19. data/examples/simple_fanout_09.rb +3 -1
  20. data/examples/simple_headers_08.rb +5 -3
  21. data/examples/simple_headers_09.rb +5 -3
  22. data/examples/simple_publisher_08.rb +3 -1
  23. data/examples/simple_publisher_09.rb +3 -1
  24. data/examples/simple_topic_08.rb +5 -3
  25. data/examples/simple_topic_09.rb +5 -3
  26. data/ext/amqp-0.8.json +616 -0
  27. data/ext/amqp-0.9.1.json +388 -0
  28. data/ext/config.yml +4 -0
  29. data/ext/qparser.rb +463 -0
  30. data/lib/bunny.rb +88 -66
  31. data/lib/bunny/channel08.rb +38 -38
  32. data/lib/bunny/channel09.rb +37 -37
  33. data/lib/bunny/client08.rb +184 -206
  34. data/lib/bunny/client09.rb +277 -363
  35. data/lib/bunny/consumer.rb +35 -0
  36. data/lib/bunny/exchange08.rb +37 -41
  37. data/lib/bunny/exchange09.rb +106 -124
  38. data/lib/bunny/queue08.rb +216 -202
  39. data/lib/bunny/queue09.rb +256 -326
  40. data/lib/bunny/subscription08.rb +30 -29
  41. data/lib/bunny/subscription09.rb +84 -83
  42. data/lib/bunny/version.rb +5 -0
  43. data/lib/qrack/amq-client-url.rb +165 -0
  44. data/lib/qrack/channel.rb +19 -17
  45. data/lib/qrack/client.rb +152 -151
  46. data/lib/qrack/errors.rb +5 -0
  47. data/lib/qrack/protocol/protocol08.rb +132 -130
  48. data/lib/qrack/protocol/protocol09.rb +133 -131
  49. data/lib/qrack/protocol/spec08.rb +2 -0
  50. data/lib/qrack/protocol/spec09.rb +2 -0
  51. data/lib/qrack/qrack08.rb +7 -10
  52. data/lib/qrack/qrack09.rb +7 -10
  53. data/lib/qrack/queue.rb +27 -40
  54. data/lib/qrack/subscription.rb +102 -101
  55. data/lib/qrack/transport/buffer08.rb +266 -264
  56. data/lib/qrack/transport/buffer09.rb +268 -264
  57. data/lib/qrack/transport/frame08.rb +13 -11
  58. data/lib/qrack/transport/frame09.rb +9 -7
  59. data/spec/spec_08/bunny_spec.rb +48 -45
  60. data/spec/spec_08/connection_spec.rb +10 -7
  61. data/spec/spec_08/exchange_spec.rb +145 -143
  62. data/spec/spec_08/queue_spec.rb +161 -161
  63. data/spec/spec_09/bunny_spec.rb +46 -44
  64. data/spec/spec_09/connection_spec.rb +15 -8
  65. data/spec/spec_09/exchange_spec.rb +147 -145
  66. data/spec/spec_09/queue_spec.rb +182 -184
  67. metadata +60 -41
  68. data/README.rdoc +0 -66
@@ -0,0 +1,388 @@
1
+ {
2
+ "name": "AMQP",
3
+ "major-version": 0,
4
+ "minor-version": 9,
5
+ "revision": 1,
6
+ "port": 5672,
7
+ "copyright": [
8
+ "Copyright (C) 2008-2009 LShift Ltd, Cohesive Financial Technologies LLC,\n",
9
+ "and Rabbit Technologies Ltd\n",
10
+ "\n",
11
+ "Permission is hereby granted, free of charge, to any person\n",
12
+ "obtaining a copy of this file (the \"Software\"), to deal in the\n",
13
+ "Software without restriction, including without limitation the \n",
14
+ "rights to use, copy, modify, merge, publish, distribute, \n",
15
+ "sublicense, and/or sell copies of the Software, and to permit \n",
16
+ "persons to whom the Software is furnished to do so, subject to \n",
17
+ "the following conditions:\n",
18
+ "\n",
19
+ "The above copyright notice and this permission notice shall be\n",
20
+ "included in all copies or substantial portions of the Software.\n",
21
+ "\n",
22
+ "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n",
23
+ "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n",
24
+ "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n",
25
+ "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n",
26
+ "HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n",
27
+ "WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
28
+ "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n",
29
+ "OTHER DEALINGS IN THE SOFTWARE.\n",
30
+ "\n",
31
+ "Class information entered from amqp_xml0-8.pdf and domain types from amqp-xml-doc0-9.pdf\n",
32
+ "Updated for 0-9-1 by Tony Garnock-Jones\n",
33
+ "\n",
34
+ "b3cb053f15e7b98808c0ccc67f23cb3e amqp_xml0-8.pdf\n",
35
+ "http://www.twiststandards.org/index.php?option=com_docman&task=cat_view&gid=28&&Itemid=90\n",
36
+ "8444db91e2949dbecfb2585e9eef6d64 amqp-xml-doc0-9.pdf\n",
37
+ "https://jira.amqp.org/confluence/download/attachments/720900/amqp-xml-doc0-9.pdf?version=1\n"],
38
+
39
+ "domains": [
40
+ ["bit", "bit"],
41
+ ["channel-id", "longstr"],
42
+ ["class-id", "short"],
43
+ ["consumer-tag", "shortstr"],
44
+ ["delivery-tag", "longlong"],
45
+ ["destination", "shortstr"],
46
+ ["duration", "longlong"],
47
+ ["exchange-name", "shortstr"],
48
+ ["long", "long"],
49
+ ["longlong", "longlong"],
50
+ ["longstr", "longstr"],
51
+ ["method-id", "short"],
52
+ ["no-ack", "bit"],
53
+ ["no-local", "bit"],
54
+ ["octet", "octet"],
55
+ ["offset", "longlong"],
56
+ ["path", "shortstr"],
57
+ ["peer-properties", "table"],
58
+ ["queue-name", "shortstr"],
59
+ ["redelivered", "bit"],
60
+ ["reference", "longstr"],
61
+ ["reject-code", "short"],
62
+ ["reject-text", "shortstr"],
63
+ ["reply-code", "short"],
64
+ ["reply-text", "shortstr"],
65
+ ["security-token", "longstr"],
66
+ ["short", "short"],
67
+ ["shortstr", "shortstr"],
68
+ ["table", "table"],
69
+ ["timestamp", "timestamp"]
70
+ ],
71
+
72
+ "constants": [
73
+ {"name": "FRAME-METHOD", "value": 1},
74
+ {"name": "FRAME-HEADER", "value": 2},
75
+ {"name": "FRAME-BODY", "value": 3},
76
+ {"name": "FRAME-HEARTBEAT", "value": 8},
77
+ {"name": "FRAME-MIN-SIZE", "value": 4096},
78
+ {"name": "FRAME-END", "value": 206},
79
+ {"name": "REPLY-SUCCESS", "value": 200},
80
+ {"name": "CONTENT-TOO-LARGE", "value": 311, "class": "soft-error"},
81
+ {"name": "NO-ROUTE", "value": 312, "class": "soft-error"},
82
+ {"name": "NO-CONSUMERS", "value": 313, "class": "soft-error"},
83
+ {"name": "ACCESS-REFUSED", "value": 403, "class": "soft-error"},
84
+ {"name": "NOT-FOUND", "value": 404, "class": "soft-error"},
85
+ {"name": "RESOURCE-LOCKED", "value": 405, "class": "soft-error"},
86
+ {"name": "PRECONDITION-FAILED", "value": 406, "class": "soft-error"},
87
+ {"name": "CONNECTION-FORCED", "value": 320, "class": "hard-error"},
88
+ {"name": "INVALID-PATH", "value": 402, "class": "hard-error"},
89
+ {"name": "FRAME-ERROR", "value": 501, "class": "hard-error"},
90
+ {"name": "SYNTAX-ERROR", "value": 502, "class": "hard-error"},
91
+ {"name": "COMMAND-INVALID", "value": 503, "class": "hard-error"},
92
+ {"name": "CHANNEL-ERROR", "value": 504, "class": "hard-error"},
93
+ {"name": "UNEXPECTED-FRAME", "value": 505, "class": "hard-error"},
94
+ {"name": "RESOURCE-ERROR", "value": 506, "class": "hard-error"},
95
+ {"name": "NOT-ALLOWED", "value": 530, "class": "hard-error"},
96
+ {"name": "NOT-IMPLEMENTED", "value": 540, "class": "hard-error"},
97
+ {"name": "INTERNAL-ERROR", "value": 541, "class": "hard-error"}
98
+ ],
99
+
100
+ "classes": [
101
+ {
102
+ "id": 10,
103
+ "methods": [{"id": 10,
104
+ "arguments": [{"type": "octet", "name": "version-major", "default-value": 0},
105
+ {"type": "octet", "name": "version-minor", "default-value": 8},
106
+ {"domain": "peer-properties", "name": "server properties"},
107
+ {"type": "longstr", "name": "mechanisms", "default-value": "PLAIN"},
108
+ {"type": "longstr", "name": "locales", "default-value": "en_US"}],
109
+ "name": "start"},
110
+ {"id": 11,
111
+ "arguments": [{"domain": "peer-properties", "name": "client-properties"},
112
+ {"type": "shortstr", "name": "mechanism", "default-value": "PLAIN"},
113
+ {"type": "longstr", "name": "response"},
114
+ {"type": "shortstr", "name": "locale", "default-value": "en_US"}],
115
+ "name": "start-ok"},
116
+ {"id": 20,
117
+ "arguments": [{"type": "longstr", "name": "challenge"}],
118
+ "name": "secure"},
119
+ {"id": 21,
120
+ "arguments": [{"type": "longstr", "name": "response"}],
121
+ "name": "secure-ok"},
122
+ {"id": 30,
123
+ "arguments": [{"type": "short", "name": "channel-max", "default-value": 0},
124
+ {"type": "long", "name": "frame-max", "default-value": 0},
125
+ {"type": "short", "name": "heartbeat", "default-value": 0}],
126
+ "name": "tune"},
127
+ {"id": 31,
128
+ "arguments": [{"type": "short", "name": "channel-max", "default-value": 0},
129
+ {"type": "long", "name": "frame-max", "default-value": 0},
130
+ {"type": "short", "name": "heartbeat", "default-value": 0}],
131
+ "name": "tune-ok"},
132
+ {"id": 40,
133
+ "arguments": [{"type": "shortstr", "name": "virtual-host", "default-value": "/"},
134
+ {"type": "shortstr", "name": "deprecated-capabilities", "default-value": ""},
135
+ {"type": "bit", "name": "deprecated-insist", "default-value": false}],
136
+ "name": "open"},
137
+ {"id": 41,
138
+ "arguments": [{"type": "shortstr", "name": "deprecated-known-hosts", "default-value": ""}],
139
+ "name": "open-ok"},
140
+ {"id": 50,
141
+ "arguments": [{"type": "short", "name": "reply-code"},
142
+ {"type": "shortstr", "name": "reply-text", "default-value": ""},
143
+ {"type": "short", "name": "class-id"},
144
+ {"type": "short", "name": "method-id"}],
145
+ "name": "close"},
146
+ {"id": 51,
147
+ "arguments": [],
148
+ "name": "close-ok"}],
149
+ "name": "connection",
150
+ "properties": []
151
+ },
152
+ {
153
+ "id": 20,
154
+ "methods": [{"id": 10,
155
+ "arguments": [{"type": "shortstr", "name": "deprecated-out-of-band", "default-value": ""}],
156
+ "name": "open"},
157
+ {"id": 11,
158
+ "arguments": [{"type": "longstr", "name": "deprecated-channel-id", "default-value": ""}],
159
+ "name": "open-ok"},
160
+ {"id": 20,
161
+ "arguments": [{"type": "bit", "name": "active"}],
162
+ "name": "flow"},
163
+ {"id": 21,
164
+ "arguments": [{"type": "bit", "name": "active"}],
165
+ "name": "flow-ok"},
166
+ {"id": 40,
167
+ "arguments": [{"type": "short", "name": "reply-code"},
168
+ {"type": "shortstr", "name": "reply-text", "default-value": ""},
169
+ {"type": "short", "name": "class-id"},
170
+ {"type": "short", "name": "method-id"}],
171
+ "name": "close"},
172
+ {"id": 41,
173
+ "arguments": [],
174
+ "name": "close-ok"}],
175
+ "name": "channel"
176
+ },
177
+ {
178
+ "id": 40,
179
+ "methods": [{"id": 10,
180
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
181
+ {"type": "shortstr", "name": "exchange"},
182
+ {"type": "shortstr", "name": "type", "default-value": "direct"},
183
+ {"type": "bit", "name": "passive", "default-value": false},
184
+ {"type": "bit", "name": "durable", "default-value": false},
185
+ {"type": "bit", "name": "deprecated-auto-delete", "default-value": false},
186
+ {"type": "bit", "name": "deprecated-internal", "default-value": false},
187
+ {"type": "bit", "name": "nowait", "default-value": false},
188
+ {"type": "table", "name": "arguments", "default-value": {}}],
189
+ "name": "declare"},
190
+ {"id": 11,
191
+ "arguments": [],
192
+ "name": "declare-ok"},
193
+ {"id": 20,
194
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
195
+ {"type": "shortstr", "name": "exchange"},
196
+ {"type": "bit", "name": "if-unused", "default-value": false},
197
+ {"type": "bit", "name": "nowait", "default-value": false}],
198
+ "name": "delete"},
199
+ {"id": 21,
200
+ "arguments": [],
201
+ "name": "delete-ok"}],
202
+ "name": "exchange"
203
+ },
204
+ {
205
+ "id": 50,
206
+ "methods": [{"id": 10,
207
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
208
+ {"type": "shortstr", "name": "queue", "default-value": ""},
209
+ {"type": "bit", "name": "passive", "default-value": false},
210
+ {"type": "bit", "name": "durable", "default-value": false},
211
+ {"type": "bit", "name": "exclusive", "default-value": false},
212
+ {"type": "bit", "name": "auto-delete", "default-value": false},
213
+ {"type": "bit", "name": "nowait", "default-value": false},
214
+ {"type": "table", "name": "arguments", "default-value": {}}],
215
+ "name": "declare"},
216
+ {"id": 11,
217
+ "arguments": [{"type": "shortstr", "name": "queue"},
218
+ {"type": "long", "name": "message-count"},
219
+ {"type": "long", "name": "consumer-count"}],
220
+ "name": "declare-ok"},
221
+ {"id": 20,
222
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
223
+ {"type": "shortstr", "name": "queue"},
224
+ {"type": "shortstr", "name": "exchange"},
225
+ {"type": "shortstr", "name": "routing-key"},
226
+ {"type": "bit", "name": "nowait", "default-value": false},
227
+ {"type": "table", "name": "arguments", "default-value": {}}],
228
+ "name": "bind"},
229
+ {"id": 21,
230
+ "arguments": [],
231
+ "name": "bind-ok"},
232
+ {"id": 30,
233
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
234
+ {"type": "shortstr", "name": "queue"},
235
+ {"type": "bit", "name": "nowait", "default-value": false}],
236
+ "name": "purge"},
237
+ {"id": 31,
238
+ "arguments": [{"type": "long", "name": "message-count"}],
239
+ "name": "purge-ok"},
240
+ {"id": 40,
241
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
242
+ {"type": "shortstr", "name": "queue"},
243
+ {"type": "bit", "name": "if-unused", "default-value": false},
244
+ {"type": "bit", "name": "if-empty", "default-value": false},
245
+ {"type": "bit", "name": "nowait", "default-value": false}],
246
+ "name": "delete"},
247
+ {"id": 41,
248
+ "arguments": [{"type": "long", "name": "message-count"}],
249
+ "name": "delete-ok"},
250
+ {"id": 50,
251
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
252
+ {"type": "shortstr", "name": "queue"},
253
+ {"type": "shortstr", "name": "exchange"},
254
+ {"type": "shortstr", "name": "routing-key"},
255
+ {"type": "table", "name": "arguments"}],
256
+ "name": "unbind"},
257
+ {"id": 51,
258
+ "arguments": [],
259
+ "name": "unbind-ok"}
260
+ ],
261
+ "name": "queue"
262
+ },
263
+ {
264
+ "id": 60,
265
+ "methods": [{"id": 10,
266
+ "arguments": [{"type": "long", "name": "prefetch-size", "default-value": 0},
267
+ {"type": "short", "name": "prefetch-count", "default-value": 0},
268
+ {"type": "bit", "name": "global", "default-value": false}],
269
+ "name": "qos"},
270
+ {"id": 11,
271
+ "arguments": [],
272
+ "name": "qos-ok"},
273
+ {"id": 20,
274
+ "arguments": [{"domain": "short", "name": "deprecated-ticket", "default-value": 1},
275
+ {"domain": "queue-name", "name": "queue"},
276
+ {"type": "shortstr", "name": "consumer-tag"},
277
+ {"type": "bit", "name": "no-local", "default-value": false},
278
+ {"type": "bit", "name": "no-ack", "default-value": false},
279
+ {"type": "bit", "name": "exclusive", "default-value": false},
280
+ {"type": "bit", "name": "nowait", "default-value": false},
281
+ {"type": "table", "name": "filter", "default-value": {}}],
282
+ "name": "consume"},
283
+ {"id": 21,
284
+ "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
285
+ "name": "consume-ok"},
286
+ {"id": 30,
287
+ "arguments": [{"type": "shortstr", "name": "consumer-tag"},
288
+ {"type": "bit", "name": "nowait", "default-value": false}],
289
+ "name": "cancel"},
290
+ {"id": 31,
291
+ "arguments": [{"type": "shortstr", "name": "consumer-tag"}],
292
+ "name": "cancel-ok"},
293
+ {"content": true,
294
+ "id": 40,
295
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
296
+ {"type": "shortstr", "name": "exchange", "default-value": ""},
297
+ {"type": "shortstr", "name": "routing-key"},
298
+ {"type": "bit", "name": "mandatory", "default-value": false},
299
+ {"type": "bit", "name": "immediate", "default-value": false}],
300
+ "name": "publish"},
301
+ {"content": true,
302
+ "id": 50,
303
+ "arguments": [{"type": "short", "name": "reply-code"},
304
+ {"type": "shortstr", "name": "reply-text", "default-value": ""},
305
+ {"type": "shortstr", "name": "exchange"},
306
+ {"type": "shortstr", "name": "routing-key"}],
307
+ "name": "return"},
308
+ {"content": true,
309
+ "id": 60,
310
+ "arguments": [{"type": "shortstr", "name": "consumer-tag"},
311
+ {"type": "longlong", "name": "delivery-tag"},
312
+ {"type": "bit", "name": "redelivered", "default-value": false},
313
+ {"type": "shortstr", "name": "exchange"},
314
+ {"type": "shortstr", "name": "routing-key"}],
315
+ "name": "deliver"},
316
+ {"id": 70,
317
+ "arguments": [{"type": "short", "name": "deprecated-ticket", "default-value": 1},
318
+ {"type": "shortstr", "name": "queue"},
319
+ {"type": "bit", "name": "no-ack", "default-value": false}],
320
+ "name": "get"},
321
+ {"content": true,
322
+ "id": 71,
323
+ "arguments": [{"type": "longlong", "name": "delivery-tag"},
324
+ {"type": "bit", "name": "redelivered", "default-value": false},
325
+ {"type": "shortstr", "name": "exchange"},
326
+ {"type": "shortstr", "name": "routing-key"},
327
+ {"type": "long", "name": "message-count"}],
328
+ "name": "get-ok"},
329
+ {"id": 72,
330
+ "arguments": [{"type": "shortstr", "name": "deprecated-cluster-id", "default-value": ""}],
331
+ "name": "get-empty"},
332
+ {"id": 80,
333
+ "arguments": [{"type": "longlong", "name": "delivery-tag", "default-value": 0},
334
+ {"type": "bit", "name": "multiple", "default-value": true}],
335
+ "name": "ack"},
336
+ {"id": 90,
337
+ "arguments": [{"type": "longlong", "name": "delivery-tag"},
338
+ {"type": "bit", "name": "requeue", "default-value": true}],
339
+ "name": "reject"},
340
+ {"id": 100,
341
+ "arguments": [{"type": "bit", "name": "requeue", "default-value": false}],
342
+ "name": "recover-async"},
343
+ {"id": 110,
344
+ "arguments": [{"type": "bit", "name": "requeue", "default-value": false}],
345
+ "name": "recover"},
346
+ {"id": 111,
347
+ "arguments": [],
348
+ "name": "recover-ok"}],
349
+ "name": "basic",
350
+ "properties": [{"type": "shortstr", "name": "content-type"},
351
+ {"type": "shortstr", "name": "content-encoding"},
352
+ {"type": "table", "name": "headers"},
353
+ {"type": "octet", "name": "delivery-mode"},
354
+ {"type": "octet", "name": "priority"},
355
+ {"type": "shortstr", "name": "correlation-id"},
356
+ {"type": "shortstr", "name": "reply-to"},
357
+ {"type": "shortstr", "name": "expiration"},
358
+ {"type": "shortstr", "name": "message-id"},
359
+ {"type": "timestamp", "name": "timestamp"},
360
+ {"type": "shortstr", "name": "type"},
361
+ {"type": "shortstr", "name": "user-id"},
362
+ {"type": "shortstr", "name": "app-id"},
363
+ {"type": "shortstr", "name": "deprecated-cluster-id"}]
364
+ },
365
+ {
366
+ "id": 90,
367
+ "methods": [{"id": 10,
368
+ "arguments": [],
369
+ "name": "select"},
370
+ {"id": 11,
371
+ "arguments": [],
372
+ "name": "select-ok"},
373
+ {"id": 20,
374
+ "arguments": [],
375
+ "name": "commit"},
376
+ {"id": 21,
377
+ "arguments": [],
378
+ "name": "commit-ok"},
379
+ {"id": 30,
380
+ "arguments": [],
381
+ "name": "rollback"},
382
+ {"id": 31,
383
+ "arguments": [],
384
+ "name": "rollback-ok"}],
385
+ "name": "tx"
386
+ }
387
+ ]
388
+ }
@@ -0,0 +1,4 @@
1
+ ---
2
+ :spec_out: '../lib/qrack/protocol/spec08.rb'
3
+ :frame_out: '../lib/qrack/transport/frame08.rb'
4
+ :spec_in: 'amqp-0.8.json'
@@ -0,0 +1,463 @@
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
+ #:stopdoc:
221
+ # this file was autogenerated on <%= Time.now.to_s %>
222
+ # using <%= specfile.ljust(16) %> (mtime: <%= File.mtime(specpath) %>)
223
+ #
224
+ # DO NOT EDIT! (edit ext/qparser.rb and config.yml instead, and run 'ruby qparser.rb')
225
+
226
+ module Qrack
227
+ module Protocol
228
+ HEADER = "AMQP".freeze
229
+ VERSION_MAJOR = <%= spec_info['major'] %>
230
+ VERSION_MINOR = <%= spec_info['minor'] %>
231
+ REVISION = <%= spec_info['revision'] %>
232
+ PORT = <%= spec_info['port'] %>
233
+
234
+ RESPONSES = {
235
+ <%- other_constants.each do |value, name| -%>
236
+ <%= value %> => :<%= name.gsub(/\s|-/, '_').upcase -%>,
237
+ <%- end -%>
238
+ }
239
+
240
+ FIELDS = [
241
+ <%- data_types.each do |d| -%>
242
+ :<%= d -%>,
243
+ <%- end -%>
244
+ ]
245
+
246
+ class Class
247
+ class << self
248
+ FIELDS.each do |f|
249
+ class_eval %[
250
+ def #{f} name
251
+ properties << [ :#{f}, name ] unless properties.include?([:#{f}, name])
252
+ attr_accessor name
253
+ end
254
+ ]
255
+ end
256
+
257
+ def properties() @properties ||= [] end
258
+
259
+ def id() self::ID end
260
+ def name() self::NAME end
261
+ end
262
+
263
+ class Method
264
+ class << self
265
+ FIELDS.each do |f|
266
+ class_eval %[
267
+ def #{f} name
268
+ arguments << [ :#{f}, name ] unless arguments.include?([:#{f}, name])
269
+ attr_accessor name
270
+ end
271
+ ]
272
+ end
273
+
274
+ def arguments() @arguments ||= [] end
275
+
276
+ def parent() Protocol.const_get(self.to_s[/Protocol::(.+?)::/,1]) end
277
+ def id() self::ID end
278
+ def name() self::NAME end
279
+ end
280
+
281
+ def == b
282
+ self.class.arguments.inject(true) do |eql, (type, name)|
283
+ eql and __send__("#{name}") == b.__send__("#{name}")
284
+ end
285
+ end
286
+ end
287
+
288
+ def self.methods() @methods ||= {} end
289
+
290
+ def self.Method(id, name)
291
+ @_base_methods ||= {}
292
+ @_base_methods[id] ||= ::Class.new(Method) do
293
+ class_eval %[
294
+ def self.inherited klass
295
+ klass.const_set(:ID, #{id})
296
+ klass.const_set(:NAME, :#{name.to_s})
297
+ klass.parent.methods[#{id}] = klass
298
+ klass.parent.methods[klass::NAME] = klass
299
+ end
300
+ ]
301
+ end
302
+ end
303
+ end
304
+
305
+ def self.classes() @classes ||= {} end
306
+
307
+ def self.Class(id, name)
308
+ @_base_classes ||= {}
309
+ @_base_classes[id] ||= ::Class.new(Class) do
310
+ class_eval %[
311
+ def self.inherited klass
312
+ klass.const_set(:ID, #{id})
313
+ klass.const_set(:NAME, :#{name.to_s})
314
+ Protocol.classes[#{id}] = klass
315
+ Protocol.classes[klass::NAME] = klass
316
+ end
317
+ ]
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ module Qrack
324
+ module Protocol
325
+ <%- class_defs.each do |h| -%>
326
+ class <%= h[:name].capitalize.ljust(12) %> < Class( <%= h[:index].to_s.rjust(3) %>, :<%= h[:name].ljust(12) %> ); end
327
+ <%- end -%>
328
+
329
+ <%- class_defs.each do |c| -%>
330
+ class <%= c[:name].capitalize %>
331
+ <%- c[:fields].each do |p| -%>
332
+ <%= p[:domain].ljust(10) %> :<%= p[:name].tr('-','_') %>
333
+ <%- end if c[:fields] -%>
334
+
335
+ <%- c[:methods].each do |m| -%>
336
+ class <%= m[:name].capitalize.gsub(/-(.)/){ "#{$1.upcase}"}.ljust(12) %> < Method( <%= m[:index].to_s.rjust(3) %>, :<%= m[:name].tr('- ','_').ljust(14) %> ); end
337
+ <%- end -%>
338
+
339
+ <%- c[:methods].each do |m| -%>
340
+
341
+ class <%= m[:name].capitalize.gsub(/-(.)/){ "#{$1.upcase}"} %>
342
+ <%- m[:fields].each do |a| -%>
343
+ <%- if a[:domain] -%>
344
+ <%= a[:domain].ljust(16) %> :<%= a[:name].tr('- ','_') %>
345
+ <%- end -%>
346
+ <%- end -%>
347
+ end
348
+ <%- end -%>
349
+
350
+ end
351
+
352
+ <%- end -%>
353
+ end
354
+
355
+ end
356
+ ].gsub!(/^ /,''), nil, '>-%').result(binding)
357
+ )
358
+
359
+ # Close spec.rb file
360
+ spec_rb.close
361
+
362
+ # Generate frame.rb file
363
+
364
+ frame_rb = File.open(CONFIG[:frame_out], 'w')
365
+ frame_rb.puts(
366
+ ERB.new(%q[
367
+ #:stopdoc:
368
+ # this file was autogenerated on <%= Time.now.to_s %>
369
+ #
370
+ # DO NOT EDIT! (edit ext/qparser.rb and config.yml instead, and run 'ruby qparser.rb')
371
+
372
+ module Qrack
373
+ module Transport
374
+ class Frame
375
+
376
+ FOOTER = <%= frame_footer %>
377
+ ID = 0
378
+
379
+ @types = {
380
+ <%- frame_constants.each do |value, name| -%>
381
+ <%= value %> => '<%= name %>',
382
+ <%- end -%>
383
+ }
384
+
385
+ attr_accessor :channel, :payload
386
+
387
+ def initialize payload = nil, channel = 0
388
+ @channel, @payload = channel, payload
389
+ end
390
+
391
+ def id
392
+ self.class::ID
393
+ end
394
+
395
+ def to_binary
396
+ buf = Transport::Buffer.new
397
+ buf.write :octet, id
398
+ buf.write :short, channel
399
+ buf.write :longstr, payload
400
+ buf.write :octet, FOOTER
401
+ buf.rewind
402
+ buf
403
+ end
404
+
405
+ def to_s
406
+ to_binary.to_s
407
+ end
408
+
409
+ def == frame
410
+ [ :id, :channel, :payload ].inject(true) do |eql, field|
411
+ eql and __send__(field) == frame.__send__(field)
412
+ end
413
+ end
414
+
415
+ def self.parse buf
416
+ buf = Transport::Buffer.new(buf) unless buf.is_a? Transport::Buffer
417
+ buf.extract do
418
+ id, channel, payload, footer = buf.read(:octet, :short, :longstr, :octet)
419
+ Qrack::Transport.const_get(@types[id]).new(payload, channel) if footer == FOOTER
420
+ end
421
+ end
422
+
423
+ end
424
+
425
+ class Method < Frame
426
+
427
+ ID = 1
428
+
429
+ def initialize payload = nil, channel = 0
430
+ super
431
+ unless @payload.is_a? Protocol::Class::Method or @payload.nil?
432
+ @payload = Protocol.parse(@payload)
433
+ end
434
+ end
435
+ end
436
+
437
+ class Header < Frame
438
+
439
+ ID = 2
440
+
441
+ def initialize payload = nil, channel = 0
442
+ super
443
+ unless @payload.is_a? Protocol::Header or @payload.nil?
444
+ @payload = Protocol::Header.new(@payload)
445
+ end
446
+ end
447
+ end
448
+
449
+ <%- frame_constants.each do |value, name| -%>
450
+ <%- if value > 2 -%>
451
+ class <%= name %> < Frame
452
+ ID = <%= value %>
453
+ end
454
+
455
+ <%- end -%>
456
+ <%- end -%>
457
+ end
458
+ end
459
+ ].gsub!(/^ /,''), nil, '>-%').result(binding)
460
+ )
461
+
462
+ # Close frame.rb file
463
+ frame_rb.close