cql-rb 1.1.0.pre0 → 1.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cql/byte_buffer.rb +1 -0
- data/lib/cql/client.rb +20 -8
- data/lib/cql/client/asynchronous_client.rb +128 -43
- data/lib/cql/client/synchronous_client.rb +25 -5
- data/lib/cql/client/synchronous_prepared_statement.rb +4 -2
- data/lib/cql/future.rb +97 -0
- data/lib/cql/io/connection.rb +1 -0
- data/lib/cql/io/io_reactor.rb +2 -0
- data/lib/cql/protocol/cql_protocol_handler.rb +16 -1
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +286 -52
- data/spec/cql/client/synchronous_client_spec.rb +24 -1
- data/spec/cql/client/synchronous_prepared_statement_spec.rb +24 -0
- data/spec/cql/future_spec.rb +134 -0
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +7 -0
- data/spec/support/fake_io_reactor.rb +53 -13
- metadata +2 -4
- data/spec/cql/client/client_shared.rb +0 -27
@@ -1,7 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
-
require 'cql/client/client_shared'
|
5
4
|
|
6
5
|
|
7
6
|
module Cql
|
@@ -99,6 +98,30 @@ module Cql
|
|
99
98
|
client.async.should equal(async_client)
|
100
99
|
end
|
101
100
|
end
|
101
|
+
|
102
|
+
context 'when exceptions are raised' do
|
103
|
+
it 'replaces the backtrace of the asynchronous call to make it less confusing' do
|
104
|
+
error = CqlError.new('Bork')
|
105
|
+
error.set_backtrace(['Hello', 'World'])
|
106
|
+
future.stub(:get).and_raise(error)
|
107
|
+
async_client.stub(:execute).and_return(future)
|
108
|
+
begin
|
109
|
+
client.execute('SELECT * FROM something')
|
110
|
+
rescue CqlError => e
|
111
|
+
e.backtrace.first.should match(%r{/synchronous_client.rb:\d+:in `execute'})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'does not replace the backtrace of non-CqlError errors' do
|
116
|
+
future.stub(:get).and_raise('Bork')
|
117
|
+
async_client.stub(:execute).and_return(future)
|
118
|
+
begin
|
119
|
+
client.execute('SELECT * FROM something')
|
120
|
+
rescue => e
|
121
|
+
e.backtrace.first.should_not match(%r{/synchronous_client.rb:\d+:in `execute'})
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
102
125
|
end
|
103
126
|
end
|
104
127
|
end
|
@@ -60,6 +60,30 @@ module Cql
|
|
60
60
|
statement.async.should equal(async_statement)
|
61
61
|
end
|
62
62
|
end
|
63
|
+
|
64
|
+
context 'when exceptions are raised' do
|
65
|
+
it 'replaces the backtrace of the asynchronous call to make it less confusing' do
|
66
|
+
error = CqlError.new('Bork')
|
67
|
+
error.set_backtrace(['Hello', 'World'])
|
68
|
+
future.stub(:get).and_raise(error)
|
69
|
+
async_statement.stub(:execute).and_return(future)
|
70
|
+
begin
|
71
|
+
statement.execute('SELECT * FROM something')
|
72
|
+
rescue CqlError => e
|
73
|
+
e.backtrace.first.should match(%r{/synchronous_prepared_statement.rb:\d+:in `execute'})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'does not replace the backtrace of non-CqlError errors' do
|
78
|
+
future.stub(:get).and_raise('Bork')
|
79
|
+
async_statement.stub(:execute).and_return(future)
|
80
|
+
begin
|
81
|
+
statement.execute('SELECT * FROM something')
|
82
|
+
rescue => e
|
83
|
+
e.backtrace.first.should_not match(%r{/synchronous_prepared_statement.rb:\d+:in `execute'})
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
63
87
|
end
|
64
88
|
end
|
65
89
|
end
|
data/spec/cql/future_spec.rb
CHANGED
@@ -223,6 +223,60 @@ module Cql
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
+
describe '.first' do
|
227
|
+
context 'it returns a new future which' do
|
228
|
+
it 'is complete when the first of the source futures is complete' do
|
229
|
+
f1 = Future.new
|
230
|
+
f2 = Future.new
|
231
|
+
f3 = Future.new
|
232
|
+
ff = Future.first(f1, f2, f3)
|
233
|
+
f2.complete!
|
234
|
+
ff.should be_complete
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'completes with the value of the first source future' do
|
238
|
+
f1 = Future.new
|
239
|
+
f2 = Future.new
|
240
|
+
f3 = Future.new
|
241
|
+
ff = Future.first(f1, f2, f3)
|
242
|
+
f2.complete!('foo')
|
243
|
+
ff.get.should == 'foo'
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'is unaffected by the completion of the other futures' do
|
247
|
+
f1 = Future.new
|
248
|
+
f2 = Future.new
|
249
|
+
f3 = Future.new
|
250
|
+
ff = Future.first(f1, f2, f3)
|
251
|
+
f2.complete!
|
252
|
+
f1.complete!
|
253
|
+
f3.complete!
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'fails if all of the source futures fail' do
|
257
|
+
f1 = Future.new
|
258
|
+
f2 = Future.new
|
259
|
+
f3 = Future.new
|
260
|
+
ff = Future.first(f1, f2, f3)
|
261
|
+
f2.fail!(StandardError.new('bork'))
|
262
|
+
f1.fail!(StandardError.new('bork'))
|
263
|
+
f3.fail!(StandardError.new('bork'))
|
264
|
+
ff.should be_failed
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'fails with the error of the last future to fail' do
|
268
|
+
f1 = Future.new
|
269
|
+
f2 = Future.new
|
270
|
+
f3 = Future.new
|
271
|
+
ff = Future.first(f1, f2, f3)
|
272
|
+
f2.fail!(StandardError.new('bork2'))
|
273
|
+
f1.fail!(StandardError.new('bork1'))
|
274
|
+
f3.fail!(StandardError.new('bork3'))
|
275
|
+
expect { ff.get }.to raise_error('bork3')
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
226
280
|
describe '#map' do
|
227
281
|
context 'returns a new future that' do
|
228
282
|
it 'will complete with the result of the given block' do
|
@@ -271,6 +325,86 @@ module Cql
|
|
271
325
|
end
|
272
326
|
end
|
273
327
|
|
328
|
+
describe '#recover' do
|
329
|
+
context 'returns a new future that' do
|
330
|
+
it 'completes with a value when the source future fails' do
|
331
|
+
f1 = Future.new
|
332
|
+
f2 = f1.recover { 'foo' }
|
333
|
+
f1.fail!(StandardError.new('Bork!'))
|
334
|
+
f2.get.should == 'foo'
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'yields the error to the block' do
|
338
|
+
f1 = Future.new
|
339
|
+
f2 = f1.recover { |e| e.message }
|
340
|
+
f1.fail!(StandardError.new('Bork!'))
|
341
|
+
f2.get.should == 'Bork!'
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'completes with the value of the source future when the source future is successful' do
|
345
|
+
f1 = Future.new
|
346
|
+
f2 = f1.recover { 'foo' }
|
347
|
+
f1.complete!('bar')
|
348
|
+
f2.get.should == 'bar'
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'fails with the error raised in the given block' do
|
352
|
+
f1 = Future.new
|
353
|
+
f2 = f1.recover { raise 'Snork!' }
|
354
|
+
f1.fail!(StandardError.new('Bork!'))
|
355
|
+
expect { f2.get }.to raise_error('Snork!')
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
describe '#fallback' do
|
361
|
+
context 'returns a new future that' do
|
362
|
+
it 'completes with the value of the fallback future when the source future fails' do
|
363
|
+
f1 = Future.new
|
364
|
+
f2 = Future.new
|
365
|
+
f3 = f1.fallback { f2 }
|
366
|
+
f1.fail!(StandardError.new('Bork!'))
|
367
|
+
f2.complete!('foo')
|
368
|
+
f3.get.should == 'foo'
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'yields the error to the block' do
|
372
|
+
f1 = Future.new
|
373
|
+
f2 = Future.new
|
374
|
+
f3 = f1.fallback do |error|
|
375
|
+
Future.completed(error.message)
|
376
|
+
end
|
377
|
+
f1.fail!(StandardError.new('Bork!'))
|
378
|
+
f3.get.should == 'Bork!'
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'completes with the value of the source future when the source future succeeds' do
|
382
|
+
f1 = Future.new
|
383
|
+
f2 = Future.new
|
384
|
+
f3 = f1.fallback { f2 }
|
385
|
+
f2.complete!('bar')
|
386
|
+
f1.complete!('foo')
|
387
|
+
f3.get.should == 'foo'
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'fails when the block raises an error' do
|
391
|
+
f1 = Future.new
|
392
|
+
f2 = f1.fallback { raise 'Bork!' }
|
393
|
+
f1.fail!(StandardError.new('Splork!'))
|
394
|
+
expect { f2.get }.to raise_error('Bork!')
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'fails when the fallback future fails' do
|
398
|
+
f1 = Future.new
|
399
|
+
f2 = Future.new
|
400
|
+
f3 = f1.fallback { f2 }
|
401
|
+
f2.fail!(StandardError.new('Bork!'))
|
402
|
+
f1.fail!(StandardError.new('Fnork!'))
|
403
|
+
expect { f3.get }.to raise_error('Bork!')
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
274
408
|
describe '.completed' do
|
275
409
|
let :future do
|
276
410
|
described_class.completed('hello world')
|
@@ -41,6 +41,13 @@ module Cql
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
describe '#[]/#[]=' do
|
45
|
+
it 'is thread safe storage for arbitrary data' do
|
46
|
+
protocol_handler[:host_id] = 42
|
47
|
+
protocol_handler[:host_id].should == 42
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
44
51
|
describe '#send_request' do
|
45
52
|
before do
|
46
53
|
connection.stub(:write).and_yield(buffer)
|
@@ -10,20 +10,29 @@ class FakeIoReactor
|
|
10
10
|
@default_host = nil
|
11
11
|
@connection_listeners = []
|
12
12
|
@started_future = Cql::Future.new
|
13
|
-
@
|
13
|
+
@before_startup_handler = nil
|
14
|
+
@down_nodes = []
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
@
|
17
|
+
def node_down(hostname)
|
18
|
+
@down_nodes << hostname
|
19
|
+
end
|
20
|
+
|
21
|
+
def before_startup(&handler)
|
22
|
+
@before_startup_handler = handler
|
18
23
|
end
|
19
24
|
|
20
25
|
def connect(host, port, timeout)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
if @down_nodes.include?(host)
|
27
|
+
Cql::Future.failed(Cql::Io::ConnectionError.new('Node down'))
|
28
|
+
else
|
29
|
+
connection = FakeConnection.new(host, port, timeout)
|
30
|
+
@connections << connection
|
31
|
+
@connection_listeners.each do |listener|
|
32
|
+
listener.call(connection)
|
33
|
+
end
|
34
|
+
Cql::Future.completed(connection)
|
25
35
|
end
|
26
|
-
Cql::Future.completed(connection)
|
27
36
|
end
|
28
37
|
|
29
38
|
def on_connection(&listener)
|
@@ -32,8 +41,13 @@ class FakeIoReactor
|
|
32
41
|
|
33
42
|
def start
|
34
43
|
@running = true
|
35
|
-
|
36
|
-
|
44
|
+
if @before_startup_handler
|
45
|
+
@before_startup_handler = nil
|
46
|
+
Thread.start do
|
47
|
+
@before_startup_handler.call
|
48
|
+
@started_future.complete!(self)
|
49
|
+
end
|
50
|
+
elsif !(@started_future.complete? || @started_future.failed?)
|
37
51
|
@started_future.complete!(self)
|
38
52
|
end
|
39
53
|
@started_future
|
@@ -61,14 +75,28 @@ class FakeConnection
|
|
61
75
|
@responses = []
|
62
76
|
@closed = false
|
63
77
|
@keyspace = nil
|
78
|
+
@data = {}
|
79
|
+
@request_handler = method(:default_request_handler)
|
80
|
+
end
|
81
|
+
|
82
|
+
def [](key)
|
83
|
+
@data[key]
|
84
|
+
end
|
85
|
+
|
86
|
+
def []=(key, value)
|
87
|
+
@data[key] = value
|
88
|
+
end
|
89
|
+
|
90
|
+
def connected?
|
91
|
+
!@closed
|
64
92
|
end
|
65
93
|
|
66
94
|
def close
|
67
95
|
@closed = true
|
68
96
|
end
|
69
97
|
|
70
|
-
def
|
71
|
-
@
|
98
|
+
def handle_request(&handler)
|
99
|
+
@request_handler = handler
|
72
100
|
end
|
73
101
|
|
74
102
|
def send_request(request)
|
@@ -76,11 +104,23 @@ class FakeConnection
|
|
76
104
|
Cql::Future.failed(Cql::NotConnectedError.new)
|
77
105
|
else
|
78
106
|
@requests << request
|
79
|
-
response = @
|
107
|
+
response = @request_handler.call(request)
|
80
108
|
if response.is_a?(Cql::Protocol::SetKeyspaceResultResponse)
|
81
109
|
@keyspace = response.keyspace
|
82
110
|
end
|
83
111
|
Cql::Future.completed(response)
|
84
112
|
end
|
85
113
|
end
|
114
|
+
|
115
|
+
def default_request_handler(request)
|
116
|
+
response = @responses.shift
|
117
|
+
unless response
|
118
|
+
case request
|
119
|
+
when Cql::Protocol::StartupRequest
|
120
|
+
Cql::Protocol::ReadyResponse.new
|
121
|
+
when Cql::Protocol::QueryRequest
|
122
|
+
Cql::Protocol::RowsResultResponse.new([], [])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
86
126
|
end
|
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.pre1
|
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-07-
|
12
|
+
date: 2013-07-24 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A pure Ruby CQL3 driver for Cassandra
|
15
15
|
email:
|
@@ -71,7 +71,6 @@ files:
|
|
71
71
|
- spec/cql/byte_buffer_spec.rb
|
72
72
|
- spec/cql/client/asynchronous_client_spec.rb
|
73
73
|
- spec/cql/client/asynchronous_prepared_statement_spec.rb
|
74
|
-
- spec/cql/client/client_shared.rb
|
75
74
|
- spec/cql/client/column_metadata_spec.rb
|
76
75
|
- spec/cql/client/request_runner_spec.rb
|
77
76
|
- spec/cql/client/synchronous_client_spec.rb
|
@@ -133,7 +132,6 @@ test_files:
|
|
133
132
|
- spec/cql/byte_buffer_spec.rb
|
134
133
|
- spec/cql/client/asynchronous_client_spec.rb
|
135
134
|
- spec/cql/client/asynchronous_prepared_statement_spec.rb
|
136
|
-
- spec/cql/client/client_shared.rb
|
137
135
|
- spec/cql/client/column_metadata_spec.rb
|
138
136
|
- spec/cql/client/request_runner_spec.rb
|
139
137
|
- spec/cql/client/synchronous_client_spec.rb
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
shared_context 'client setup' do
|
4
|
-
let :connection_options do
|
5
|
-
{:host => 'example.com', :port => 12321, :io_reactor => io_reactor}
|
6
|
-
end
|
7
|
-
|
8
|
-
let :io_reactor do
|
9
|
-
FakeIoReactor.new
|
10
|
-
end
|
11
|
-
|
12
|
-
def connections
|
13
|
-
io_reactor.connections
|
14
|
-
end
|
15
|
-
|
16
|
-
def last_connection
|
17
|
-
connections.last
|
18
|
-
end
|
19
|
-
|
20
|
-
def requests
|
21
|
-
last_connection.requests
|
22
|
-
end
|
23
|
-
|
24
|
-
def last_request
|
25
|
-
requests.last
|
26
|
-
end
|
27
|
-
end
|