amq-protocol 0.7.0.beta2 → 0.7.0

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/Gemfile CHANGED
@@ -25,4 +25,5 @@ end
25
25
  group(:test) do
26
26
  gem "rspec", ">=2.0.0"
27
27
  gem "simplecov", :platform => :mri_19
28
+ gem "effin_utf8"
28
29
  end
@@ -1,6 +1,9 @@
1
1
  # encoding: binary
2
2
 
3
3
  require "amq/protocol/client"
4
+ require "amq/protocol/type_constants"
5
+ require "amq/protocol/table_value_encoder"
6
+ require "amq/protocol/table_value_decoder"
4
7
 
5
8
  # We will need to introduce concept of mappings, because
6
9
  # AMQP 0.9, 0.9.1 and RabbitMQ uses different letters for entities
@@ -8,146 +11,129 @@ require "amq/protocol/client"
8
11
  module AMQ
9
12
  module Protocol
10
13
  class Table
14
+
15
+ #
16
+ # Behaviors
17
+ #
18
+
19
+ include TypeConstants
20
+
21
+
22
+ #
23
+ # API
24
+ #
25
+
11
26
  class InvalidTableError < StandardError
12
27
  def initialize(key, value)
13
28
  super("Invalid table value on key #{key}: #{value.inspect} (#{value.class})")
14
29
  end
15
30
  end
16
31
 
17
- TYPE_STRING = 'S'.freeze
18
- TYPE_INTEGER = 'I'.freeze
19
- TYPE_HASH = 'F'.freeze
20
- TYPE_TIME = 'T'.freeze
21
- TYPE_DECIMAL = 'D'.freeze
22
- TYPE_BOOLEAN = 't'.freeze
23
- TYPE_SIGNED_8BIT = 'b'.freeze
24
- TYPE_SIGNED_16BIT = 's'.freeze
25
- TYPE_SIGNED_64BIT = 'l'.freeze
26
- TYPE_32BIT_FLOAT = 'f'.freeze
27
- TYPE_64BIT_FLOAT = 'd'.freeze
28
- TYPE_VOID = 'V'.freeze
29
- TYPE_BYTE_ARRAY = 'x'.freeze
30
- TEN = '10'.freeze
31
-
32
- BOOLEAN_TRUE = "\x01".freeze
33
- BOOLEAN_FALSE = "\x00".freeze
34
-
35
32
 
36
33
  def self.encode(table)
37
34
  buffer = String.new
35
+
38
36
  table ||= {}
37
+
39
38
  table.each do |key, value|
40
39
  key = key.to_s # it can be a symbol as well
41
40
  buffer << key.bytesize.chr + key
42
41
 
43
42
  case value
44
- when String then
45
- buffer << TYPE_STRING
46
- buffer << [value.bytesize].pack(PACK_UINT32)
47
- buffer << value
48
- when Integer then
49
- buffer << TYPE_INTEGER
50
- buffer << [value].pack(PACK_UINT32)
51
- when Float then
52
- buffer << TYPE_64BIT_FLOAT
53
- buffer << [value].pack(PACK_64BIT_FLOAT)
54
- when true, false then
55
- buffer << TYPE_BOOLEAN
56
- buffer << (value ? BOOLEAN_TRUE : BOOLEAN_FALSE)
57
43
  when Hash then
58
- buffer << TYPE_HASH # TODO: encoding support
44
+ buffer << TYPE_HASH
59
45
  buffer << self.encode(value)
60
- when Time then
61
- buffer << TYPE_TIME
62
- buffer << [value.to_i].pack(PACK_INT64).reverse # FIXME: there has to be a more efficient way
63
- when nil then
64
- buffer << TYPE_VOID
65
46
  else
66
- # We don't want to require these libraries.
67
- if defined?(BigDecimal) && value.is_a?(BigDecimal)
68
- buffer << TYPE_DECIMAL
69
- if value.exponent < 0
70
- decimals = -value.exponent
71
- # p [value.exponent] # normalize
72
- raw = (value * (decimals ** 10)).to_i
73
- #pieces.append(struct.pack('>cBI', 'D', decimals, raw)) # byte integer
74
- buffer << [decimals + 1, raw].pack(PACK_UCHAR_UINT32) # somewhat like floating point
75
- else
76
- # per spec, the "decimals" octet is unsigned (!)
77
- buffer << [0, value.to_i].pack(PACK_UCHAR_UINT32)
78
- end
79
- else
80
- raise InvalidTableError.new(key, value)
81
- end
47
+ buffer << TableValueEncoder.encode(value)
82
48
  end
83
49
  end
84
50
 
85
51
  [buffer.bytesize].pack(PACK_UINT32) + buffer
86
52
  end
87
53
 
88
- def self.length(data)
89
- data.unpack(PACK_UINT32).first
90
- end
54
+
55
+
91
56
 
92
57
  def self.decode(data)
93
- table = Hash.new
94
- size = data.unpack(PACK_UINT32).first
95
- offset = 4
96
- while offset < size
97
- key_length = data.slice(offset, 1).unpack(PACK_CHAR).first
98
- offset += 1
99
- key = data.slice(offset, key_length)
100
- offset += key_length
101
- type = data.slice(offset, 1)
102
- offset += 1
103
-
104
- case type
105
- when TYPE_STRING
106
- length = data.slice(offset, 4).unpack(PACK_UINT32).first
107
- offset += 4
108
- value = data.slice(offset, length)
109
- offset += length
110
- when TYPE_INTEGER
111
- value = data.slice(offset, 4).unpack(PACK_UINT32).first
112
- offset += 4
113
- when TYPE_DECIMAL
114
- decimals, raw = data.slice(offset, 5).unpack(PACK_UCHAR_UINT32)
115
- offset += 5
116
- value = BigDecimal.new(raw.to_s) * (BigDecimal.new(TEN) ** -decimals)
117
- when TYPE_TIME
118
- timestamp = data.slice(offset, 8).unpack(PACK_UINT32_X2).last
119
- value = Time.at(timestamp)
120
- offset += 8
121
- when TYPE_HASH
122
- length = data.slice(offset, 4).unpack(PACK_UINT32).first
123
- value = self.decode(data.slice(offset, length + 4))
124
- offset += 4 + length
125
- when TYPE_BOOLEAN
126
- value = data.slice(offset, 2)
127
- integer = value.unpack(PACK_CHAR).first # 0 or 1
128
- value = (integer == 1)
129
- offset += 1
130
- value
131
- when TYPE_SIGNED_8BIT then raise NotImplementedError.new
132
- when TYPE_SIGNED_16BIT then raise NotImplementedError.new
133
- when TYPE_SIGNED_64BIT then raise NotImplementedError.new
134
- when TYPE_32BIT_FLOAT then
135
- value = data.slice(offset, 4).unpack(PACK_32BIT_FLOAT).first
136
- offset += 4
137
- when TYPE_64BIT_FLOAT then
138
- value = data.slice(offset, 8).unpack(PACK_64BIT_FLOAT).first
139
- offset += 8
140
- when TYPE_VOID
141
- value = nil
142
- when TYPE_BYTE_ARRAY
143
- else
144
- raise "Not a valid type: #{type.inspect}\nData: #{data.inspect}\nUnprocessed data: #{data[offset..-1].inspect}\nOffset: #{offset}\nTotal size: #{size}\nProcessed data: #{table.inspect}"
145
- end
146
- table[key] = value
58
+ table = Hash.new
59
+ table_length = data.unpack(PACK_UINT32).first
60
+
61
+ return table if table_length.zero?
62
+
63
+ offset = 4
64
+ while offset <= table_length
65
+ key, offset = decode_table_key(data, offset)
66
+ type, offset = TableValueDecoder.decode_value_type(data, offset)
67
+
68
+ table[key] = case type
69
+ when TYPE_STRING
70
+ v, offset = TableValueDecoder.decode_string(data, offset)
71
+ v
72
+ when TYPE_INTEGER
73
+ v, offset = TableValueDecoder.decode_integer(data, offset)
74
+ v
75
+ when TYPE_DECIMAL
76
+ v, offset = TableValueDecoder.decode_big_decimal(data, offset)
77
+ v
78
+ when TYPE_TIME
79
+ v, offset = TableValueDecoder.decode_time(data, offset)
80
+ v
81
+ when TYPE_HASH
82
+ v, offset = TableValueDecoder.decode_hash(data, offset)
83
+ v
84
+ when TYPE_BOOLEAN
85
+ v, offset = TableValueDecoder.decode_boolean(data, offset)
86
+ v
87
+ when TYPE_SIGNED_8BIT then raise NotImplementedError.new
88
+ when TYPE_SIGNED_16BIT then raise NotImplementedError.new
89
+ when TYPE_SIGNED_64BIT then raise NotImplementedError.new
90
+ when TYPE_32BIT_FLOAT then
91
+ v, offset = TableValueDecoder.decode_32bit_float(data, offset)
92
+ v
93
+ when TYPE_64BIT_FLOAT then
94
+ v, offset = TableValueDecoder.decode_64bit_float(data, offset)
95
+ v
96
+ when TYPE_VOID
97
+ nil
98
+ when TYPE_ARRAY
99
+ v, offset = TableValueDecoder.decode_array(data, offset)
100
+ v
101
+ else
102
+ raise ArgumentError, "Not a valid type: #{type.inspect}\nData: #{data.inspect}\nUnprocessed data: #{data[offset..-1].inspect}\nOffset: #{offset}\nTotal size: #{table_length}\nProcessed data: #{table.inspect}"
103
+ end
147
104
  end
148
105
 
149
106
  table
107
+ end # self.decode
108
+
109
+
110
+ def self.length(data)
111
+ data.unpack(PACK_UINT32).first
150
112
  end
151
- end
152
- end
153
- end
113
+
114
+
115
+ def self.hash_size(value)
116
+ acc = 0
117
+ value.each do |k, v|
118
+ acc += (1 + k.to_s.bytesize)
119
+ acc += TableValueEncoder.field_value_size(v)
120
+ end
121
+
122
+ acc
123
+ end # self.hash_size(value)
124
+
125
+
126
+ def self.decode_table_key(data, offset)
127
+ key_length = data.slice(offset, 1).unpack(PACK_CHAR).first
128
+ offset += 1
129
+ key = data.slice(offset, key_length)
130
+ offset += key_length
131
+
132
+ [key, offset]
133
+ end # self.decode_table_key(data, offset)
134
+
135
+
136
+
137
+ end # Table
138
+ end # Protocol
139
+ end # AMQ
@@ -0,0 +1,151 @@
1
+ # encoding: binary
2
+
3
+ require "amq/protocol/client"
4
+ require "amq/protocol/type_constants"
5
+ require "amq/protocol/table"
6
+
7
+ module AMQ
8
+ module Protocol
9
+
10
+ class TableValueDecoder
11
+
12
+ #
13
+ # Behaviors
14
+ #
15
+
16
+ include TypeConstants
17
+
18
+
19
+ #
20
+ # API
21
+ #
22
+
23
+ def self.decode_array(data, initial_offset)
24
+ array_length = data.slice(initial_offset, 4).unpack(PACK_UINT32).first
25
+
26
+ ary = Array.new
27
+ offset = initial_offset + 4
28
+
29
+ while offset <= (initial_offset + array_length)
30
+ type, offset = decode_value_type(data, offset)
31
+
32
+ i = case type
33
+ when TYPE_STRING
34
+ v, offset = decode_string(data, offset)
35
+ v
36
+ when TYPE_INTEGER
37
+ v, offset = decode_integer(data, offset)
38
+ v
39
+ when TYPE_DECIMAL
40
+ v, offset = decode_big_decimal(data, offset)
41
+ v
42
+ when TYPE_TIME
43
+ v, offset = decode_time(data, offset)
44
+ v
45
+ when TYPE_HASH
46
+ v, offset = decode_hash(data, offset)
47
+ v
48
+ when TYPE_BOOLEAN
49
+ v, offset = decode_boolean(data, offset)
50
+ v
51
+ when TYPE_SIGNED_8BIT then raise NotImplementedError.new
52
+ when TYPE_SIGNED_16BIT then raise NotImplementedError.new
53
+ when TYPE_SIGNED_64BIT then raise NotImplementedError.new
54
+ when TYPE_32BIT_FLOAT then
55
+ v, offset = decode_32bit_float(data, offset)
56
+ v
57
+ when TYPE_64BIT_FLOAT then
58
+ v, offset = decode_64bit_float(data, offset)
59
+ v
60
+ when TYPE_VOID
61
+ nil
62
+ when TYPE_ARRAY
63
+ v, offset = TableValueDecoder.decode_array(data, offset)
64
+ v
65
+ else
66
+ raise ArgumentError.new("unsupported type: #{type.inspect}")
67
+ end
68
+
69
+ ary << i
70
+ end
71
+
72
+
73
+ [ary, initial_offset + array_length + 4]
74
+ end # self.decode_array(data, initial_offset)
75
+
76
+
77
+ def self.decode_string(data, offset)
78
+ length = data.slice(offset, 4).unpack(PACK_UINT32).first
79
+ offset += 4
80
+ v = data.slice(offset, length)
81
+ offset += length
82
+
83
+ [v, offset]
84
+ end # self.decode_string(data, offset)
85
+
86
+
87
+ def self.decode_integer(data, offset)
88
+ v = data.slice(offset, 4).unpack(PACK_UINT32).first
89
+ offset += 4
90
+
91
+ [v, offset]
92
+ end # self.decode_integer(data, offset)
93
+
94
+
95
+ def self.decode_big_decimal(data, offset)
96
+ decimals, raw = data.slice(offset, 5).unpack(PACK_UCHAR_UINT32)
97
+ offset += 5
98
+ v = BigDecimal.new(raw.to_s) * (BigDecimal.new(TEN) ** -decimals)
99
+
100
+ [v, offset]
101
+ end # self.decode_big_decimal(data, offset)
102
+
103
+
104
+ def self.decode_time(data, offset)
105
+ timestamp = data.slice(offset, 8).unpack(PACK_UINT32_X2).last
106
+ v = Time.at(timestamp)
107
+ offset += 8
108
+
109
+ [v, offset]
110
+ end # self.decode_time(data, offset)
111
+
112
+
113
+ def self.decode_boolean(data, offset)
114
+ integer = data.slice(offset, 2).unpack(PACK_CHAR).first # 0 or 1
115
+ offset += 1
116
+ [(integer == 1), offset]
117
+ end # self.decode_boolean(data, offset)
118
+
119
+
120
+ def self.decode_32bit_float(data, offset)
121
+ v = data.slice(offset, 4).unpack(PACK_32BIT_FLOAT).first
122
+ offset += 4
123
+
124
+ [v, offset]
125
+ end # self.decode_32bit_float(data, offset)
126
+
127
+
128
+ def self.decode_64bit_float(data, offset)
129
+ v = data.slice(offset, 8).unpack(PACK_64BIT_FLOAT).first
130
+ offset += 8
131
+
132
+ [v, offset]
133
+ end # self.decode_64bit_float(data, offset)
134
+
135
+
136
+ def self.decode_value_type(data, offset)
137
+ [data.slice(offset, 1), offset + 1]
138
+ end # self.decode_value_type(data, offset)
139
+
140
+
141
+
142
+ def self.decode_hash(data, offset)
143
+ length = data.slice(offset, 4).unpack(PACK_UINT32).first
144
+ v = Table.decode(data.slice(offset, length + 4))
145
+ offset += 4 + length
146
+
147
+ [v, offset]
148
+ end # self.decode_hash(data, offset)
149
+ end # TableValueDecoder
150
+ end # Protocol
151
+ end # AMQ
@@ -0,0 +1,117 @@
1
+ # encoding: binary
2
+
3
+ require "amq/protocol/client"
4
+ require "amq/protocol/type_constants"
5
+ require "amq/protocol/table"
6
+
7
+ module AMQ
8
+ module Protocol
9
+
10
+ class TableValueEncoder
11
+
12
+ #
13
+ # Behaviors
14
+ #
15
+
16
+ include TypeConstants
17
+
18
+ #
19
+ # API
20
+ #
21
+
22
+ def self.encode(value)
23
+ accumulator = String.new
24
+
25
+ case value
26
+ when String then
27
+ accumulator << TYPE_STRING
28
+ accumulator << [value.bytesize].pack(PACK_UINT32)
29
+ accumulator << value
30
+ when Symbol then
31
+ v = value.to_s
32
+ accumulator << TYPE_STRING
33
+ accumulator << [v.bytesize].pack(PACK_UINT32)
34
+ accumulator << v
35
+ when Integer then
36
+ accumulator << TYPE_INTEGER
37
+ accumulator << [value].pack(PACK_UINT32)
38
+ when Float then
39
+ accumulator << TYPE_64BIT_FLOAT
40
+ accumulator << [value].pack(PACK_64BIT_FLOAT)
41
+ when true, false then
42
+ accumulator << TYPE_BOOLEAN
43
+ accumulator << (value ? BOOLEAN_TRUE : BOOLEAN_FALSE)
44
+ when Time then
45
+ accumulator << TYPE_TIME
46
+ accumulator << [value.to_i].pack(PACK_INT64).reverse # FIXME: there has to be a more efficient way
47
+ when nil then
48
+ accumulator << TYPE_VOID
49
+ when Array then
50
+ accumulator << TYPE_ARRAY
51
+ accumulator << [self.array_size(value)].pack(PACK_UINT32)
52
+
53
+ value.each { |v| accumulator << self.encode(v) }
54
+ when Hash then
55
+ accumulator << TYPE_HASH
56
+ accumulator << AMQ::Protocol::Table.encode(value)
57
+ else
58
+ # We don't want to require these libraries.
59
+ if defined?(BigDecimal) && value.is_a?(BigDecimal)
60
+ accumulator << TYPE_DECIMAL
61
+ if value.exponent < 0
62
+ decimals = -value.exponent
63
+ raw = (value * (decimals ** 10)).to_i
64
+ accumulator << [decimals + 1, raw].pack(PACK_UCHAR_UINT32) # somewhat like floating point
65
+ else
66
+ # per spec, the "decimals" octet is unsigned (!)
67
+ accumulator << [0, value.to_i].pack(PACK_UCHAR_UINT32)
68
+ end
69
+ else
70
+ raise ArgumentError.new("Unsupported value #{value.inspect} of type #{value.class.name}")
71
+ end # if
72
+ end # case
73
+
74
+ accumulator
75
+ end # self.encode(value)
76
+
77
+
78
+
79
+
80
+ def self.field_value_size(value)
81
+ # the type tag takes 1 byte
82
+ acc = 1
83
+
84
+ case value
85
+ when String then
86
+ acc += (value.bytesize + 4)
87
+ when Integer then
88
+ acc += 4
89
+ when Float then
90
+ acc += 8
91
+ when Time, DateTime then
92
+ acc += 8
93
+ when true, false then
94
+ acc += 1
95
+ when nil then
96
+ # nothing, type tag alone is enough
97
+ when Hash then
98
+ acc += (4 + Table.hash_size(value))
99
+ when Array then
100
+ acc += (4 + self.array_size(value))
101
+ end
102
+
103
+ acc
104
+ end # self.field_value_size(value)
105
+
106
+
107
+ def self.array_size(value)
108
+ acc = 0
109
+ value.each { |v| acc += self.field_value_size(v) }
110
+
111
+ acc
112
+ end # self.array_size(value)
113
+
114
+ end # TableValueEncoder
115
+
116
+ end # Protocol
117
+ end # AMQ
@@ -0,0 +1,26 @@
1
+ # encoding: binary
2
+
3
+ module AMQ
4
+ module Protocol
5
+ module TypeConstants
6
+ TYPE_STRING = 'S'.freeze
7
+ TYPE_INTEGER = 'I'.freeze
8
+ TYPE_HASH = 'F'.freeze
9
+ TYPE_TIME = 'T'.freeze
10
+ TYPE_DECIMAL = 'D'.freeze
11
+ TYPE_BOOLEAN = 't'.freeze
12
+ TYPE_SIGNED_8BIT = 'b'.freeze
13
+ TYPE_SIGNED_16BIT = 's'.freeze
14
+ TYPE_SIGNED_64BIT = 'l'.freeze
15
+ TYPE_32BIT_FLOAT = 'f'.freeze
16
+ TYPE_64BIT_FLOAT = 'd'.freeze
17
+ TYPE_VOID = 'V'.freeze
18
+ TYPE_BYTE_ARRAY = 'x'.freeze
19
+ TYPE_ARRAY = 'A'.freeze
20
+ TEN = '10'.freeze
21
+
22
+ BOOLEAN_TRUE = "\x01".freeze
23
+ BOOLEAN_FALSE = "\x00".freeze
24
+ end # TypeConstants
25
+ end # Protocol
26
+ end # AMQ
@@ -1,5 +1,5 @@
1
1
  module AMQ
2
2
  module Protocol
3
- VERSION = "0.7.0.beta2"
3
+ VERSION = "0.7.0"
4
4
  end # Protocol
5
5
  end # AMQ
@@ -84,7 +84,7 @@ module AMQ
84
84
  end # DATA.each
85
85
 
86
86
 
87
- it "is capable of decoding booleans" do
87
+ it "is capable of decoding boolean table values" do
88
88
  input1 = { "boolval" => true }
89
89
  Table.decode(Table.encode(input1)).should == input1
90
90
 
@@ -95,6 +95,76 @@ module AMQ
95
95
 
96
96
 
97
97
 
98
+ it "is capable of decoding string table values" do
99
+ input = { "stringvalue" => "string" }
100
+ Table.decode(Table.encode(input)).should == input
101
+ end
102
+
103
+
104
+
105
+ it "is capable of decoding integer table values" do
106
+ input = { "intvalue" => 10 }
107
+ Table.decode(Table.encode(input)).should == input
108
+ end
109
+
110
+
111
+
112
+ it "is capable of decoding long table values" do
113
+ input = { "longvalue" => 912598613 }
114
+ Table.decode(Table.encode(input)).should == input
115
+ end
116
+
117
+
118
+
119
+ it "is capable of decoding float table values" do
120
+ input = { "floatvalue" => 100.0 }
121
+ Table.decode(Table.encode(input)).should == input
122
+ end
123
+
124
+
125
+
126
+ it "is capable of decoding time table values" do
127
+ input = { "intvalue" => Time.parse("2011-07-14 01:17:46 +0400") }
128
+ Table.decode(Table.encode(input)).should == input
129
+ end
130
+
131
+
132
+
133
+ it "is capable of decoding empty hash table values" do
134
+ input = { "hashvalue" => Hash.new }
135
+ Table.decode(Table.encode(input)).should == input
136
+ end
137
+
138
+
139
+
140
+ xit "is capable of decoding empty array table values" do
141
+ input = { "arrayvalue" => Array.new }
142
+ Table.decode(Table.encode(input)).should == input
143
+ end
144
+
145
+
146
+ xit "is capable of decoding single string value array table values" do
147
+ input = { "arrayvalue" => ["amq-protocol"] }
148
+ Table.decode(Table.encode(input)).should == input
149
+ end
150
+
151
+
152
+
153
+ it "is capable of decoding simple nested hash table values" do
154
+ input = { "hashvalue" => { "a" => "b" } }
155
+ Table.decode(Table.encode(input)).should == input
156
+ end
157
+
158
+
159
+
160
+ it "is capable of decoding nil table values" do
161
+ input = { "nil" => nil }
162
+ Table.decode(Table.encode(input)).should == input
163
+ end
164
+
165
+
166
+
167
+
98
168
  it "is capable of decoding tables" do
99
169
  input = {
100
170
  "boolval" => true,
@@ -108,6 +178,56 @@ module AMQ
108
178
  Table.decode(Table.encode(input)).should == input
109
179
  end
110
180
 
181
+
182
+
183
+ it "is capable of decoding deeply nested tables" do
184
+ input = {
185
+ "hashval" => {
186
+ "protocol" => {
187
+ "name" => "AMQP",
188
+ "major" => 0,
189
+ "minor" => "9",
190
+ "rev" => 1.0,
191
+ "spec" => {
192
+ "url" => "http://bit.ly/hw2ELX",
193
+ "utf8" => "à bientôt"
194
+ }
195
+ },
196
+ "true" => true,
197
+ "false" => false,
198
+ "nil" => nil
199
+ }
200
+ }
201
+ Table.decode(Table.encode(input)).should == input
202
+ end
203
+
204
+
205
+
206
+ it "is capable of decoding array values in tables" do
207
+ input1 = {
208
+ "arrayval1" => [198, 3, 77, 8.0, ["inner", "array", { "oh" => "well", "it" => "should work", "3" => 6 }], "two", { "a" => "value", "is" => nil }],
209
+ "arrayval2" => [198, 3, 77, "two", { "a" => "value", "is" => nil }, 8.0, ["inner", "array", { "oh" => "well", "it" => "should work", "3" => 6 }]]
210
+ }
211
+ Table.decode(Table.encode(input1)).should == input1
212
+
213
+
214
+ input2 = {
215
+ "coordinates" => {
216
+ "latitude" => 59.35,
217
+ "longitude" => 18.066667
218
+ },
219
+ "time" => @now,
220
+ "participants" => 11,
221
+ "venue" => "Stockholm",
222
+ "true_field" => true,
223
+ "false_field" => false,
224
+ "nil_field" => nil,
225
+ "ary_field" => ["one", 2.0, 3]
226
+ }
227
+
228
+ Table.decode(Table.encode(input2)).should == input2
229
+ end
230
+
111
231
  end # describe
112
232
  end
113
233
  end
@@ -0,0 +1,66 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../../spec_helper', __FILE__)
3
+
4
+ require 'time'
5
+ require "amq/protocol/table_value_decoder"
6
+
7
+ module AMQ
8
+ module Protocol
9
+ describe TableValueDecoder do
10
+
11
+ it "is capable of decoding basic arrays TableValueEncoder encodes" do
12
+ input1 = [1, 2, 3]
13
+
14
+ value, offset = described_class.decode_array(TableValueEncoder.encode(input1), 1)
15
+ value.size.should == 3
16
+ value.first.should == 1
17
+ value.should == input1
18
+
19
+
20
+
21
+ input2 = ["one", 2, "three"]
22
+
23
+ value, offset = described_class.decode_array(TableValueEncoder.encode(input2), 1)
24
+ value.size.should == 3
25
+ value.first.should == "one"
26
+ value.should == input2
27
+
28
+
29
+
30
+ input3 = ["one", 2, "three", 4.0, 5000000.0]
31
+
32
+ value, offset = described_class.decode_array(TableValueEncoder.encode(input3), 1)
33
+ value.size.should == 5
34
+ value.last.should == 5000000.0
35
+ value.should == input3
36
+ end
37
+
38
+
39
+
40
+ it "is capable of decoding arrays TableValueEncoder encodes" do
41
+ input1 = [{ "one" => 2 }, 3]
42
+ data1 = TableValueEncoder.encode(input1)
43
+
44
+ # puts(TableValueEncoder.encode({ "one" => 2 }).inspect)
45
+ # puts(TableValueEncoder.encode(input1).inspect)
46
+
47
+
48
+ value, offset = described_class.decode_array(data1, 1)
49
+ value.size.should == 2
50
+ value.first.should == Hash["one" => 2]
51
+ value.should == input1
52
+
53
+
54
+
55
+ input2 = ["one", 2, { "three" => { "four" => 5.0 } }]
56
+
57
+ value, offset = described_class.decode_array(TableValueEncoder.encode(input2), 1)
58
+ value.size.should == 3
59
+ value.last["three"]["four"].should == 5.0
60
+ value.should == input2
61
+ end
62
+
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,138 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../../spec_helper', __FILE__)
3
+
4
+ require 'time'
5
+ require "amq/protocol/table_value_encoder"
6
+
7
+ module AMQ
8
+ module Protocol
9
+ describe TableValueEncoder do
10
+
11
+
12
+ it "calculates size of string field values" do
13
+ described_class.field_value_size("amqp").should == 9
14
+ described_class.encode("amqp").bytesize.should == 9
15
+
16
+ described_class.field_value_size("amq-protocol").should == 17
17
+ described_class.encode("amq-protocol").bytesize.should == 17
18
+
19
+ described_class.field_value_size("à bientôt").should == 16
20
+ described_class.encode("à bientôt").bytesize.should == 16
21
+ end
22
+
23
+ it "calculates size of integer field values" do
24
+ described_class.field_value_size(10).should == 5
25
+ described_class.encode(10).bytesize.should == 5
26
+ end
27
+
28
+ it "calculates size of float field values (considering them to be 64-bit)" do
29
+ described_class.field_value_size(10.0).should == 9
30
+ described_class.encode(10.0).bytesize.should == 9
31
+
32
+ described_class.field_value_size(120000.0).should == 9
33
+ described_class.encode(120000.0).bytesize.should == 9
34
+ end
35
+
36
+ it "calculates size of boolean field values" do
37
+ described_class.field_value_size(true).should == 2
38
+ described_class.encode(true).bytesize.should == 2
39
+
40
+ described_class.field_value_size(false).should == 2
41
+ described_class.encode(false).bytesize.should == 2
42
+ end
43
+
44
+ it "calculates size of void field values" do
45
+ described_class.field_value_size(nil).should == 1
46
+ described_class.encode(nil).bytesize.should == 1
47
+ end
48
+
49
+ it "calculates size of time field values" do
50
+ t = Time.parse("2011-07-14 01:17:46 +0400")
51
+
52
+ described_class.field_value_size(t).should == 9
53
+ described_class.encode(t).bytesize.should == 9
54
+ end
55
+
56
+
57
+ it "calculates size of basic table field values" do
58
+ input1 = { "key" => "value" }
59
+ described_class.field_value_size(input1).should == 19
60
+ described_class.encode(input1).bytesize.should == 19
61
+
62
+
63
+ input2 = { "intval" => 1 }
64
+ described_class.field_value_size(input2).should == 17
65
+ described_class.encode(input2).bytesize.should == 17
66
+
67
+
68
+ input3 = { "intval" => 1, "key" => "value" }
69
+ described_class.field_value_size(input3).should == 31
70
+ described_class.encode(input3).bytesize.should == 31
71
+ end
72
+
73
+
74
+ it "calculates size of table field values" do
75
+ input1 = {
76
+ "hashval" => {
77
+ "protocol" => {
78
+ "name" => "AMQP",
79
+ "major" => 0,
80
+ "minor" => "9",
81
+ "rev" => 1.0,
82
+ "spec" => {
83
+ "url" => "http://bit.ly/hw2ELX",
84
+ "utf8" => one_point_eight? ? "à bientôt" : "à bientôt".force_encoding(::Encoding::ASCII_8BIT)
85
+ }
86
+ },
87
+ "true" => true,
88
+ "false" => false,
89
+ "nil" => nil
90
+ }
91
+ }
92
+
93
+ described_class.field_value_size(input1).should == 162
94
+ # puts(described_class.encode(input1).inspect)
95
+ described_class.encode(input1).bytesize.should == 162
96
+
97
+
98
+
99
+ input2 = {
100
+ "boolval" => true,
101
+ "intval" => 1,
102
+ "strval" => "Test",
103
+ "timestampval" => Time.parse("2011-07-14 01:17:46 +0400"),
104
+ "floatval" => 3.14,
105
+ "longval" => 912598613,
106
+ "hashval" => { "protocol" => "AMQP091", "true" => true, "false" => false, "nil" => nil }
107
+ }
108
+
109
+ described_class.field_value_size(input2).should == 150
110
+ described_class.encode(input2).bytesize.should == 150
111
+ end
112
+
113
+ it "calculates size of basic array field values" do
114
+ input1 = [1, 2, 3]
115
+
116
+ described_class.field_value_size(input1).should == 20
117
+ described_class.encode(input1).bytesize.should == 20
118
+
119
+
120
+ input2 = ["one", "two", "three"]
121
+ described_class.field_value_size(input2).should == 31
122
+ described_class.encode(input2).bytesize.should == 31
123
+
124
+
125
+ input3 = ["one", 2, "three"]
126
+ described_class.field_value_size(input3).should == 28
127
+ described_class.encode(input3).bytesize.should == 28
128
+
129
+
130
+ input4 = ["one", 2, "three", ["four", 5, [6.0]]]
131
+ described_class.field_value_size(input4).should == 61
132
+ described_class.encode(input4).bytesize.should == 61
133
+ end
134
+
135
+
136
+ end # TableValueEncoder
137
+ end # Protocol
138
+ end # AMQ
metadata CHANGED
@@ -1,15 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amq-protocol
3
3
  version: !ruby/object:Gem::Version
4
- hash: 62196439
5
- prerelease: 6
4
+ hash: 3
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 7
9
9
  - 0
10
- - beta
11
- - 2
12
- version: 0.7.0.beta2
10
+ version: 0.7.0
13
11
  platform: ruby
14
12
  authors:
15
13
  - Jakub Stastny
@@ -20,7 +18,8 @@ autorequire:
20
18
  bindir: bin
21
19
  cert_chain: []
22
20
 
23
- date: 2011-07-14 00:00:00 Z
21
+ date: 2011-07-17 00:00:00 +04:00
22
+ default_executable:
24
23
  dependencies: []
25
24
 
26
25
  description: " amq-protocol is an AMQP 0.9.1 serialization library for Ruby. It is not an\n AMQP client: amq-protocol only handles serialization and deserialization.\n If you want to write your own AMQP client, this gem can help you with that.\n"
@@ -53,6 +52,9 @@ files:
53
52
  - lib/amq/protocol/client.rb
54
53
  - lib/amq/protocol/frame.rb
55
54
  - lib/amq/protocol/table.rb
55
+ - lib/amq/protocol/table_value_decoder.rb
56
+ - lib/amq/protocol/table_value_encoder.rb
57
+ - lib/amq/protocol/type_constants.rb
56
58
  - lib/amq/protocol/version.rb
57
59
  - post-processing.rb
58
60
  - protocol.rb.pytemplate
@@ -67,9 +69,12 @@ files:
67
69
  - spec/amq/protocol/queue_spec.rb
68
70
  - spec/amq/protocol/table_spec.rb
69
71
  - spec/amq/protocol/tx_spec.rb
72
+ - spec/amq/protocol/value_decoder_spec.rb
73
+ - spec/amq/protocol/value_encoder_spec.rb
70
74
  - spec/amq/protocol_spec.rb
71
75
  - spec/spec_helper.rb
72
76
  - tasks.rb
77
+ has_rdoc: true
73
78
  homepage: http://github.com/ruby-amqp/amq-protocol
74
79
  licenses: []
75
80
 
@@ -90,18 +95,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
95
  required_rubygems_version: !ruby/object:Gem::Requirement
91
96
  none: false
92
97
  requirements:
93
- - - ">"
98
+ - - ">="
94
99
  - !ruby/object:Gem::Version
95
- hash: 25
100
+ hash: 3
96
101
  segments:
97
- - 1
98
- - 3
99
- - 1
100
- version: 1.3.1
102
+ - 0
103
+ version: "0"
101
104
  requirements: []
102
105
 
103
106
  rubyforge_project: amq-protocol
104
- rubygems_version: 1.8.5
107
+ rubygems_version: 1.6.2
105
108
  signing_key:
106
109
  specification_version: 3
107
110
  summary: AMQP 0.9.1 encoder & decoder.