cql-rb 1.0.0.pre0

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.
@@ -0,0 +1,109 @@
1
+ # encoding: utf-8
2
+
3
+ module Cql
4
+ module Protocol
5
+ module Encoding
6
+ extend self
7
+
8
+ def write_int(buffer, n)
9
+ buffer << [n].pack(Formats::INT_FORMAT)
10
+ end
11
+
12
+ def write_short(buffer, n)
13
+ buffer << [n].pack(Formats::SHORT_FORMAT)
14
+ end
15
+
16
+ def write_string(buffer, str)
17
+ buffer << [str.length].pack(Formats::SHORT_FORMAT)
18
+ buffer << str
19
+ buffer
20
+ end
21
+
22
+ def write_long_string(buffer, str)
23
+ buffer << [str.length].pack(Formats::INT_FORMAT)
24
+ buffer << str
25
+ buffer
26
+ end
27
+
28
+ def write_uuid(buffer, uuid)
29
+ write_varint(buffer, uuid.value)
30
+ end
31
+
32
+ def write_string_list(buffer, strs)
33
+ buffer << [strs.size].pack(Formats::SHORT_FORMAT)
34
+ strs.each do |str|
35
+ write_string(buffer, str)
36
+ end
37
+ buffer
38
+ end
39
+
40
+ def write_bytes(buffer, bytes)
41
+ if bytes
42
+ write_int(buffer, bytes.length)
43
+ buffer << bytes
44
+ else
45
+ write_int(buffer, -1)
46
+ end
47
+ buffer
48
+ end
49
+
50
+ def write_short_bytes(buffer, bytes)
51
+ if bytes
52
+ write_short(buffer, bytes.length)
53
+ buffer << bytes
54
+ else
55
+ write_short(buffer, -1)
56
+ end
57
+ buffer
58
+ end
59
+
60
+ def write_consistency(buffer, consistency)
61
+ index = CONSISTENCIES.index(consistency)
62
+ raise EncodingError, %(Unknown consistency "#{consistency}") unless index
63
+ write_short(buffer, index)
64
+ end
65
+
66
+ def write_string_map(buffer, map)
67
+ buffer << [map.size].pack(Formats::SHORT_FORMAT)
68
+ map.each do |key, value|
69
+ write_string(buffer, key)
70
+ write_string(buffer, value)
71
+ end
72
+ buffer
73
+ end
74
+
75
+ def write_long(buffer, n)
76
+ top = n >> 32
77
+ bottom = n & 0xffffffff
78
+ write_int(buffer, top)
79
+ write_int(buffer, bottom)
80
+ end
81
+
82
+ def write_varint(buffer, n)
83
+ num = n
84
+ bytes = []
85
+ until num == 0 || num == -1
86
+ bytes << (num & 0xff)
87
+ num = num >> 8
88
+ end
89
+ buffer << bytes.reverse.pack(Formats::BYTES_FORMAT)
90
+ end
91
+
92
+ def write_decimal(buffer, n)
93
+ sign, number_string, _, size = n.split
94
+ num = number_string.to_i
95
+ raw = write_varint('', num)
96
+ write_int(buffer, number_string.length - size)
97
+ buffer << raw
98
+ end
99
+
100
+ def write_double(buffer, n)
101
+ buffer << [n].pack(Formats::DOUBLE_FORMAT)
102
+ end
103
+
104
+ def write_float(buffer, n)
105
+ buffer << [n].pack(Formats::FLOAT_FORMAT)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,228 @@
1
+ # encoding: utf-8
2
+
3
+ module Cql
4
+ module Protocol
5
+ class RequestFrame
6
+ def initialize(body, stream_id=0)
7
+ @body = body
8
+ @stream_id = stream_id
9
+ raise InvalidStreamIdError, 'The stream ID must be between 0 and 127' unless 0 <= @stream_id && @stream_id < 128
10
+ end
11
+
12
+ def write(io)
13
+ buffer = [1, 0, @stream_id, @body.opcode, 0].pack(Formats::HEADER_FORMAT)
14
+ buffer = @body.write(buffer)
15
+ buffer[4, 4] = [buffer.length - 8].pack(Formats::INT_FORMAT)
16
+ io << buffer
17
+ end
18
+ end
19
+
20
+ class RequestBody
21
+ include Encoding
22
+
23
+ attr_reader :opcode
24
+
25
+ def initialize(opcode)
26
+ @opcode = opcode
27
+ end
28
+ end
29
+
30
+ class StartupRequest < RequestBody
31
+ def initialize(cql_version='3.0.0', compression=nil)
32
+ super(1)
33
+ @arguments = {CQL_VERSION => cql_version}
34
+ @arguments[COMPRESSION] = compression if compression
35
+ end
36
+
37
+ def write(io)
38
+ write_string_map(io, @arguments)
39
+ io
40
+ end
41
+
42
+ def to_s
43
+ %(STARTUP #@arguments)
44
+ end
45
+
46
+ private
47
+
48
+ CQL_VERSION = 'CQL_VERSION'.freeze
49
+ COMPRESSION = 'COMPRESSION'.freeze
50
+ end
51
+
52
+ class OptionsRequest < RequestBody
53
+ def initialize
54
+ super(5)
55
+ end
56
+
57
+ def write(io)
58
+ io
59
+ end
60
+
61
+ def to_s
62
+ %(OPTIONS)
63
+ end
64
+ end
65
+
66
+ class RegisterRequest < RequestBody
67
+ def initialize(*events)
68
+ super(11)
69
+ @events = events
70
+ end
71
+
72
+ def write(io)
73
+ write_string_list(io, @events)
74
+ end
75
+
76
+ def to_s
77
+ %(REGISTER #@events)
78
+ end
79
+ end
80
+
81
+ class QueryRequest < RequestBody
82
+ attr_reader :cql, :consistency
83
+
84
+ def initialize(cql, consistency)
85
+ super(7)
86
+ @cql = cql
87
+ @consistency = consistency
88
+ end
89
+
90
+ def write(io)
91
+ write_long_string(io, @cql)
92
+ write_consistency(io, @consistency)
93
+ end
94
+
95
+ def to_s
96
+ %(QUERY "#@cql" #{@consistency.to_s.upcase})
97
+ end
98
+
99
+ def eql?(rq)
100
+ self.class === rq && rq.cql.eql?(self.cql) && rq.consistency.eql?(self.consistency)
101
+ end
102
+ alias_method :==, :eql?
103
+
104
+ def hash
105
+ @h ||= (@cql.hash * 31) ^ consistency.hash
106
+ end
107
+ end
108
+
109
+ class PrepareRequest < RequestBody
110
+ attr_reader :cql
111
+
112
+ def initialize(cql)
113
+ super(9)
114
+ @cql = cql
115
+ end
116
+
117
+ def write(io)
118
+ write_long_string(io, @cql)
119
+ end
120
+
121
+ def to_s
122
+ %(PREPARE "#@cql")
123
+ end
124
+
125
+ def eql?(rq)
126
+ self.class === rq && rq.cql == self.cql
127
+ end
128
+ alias_method :==, :eql?
129
+
130
+ def hash
131
+ @h ||= @cql.hash
132
+ end
133
+ end
134
+
135
+ class ExecuteRequest < RequestBody
136
+ attr_reader :id, :metadata, :values, :consistency
137
+
138
+ def initialize(id, metadata, values, consistency)
139
+ super(10)
140
+ raise ArgumentError, "Metadata for #{metadata.size} columns, but #{values.size} values given" if metadata.size != values.size
141
+ @id = id
142
+ @metadata = metadata
143
+ @values = values
144
+ @consistency = consistency
145
+ end
146
+
147
+ def write(io)
148
+ write_short_bytes(io, @id)
149
+ write_short(io, @metadata.size)
150
+ @metadata.each_with_index do |(_, _, _, type), index|
151
+ write_value(io, @values[index], type)
152
+ end
153
+ write_consistency(io, @consistency)
154
+ end
155
+
156
+ def to_s
157
+ id = @id.each_byte.map { |x| x.to_s(16) }.join('')
158
+ %(EXECUTE #{id} #@values #{@consistency.to_s.upcase})
159
+ end
160
+
161
+ def eql?(rq)
162
+ self.class === rq && rq.id == self.id && rq.metadata == self.metadata && rq.values == self.values && rq.consistency == self.consistency
163
+ end
164
+ alias_method :==, :eql?
165
+
166
+ def hash
167
+ @h ||= begin
168
+ h = 0
169
+ h = ((h & 33554431) * 31) ^ @id.hash
170
+ h = ((h & 33554431) * 31) ^ @metadata.hash
171
+ h = ((h & 33554431) * 31) ^ @values.hash
172
+ h = ((h & 33554431) * 31) ^ @consistency.hash
173
+ h
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def write_value(io, value, type)
180
+ case type
181
+ when :ascii
182
+ write_bytes(io, value.encode(::Encoding::ASCII))
183
+ when :bigint
184
+ write_int(io, 8)
185
+ write_long(io, value)
186
+ when :blob
187
+ write_bytes(io, value.encode(::Encoding::BINARY))
188
+ when :boolean
189
+ write_int(io, 1)
190
+ io << (value ? Constants::TRUE_BYTE : Constants::FALSE_BYTE)
191
+ when :decimal
192
+ raw = write_decimal('', value)
193
+ write_int(io, raw.size)
194
+ io << raw
195
+ when :double
196
+ write_int(io, 8)
197
+ write_double(io, value)
198
+ when :float
199
+ write_int(io, 4)
200
+ write_float(io, value)
201
+ when :inet
202
+ write_int(io, value.ipv6? ? 16 : 4)
203
+ io << value.hton
204
+ when :int
205
+ write_int(io, 4)
206
+ write_int(io, value)
207
+ when :text, :varchar
208
+ write_bytes(io, value.encode(::Encoding::UTF_8))
209
+ when :timestamp
210
+ ms = (value.to_f * 1000).to_i
211
+ write_int(io, 8)
212
+ write_long(io, ms)
213
+ when :timeuuid, :uuid
214
+ write_int(io, 16)
215
+ write_uuid(io, value)
216
+ when :varint
217
+ raw = write_varint('', value)
218
+ write_int(io, raw.length)
219
+ io << raw
220
+ else
221
+ raise UnsupportedColumnTypeError, %(Unsupported column type: #{type})
222
+ end
223
+ rescue TypeError => e
224
+ raise TypeError, %("#{value}" cannot be encoded as #{type.to_s.upcase}: #{e.message}), e.backtrace
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,551 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ipaddr'
4
+ require 'bigdecimal'
5
+ require 'set'
6
+
7
+
8
+ module Cql
9
+ module Protocol
10
+ class ResponseFrame
11
+ def initialize(buffer='')
12
+ @headers = FrameHeaders.new(buffer)
13
+ check_complete!
14
+ end
15
+
16
+ def stream_id
17
+ @headers && @headers.stream_id
18
+ end
19
+
20
+ def header_length
21
+ 8
22
+ end
23
+
24
+ def body_length
25
+ @headers && @headers.length
26
+ end
27
+
28
+ def body
29
+ @body.response
30
+ end
31
+
32
+ def complete?
33
+ @body && @body.complete?
34
+ end
35
+
36
+ def <<(str)
37
+ if @body
38
+ @body << str
39
+ else
40
+ @headers << str
41
+ check_complete!
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def check_complete!
48
+ if @headers.complete?
49
+ @body = create_body
50
+ end
51
+ end
52
+
53
+ def create_body
54
+ body_type = begin
55
+ case @headers.opcode
56
+ when 0x00 then ErrorResponse
57
+ when 0x02 then ReadyResponse
58
+ when 0x06 then SupportedResponse
59
+ when 0x08 then ResultResponse
60
+ when 0x0c then EventResponse
61
+ else
62
+ raise UnsupportedOperationError, "The operation #{@headers.opcode} is not supported"
63
+ end
64
+ end
65
+ FrameBody.new(@headers.buffer, @headers.length, body_type)
66
+ end
67
+
68
+ class FrameHeaders
69
+ attr_reader :buffer, :protocol_version, :stream_id, :opcode, :length
70
+
71
+ def initialize(buffer)
72
+ @buffer = buffer
73
+ check_complete!
74
+ end
75
+
76
+ def <<(str)
77
+ @buffer << str
78
+ check_complete!
79
+ end
80
+
81
+ def complete?
82
+ !!@protocol_version
83
+ end
84
+
85
+ private
86
+
87
+ def check_complete!
88
+ if @buffer.length >= 8
89
+ @protocol_version, @flags, @stream_id, @opcode, @length = @buffer.slice!(0, 8).unpack(Formats::HEADER_FORMAT)
90
+ raise UnsupportedFrameTypeError, 'Request frames are not supported' if @protocol_version > 0
91
+ @protocol_version &= 0x7f
92
+ end
93
+ end
94
+ end
95
+
96
+ class FrameBody
97
+ attr_reader :response, :buffer
98
+
99
+ def initialize(buffer, length, type)
100
+ @buffer = buffer
101
+ @length = length
102
+ @type = type
103
+ check_complete!
104
+ end
105
+
106
+ def <<(str)
107
+ @buffer << str
108
+ check_complete!
109
+ end
110
+
111
+ def complete?
112
+ !!@response
113
+ end
114
+
115
+ private
116
+
117
+ def check_complete!
118
+ if @buffer.length >= @length
119
+ extra_length = @buffer.length - @length
120
+ @response = @type.decode!(@buffer)
121
+ if @buffer.length > extra_length
122
+ @buffer.slice!(0, @buffer.length - extra_length)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ class ResponseBody
130
+ extend Decoding
131
+
132
+ def self.decode!(buffer)
133
+ end
134
+ end
135
+
136
+ class ErrorResponse < ResponseBody
137
+ attr_reader :code, :message
138
+
139
+ def initialize(*args)
140
+ @code, @message = args
141
+ end
142
+
143
+ def self.decode!(buffer)
144
+ code = read_int!(buffer)
145
+ message = read_string!(buffer)
146
+ case code
147
+ when 0x1000, 0x1100, 0x1200, 0x2400, 0x2500
148
+ DetailedErrorResponse.decode!(code, message, buffer)
149
+ else
150
+ new(code, message)
151
+ end
152
+ end
153
+
154
+ def to_s
155
+ %(ERROR #@code "#@message")
156
+ end
157
+ end
158
+
159
+ class DetailedErrorResponse < ErrorResponse
160
+ attr_reader :details
161
+
162
+ def initialize(code, message, details)
163
+ super(code, message)
164
+ @details = details
165
+ end
166
+
167
+ def self.decode!(code, message, buffer)
168
+ details = {}
169
+ case code
170
+ when 0x1000 # unavailable
171
+ details[:cl] = read_consistency!(buffer)
172
+ details[:required] = read_int!(buffer)
173
+ details[:alive] = read_int!(buffer)
174
+ when 0x1100 # write_timeout
175
+ details[:cl] = read_consistency!(buffer)
176
+ details[:received] = read_int!(buffer)
177
+ details[:blockfor] = read_int!(buffer)
178
+ details[:write_type] = read_string!(buffer)
179
+ when 0x1200 # read_timeout
180
+ details[:cl] = read_consistency!(buffer)
181
+ details[:received] = read_int!(buffer)
182
+ details[:blockfor] = read_int!(buffer)
183
+ details[:data_present] = read_byte!(buffer) != 0
184
+ when 0x2400 # already_exists
185
+ details[:ks] = read_string!(buffer)
186
+ details[:table] = read_string!(buffer)
187
+ when 0x2500
188
+ details[:id] = read_short_bytes!(buffer)
189
+ end
190
+ new(code, message, details)
191
+ end
192
+
193
+ def to_s
194
+ %(ERROR #@code "#@message" #@details)
195
+ end
196
+ end
197
+
198
+ class ReadyResponse < ResponseBody
199
+ def self.decode!(buffer)
200
+ new
201
+ end
202
+
203
+ def eql?(rs)
204
+ self.class === rs
205
+ end
206
+ alias_method :==, :eql?
207
+
208
+ def hash
209
+ @h ||= to_s.hash ^ 0xbadc0de
210
+ end
211
+
212
+ def to_s
213
+ 'READY'
214
+ end
215
+ end
216
+
217
+ class SupportedResponse < ResponseBody
218
+ attr_reader :options
219
+
220
+ def initialize(options)
221
+ @options = options
222
+ end
223
+
224
+ def self.decode!(buffer)
225
+ new(read_string_multimap!(buffer))
226
+ end
227
+
228
+ def to_s
229
+ %(SUPPORTED #{options})
230
+ end
231
+ end
232
+
233
+ class ResultResponse < ResponseBody
234
+ def self.decode!(buffer)
235
+ kind = read_int!(buffer)
236
+ case kind
237
+ when 0x01
238
+ VoidResultResponse.decode!(buffer)
239
+ when 0x02
240
+ RowsResultResponse.decode!(buffer)
241
+ when 0x03
242
+ SetKeyspaceResultResponse.decode!(buffer)
243
+ when 0x04
244
+ PreparedResultResponse.decode!(buffer)
245
+ when 0x05
246
+ SchemaChangeResultResponse.decode!(buffer)
247
+ else
248
+ raise UnsupportedResultKindError, %(Unsupported result kind: #{kind})
249
+ end
250
+ end
251
+
252
+ def void?
253
+ false
254
+ end
255
+ end
256
+
257
+ class VoidResultResponse < ResultResponse
258
+ def self.decode!(buffer)
259
+ new
260
+ end
261
+
262
+ def to_s
263
+ %(RESULT VOID)
264
+ end
265
+
266
+ def void?
267
+ true
268
+ end
269
+ end
270
+
271
+ class RowsResultResponse < ResultResponse
272
+ attr_reader :rows, :metadata
273
+
274
+ def initialize(*args)
275
+ @rows, @metadata = args
276
+ end
277
+
278
+ def self.decode!(buffer)
279
+ column_specs = read_metadata!(buffer)
280
+ new(read_rows!(buffer, column_specs), column_specs)
281
+ end
282
+
283
+ def to_s
284
+ %(RESULT ROWS #@metadata #@rows)
285
+ end
286
+
287
+ private
288
+
289
+ def self.read_column_type!(buffer)
290
+ id, type = read_option!(buffer) do |id, b|
291
+ case id
292
+ when 0x01 then :ascii
293
+ when 0x02 then :bigint
294
+ when 0x03 then :blob
295
+ when 0x04 then :boolean
296
+ when 0x05 then :counter
297
+ when 0x06 then :decimal
298
+ when 0x07 then :double
299
+ when 0x08 then :float
300
+ when 0x09 then :int
301
+ # when 0x0a then :text
302
+ when 0x0b then :timestamp
303
+ when 0x0c then :uuid
304
+ when 0x0d then :varchar
305
+ when 0x0e then :varint
306
+ when 0x0f then :timeuuid
307
+ when 0x10 then :inet
308
+ when 0x20
309
+ sub_type = read_column_type!(buffer)
310
+ [:list, sub_type]
311
+ when 0x21
312
+ key_type = read_column_type!(buffer)
313
+ value_type = read_column_type!(buffer)
314
+ [:map, key_type, value_type]
315
+ when 0x22
316
+ sub_type = read_column_type!(buffer)
317
+ [:set, sub_type]
318
+ else
319
+ raise UnsupportedColumnTypeError, %(Unsupported column type: #{id})
320
+ end
321
+ end
322
+ type
323
+ end
324
+
325
+ def self.read_metadata!(buffer)
326
+ flags = read_int!(buffer)
327
+ columns_count = read_int!(buffer)
328
+ if flags & 0x01 == 0x01
329
+ global_keyspace_name = read_string!(buffer)
330
+ global_table_name = read_string!(buffer)
331
+ end
332
+ column_specs = columns_count.times.map do
333
+ if global_keyspace_name
334
+ keyspace_name = global_keyspace_name
335
+ table_name = global_table_name
336
+ else
337
+ keyspace_name = read_string!(buffer)
338
+ table_name = read_string!(buffer)
339
+ end
340
+ column_name = read_string!(buffer)
341
+ type = read_column_type!(buffer)
342
+ [keyspace_name, table_name, column_name, type]
343
+ end
344
+ end
345
+
346
+ def self.convert_type(bytes, type)
347
+ return nil unless bytes
348
+ case type
349
+ when :ascii
350
+ bytes.force_encoding(::Encoding::ASCII)
351
+ when :bigint
352
+ read_long!(bytes)
353
+ when :blob
354
+ bytes
355
+ when :boolean
356
+ bytes == Constants::TRUE_BYTE
357
+ when :counter
358
+ read_long!(bytes)
359
+ when :decimal
360
+ read_decimal!(bytes)
361
+ when :double
362
+ read_double!(bytes)
363
+ when :float
364
+ read_float!(bytes)
365
+ when :int
366
+ read_int!(bytes)
367
+ when :timestamp
368
+ timestamp = read_long!(bytes)
369
+ Time.at(timestamp/1000.0)
370
+ when :varchar, :text
371
+ bytes.force_encoding(::Encoding::UTF_8)
372
+ when :varint
373
+ read_varint!(bytes)
374
+ when :timeuuid, :uuid
375
+ read_uuid!(bytes)
376
+ when :inet
377
+ IPAddr.new_ntoh(bytes)
378
+ when Array
379
+ case type.first
380
+ when :list
381
+ list = []
382
+ size = read_short!(bytes)
383
+ size.times do
384
+ list << convert_type(read_short_bytes!(bytes), type.last)
385
+ end
386
+ list
387
+ when :map
388
+ map = {}
389
+ size = read_short!(bytes)
390
+ size.times do
391
+ key = convert_type(read_short_bytes!(bytes), type[1])
392
+ value = convert_type(read_short_bytes!(bytes), type[2])
393
+ map[key] = value
394
+ end
395
+ map
396
+ when :set
397
+ set = Set.new
398
+ size = read_short!(bytes)
399
+ size.times do
400
+ set << convert_type(read_short_bytes!(bytes), type.last)
401
+ end
402
+ set
403
+ end
404
+ end
405
+ end
406
+
407
+ def self.read_rows!(buffer, column_specs)
408
+ rows_count = read_int!(buffer)
409
+ rows = []
410
+ rows_count.times do |row_index|
411
+ row = {}
412
+ column_specs.each do |column_spec|
413
+ column_value = read_bytes!(buffer)
414
+ row[column_spec[2]] = convert_type(column_value, column_spec[3])
415
+ end
416
+ rows << row
417
+ end
418
+ rows
419
+ end
420
+ end
421
+
422
+ class SetKeyspaceResultResponse < ResultResponse
423
+ attr_reader :keyspace
424
+
425
+ def initialize(keyspace)
426
+ @keyspace = keyspace
427
+ end
428
+
429
+ def self.decode!(buffer)
430
+ new(read_string!(buffer))
431
+ end
432
+
433
+ def to_s
434
+ %(RESULT SET_KEYSPACE "#@keyspace")
435
+ end
436
+ end
437
+
438
+ class PreparedResultResponse < ResultResponse
439
+ attr_reader :id, :metadata
440
+
441
+ def initialize(*args)
442
+ @id, @metadata = args
443
+ end
444
+
445
+ def self.decode!(buffer)
446
+ id = read_short_bytes!(buffer)
447
+ metadata = RowsResultResponse.read_metadata!(buffer)
448
+ new(id, metadata)
449
+ end
450
+
451
+ def to_s
452
+ %(RESULT PREPARED #{id.each_byte.map { |x| x.to_s(16) }.join('')} #@metadata)
453
+ end
454
+ end
455
+
456
+ class SchemaChangeResultResponse < ResultResponse
457
+ attr_reader :change, :keyspace, :table
458
+
459
+ def initialize(*args)
460
+ @change, @keyspace, @table = args
461
+ end
462
+
463
+ def self.decode!(buffer)
464
+ new(read_string!(buffer), read_string!(buffer), read_string!(buffer))
465
+ end
466
+
467
+ def to_s
468
+ %(RESULT SCHEMA_CHANGE #@change "#@keyspace" "#@table")
469
+ end
470
+ end
471
+
472
+ class EventResponse < ResultResponse
473
+ def self.decode!(buffer)
474
+ type = read_string!(buffer)
475
+ case type
476
+ when SchemaChangeEventResponse::TYPE
477
+ SchemaChangeEventResponse.decode!(buffer)
478
+ when StatusChangeEventResponse::TYPE
479
+ StatusChangeEventResponse.decode!(buffer)
480
+ when TopologyChangeEventResponse::TYPE
481
+ TopologyChangeEventResponse.decode!(buffer)
482
+ else
483
+ raise UnsupportedEventTypeError, %(Unsupported event type: "#{type}")
484
+ end
485
+ end
486
+ end
487
+
488
+ class SchemaChangeEventResponse < EventResponse
489
+ TYPE = 'SCHEMA_CHANGE'.freeze
490
+
491
+ attr_reader :type, :change, :keyspace, :table
492
+
493
+ def initialize(*args)
494
+ @change, @keyspace, @table = args
495
+ @type = TYPE
496
+ end
497
+
498
+ def self.decode!(buffer)
499
+ new(read_string!(buffer), read_string!(buffer), read_string!(buffer))
500
+ end
501
+
502
+ def eql?(rs)
503
+ rs.type == self.type && rs.change == self.change && rs.keyspace == self.keyspace && rs.table == self.table
504
+ end
505
+ alias_method :==, :eql?
506
+
507
+ def hash
508
+ @h ||= begin
509
+ h = 0
510
+ h = ((h & 33554431) * 31) ^ @type.hash
511
+ h = ((h & 33554431) * 31) ^ @change.hash
512
+ h = ((h & 33554431) * 31) ^ @keyspace.hash
513
+ h = ((h & 33554431) * 31) ^ @table.hash
514
+ h
515
+ end
516
+ end
517
+
518
+ def to_s
519
+ %(EVENT #@type #@change "#@keyspace" "#@table")
520
+ end
521
+ end
522
+
523
+ class StatusChangeEventResponse < EventResponse
524
+ TYPE = 'STATUS_CHANGE'.freeze
525
+
526
+ attr_reader :type, :change, :address, :port
527
+
528
+ def initialize(*args)
529
+ @change, @address, @port = args
530
+ @type = TYPE
531
+ end
532
+
533
+ def self.decode!(buffer)
534
+ new(read_string!(buffer), *read_inet!(buffer))
535
+ end
536
+
537
+ def to_s
538
+ %(EVENT #@type #@change #@address:#@port)
539
+ end
540
+ end
541
+
542
+ class TopologyChangeEventResponse < StatusChangeEventResponse
543
+ TYPE = 'TOPOLOGY_CHANGE'.freeze
544
+
545
+ def initialize(*args)
546
+ super
547
+ @type = TYPE
548
+ end
549
+ end
550
+ end
551
+ end