eurydice 1.1.0.b4-java → 1.1.1.b1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ module Eurydice
4
+ shared_examples 'Cluster' do |cluster|
5
+ describe '#keyspace' do
6
+ before do
7
+ @keyspace_name = "eurydice_test_space_#{rand(1000)}"
8
+ if @cluster.keyspaces.include?(@keyspace_name)
9
+ @cluster.keyspace(@keyspace_name).drop!
10
+ end
11
+ end
12
+
13
+ it 'can connect' do
14
+ @cluster = Eurydice.connect
15
+ @cluster.should be_connected
16
+ end
17
+
18
+ after do
19
+ @keyspace.drop! rescue nil
20
+ end
21
+
22
+ it 'creates a keyspace' do
23
+ @keyspace = @cluster.keyspace(@keyspace_name)
24
+ @keyspace.exists?.should be_true
25
+ end
26
+
27
+ it 'defers the creation of a keyspace with :create => false' do
28
+ @keyspace = @cluster.keyspace(@keyspace_name, :create => false)
29
+ @keyspace.exists?.should be_false
30
+ @keyspace.create!
31
+ @keyspace.exists?.should be_true
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,593 @@
1
+ # encoding: utf-8
2
+
3
+ module Eurydice
4
+ shared_examples 'ColumnFamily' do
5
+ describe '#create!' do
6
+ after do
7
+ @cf.drop! rescue nil
8
+ end
9
+
10
+ it 'can create a column family' do
11
+ @cf = @keyspace.column_family(@cf_name)
12
+ @cf.exists?.should be_true
13
+ end
14
+
15
+ it 'defers the creation of a keyspace with :create => false' do
16
+ @cf = @keyspace.column_family(@cf_name, :create => false)
17
+ @cf.exists?.should be_false
18
+ @cf.create!
19
+ @cf.exists?.should be_true
20
+ end
21
+
22
+ marshal_types = {
23
+ 'a fully qualified name' => 'org.apache.cassandra.db.marshal.UTF8Type',
24
+ 'the package name omitted' => 'UTF8Type',
25
+ 'an alias' => :utf8
26
+ }
27
+
28
+ context 'creating a column family with a specific comparator type' do
29
+ marshal_types.each do |desc, type|
30
+ it "with #{desc}" do
31
+ @cf = @keyspace.column_family(@cf_name, :create => false)
32
+ @cf.create!(:comparator_type => type)
33
+ @cf.definition(true)[:comparator_type].should == 'org.apache.cassandra.db.marshal.UTF8Type'
34
+ end
35
+ end
36
+ end
37
+
38
+ context 'creating a column family with a specific subcomparator type' do
39
+ marshal_types.each do |desc, type|
40
+ it "with #{desc}" do
41
+ @cf = @keyspace.column_family(@cf_name, :create => false)
42
+ @cf.create!(:column_type => :super, :subcomparator_type => type)
43
+ @cf.definition(true)[:subcomparator_type].should == 'org.apache.cassandra.db.marshal.UTF8Type'
44
+ end
45
+ end
46
+ end
47
+
48
+ context 'creating a column family with a specific default validation class' do
49
+ marshal_types.each do |desc, type|
50
+ it "with #{desc}" do
51
+ @cf = @keyspace.column_family(@cf_name, :create => false)
52
+ @cf.create!(:default_validation_class => type)
53
+ @cf.definition(true)[:default_validation_class].should == 'org.apache.cassandra.db.marshal.UTF8Type'
54
+ end
55
+ end
56
+ end
57
+
58
+ context 'creating a column family with a specific validation class for a column' do
59
+ marshal_types.each do |desc, type|
60
+ it "with #{desc}" do
61
+ @cf = @keyspace.column_family(@cf_name, :create => false)
62
+ @cf.create!(:column_metadata => {'xyz' => {:validation_class => type}})
63
+ @cf.definition(true)[:column_metadata]['xyz'][:validation_class].should == 'org.apache.cassandra.db.marshal.UTF8Type'
64
+ end
65
+ end
66
+ end
67
+
68
+ it 'creates a column family with an index' do
69
+ @cf = @keyspace.column_family(@cf_name, :create => false)
70
+ @cf.create!(:column_metadata => {'xyz' => {:index_name => 'abc', :index_type => :keys, :validation_class => :ascii}})
71
+ @cf.definition(true)[:column_metadata]['xyz'][:index_name].should == 'abc'
72
+ end
73
+
74
+ it 'creates a column family with a specific column type' do
75
+ @cf = @keyspace.column_family(@cf_name, :create => false)
76
+ @cf.create!(:column_type => :super)
77
+ @cf.definition(true)[:column_type].should == :super
78
+ end
79
+ end
80
+
81
+ describe '#drop!' do
82
+ it 'drops the column family' do
83
+ cf = @keyspace.column_family('test_family')
84
+ cf.drop!
85
+ cf.exists?.should_not be_true
86
+ end
87
+ end
88
+
89
+ describe '#truncate!' do
90
+ before do
91
+ @cf = @keyspace.column_family('test_family', :create => false)
92
+ @cf.drop! rescue nil
93
+ @cf.create!
94
+ end
95
+
96
+ after do
97
+ @cf.drop!
98
+ end
99
+
100
+ it 'removes all rows' do
101
+ @cf.insert('ABC', {'test' => 'abc'})
102
+ @cf.insert('DEF', {'test' => 'def'})
103
+ @cf.insert('GHI', {'test' => 'ghi'})
104
+ @cf.truncate!
105
+ @cf.get('ABC').should be_nil
106
+ @cf.get('DEF').should be_nil
107
+ @cf.get('GHI').should be_nil
108
+ end
109
+ end
110
+
111
+ describe '#definition' do
112
+ before do
113
+ @cf = @keyspace.column_family('test_family', :create => false)
114
+ @cf.drop! rescue nil
115
+ @cf.create!
116
+ end
117
+
118
+ after do
119
+ @cf.drop!
120
+ end
121
+
122
+ it 'returns column family metadata' do
123
+ definition = @cf.definition
124
+ definition[:name].should == 'test_family'
125
+ definition[:default_validation_class].should == 'org.apache.cassandra.db.marshal.BytesType'
126
+ end
127
+ end
128
+
129
+ describe '#key?' do
130
+ before do
131
+ @cf = @keyspace.column_family('test_family', :create => false)
132
+ @cf.drop! rescue nil
133
+ @cf.create!
134
+ end
135
+
136
+ after do
137
+ @cf.drop!
138
+ end
139
+
140
+ it 'returns true if a row with the specified key exists' do
141
+ @cf.insert('ABC', 'xyz' => 'def')
142
+ @cf.key?('ABC').should be_true
143
+ end
144
+
145
+ it 'returns false if a row with the specified key does not exist' do
146
+ @cf.key?('XYZ').should be_false
147
+ end
148
+
149
+ it 'returns false if a row has no columns' do
150
+ @cf.insert('ABC', 'xyz' => 'def')
151
+ @cf.delete_column('ABC', 'xyz')
152
+ @cf.key?('ABC').should be_false
153
+ end
154
+
155
+ it 'is aliased as #row_exists?' do
156
+ @cf.insert('ABC', 'xyz' => 'def')
157
+ @cf.row_exists?('ABC').should be_true
158
+ end
159
+ end
160
+
161
+ context 'loading, storing and removing' do
162
+ before do
163
+ @cf = @keyspace.column_family('test_family')
164
+ @cf.truncate!
165
+ end
166
+
167
+ before do
168
+ @counter_cf = @keyspace.column_family('counter_family', :create => false)
169
+ @counter_cf.create!(:default_validation_class => :counter) unless @counter_cf.exists?
170
+ @counter_cf.truncate!
171
+ end
172
+
173
+ describe '#update/#insert' do
174
+ it 'writes a column' do
175
+ @cf.insert('ABC', 'xyz' => 'abc')
176
+ @cf.get('ABC').should == {'xyz' => 'abc'}
177
+ end
178
+
179
+ it '#update and #insert are synonyms' do
180
+ @cf.update('ABC', 'foo' => 'bar')
181
+ @cf.insert('ABC', 'xyz' => 'abc')
182
+ @cf.get('ABC').should == {'xyz' => 'abc', 'foo' => 'bar'}
183
+ end
184
+
185
+ it 'writes many columns' do
186
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
187
+ @cf.get('ABC').should == {'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar'}
188
+ end
189
+
190
+ it 'writes with a custom consistency level' do
191
+ # TODO: not sure how to test, this just tests that no error is raised
192
+ @cf.insert('ABC', {'xyz' => 'abc'}, {:consistency_level => :quorum})
193
+ @cf.get('ABC').should == {'xyz' => 'abc'}
194
+ end
195
+
196
+ it 'writes with a custom consistency level (:cl is an alias for :consistency_level)' do
197
+ # TODO: not sure how to test, this just tests that no error is raised
198
+ @cf.insert('ABC', {'xyz' => 'abc'}, {:cl => :one})
199
+ @cf.get('ABC').should == {'xyz' => 'abc'}
200
+ end
201
+
202
+ it 'writes a column with a TTL' do
203
+ @cf.insert('ABC', {'xyz' => 'abc'}, {:ttl => 1})
204
+ tries = 5
205
+ begin
206
+ sleep(0.5)
207
+ @cf.get('ABC').should be_nil
208
+ rescue RSpec::Expectations::ExpectationNotMetError => e
209
+ # since we're testing timings in an external system we need to wait, but
210
+ # to not block the tests by waiting excessively long we wait for a short
211
+ # time and retry the test a few times
212
+ if (tries -= 1) > 0
213
+ retry
214
+ else
215
+ raise
216
+ end
217
+ end
218
+ end
219
+
220
+ context 'with explicit column data types' do
221
+ it 'writes integer columns keys as longs' do
222
+ @cf.insert('ABC', {42 => 'foo'}, :comparator => :long)
223
+ @cf.get('ABC', :comparator => :long).should == {42 => 'foo'}
224
+ end
225
+
226
+ it 'writes integer values as longs' do
227
+ @cf.insert('ABC', {'xyz' => 3}, :validations => {'xyz' => :long})
228
+ @cf.get('ABC', :validations => {'xyz' => :long}).should == {'xyz' => 3}
229
+ end
230
+ end
231
+ end
232
+
233
+ describe '#increment' do
234
+ it 'can increment a counter column' do
235
+ @counter_cf.increment('ABC', 'count')
236
+ @counter_cf.get_column('ABC', 'count').should == 1
237
+ end
238
+
239
+ it 'can increment a counter column by the specified amount' do
240
+ @counter_cf.increment('ABC', 'count', 3)
241
+ @counter_cf.increment('ABC', 'count', 2)
242
+ @counter_cf.get_column('ABC', 'count').should == 5
243
+ end
244
+
245
+ [:inc, :incr, :increment_column].each do |name|
246
+ it "is aliased as #{name}" do
247
+ @counter_cf.send(name, 'ABC', 'count')
248
+ @counter_cf.get_column('ABC', 'count').should == 1
249
+ end
250
+ end
251
+ end
252
+
253
+ describe '#get' do
254
+ context 'with a single row key' do
255
+ it 'loads a row' do
256
+ @cf.insert('ABC', 'xyz' => 'abc')
257
+ @cf.get('ABC').should == {'xyz' => 'abc'}
258
+ end
259
+
260
+ it 'loads all columns for a row by default' do
261
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
262
+ @cf.get('ABC').should == {'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar'}
263
+ end
264
+
265
+ it 'loads the specified columns' do
266
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
267
+ @cf.get('ABC', :columns => %w(hello foo)).should == {'hello' => 'world', 'foo' => 'bar'}
268
+ end
269
+
270
+ it 'loads the specified range of columns' do
271
+ @cf.insert('ABC', 'a' => 'A', 'd' => 'D', 'f' => 'F', 'g' => 'G', 'b' => 'B', 'x' => 'X')
272
+ @cf.get('ABC', :columns => 'b'...'f').should == {'b' => 'B', 'd' => 'D', 'f' => 'F'}
273
+ end
274
+
275
+ it 'loads a max number of columns' do
276
+ @cf.insert('ABC', Hash[('a'..'z').map { |a| [a, a.upcase] }.shuffle])
277
+ @cf.get('ABC', :max_column_count => 10).should == Hash[('a'..'z').take(10).map { |a| [a, a.upcase] }]
278
+ end
279
+
280
+ it 'loads a page of columns' do
281
+ @cf.insert('ABC', Hash[('a'..'z').map { |a| [a, a.upcase] }.shuffle])
282
+ @cf.get('ABC', :from_column => 'm', :max_column_count => 10).should == Hash[('m'..'z').take(10).map { |a| [a, a.upcase] }]
283
+ end
284
+
285
+ it 'raises an error if both :columns and :from_column are given' do
286
+ expect { @cf.get('ABC', :columns => 'a'..'z', :from_column => 'm') }.to raise_error(ArgumentError)
287
+ end
288
+
289
+ it 'loads columns in reverse order with :reversed => true' do
290
+ @cf.insert('ABC', Hash[('a'..'f').map { |a| [a, a.upcase] }.shuffle])
291
+ @cf.get('ABC', :reversed => true).keys.should == ('a'..'f').to_a.reverse
292
+ end
293
+
294
+ it 'returns nil if no row was found' do
295
+ @cf.get('XYZ').should be_nil
296
+ end
297
+
298
+ it 'loads the value of counter columns' do
299
+ @counter_cf.increment('ABC', 'a', 1)
300
+ @counter_cf.increment('ABC', 'b', 2)
301
+ @counter_cf.increment('ABC', 'c', 4)
302
+ @counter_cf.increment('ABC', 'd', 8)
303
+ @counter_cf.get('ABC', :columns => %w(a b c d)).should == {'a' => 1, 'b' => 2, 'c' => 4, 'd' => 8}
304
+ end
305
+ end
306
+
307
+ context 'with multiple row keys' do
308
+ it 'loads multiple rows' do
309
+ @cf.insert('ABC', 'xyz' => 'abc', 'foo' => 'bar')
310
+ @cf.insert('DEF', 'xyz' => 'def', 'hello' => 'world')
311
+ @cf.insert('GHI', 'xyz' => 'ghi', 'foo' => 'oof')
312
+ @cf.get(%w(ABC GHI)).should == {
313
+ 'ABC' => {'xyz' => 'abc', 'foo' => 'bar'},
314
+ 'GHI' => {'xyz' => 'ghi', 'foo' => 'oof'}
315
+ }
316
+ end
317
+
318
+ it 'does not include rows that do not exist in the result' do
319
+ @cf.insert('ABC', 'xyz' => 'abc', 'foo' => 'bar')
320
+ @cf.insert('DEF', 'xyz' => 'def', 'hello' => 'world')
321
+ @cf.insert('GHI', 'xyz' => 'ghi', 'foo' => 'oof')
322
+ @cf.get(%w(ABC GHI XYZ)).should == {
323
+ 'ABC' => {'xyz' => 'abc', 'foo' => 'bar'},
324
+ 'GHI' => {'xyz' => 'ghi', 'foo' => 'oof'}
325
+ }
326
+ end
327
+
328
+ it 'loads columns for multiple rows' do
329
+ @cf.insert('ABC', 'xyz' => 'abc', 'foo' => 'bar')
330
+ @cf.insert('DEF', 'xyz' => 'def', 'hello' => 'world')
331
+ @cf.insert('GHI', 'xyz' => 'ghi', 'foo' => 'oof')
332
+ @cf.get(%w(ABC GHI), :columns => %w(xyz foo)).should == {'ABC' => {'xyz' => 'abc', 'foo' => 'bar'}, 'GHI' => {'xyz' => 'ghi', 'foo' => 'oof'}}
333
+ end
334
+
335
+ it 'does not include rows that do not have the specified column' do
336
+ @cf.insert('ABC', 'foo' => 'bar')
337
+ @cf.insert('DEF', 'xyz' => 'def', 'hello' => 'world')
338
+ @cf.insert('GHI', 'xyz' => 'ghi', 'foo' => 'oof', 'abc' => '123')
339
+ @cf.get(%w(ABC GHI), :columns => %w(xyz abc)).should == {'GHI' => {'xyz' => 'ghi', 'abc' => '123'}}
340
+ end
341
+
342
+ it 'does not include rows that do not exist in the results' do
343
+ @cf.insert('DEF', 'xyz' => 'def', 'hello' => 'world')
344
+ @cf.insert('GHI', 'xyz' => 'ghi', 'foo' => 'oof')
345
+ @cf.get(%w(ABC GHI), :columns => %w(xyz foo)).should == {'GHI' => {'xyz' => 'ghi', 'foo' => 'oof'}}
346
+ end
347
+
348
+ it 'loads all columns in a range from multiple rows' do
349
+ @cf.insert('ABC', 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D')
350
+ @cf.insert('DEF', 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'f' => 'F')
351
+ @cf.insert('GHI', 'a' => 'A', 'b' => 'B', 'd' => 'D')
352
+ @cf.get(%w(ABC GHI), :columns => 'b'...'d').should == {
353
+ 'ABC' => {'b' => 'B', 'c' => 'C', 'd' => 'D'},
354
+ 'GHI' => {'b' => 'B', 'd' => 'D'}
355
+ }
356
+ end
357
+
358
+ it 'returns an empty hash if no rows exist' do
359
+ @cf.get(%w(ABC GHI)).should == {}
360
+ end
361
+
362
+ it 'loads the value of counter columns' do
363
+ @counter_cf.increment('ABC', 'a', 1)
364
+ @counter_cf.increment('ABC', 'b', 2)
365
+ @counter_cf.increment('DEF', 'c', 4)
366
+ @counter_cf.increment('DEF', 'd', 8)
367
+ @counter_cf.get(%w(ABC DEF), :columns => %w(a b c d)).should == {
368
+ 'ABC' => {'a' => 1, 'b' => 2},
369
+ 'DEF' => {'c' => 4, 'd' => 8}
370
+ }
371
+ end
372
+ end
373
+
374
+ context 'with options' do
375
+ it 'loads with a custom consistency level' do
376
+ # TODO: not sure how to test, this just tests that no error is raised
377
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
378
+ @cf.get('ABC', :consistency_level => :quorum).should == {'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar'}
379
+ end
380
+
381
+ it 'loads with a custom consistency level (:cl is an alias for :consistency_level)' do
382
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
383
+ @cf.get('ABC', :cl => :one).should == {'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar'}
384
+ end
385
+ end
386
+ end
387
+
388
+ describe '#get_column' do
389
+ it 'loads a single column for a row' do
390
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
391
+ @cf.get_column('ABC', 'hello').should == 'world'
392
+ end
393
+
394
+ it 'loads with a custom consistency level' do
395
+ # TODO: not sure how to test, this just tests that no error is raised
396
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
397
+ @cf.get_column('ABC', 'hello', :consistency_level => :quorum).should == 'world'
398
+ end
399
+
400
+ it 'loads with a custom consistency level (:cl is an alias for :consistency_level)' do
401
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
402
+ @cf.get_column('ABC', 'hello', :cl => :one).should == 'world'
403
+ end
404
+
405
+ it 'returns nil if no row was found' do
406
+ @cf.get_column('XYZ', 'abc').should be_nil
407
+ end
408
+
409
+ it 'returns nil if no column was found' do
410
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
411
+ @cf.get_column('XYZ', 'abc').should be_nil
412
+ end
413
+
414
+ it 'returns the value of a counter column' do
415
+ @counter_cf.increment('ABC', 'x', 8)
416
+ @counter_cf.get_column('ABC', 'x').should == 8
417
+ end
418
+ end
419
+
420
+ describe '#get_column_count' do
421
+ it 'returns the number of columns in the specified row' do
422
+ @cf.insert('ABC', Hash[('a'..'z').zip(0..100)])
423
+ @cf.get_column_count('ABC').should == 26
424
+ end
425
+
426
+ it 'returns zero if the row does not exist' do
427
+ @cf.get_column_count('X').should == 0
428
+ end
429
+
430
+ it 'returns the number of columns in the specified range' do
431
+ @cf.insert('ABC', Hash[('a'..'z').zip(0..100)])
432
+ @cf.get_column_count('ABC', :columns => 'm'..'q').should == 5
433
+ end
434
+
435
+ it 'returns the number of columns after the specified column' do
436
+ @cf.insert('ABC', Hash[('a'..'z').zip(0..100)])
437
+ @cf.get_column_count('ABC', :from_column => 's').should == 8
438
+ end
439
+ end
440
+
441
+ describe '#each_column' do
442
+ before do
443
+ @cf.insert('ABC', Hash[('a'..'z').map { |a| [a, a.upcase] }.shuffle])
444
+ end
445
+
446
+ it 'yields each column in a row' do
447
+ row = {}
448
+ @cf.each_column('ABC') do |k, v|
449
+ row[k] = v
450
+ end
451
+ row.should == Hash[('a'..'z').map { |a| [a, a.upcase] }]
452
+ end
453
+
454
+ it 'returns an Enumerator that yields each column in a row' do
455
+ row = {}
456
+ enum = @cf.each_column('ABC')
457
+ enum.each do |pair|
458
+ k, v = *pair # JRuby 1.6.4 Enumerator#each does not splat the arguments
459
+ row[k] = v
460
+ end
461
+ row.should == Hash[('a'..'z').map { |a| [a, a.upcase] }]
462
+ end
463
+
464
+ it 'yields each column in reverse order with :reversed => true' do
465
+ column_keys = []
466
+ @cf.each_column('ABC', :reversed => true) do |k, v|
467
+ column_keys << k
468
+ end
469
+ column_keys.should == ('a'..'z').to_a.reverse
470
+ end
471
+
472
+ it 'can start after a specified key' do
473
+ column_keys = []
474
+ @cf.each_column('ABC', :start_beyond => 'w') do |k, v|
475
+ column_keys << k
476
+ end
477
+ column_keys.should == ('x'..'z').to_a
478
+ end
479
+
480
+ it 'can use a custom batch size' do
481
+ # TODO: not sure how to test, this just tests that no error is raised
482
+ row = {}
483
+ @cf.each_column('ABC', :batch_size => 2) do |k, v|
484
+ row[k] = v
485
+ end
486
+ row.should == Hash[('a'..'z').map { |a| [a, a.upcase] }]
487
+ end
488
+
489
+ it 'loads with a custom consistency level' do
490
+ # TODO: not sure how to test, this just tests that no error is raised
491
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
492
+ @cf.each_column('ABC', :consistency_level => :quorum) do |k, v|
493
+ end
494
+ end
495
+
496
+ it 'loads with a custom consistency level (:cl is an alias for :consistency_level)' do
497
+ @cf.insert('ABC', 'xyz' => 'abc', 'hello' => 'world', 'foo' => 'bar')
498
+ @cf.each_column('ABC', :cl => :one) do |k, v|
499
+ end
500
+ end
501
+ end
502
+
503
+ describe '#get_indexed' do
504
+ before do
505
+ @cf = @keyspace.column_family('indexed_test_family', :create => false)
506
+ @cf.drop! rescue nil
507
+ @cf.create!(:column_metadata => {
508
+ 'name' => {
509
+ :validation_class => :ascii,
510
+ :index_name => 'name_index',
511
+ :index_type => :keys
512
+ },
513
+ 'age' => {
514
+ :validation_class => :long,
515
+ :index_name => 'age_index',
516
+ :index_type => :keys
517
+ }
518
+ })
519
+ end
520
+
521
+ it 'loads rows by index' do
522
+ @cf.insert('user1', {'name' => 'sue'})
523
+ @cf.insert('user2', {'name' => 'phil'})
524
+ @cf.get_indexed('name', :==, 'sue').should == {'user1' => {'name' => 'sue'}}
525
+ end
526
+
527
+ it 'loads rows by index (using :eq instead of :==)' do
528
+ @cf.insert('user1', {'name' => 'sue'})
529
+ @cf.insert('user2', {'name' => 'phil'})
530
+ @cf.get_indexed('name', :eq, 'sue').should == {'user1' => {'name' => 'sue'}}
531
+ end
532
+
533
+ it 'limits the number of returned rows' do
534
+ names = %w(sue phil sam jim)
535
+ 100.times do |i|
536
+ row = {'name' => names[i % names.size], 'age' => i % names.size}
537
+ @cf.insert("user:#{i}", row, :validations => {'age' => :long})
538
+ end
539
+ @cf.get_indexed('age', :==, 3, :max_row_count => 3, :validations => {'age' => :long}).should have(3).items
540
+ end
541
+
542
+ it 'raises an error if the index operator is not supported' do
543
+ expect { @cf.get_indexed('name', :%, 'me') }.to raise_error(ArgumentError)
544
+ end
545
+ end
546
+
547
+ describe '#delete' do
548
+ it 'removes a row' do
549
+ @cf.insert('ABC', 'xyz' => 'abc')
550
+ @cf.delete('ABC')
551
+ @cf.get('ABC').should == nil
552
+ end
553
+ end
554
+
555
+ describe '#delete_column' do
556
+ it 'removes a single column' do
557
+ @cf.insert('ABC', 'xyz' => 'abc', 'foo' => 'bar')
558
+ @cf.delete_column('ABC', 'foo')
559
+ @cf.get('ABC').should == {'xyz' => 'abc'}
560
+ end
561
+ end
562
+
563
+ describe '#delete_columns' do
564
+ it 'removes multiple columns' do
565
+ @cf.insert('ABC', 'xyz' => 'abc', 'foo' => 'bar', 'hello' => 'world')
566
+ @cf.delete_columns('ABC', %w(foo xyz))
567
+ @cf.get('ABC').should == {'hello' => 'world'}
568
+ end
569
+ end
570
+ end
571
+
572
+ context 'batch mutation' do
573
+ before do
574
+ @cf = @keyspace.column_family('test_family')
575
+ @cf.truncate!
576
+ end
577
+
578
+ describe '#batch' do
579
+ it 'yields a batch object which can be used to perform multiple mutations' do
580
+ @cf.batch do |batch|
581
+ batch.update('first_row', 'col1' => 'hello1', 'col2' => 'hello2')
582
+ batch.update('second_row', 'col1' => 'hello3', 'xyz' => 'hello', 'abc' => 'hello')
583
+ batch.delete_column('first_row', 'col2')
584
+ batch.delete_columns('second_row', %w(xyz abc))
585
+ batch.update('first_row', 'col3' => 'hello4')
586
+ end
587
+ @cf.get('first_row').should == {'col1' => 'hello1', 'col3' => 'hello4'}
588
+ @cf.get('second_row').should == {'col1' => 'hello3'}
589
+ end
590
+ end
591
+ end
592
+ end
593
+ end