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

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.
@@ -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