cql-rb 1.1.0.pre0 → 1.1.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|