cql-rb 1.1.0.pre1 → 1.1.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +26 -9
- data/lib/cql/client/asynchronous_client.rb +11 -3
- data/lib/cql/io.rb +1 -0
- data/lib/cql/io/connection.rb +2 -2
- data/lib/cql/io/io_reactor.rb +54 -3
- data/lib/cql/protocol/decoding.rb +29 -4
- data/lib/cql/protocol/type_converter.rb +74 -50
- data/lib/cql/uuid.rb +2 -2
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +33 -2
- data/spec/cql/io/connection_spec.rb +1 -1
- data/spec/cql/io/io_reactor_spec.rb +88 -2
- data/spec/cql/protocol/decoding_spec.rb +27 -2
- data/spec/cql/protocol/type_converter_spec.rb +52 -0
- data/spec/cql/uuid_spec.rb +5 -1
- data/spec/integration/protocol_spec.rb +6 -6
- data/spec/integration/regression_spec.rb +93 -9
- data/spec/support/fake_io_reactor.rb +3 -1
- metadata +5 -3
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Ruby CQL3 driver
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/iconara/cql-rb.png?branch=master)](https://travis-ci.org/iconara/cql-rb)
|
4
|
-
[![Coverage Status](https://coveralls.io/repos/iconara/cql-rb/badge.png
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/iconara/cql-rb/badge.png)](https://coveralls.io/r/iconara/cql-rb)
|
5
5
|
|
6
6
|
# Requirements
|
7
7
|
|
@@ -28,7 +28,7 @@ rows.each do |row|
|
|
28
28
|
end
|
29
29
|
```
|
30
30
|
|
31
|
-
when you're done you can call `#close` to disconnect from Cassandra. You can connect to multiple Cassandra nodes by passing multiple comma separated host names to the `:host` option.
|
31
|
+
when you're done you can call `#close` to disconnect from Cassandra. You can connect to multiple Cassandra nodes by passing multiple comma separated host names to the `:host` option -- _this is deprecated, in v1.1.0 there is a new option `:hosts` that takes a list of host names_.
|
32
32
|
|
33
33
|
# Usage
|
34
34
|
|
@@ -203,19 +203,36 @@ Yes it is, and your data is probably safe. cql-rb is just not completely there y
|
|
203
203
|
|
204
204
|
## Something else is not working
|
205
205
|
|
206
|
-
Open an issue and
|
206
|
+
Open an issue and someone will try to help you out. Please include the gem version, Casandra version and Ruby version, and explain as much about what you're doing as you can, preferably the smallest piece of code that reliably triggers the problem. The more information you give, the better the chances you will get help.
|
207
|
+
|
208
|
+
# Changelog & versioning
|
209
|
+
|
210
|
+
Check out the [releases on GitHub](https://github.com/iconara/cql-rb/releases). Version numbering follows the [semantic versioning](http://semver.org/) scheme.
|
207
211
|
|
208
212
|
# Known bugs & limitations
|
209
213
|
|
210
|
-
* No automatic peer discovery.
|
211
|
-
* No automatic reconnection on connection failures.
|
212
|
-
*
|
213
|
-
*
|
214
|
+
* No automatic peer discovery -- _this is in HEAD and will be released with v1.1.0_.
|
215
|
+
* No automatic reconnection on connection failures -- _this is planned for v1.1.0_.
|
216
|
+
* No support for request timeouts (other than server-initiated), but requests to a node fail when that node goes down.
|
217
|
+
* No support for compression.
|
218
|
+
* No support for request tracing.
|
219
|
+
* JRuby 1.6.8 and earlier is not supported, the tests pass in 1.6.8, but 1.6.4 is known not to work. Travis does not support JRuby 1.6.x so there's no way to get good coverage of what works and not. The only known issue in 1.6.8 is that connection failures aren't handled correctly.
|
214
220
|
* Large results are buffered in memory until the whole response has been loaded, the protocol makes it possible to start to deliver rows to the client code as soon as the metadata is loaded, but this is not supported yet.
|
215
221
|
* There is no cluster introspection utilities (like the `DESCRIBE` commands in `cqlsh`).
|
216
|
-
* No support for request tracing.
|
217
222
|
|
218
|
-
|
223
|
+
# How to contribute
|
224
|
+
|
225
|
+
Fork the repository, make your changes in a topic branch that branches off from the right place in the history (HEAD isn't necessarily always right), make your changes and finally submit a pull request.
|
226
|
+
|
227
|
+
Follow the style of the existing code, make sure that existing tests pass, and that everything new has good test coverage. Put some effort into writing clear and concise commit messages, and write a good pull request description.
|
228
|
+
|
229
|
+
It takes time to understand other people's code, and even more time to understand a patch, so do as much as you can to make the maintainers' work easier. Be prepared for rejection, many times a feature is already planned, or the proposed design would be in the way of other planned features, or the maintainers' just feel that it will be faster to implement the features themselves than to try to integrate your patch.
|
230
|
+
|
231
|
+
Feel free to open a pull request before the feature is finished, that way you can have a conversation with the maintainers' during the development, and you can make adjustments to the design as you go along instead of having your whole feature rejected because of reasons such as those above. If you do, please make it clear that the pull request is a work in progress, or a request for comment.
|
232
|
+
|
233
|
+
Always remember that the maintainers' work on this project in their free time and that they don't work for you, or for your benefit. They have no obligation to do what you think is right -- but if you're nice they might anyway.
|
234
|
+
|
235
|
+
# Copyright
|
219
236
|
|
220
237
|
Copyright 2013 Theo Hultberg/Iconara
|
221
238
|
|
@@ -108,8 +108,9 @@ module Cql
|
|
108
108
|
|
109
109
|
private
|
110
110
|
|
111
|
-
KEYSPACE_NAME_PATTERN = /^\w[\w\d_]
|
111
|
+
KEYSPACE_NAME_PATTERN = /^\w[\w\d_]*$|^"\w[\w\d_]*"$/
|
112
112
|
DEFAULT_CONSISTENCY_LEVEL = :quorum
|
113
|
+
BIND_ALL_IP = '0.0.0.0'.freeze
|
113
114
|
|
114
115
|
class FailedConnection
|
115
116
|
attr_reader :error
|
@@ -170,14 +171,21 @@ module Cql
|
|
170
171
|
connected_seeds = seed_connections.select(&:connected?)
|
171
172
|
connection = connected_seeds.sample
|
172
173
|
return Future.completed([]) unless connection
|
173
|
-
request = Protocol::QueryRequest.new('SELECT data_center, host_id, rpc_address FROM system.peers', :one)
|
174
|
+
request = Protocol::QueryRequest.new('SELECT peer, data_center, host_id, rpc_address FROM system.peers', :one)
|
174
175
|
peer_info = execute_request(request, connection)
|
175
176
|
peer_info.flat_map do |result|
|
176
177
|
seed_dcs = connected_seeds.map { |c| c[:data_center] }.uniq
|
177
178
|
unconnected_peers = result.select do |row|
|
178
179
|
seed_dcs.include?(row['data_center']) && connected_seeds.none? { |c| c[:host_id] == row['host_id'] }
|
179
180
|
end
|
180
|
-
node_addresses = unconnected_peers.map
|
181
|
+
node_addresses = unconnected_peers.map do |row|
|
182
|
+
rpc_address = row['rpc_address'].to_s
|
183
|
+
if rpc_address == BIND_ALL_IP
|
184
|
+
row['peer']
|
185
|
+
else
|
186
|
+
rpc_address
|
187
|
+
end
|
188
|
+
end
|
181
189
|
if node_addresses.any?
|
182
190
|
connect_to_hosts(node_addresses, initial_keyspace, false)
|
183
191
|
else
|
data/lib/cql/io.rb
CHANGED
data/lib/cql/io/connection.rb
CHANGED
@@ -13,13 +13,13 @@ module Cql
|
|
13
13
|
attr_reader :host, :port, :connection_timeout
|
14
14
|
|
15
15
|
# @private
|
16
|
-
def initialize(host, port, connection_timeout, unblocker, socket_impl=Socket
|
16
|
+
def initialize(host, port, connection_timeout, unblocker, clock, socket_impl=Socket)
|
17
17
|
@host = host
|
18
18
|
@port = port
|
19
19
|
@connection_timeout = connection_timeout
|
20
20
|
@unblocker = unblocker
|
21
|
-
@socket_impl = socket_impl
|
22
21
|
@clock = clock
|
22
|
+
@socket_impl = socket_impl
|
23
23
|
@lock = Mutex.new
|
24
24
|
@connected = false
|
25
25
|
@write_buffer = ByteBuffer.new
|
data/lib/cql/io/io_reactor.rb
CHANGED
@@ -89,6 +89,7 @@ module Cql
|
|
89
89
|
#
|
90
90
|
def initialize(protocol_handler_factory, options={})
|
91
91
|
@protocol_handler_factory = protocol_handler_factory
|
92
|
+
@clock = options[:clock] || Time
|
92
93
|
@unblocker = Unblocker.new
|
93
94
|
@io_loop = IoLoopBody.new(options)
|
94
95
|
@io_loop.add_socket(@unblocker)
|
@@ -138,6 +139,7 @@ module Cql
|
|
138
139
|
@io_loop.tick until @stopped
|
139
140
|
ensure
|
140
141
|
@io_loop.close_sockets
|
142
|
+
@io_loop.cancel_timers
|
141
143
|
@running = false
|
142
144
|
if $!
|
143
145
|
@stopped_future.fail!($!)
|
@@ -180,7 +182,7 @@ module Cql
|
|
180
182
|
# object that will be your interface to interact with the connection
|
181
183
|
#
|
182
184
|
def connect(host, port, timeout)
|
183
|
-
connection = Connection.new(host, port, timeout, @unblocker)
|
185
|
+
connection = Connection.new(host, port, timeout, @unblocker, @clock)
|
184
186
|
f = connection.connect
|
185
187
|
protocol_handler = @protocol_handler_factory.new(connection)
|
186
188
|
@io_loop.add_socket(connection)
|
@@ -188,6 +190,18 @@ module Cql
|
|
188
190
|
f.map { protocol_handler }
|
189
191
|
end
|
190
192
|
|
193
|
+
# Returns a future that completes after the specified number of seconds.
|
194
|
+
#
|
195
|
+
# @param timeout [Float] the number of seconds to wait until the returned
|
196
|
+
# future is completed
|
197
|
+
# @return [Cql::Future] a future that completes when the timer expires
|
198
|
+
#
|
199
|
+
def schedule_timer(timeout)
|
200
|
+
f = Future.new
|
201
|
+
@io_loop.schedule_timer(timeout, f)
|
202
|
+
f
|
203
|
+
end
|
204
|
+
|
191
205
|
def to_s
|
192
206
|
@io_loop.to_s
|
193
207
|
end
|
@@ -250,8 +264,10 @@ module Cql
|
|
250
264
|
class IoLoopBody
|
251
265
|
def initialize(options={})
|
252
266
|
@selector = options[:selector] || IO
|
267
|
+
@clock = options[:clock] || Time
|
253
268
|
@lock = Mutex.new
|
254
269
|
@sockets = []
|
270
|
+
@timers = []
|
255
271
|
end
|
256
272
|
|
257
273
|
def add_socket(socket)
|
@@ -262,6 +278,15 @@ module Cql
|
|
262
278
|
end
|
263
279
|
end
|
264
280
|
|
281
|
+
def schedule_timer(timeout, future)
|
282
|
+
@lock.synchronize do
|
283
|
+
timers = @timers.dup
|
284
|
+
timers << [@clock.now + timeout, future]
|
285
|
+
timers.sort_by(&:first)
|
286
|
+
@timers = timers
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
265
290
|
def close_sockets
|
266
291
|
@sockets.each do |s|
|
267
292
|
begin
|
@@ -272,7 +297,27 @@ module Cql
|
|
272
297
|
end
|
273
298
|
end
|
274
299
|
|
300
|
+
def cancel_timers
|
301
|
+
@timers.each do |pair|
|
302
|
+
if pair[1]
|
303
|
+
pair[1].fail!(CancelledError.new)
|
304
|
+
pair[1] = nil
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
275
309
|
def tick(timeout=1)
|
310
|
+
check_sockets!(timeout)
|
311
|
+
check_timers!
|
312
|
+
end
|
313
|
+
|
314
|
+
def to_s
|
315
|
+
%(#<#{IoReactor.name} @connections=[#{@sockets.map(&:to_s).join(', ')}]>)
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
def check_sockets!(timeout)
|
276
321
|
readables, writables, connecting = [], [], []
|
277
322
|
sockets = @sockets
|
278
323
|
sockets.reject! { |s| s.closed? }
|
@@ -287,8 +332,14 @@ module Cql
|
|
287
332
|
w && w.each(&:flush)
|
288
333
|
end
|
289
334
|
|
290
|
-
def
|
291
|
-
|
335
|
+
def check_timers!
|
336
|
+
timers = @timers
|
337
|
+
timers.each do |pair|
|
338
|
+
break unless pair[0] <= @clock.now && pair[1]
|
339
|
+
pair[1].complete!
|
340
|
+
pair[1] = nil
|
341
|
+
end
|
342
|
+
timers.reject! { |pair| pair[1].nil? }
|
292
343
|
end
|
293
344
|
end
|
294
345
|
end
|
@@ -31,15 +31,34 @@ module Cql
|
|
31
31
|
def read_decimal!(buffer, length=buffer.length)
|
32
32
|
size = read_int!(buffer)
|
33
33
|
number_string = read_varint!(buffer, length - 4).to_s
|
34
|
-
|
34
|
+
if number_string.length < size
|
35
|
+
if number_string.start_with?(MINUS)
|
36
|
+
number_string = number_string[1, number_string.length - 1]
|
37
|
+
fraction_string = MINUS + ZERO << DECIMAL_POINT
|
38
|
+
else
|
39
|
+
fraction_string = ZERO + DECIMAL_POINT
|
40
|
+
end
|
41
|
+
(size - number_string.length).times { fraction_string << ZERO }
|
42
|
+
fraction_string << number_string
|
43
|
+
else
|
44
|
+
fraction_string = number_string[0, number_string.length - size]
|
45
|
+
fraction_string << DECIMAL_POINT
|
46
|
+
fraction_string << number_string[number_string.length - size, number_string.length]
|
47
|
+
end
|
35
48
|
BigDecimal.new(fraction_string)
|
36
49
|
rescue RangeError => e
|
37
50
|
raise DecodingError, e.message, e.backtrace
|
38
51
|
end
|
39
52
|
|
40
53
|
def read_long!(buffer)
|
41
|
-
|
42
|
-
(
|
54
|
+
hi, lo = buffer.read(8).unpack(Formats::TWO_INTS_FORMAT)
|
55
|
+
if (hi > 0x7fffffff)
|
56
|
+
hi ^= 0xffffffff
|
57
|
+
lo ^= 0xffffffff
|
58
|
+
0 - (hi << 32) - lo - 1
|
59
|
+
else
|
60
|
+
(hi << 32) + lo
|
61
|
+
end
|
43
62
|
rescue RangeError => e
|
44
63
|
raise DecodingError, e.message, e.backtrace
|
45
64
|
end
|
@@ -57,7 +76,11 @@ module Cql
|
|
57
76
|
end
|
58
77
|
|
59
78
|
def read_int!(buffer)
|
60
|
-
buffer.read_int
|
79
|
+
val = buffer.read_int
|
80
|
+
if (val > 0x7fffffff)
|
81
|
+
val = 0 - ((val - 1) ^ 0xffffffff)
|
82
|
+
end
|
83
|
+
val
|
61
84
|
rescue RangeError => e
|
62
85
|
raise DecodingError, "Not enough bytes available to decode an int: #{e.message}", e.backtrace
|
63
86
|
end
|
@@ -161,6 +184,8 @@ module Cql
|
|
161
184
|
|
162
185
|
private
|
163
186
|
|
187
|
+
MINUS = '-'.freeze
|
188
|
+
ZERO = '0'.freeze
|
164
189
|
DECIMAL_POINT = '.'.freeze
|
165
190
|
end
|
166
191
|
end
|
@@ -36,27 +36,35 @@ module Cql
|
|
36
36
|
def to_bytes(io, type, value, size_bytes=4)
|
37
37
|
case type
|
38
38
|
when Array
|
39
|
-
unless value.is_a?(Enumerable)
|
39
|
+
unless value.nil? || value.is_a?(Enumerable)
|
40
40
|
raise InvalidValueError, 'Value for collection must be enumerable'
|
41
41
|
end
|
42
42
|
case type.first
|
43
43
|
when :list, :set
|
44
44
|
_, sub_type = type
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
if value
|
46
|
+
raw = ''
|
47
|
+
write_short(raw, value.size)
|
48
|
+
value.each do |element|
|
49
|
+
to_bytes(raw, sub_type, element, 2)
|
50
|
+
end
|
51
|
+
write_bytes(io, raw)
|
52
|
+
else
|
53
|
+
nil_to_bytes(io, size_bytes)
|
49
54
|
end
|
50
|
-
write_bytes(io, raw)
|
51
55
|
when :map
|
52
56
|
_, key_type, value_type = type
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
57
|
+
if value
|
58
|
+
raw = ''
|
59
|
+
write_short(raw, value.size)
|
60
|
+
value.each do |key, value|
|
61
|
+
to_bytes(raw, key_type, key, 2)
|
62
|
+
to_bytes(raw, value_type, value, 2)
|
63
|
+
end
|
64
|
+
write_bytes(io, raw)
|
65
|
+
else
|
66
|
+
nil_to_bytes(io, size_bytes)
|
58
67
|
end
|
59
|
-
write_bytes(io, raw)
|
60
68
|
else
|
61
69
|
raise UnsupportedColumnTypeError, %(Unsupported column collection type: #{type.first})
|
62
70
|
end
|
@@ -229,7 +237,7 @@ module Cql
|
|
229
237
|
end
|
230
238
|
|
231
239
|
def ascii_to_bytes(io, value, size_bytes)
|
232
|
-
v = value.encode(::Encoding::ASCII)
|
240
|
+
v = value && value.encode(::Encoding::ASCII)
|
233
241
|
if size_bytes == 4
|
234
242
|
write_bytes(io, v)
|
235
243
|
else
|
@@ -238,16 +246,16 @@ module Cql
|
|
238
246
|
end
|
239
247
|
|
240
248
|
def bigint_to_bytes(io, value, size_bytes)
|
241
|
-
if
|
242
|
-
|
249
|
+
if value
|
250
|
+
size_to_bytes(io, 8, size_bytes)
|
251
|
+
write_long(io, value)
|
243
252
|
else
|
244
|
-
|
253
|
+
nil_to_bytes(io, size_bytes)
|
245
254
|
end
|
246
|
-
write_long(io, value)
|
247
255
|
end
|
248
256
|
|
249
257
|
def blob_to_bytes(io, value, size_bytes)
|
250
|
-
v = value.encode(::Encoding::BINARY)
|
258
|
+
v = value && value.encode(::Encoding::BINARY)
|
251
259
|
if size_bytes == 4
|
252
260
|
write_bytes(io, v)
|
253
261
|
else
|
@@ -256,16 +264,16 @@ module Cql
|
|
256
264
|
end
|
257
265
|
|
258
266
|
def boolean_to_bytes(io, value, size_bytes)
|
259
|
-
if
|
260
|
-
|
267
|
+
if !value.nil?
|
268
|
+
size_to_bytes(io, 1, size_bytes)
|
269
|
+
io << (value ? Constants::TRUE_BYTE : Constants::FALSE_BYTE)
|
261
270
|
else
|
262
|
-
|
271
|
+
nil_to_bytes(io, size_bytes)
|
263
272
|
end
|
264
|
-
io << (value ? Constants::TRUE_BYTE : Constants::FALSE_BYTE)
|
265
273
|
end
|
266
274
|
|
267
275
|
def decimal_to_bytes(io, value, size_bytes)
|
268
|
-
raw = write_decimal('', value)
|
276
|
+
raw = value && write_decimal('', value)
|
269
277
|
if size_bytes == 4
|
270
278
|
write_bytes(io, raw)
|
271
279
|
else
|
@@ -274,43 +282,43 @@ module Cql
|
|
274
282
|
end
|
275
283
|
|
276
284
|
def double_to_bytes(io, value, size_bytes)
|
277
|
-
if
|
278
|
-
|
285
|
+
if value
|
286
|
+
size_to_bytes(io, 8, size_bytes)
|
287
|
+
write_double(io, value)
|
279
288
|
else
|
280
|
-
|
289
|
+
nil_to_bytes(io, size_bytes)
|
281
290
|
end
|
282
|
-
write_double(io, value)
|
283
291
|
end
|
284
292
|
|
285
293
|
def float_to_bytes(io, value, size_bytes)
|
286
|
-
if
|
287
|
-
|
294
|
+
if value
|
295
|
+
size_to_bytes(io, 4, size_bytes)
|
296
|
+
write_float(io, value)
|
288
297
|
else
|
289
|
-
|
298
|
+
nil_to_bytes(io, size_bytes)
|
290
299
|
end
|
291
|
-
write_float(io, value)
|
292
300
|
end
|
293
301
|
|
294
302
|
def inet_to_bytes(io, value, size_bytes)
|
295
|
-
if
|
296
|
-
|
303
|
+
if value
|
304
|
+
size_to_bytes(io, value.ipv6? ? 16 : 4, size_bytes)
|
305
|
+
io << value.hton
|
297
306
|
else
|
298
|
-
|
307
|
+
nil_to_bytes(io, size_bytes)
|
299
308
|
end
|
300
|
-
io << value.hton
|
301
309
|
end
|
302
310
|
|
303
311
|
def int_to_bytes(io, value, size_bytes)
|
304
|
-
if
|
305
|
-
|
312
|
+
if value
|
313
|
+
size_to_bytes(io, 4, size_bytes)
|
314
|
+
write_int(io, value)
|
306
315
|
else
|
307
|
-
|
316
|
+
nil_to_bytes(io, size_bytes)
|
308
317
|
end
|
309
|
-
write_int(io, value)
|
310
318
|
end
|
311
319
|
|
312
320
|
def varchar_to_bytes(io, value, size_bytes)
|
313
|
-
v = value.encode(::Encoding::UTF_8)
|
321
|
+
v = value && value.encode(::Encoding::UTF_8)
|
314
322
|
if size_bytes == 4
|
315
323
|
write_bytes(io, v)
|
316
324
|
else
|
@@ -319,32 +327,48 @@ module Cql
|
|
319
327
|
end
|
320
328
|
|
321
329
|
def timestamp_to_bytes(io, value, size_bytes)
|
322
|
-
|
323
|
-
|
324
|
-
|
330
|
+
if value
|
331
|
+
ms = (value.to_f * 1000).to_i
|
332
|
+
size_to_bytes(io, 8, size_bytes)
|
333
|
+
write_long(io, ms)
|
325
334
|
else
|
326
|
-
|
335
|
+
nil_to_bytes(io, size_bytes)
|
327
336
|
end
|
328
|
-
write_long(io, ms)
|
329
337
|
end
|
330
338
|
|
331
339
|
def uuid_to_bytes(io, value, size_bytes)
|
332
|
-
if
|
333
|
-
|
340
|
+
if value
|
341
|
+
size_to_bytes(io, 16, size_bytes)
|
342
|
+
write_uuid(io, value)
|
334
343
|
else
|
335
|
-
|
344
|
+
nil_to_bytes(io, size_bytes)
|
336
345
|
end
|
337
|
-
write_uuid(io, value)
|
338
346
|
end
|
339
347
|
|
340
348
|
def varint_to_bytes(io, value, size_bytes)
|
341
|
-
raw = write_varint('', value)
|
349
|
+
raw = value && write_varint('', value)
|
342
350
|
if size_bytes == 4
|
343
351
|
write_bytes(io, raw)
|
344
352
|
else
|
345
353
|
write_short_bytes(io, raw)
|
346
354
|
end
|
347
355
|
end
|
356
|
+
|
357
|
+
def size_to_bytes(io, size, size_bytes)
|
358
|
+
if size_bytes == 4
|
359
|
+
write_int(io, size)
|
360
|
+
else
|
361
|
+
write_short(io, size)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def nil_to_bytes(io, size_bytes)
|
366
|
+
if size_bytes == 4
|
367
|
+
write_int(io, -1)
|
368
|
+
else
|
369
|
+
write_short(io, -1)
|
370
|
+
end
|
371
|
+
end
|
348
372
|
end
|
349
373
|
end
|
350
374
|
end
|
data/lib/cql/uuid.rb
CHANGED
data/lib/cql/version.rb
CHANGED
@@ -188,6 +188,10 @@ module Cql
|
|
188
188
|
Array.new(5) { IPAddr.new("127.0.#{rand(255)}.#{rand(255)}") }
|
189
189
|
end
|
190
190
|
|
191
|
+
let :bind_all_rpc_addresses do
|
192
|
+
false
|
193
|
+
end
|
194
|
+
|
191
195
|
before do
|
192
196
|
uuid_generator = TimeUuid::Generator.new
|
193
197
|
additional_rpc_addresses = additional_nodes.dup
|
@@ -210,7 +214,12 @@ module Cql
|
|
210
214
|
end
|
211
215
|
rows = other_host_ids.map do |host_id|
|
212
216
|
ip = additional_rpc_addresses.shift
|
213
|
-
{
|
217
|
+
{
|
218
|
+
'peer' => ip,
|
219
|
+
'host_id' => host_id,
|
220
|
+
'data_center' => data_centers[ip],
|
221
|
+
'rpc_address' => bind_all_rpc_addresses ? IPAddr.new('0.0.0.0') : ip
|
222
|
+
}
|
214
223
|
end
|
215
224
|
Protocol::RowsResultResponse.new(rows, peer_metadata)
|
216
225
|
end
|
@@ -224,6 +233,17 @@ module Cql
|
|
224
233
|
connections.should have(3).items
|
225
234
|
end
|
226
235
|
|
236
|
+
context 'when the nodes have 0.0.0.0 as rpc_address' do
|
237
|
+
let :bind_all_rpc_addresses do
|
238
|
+
true
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'falls back on using the peer column' do
|
242
|
+
client.connect.get
|
243
|
+
connections.should have(3).items
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
227
247
|
it 'connects to the other nodes in the same data center' do
|
228
248
|
data_centers[additional_nodes[1]] = 'dc2'
|
229
249
|
client.connect.get
|
@@ -408,6 +428,17 @@ module Cql
|
|
408
428
|
client.connect.get
|
409
429
|
expect { client.use('system; DROP KEYSPACE system').get }.to raise_error(InvalidKeyspaceNameError)
|
410
430
|
end
|
431
|
+
|
432
|
+
it 'allows the keyspace name to be quoted' do
|
433
|
+
handle_request do |request|
|
434
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql == 'USE "system"'
|
435
|
+
Protocol::SetKeyspaceResultResponse.new('system')
|
436
|
+
end
|
437
|
+
end
|
438
|
+
client.connect.get
|
439
|
+
client.use('"system"').get
|
440
|
+
client.keyspace.should == "system"
|
441
|
+
end
|
411
442
|
end
|
412
443
|
|
413
444
|
describe '#execute' do
|
@@ -691,4 +722,4 @@ module Cql
|
|
691
722
|
end
|
692
723
|
end
|
693
724
|
end
|
694
|
-
end
|
725
|
+
end
|
@@ -7,7 +7,7 @@ module Cql
|
|
7
7
|
module Io
|
8
8
|
describe IoReactor do
|
9
9
|
let :reactor do
|
10
|
-
described_class.new(protocol_handler_factory, selector: selector)
|
10
|
+
described_class.new(protocol_handler_factory, selector: selector, clock: clock)
|
11
11
|
end
|
12
12
|
|
13
13
|
let :protocol_handler_factory do
|
@@ -18,6 +18,10 @@ module Cql
|
|
18
18
|
IoReactorSpec::FakeSelector.new
|
19
19
|
end
|
20
20
|
|
21
|
+
let :clock do
|
22
|
+
stub(:clock, now: 0)
|
23
|
+
end
|
24
|
+
|
21
25
|
describe '#start' do
|
22
26
|
after do
|
23
27
|
reactor.stop.get if reactor.running?
|
@@ -84,6 +88,20 @@ module Cql
|
|
84
88
|
reactor.stop.get
|
85
89
|
connection.should be_closed
|
86
90
|
end
|
91
|
+
|
92
|
+
it 'cancels all active timers' do
|
93
|
+
reactor.start.get
|
94
|
+
clock.stub(:now).and_return(1)
|
95
|
+
expired_timer = reactor.schedule_timer(1)
|
96
|
+
active_timer1 = reactor.schedule_timer(999)
|
97
|
+
active_timer2 = reactor.schedule_timer(111)
|
98
|
+
expired_timer.should_not_receive(:fail!)
|
99
|
+
clock.stub(:now).and_return(2)
|
100
|
+
await { expired_timer.complete? }
|
101
|
+
reactor.stop.get
|
102
|
+
active_timer1.should be_failed
|
103
|
+
active_timer2.should be_failed
|
104
|
+
end
|
87
105
|
end
|
88
106
|
|
89
107
|
describe '#on_error' do
|
@@ -163,6 +181,23 @@ module Cql
|
|
163
181
|
end
|
164
182
|
end
|
165
183
|
|
184
|
+
describe '#schedule_timer' do
|
185
|
+
before do
|
186
|
+
reactor.start.get
|
187
|
+
end
|
188
|
+
|
189
|
+
after do
|
190
|
+
reactor.stop.get
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'returns a future that completes after the specified duration' do
|
194
|
+
clock.stub(:now).and_return(1)
|
195
|
+
f = reactor.schedule_timer(0.1)
|
196
|
+
clock.stub(:now).and_return(1.1)
|
197
|
+
await { f.complete? }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
166
201
|
describe '#to_s' do
|
167
202
|
context 'returns a string that' do
|
168
203
|
it 'includes the class name' do
|
@@ -179,13 +214,17 @@ module Cql
|
|
179
214
|
|
180
215
|
describe IoLoopBody do
|
181
216
|
let :loop_body do
|
182
|
-
described_class.new(selector: selector)
|
217
|
+
described_class.new(selector: selector, clock: clock)
|
183
218
|
end
|
184
219
|
|
185
220
|
let :selector do
|
186
221
|
stub(:selector)
|
187
222
|
end
|
188
223
|
|
224
|
+
let :clock do
|
225
|
+
stub(:clock, now: 0)
|
226
|
+
end
|
227
|
+
|
189
228
|
let :socket do
|
190
229
|
stub(:socket, connected?: false, connecting?: false, writable?: false, closed?: false)
|
191
230
|
end
|
@@ -252,6 +291,30 @@ module Cql
|
|
252
291
|
selector.should_receive(:select).with(anything, anything, anything, 99).and_return([[], [], []])
|
253
292
|
loop_body.tick(99)
|
254
293
|
end
|
294
|
+
|
295
|
+
it 'completes timers that have expired' do
|
296
|
+
selector.stub(:select).and_return([nil, nil, nil])
|
297
|
+
clock.stub(:now).and_return(1)
|
298
|
+
future = Future.new
|
299
|
+
loop_body.schedule_timer(1, future)
|
300
|
+
loop_body.tick
|
301
|
+
future.should_not be_complete
|
302
|
+
clock.stub(:now).and_return(2)
|
303
|
+
loop_body.tick
|
304
|
+
future.should be_complete
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'clears out timers that have expired' do
|
308
|
+
selector.stub(:select).and_return([nil, nil, nil])
|
309
|
+
clock.stub(:now).and_return(1)
|
310
|
+
future = Future.new
|
311
|
+
loop_body.schedule_timer(1, future)
|
312
|
+
clock.stub(:now).and_return(2)
|
313
|
+
loop_body.tick
|
314
|
+
future.should be_complete
|
315
|
+
future.should_not_receive(:complete!)
|
316
|
+
loop_body.tick
|
317
|
+
end
|
255
318
|
end
|
256
319
|
|
257
320
|
describe '#close_sockets' do
|
@@ -282,6 +345,29 @@ module Cql
|
|
282
345
|
loop_body.close_sockets
|
283
346
|
end
|
284
347
|
end
|
348
|
+
|
349
|
+
describe '#cancel_timers' do
|
350
|
+
before do
|
351
|
+
selector.stub(:select).and_return([nil, nil, nil])
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'fails all active timers with a CancelledError' do
|
355
|
+
f1 = Future.new
|
356
|
+
f2 = Future.new
|
357
|
+
f3 = Future.new
|
358
|
+
clock.stub(:now).and_return(1)
|
359
|
+
loop_body.schedule_timer(1, f1)
|
360
|
+
loop_body.schedule_timer(3, f2)
|
361
|
+
loop_body.schedule_timer(3, f3)
|
362
|
+
clock.stub(:now).and_return(2)
|
363
|
+
loop_body.tick
|
364
|
+
loop_body.cancel_timers
|
365
|
+
f1.should be_complete
|
366
|
+
f2.should be_failed
|
367
|
+
f3.should be_failed
|
368
|
+
expect { f3.get }.to raise_error(CancelledError)
|
369
|
+
end
|
370
|
+
end
|
285
371
|
end
|
286
372
|
end
|
287
373
|
end
|
@@ -62,6 +62,21 @@ module Cql
|
|
62
62
|
Decoding.read_decimal!(buffer).should == BigDecimal.new('1042342234234.123423435647768234')
|
63
63
|
end
|
64
64
|
|
65
|
+
it 'decodes a negative decimal' do
|
66
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x12\xF2\xD8\x02\xB6R\x7F\x99\xEE\x98#\x99\xA9V")
|
67
|
+
Decoding.read_decimal!(buffer).should == BigDecimal.new('-1042342234234.123423435647768234')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'decodes a positive decimal with only fractions' do
|
71
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x13*\xF8\xC4\xDF\xEB]o")
|
72
|
+
Decoding.read_decimal!(buffer).should == BigDecimal.new('0.0012095473475870063')
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'decodes a negative decimal with only fractions' do
|
76
|
+
buffer = ByteBuffer.new("\x00\x00\x00\x13\xD5\a;\x20\x14\xA2\x91")
|
77
|
+
Decoding.read_decimal!(buffer).should == BigDecimal.new('-0.0012095473475870063')
|
78
|
+
end
|
79
|
+
|
65
80
|
it 'consumes the bytes' do
|
66
81
|
buffer << 'HELLO'
|
67
82
|
Decoding.read_decimal!(buffer, buffer.length - 5)
|
@@ -79,11 +94,16 @@ module Cql
|
|
79
94
|
end
|
80
95
|
|
81
96
|
describe '#read_long!' do
|
82
|
-
it 'decodes a long' do
|
97
|
+
it 'decodes a positive long' do
|
83
98
|
buffer = ByteBuffer.new("\x00\x00\xca\xfe\xba\xbe\x00\x00")
|
84
99
|
Decoding.read_long!(buffer).should == 0x0000cafebabe0000
|
85
100
|
end
|
86
101
|
|
102
|
+
it 'decodes a negative long' do
|
103
|
+
buffer = ByteBuffer.new("\xff\xff\xff\xff\xff\xff\xff\xff")
|
104
|
+
Decoding.read_long!(buffer).should == -1
|
105
|
+
end
|
106
|
+
|
87
107
|
it 'consumes the bytes' do
|
88
108
|
buffer = ByteBuffer.new("\xca\xfe\xba\xbe\xca\xfe\xba\xbe\xca\xfe\xba\xbe")
|
89
109
|
Decoding.read_long!(buffer)
|
@@ -137,10 +157,15 @@ module Cql
|
|
137
157
|
ByteBuffer.new("\x00\xff\x00\xff")
|
138
158
|
end
|
139
159
|
|
140
|
-
it 'decodes
|
160
|
+
it 'decodes a positive int' do
|
141
161
|
Decoding.read_int!(buffer).should == 0x00ff00ff
|
142
162
|
end
|
143
163
|
|
164
|
+
it 'decodes a negative int' do
|
165
|
+
buffer = ByteBuffer.new("\xff\xff\xff\xff")
|
166
|
+
Decoding.read_int!(buffer).should == -1
|
167
|
+
end
|
168
|
+
|
144
169
|
it 'consumes the bytes' do
|
145
170
|
buffer << "\xab\xcd"
|
146
171
|
Decoding.read_int!(buffer)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Cql
|
7
|
+
module Protocol
|
8
|
+
describe TypeConverter do
|
9
|
+
let :converter do
|
10
|
+
described_class.new
|
11
|
+
end
|
12
|
+
|
13
|
+
let :buffer do
|
14
|
+
''
|
15
|
+
end
|
16
|
+
|
17
|
+
TYPES = [:ascii, :bigint, :blob, :boolean, :decimal, :double, :float, :inet, :int, :text, :varchar, :timestamp, :timeuuid, :uuid, :varint].freeze
|
18
|
+
|
19
|
+
describe '#to_bytes' do
|
20
|
+
context 'when encoding normal value' do
|
21
|
+
TYPES.each do |type|
|
22
|
+
it "encodes a null #{type.upcase}" do
|
23
|
+
converter.to_bytes(buffer, type, nil, 4).should == "\xff\xff\xff\xff"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'encodes a null LIST' do
|
28
|
+
converter.to_bytes(buffer, [:list, :int], nil, 4).should == "\xff\xff\xff\xff"
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'encodes a null MAP' do
|
32
|
+
converter.to_bytes(buffer, [:map, :text, :text], nil, 4).should == "\xff\xff\xff\xff"
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'encodes a null SET' do
|
37
|
+
converter.to_bytes(buffer, [:set, :uuid], nil, 4).should == "\xff\xff\xff\xff"
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when encoding collection values' do
|
43
|
+
TYPES.each do |type|
|
44
|
+
it "encodes a null #{type.upcase}" do
|
45
|
+
converter.to_bytes(buffer, type, nil, 2).should == "\xff\xff"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/cql/uuid_spec.rb
CHANGED
@@ -36,6 +36,10 @@ module Cql
|
|
36
36
|
Uuid.new(276263553384940695775376958868900023510).should eql(Uuid.new('cfd66ccc-d857-4e90-b1e5-df98a3d40cd6'))
|
37
37
|
end
|
38
38
|
|
39
|
+
it 'is not equal when anything other than a Uuid is passed' do
|
40
|
+
[nil, 123, 'test'].each { |v| Uuid.new(276263553384940695775376958868900023510).should_not eql(v) }
|
41
|
+
end
|
42
|
+
|
39
43
|
it 'aliases #== to #eql?' do
|
40
44
|
Uuid.new(276263553384940695775376958868900023510).should == Uuid.new('cfd66ccc-d857-4e90-b1e5-df98a3d40cd6')
|
41
45
|
end
|
@@ -73,4 +77,4 @@ module Cql
|
|
73
77
|
end
|
74
78
|
end
|
75
79
|
end
|
76
|
-
end
|
80
|
+
end
|
@@ -291,12 +291,12 @@ describe 'Protocol parsing and communication' do
|
|
291
291
|
end
|
292
292
|
end
|
293
293
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
294
|
+
it 'sends a TRUNCATE command' do
|
295
|
+
in_keyspace_with_table do
|
296
|
+
response = query(%<TRUNCATE users>)
|
297
|
+
response.should be_void
|
298
|
+
end
|
299
|
+
end
|
300
300
|
|
301
301
|
it 'sends a BATCH command' do
|
302
302
|
in_keyspace_with_table do
|
@@ -65,15 +65,7 @@ describe 'Regressions' do
|
|
65
65
|
end
|
66
66
|
|
67
67
|
context 'with null values' do
|
68
|
-
|
69
|
-
client.execute(%<CREATE TABLE counters (id ASCII, counter1 COUNTER, counter2 COUNTER, PRIMARY KEY (id))>)
|
70
|
-
client.execute(%<UPDATE counters SET counter1 = counter1 + 1 WHERE id = 'foo'>)
|
71
|
-
result = client.execute(%<SELECT counter1, counter2 FROM counters WHERE id = 'foo'>)
|
72
|
-
result.first['counter1'].should == 1
|
73
|
-
result.first['counter2'].should be_nil
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'decodes null values' do
|
68
|
+
before do
|
77
69
|
client.execute(<<-CQL)
|
78
70
|
CREATE TABLE lots_of_types (
|
79
71
|
id INT,
|
@@ -98,6 +90,24 @@ describe 'Regressions' do
|
|
98
90
|
PRIMARY KEY (id)
|
99
91
|
)
|
100
92
|
CQL
|
93
|
+
client.execute(<<-CQL)
|
94
|
+
CREATE TABLE counters (
|
95
|
+
id ASCII,
|
96
|
+
counter1 COUNTER,
|
97
|
+
counter2 COUNTER,
|
98
|
+
PRIMARY KEY (id)
|
99
|
+
)
|
100
|
+
CQL
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'decodes null counters' do
|
104
|
+
client.execute(%<UPDATE counters SET counter1 = counter1 + 1 WHERE id = 'foo'>)
|
105
|
+
result = client.execute(%<SELECT counter1, counter2 FROM counters WHERE id = 'foo'>)
|
106
|
+
result.first['counter1'].should == 1
|
107
|
+
result.first['counter2'].should be_nil
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'decodes null values' do
|
101
111
|
client.execute(%<INSERT INTO lots_of_types (id) VALUES (3)>)
|
102
112
|
result = client.execute(%<SELECT * FROM lots_of_types WHERE id = 3>)
|
103
113
|
row = result.first
|
@@ -120,5 +130,79 @@ describe 'Regressions' do
|
|
120
130
|
row['map_column'].should be_nil
|
121
131
|
row['set_column'].should be_nil
|
122
132
|
end
|
133
|
+
|
134
|
+
it 'encodes null values' do
|
135
|
+
statement = client.prepare(<<-CQL)
|
136
|
+
UPDATE lots_of_types
|
137
|
+
SET
|
138
|
+
ascii_column = ?,
|
139
|
+
bigint_column = ?,
|
140
|
+
blob_column = ?,
|
141
|
+
boolean_column = ?,
|
142
|
+
decimal_column = ?,
|
143
|
+
double_column = ?,
|
144
|
+
float_column = ?,
|
145
|
+
int_column = ?,
|
146
|
+
text_column = ?,
|
147
|
+
timestamp_column = ?,
|
148
|
+
uuid_column = ?,
|
149
|
+
varchar_column = ?,
|
150
|
+
varint_column = ?,
|
151
|
+
timeuuid_column = ?,
|
152
|
+
inet_column = ?,
|
153
|
+
list_column = ?,
|
154
|
+
map_column = ?,
|
155
|
+
set_column = ?
|
156
|
+
WHERE id = 1
|
157
|
+
CQL
|
158
|
+
statement.execute(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'with negative numbers' do
|
163
|
+
it 'decodes negative counters' do
|
164
|
+
client.execute(%<CREATE TABLE counters (id ASCII, counter1 COUNTER, PRIMARY KEY (id))>)
|
165
|
+
client.execute(%<UPDATE counters SET counter1 = counter1 - 1 WHERE id = 'foo'>)
|
166
|
+
result = client.execute(%<SELECT counter1 FROM counters WHERE id = 'foo'>)
|
167
|
+
result.first['counter1'].should == -1
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'decodes negative numbers' do
|
171
|
+
client.execute(<<-CQL)
|
172
|
+
CREATE TABLE lots_of_types (
|
173
|
+
id INT,
|
174
|
+
bigint_column BIGINT,
|
175
|
+
decimal_column DECIMAL,
|
176
|
+
double_column DOUBLE,
|
177
|
+
float_column FLOAT,
|
178
|
+
int_column INT,
|
179
|
+
varint_column VARINT,
|
180
|
+
PRIMARY KEY (id)
|
181
|
+
)
|
182
|
+
CQL
|
183
|
+
client.execute(%<INSERT INTO lots_of_types (id, bigint_column, decimal_column, double_column, float_column, int_column, varint_column) VALUES (0, -1, -1, -1, -1, -1, -1)>)
|
184
|
+
client.execute(%<INSERT INTO lots_of_types (id, bigint_column, decimal_column, double_column, float_column, int_column, varint_column) VALUES (1, -9223372036854775808, -0.0012095473475870063, -2.2250738585072014e-308, -1.175494351e-38, -2147483648, -23454545674351234123365765786894351234567456)>)
|
185
|
+
result = client.execute(%<SELECT * FROM lots_of_types WHERE id IN (0, 1)>)
|
186
|
+
row0, row1 = result.to_a
|
187
|
+
row0['bigint_column'].should == -1
|
188
|
+
row0['decimal_column'].should == -1
|
189
|
+
row0['double_column'].should == -1
|
190
|
+
row0['float_column'].should == -1
|
191
|
+
row0['int_column'].should == -1
|
192
|
+
row0['varint_column'].should == -1
|
193
|
+
row1['bigint_column'].should == -9223372036854775808
|
194
|
+
row1['decimal_column'].should == BigDecimal.new('-0.0012095473475870063')
|
195
|
+
row1['double_column'].should == be_within(1.0e-308).of(-2.2250738585072014e-308)
|
196
|
+
row1['float_column'].should be_within(1.0e-38).of(-1.175494351e-38)
|
197
|
+
row1['int_column'].should == -2147483648
|
198
|
+
row1['varint_column'].should == -23454545674351234123365765786894351234567456
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'with quoted keyspace names' do
|
203
|
+
it 'handles quoted keyspace names' do
|
204
|
+
client.use('"system"')
|
205
|
+
client.keyspace.should == 'system'
|
206
|
+
end
|
123
207
|
end
|
124
208
|
end
|
@@ -23,7 +23,9 @@ class FakeIoReactor
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def connect(host, port, timeout)
|
26
|
-
if
|
26
|
+
if host == '0.0.0.0'
|
27
|
+
Cql::Future.failed(Cql::Io::ConnectionError.new('Can\'t connect to 0.0.0.0'))
|
28
|
+
elsif @down_nodes.include?(host)
|
27
29
|
Cql::Future.failed(Cql::Io::ConnectionError.new('Node down'))
|
28
30
|
else
|
29
31
|
connection = FakeConnection.new(host, port, timeout)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cql-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.0.
|
4
|
+
version: 1.1.0.pre2
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-27 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A pure Ruby CQL3 driver for Cassandra
|
15
15
|
email:
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- spec/cql/protocol/requests/startup_request_spec.rb
|
92
92
|
- spec/cql/protocol/response_frame_spec.rb
|
93
93
|
- spec/cql/protocol/responses/schema_change_result_response_spec.rb
|
94
|
+
- spec/cql/protocol/type_converter_spec.rb
|
94
95
|
- spec/cql/time_uuid_spec.rb
|
95
96
|
- spec/cql/uuid_spec.rb
|
96
97
|
- spec/integration/client_spec.rb
|
@@ -105,7 +106,7 @@ files:
|
|
105
106
|
- spec/support/fake_server.rb
|
106
107
|
homepage: http://github.com/iconara/cql-rb
|
107
108
|
licenses:
|
108
|
-
- Apache
|
109
|
+
- Apache License 2.0
|
109
110
|
post_install_message:
|
110
111
|
rdoc_options: []
|
111
112
|
require_paths:
|
@@ -152,6 +153,7 @@ test_files:
|
|
152
153
|
- spec/cql/protocol/requests/startup_request_spec.rb
|
153
154
|
- spec/cql/protocol/response_frame_spec.rb
|
154
155
|
- spec/cql/protocol/responses/schema_change_result_response_spec.rb
|
156
|
+
- spec/cql/protocol/type_converter_spec.rb
|
155
157
|
- spec/cql/time_uuid_spec.rb
|
156
158
|
- spec/cql/uuid_spec.rb
|
157
159
|
- spec/integration/client_spec.rb
|