cql-rb 1.0.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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