jamesgolick-cassandra 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,141 @@
1
+ # OrderedHash is namespaced to prevent conflicts with other implementations
2
+ class Cassandra
3
+ # Hash is ordered in Ruby 1.9!
4
+ if RUBY_VERSION >= '1.9'
5
+ OrderedHash = ::Hash
6
+ else
7
+ class OrderedHash < Hash #:nodoc:
8
+ def initialize(*args, &block)
9
+ super
10
+ @keys = []
11
+ end
12
+
13
+ def self.[](*args)
14
+ ordered_hash = new
15
+
16
+ if (args.length == 1 && args.first.is_a?(Array))
17
+ args.first.each do |key_value_pair|
18
+ next unless (key_value_pair.is_a?(Array))
19
+ ordered_hash[key_value_pair[0]] = key_value_pair[1]
20
+ end
21
+
22
+ return ordered_hash
23
+ end
24
+
25
+ unless (args.size % 2 == 0)
26
+ raise ArgumentError.new("odd number of arguments for Hash")
27
+ end
28
+
29
+ args.each_with_index do |val, ind|
30
+ next if (ind % 2 != 0)
31
+ ordered_hash[val] = args[ind + 1]
32
+ end
33
+
34
+ ordered_hash
35
+ end
36
+
37
+ def initialize_copy(other)
38
+ super
39
+ # make a deep copy of keys
40
+ @keys = other.keys
41
+ end
42
+
43
+ def []=(key, value)
44
+ @keys << key if !has_key?(key)
45
+ super
46
+ end
47
+
48
+ def delete(key)
49
+ if has_key? key
50
+ index = @keys.index(key)
51
+ @keys.delete_at index
52
+ end
53
+ super
54
+ end
55
+
56
+ def delete_if
57
+ super
58
+ sync_keys!
59
+ self
60
+ end
61
+
62
+ def reject!
63
+ super
64
+ sync_keys!
65
+ self
66
+ end
67
+
68
+ def reject(&block)
69
+ dup.reject!(&block)
70
+ end
71
+
72
+ def keys
73
+ @keys.dup
74
+ end
75
+
76
+ def values
77
+ @keys.collect { |key| self[key] }
78
+ end
79
+
80
+ def to_hash
81
+ self
82
+ end
83
+
84
+ def to_a
85
+ @keys.map { |key| [ key, self[key] ] }
86
+ end
87
+
88
+ def each_key
89
+ @keys.each { |key| yield key }
90
+ end
91
+
92
+ def each_value
93
+ @keys.each { |key| yield self[key]}
94
+ end
95
+
96
+ def each
97
+ @keys.each {|key| yield [key, self[key]]}
98
+ end
99
+
100
+ alias_method :each_pair, :each
101
+
102
+ def clear
103
+ super
104
+ @keys.clear
105
+ self
106
+ end
107
+
108
+ def shift
109
+ k = @keys.first
110
+ v = delete(k)
111
+ [k, v]
112
+ end
113
+
114
+ def merge!(other_hash)
115
+ other_hash.each {|k,v| self[k] = v }
116
+ self
117
+ end
118
+
119
+ def merge(other_hash)
120
+ dup.merge!(other_hash)
121
+ end
122
+
123
+ # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
124
+ def replace(other)
125
+ super
126
+ @keys = other.keys
127
+ self
128
+ end
129
+
130
+ def inspect
131
+ "#<OrderedHash #{super}>"
132
+ end
133
+
134
+ private
135
+
136
+ def sync_keys!
137
+ @keys.delete_if {|k| !has_key?(k)}
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,92 @@
1
+
2
+ class Cassandra
3
+ # Inner methods for actually doing the Thrift calls
4
+ module Protocol #:nodoc:
5
+ private
6
+
7
+ def _mutate(mutation_map, consistency_level)
8
+ client.batch_mutate(@keyspace, mutation_map, consistency_level)
9
+ end
10
+
11
+ def _remove(key, column_path, timestamp, consistency_level)
12
+ client.remove(@keyspace, key, column_path, timestamp, consistency_level)
13
+ end
14
+
15
+ def _count_columns(column_family, key, super_column, consistency)
16
+ client.get_count(@keyspace, key,
17
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
18
+ consistency
19
+ )
20
+ end
21
+
22
+ def _get_columns(column_family, key, columns, sub_columns, consistency)
23
+ result = if is_super(column_family)
24
+ if sub_columns
25
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
26
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
27
+ CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
28
+ consistency))
29
+ else
30
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
31
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
32
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
33
+ consistency))
34
+ end
35
+ else
36
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
37
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
38
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
39
+ consistency))
40
+ end
41
+
42
+ klass = column_name_class(column_family)
43
+ (sub_columns || columns).map { |name| result[klass.new(name)] }
44
+ end
45
+
46
+ def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
47
+ # Single values; count and range parameters have no effect
48
+ if is_super(column_family) and sub_column
49
+ column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :super_column => column, :column => sub_column)
50
+ multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
51
+ elsif !is_super(column_family) and column
52
+ column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :column => column)
53
+ multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
54
+
55
+ # Slices
56
+ else
57
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
58
+ CassandraThrift::SliceRange.new(
59
+ :reversed => reversed,
60
+ :count => count,
61
+ :start => start,
62
+ :finish => finish))
63
+
64
+ if is_super(column_family) and column
65
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
66
+ multi_sub_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
67
+ else
68
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
69
+ multi_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
70
+ end
71
+ end
72
+ end
73
+
74
+ def _get_range(column_family, start, finish, count, consistency)
75
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
76
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range => CassandraThrift::SliceRange.new(:start => '', :finish => ''))
77
+ range = CassandraThrift::KeyRange.new(:start_key => start, :end_key => finish, :count => count)
78
+ client.get_range_slices(@keyspace, column_parent, predicate, range, 1)
79
+ end
80
+
81
+ def _get_range_keys(column_family, start, finish, count, consistency)
82
+ _get_range(column_family, start, finish, count, consistency).collect{|i| i.key }
83
+ end
84
+
85
+ def each_key(column_family)
86
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family.to_s)
87
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [])
88
+ range = CassandraThrift::KeyRange.new(:start_key => '', :end_key => '')
89
+ client.get_range_slices(@keyspace, column_parent, predicate, range, 1).each{|i| yield i.key }
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,11 @@
1
+
2
+ class Time
3
+ def self.stamp
4
+ Time.now.stamp
5
+ end
6
+
7
+ def stamp
8
+ to_i * 1_000_000 + usec
9
+ end
10
+ end
11
+
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class CassandraClientTest < Test::Unit::TestCase
4
+ include Cassandra::Constants
5
+
6
+ def setup
7
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
8
+ end
9
+
10
+ def test_client_method_is_called
11
+ assert_nil @twitter.instance_variable_get(:@client)
12
+ @twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
13
+ assert_not_nil @twitter.instance_variable_get(:@client)
14
+ end
15
+
16
+ def key
17
+ caller.first[/`(.*?)'/, 1]
18
+ end
19
+
20
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+ require 'cassandra_test'
3
+ require 'cassandra/mock'
4
+
5
+ class CassandraMockTest < CassandraTest
6
+ include Cassandra::Constants
7
+
8
+ def setup
9
+ storage_xml_path = File.expand_path(File.join(File.dirname(File.dirname(__FILE__)), 'conf', 'storage-conf.xml'))
10
+ @twitter = Cassandra::Mock.new('Twitter', storage_xml_path)
11
+ @twitter.clear_keyspace!
12
+
13
+ @blogs = Cassandra::Mock.new('Multiblog', storage_xml_path)
14
+ @blogs.clear_keyspace!
15
+
16
+ @blogs_long = Cassandra::Mock.new('MultiblogLong', storage_xml_path)
17
+ @blogs_long.clear_keyspace!
18
+
19
+ @uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
20
+ @longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
21
+ end
22
+
23
+ def test_setup
24
+ assert @twitter
25
+ assert @blogs
26
+ assert @blogs_long
27
+ end
28
+
29
+ def test_schema_for_keyspace
30
+ data = {
31
+ "StatusRelationships"=>{
32
+ "CompareSubcolumnsWith"=>"org.apache.cassandra.db.marshal.TimeUUIDType",
33
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
34
+ "Type"=>"Super"},
35
+ "StatusAudits"=>{
36
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
37
+ "Type"=>"Standard"},
38
+ "Statuses"=>{
39
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
40
+ "Type"=>"Standard"},
41
+ "UserRelationships"=>{
42
+ "CompareSubcolumnsWith"=>"org.apache.cassandra.db.marshal.TimeUUIDType",
43
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
44
+ "Type"=>"Super"},
45
+ "UserAudits"=>{
46
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
47
+ "Type"=>"Standard"},
48
+ "Users"=>{"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type", "Type"=>"Standard"},
49
+ "TimelinishThings"=>
50
+ {"CompareWith"=>"org.apache.cassandra.db.marshal.BytesType", "Type"=>"Standard"}
51
+ }
52
+ stuff = @twitter.send(:schema_for_keyspace, 'Twitter')
53
+ data.keys.each do |k|
54
+ assert_equal data[k], stuff[k], k
55
+ end
56
+ end
57
+
58
+ def test_sorting_row_keys
59
+ @twitter.insert(:Statuses, 'b', {:text => 'foo'})
60
+ @twitter.insert(:Statuses, 'a', {:text => 'foo'})
61
+ assert_equal ['a'], @twitter.get_range(:Statuses, :count => 1)
62
+ end
63
+
64
+ def test_inserting_array_for_indices
65
+ @twitter.insert(:TimelinishThings, 'a', ['1','2'])
66
+ row = @twitter.get(:TimelinishThings, 'a')
67
+ assert_equal({'1' => nil, '2' => nil}, row)
68
+
69
+ assert_raises(ArgumentError) {
70
+ @twitter.insert(:UserRelationships, 'a', ['u1','u2'])
71
+ }
72
+ end
73
+ end
@@ -0,0 +1,370 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class CassandraTest < Test::Unit::TestCase
4
+ include Cassandra::Constants
5
+
6
+ def setup
7
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
8
+ @twitter.clear_keyspace!
9
+
10
+ @blogs = Cassandra.new('Multiblog')
11
+ @blogs.clear_keyspace!
12
+
13
+ @blogs_long = Cassandra.new('MultiblogLong')
14
+ @blogs_long.clear_keyspace!
15
+
16
+ @uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
17
+ @longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
18
+ end
19
+
20
+ def test_inspect
21
+ assert_nothing_raised do
22
+ @blogs.inspect
23
+ @twitter.inspect
24
+ end
25
+ end
26
+
27
+ def test_get_key
28
+ @twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
29
+ assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
30
+ assert_equal({}, @twitter.get(:Users, 'bogus'))
31
+ end
32
+
33
+ def test_get_key_preserving_order
34
+ # In-order hash is preserved
35
+ hash = OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]
36
+ @twitter.insert(:Users, key, hash)
37
+ assert_equal(hash.keys, @twitter.get(:Users, key).keys)
38
+
39
+ @twitter.remove(:Users, key)
40
+
41
+ # Out-of-order hash is returned sorted
42
+ hash = OrderedHash['b', '', 'c', '', 'd', '', 'a', '']
43
+ @twitter.insert(:Users, key, hash)
44
+ assert_equal(hash.keys.sort, @twitter.get(:Users, key).keys)
45
+ assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
46
+ end
47
+
48
+ def test_get_first_time_uuid_column
49
+ @blogs.insert(:Blogs, key,
50
+ {@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
51
+
52
+ assert_equal({@uuids[0] => 'I like this cat'}, @blogs.get(:Blogs, key, :count => 1))
53
+ assert_equal({@uuids[2] => 'I disagree'}, @blogs.get(:Blogs, key, :count => 1, :reversed => true))
54
+ assert_equal({}, @blogs.get(:Blogs, 'bogus'))
55
+ end
56
+
57
+ def test_get_multiple_time_uuid_columns
58
+ @blogs.insert(:Blogs, key,
59
+ {@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
60
+
61
+ assert_equal(['I like this cat', 'Buttons is cuter'], @blogs.get_columns(:Blogs, key, @uuids[0..1]))
62
+ end
63
+
64
+ def test_get_first_long_column
65
+ @blogs_long.insert(:Blogs, key,
66
+ {@longs[0] => 'I like this cat', @longs[1] => 'Buttons is cuter', @longs[2] => 'I disagree'})
67
+
68
+ assert_equal({@longs[0] => 'I like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
69
+ assert_equal({@longs[2] => 'I disagree'}, @blogs_long.get(:Blogs, key, :count => 1, :reversed => true))
70
+ assert_equal({}, @blogs_long.get(:Blogs, 'bogus'))
71
+ end
72
+
73
+ def test_long_remove_bug
74
+ @blogs_long.insert(:Blogs, key, {@longs[0] => 'I like this cat'})
75
+ @blogs_long.remove(:Blogs, key)
76
+ assert_equal({}, @blogs_long.get(:Blogs, key, :count => 1))
77
+
78
+ @blogs_long.insert(:Blogs, key, {@longs[0] => 'I really like this cat'})
79
+ assert_equal({@longs[0] => 'I really like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
80
+ end
81
+
82
+ def test_get_with_count
83
+ @twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
84
+ assert_equal 1, @twitter.get(:Statuses, key, :count => 1).size
85
+ assert_equal 2, @twitter.get(:Statuses, key, :count => 2).size
86
+ end
87
+
88
+ def test_get_value
89
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
90
+ assert_equal 'v', @twitter.get(:Statuses, key, 'body')
91
+ assert_nil @twitter.get(:Statuses, 'bogus', 'body')
92
+
93
+ assert @twitter.exists?(:Statuses, key, 'body')
94
+ assert !@twitter.exists?(:Statuses, 'bogus', 'body')
95
+ end
96
+
97
+ def test_exists_with_only_key
98
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
99
+ assert @twitter.exists?(:Statuses, key)
100
+ end
101
+
102
+ def test_get_super_key
103
+ columns = {'user_timelines' => {@uuids[4] => '4', @uuids[5] => '5'}}
104
+ @twitter.insert(:StatusRelationships, key, columns)
105
+ assert_equal(columns, @twitter.get(:StatusRelationships, key))
106
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
107
+ end
108
+
109
+ def test_get_several_super_keys
110
+ columns = {
111
+ 'user_timelines' => {@uuids[1] => 'v1'},
112
+ 'mentions_timelines' => {@uuids[2] => 'v2'}}
113
+ @twitter.insert(:StatusRelationships, key, columns)
114
+
115
+ assert_equal(columns, @twitter.get(:StatusRelationships, key))
116
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
117
+ end
118
+
119
+ def test_get_super_sub_keys_with_count
120
+ @twitter.insert(:StatusRelationships, key,
121
+ {'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2', @uuids[3] => 'v3'}})
122
+ assert_equal({@uuids[1] => 'v1'},
123
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1))
124
+ assert_equal({@uuids[3] => 'v3'},
125
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1, :reversed => true))
126
+ end
127
+
128
+ def test_get_super_sub_keys_with_ranges
129
+ @twitter.insert(:StatusRelationships, key,
130
+ {'user_timelines' => {
131
+ @uuids[1] => 'v1',
132
+ @uuids[2] => 'v2',
133
+ @uuids[3] => 'v3',
134
+ @uuids[4] => 'v4',
135
+ @uuids[5] => 'v5'}})
136
+
137
+ keys = @twitter.get(:StatusRelationships, key, "user_timelines").keys
138
+ assert_equal keys.sort, keys
139
+ assert_equal({@uuids[1] => 'v1'}, @twitter.get(:StatusRelationships, key, "user_timelines", :finish => @uuids[2], :count => 1))
140
+ assert_equal({@uuids[2] => 'v2'}, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :count => 1))
141
+ assert_equal 4, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :finish => @uuids[5]).size
142
+ end
143
+
144
+ def test_get_super_sub_key
145
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
146
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
147
+ assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
148
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
149
+ # FIXME Not sure if this is valid
150
+ # assert_nil @twitter.exists?(:StatusRelationships, 'bogus', 'user_timelines')
151
+ end
152
+
153
+ def test_get_super_value
154
+ columns = {@uuids[1] => 'v1'}
155
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
156
+ assert_equal('v1', @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first))
157
+ assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', columns.keys.first)
158
+ end
159
+
160
+
161
+ #TODO: add a OPP keyspace for this
162
+ # def test_get_range
163
+ # @twitter.insert(:Statuses, '2', {'body' => '1'})
164
+ # @twitter.insert(:Statuses, '3', {'body' => '1'})
165
+ # @twitter.insert(:Statuses, '4', {'body' => '1'})
166
+ # @twitter.insert(:Statuses, '5', {'body' => '1'})
167
+ # @twitter.insert(:Statuses, '6', {'body' => '1'})
168
+ # assert_equal(['3', '4', '5'], @twitter.get_range(:Statuses, :start => '3', :finish => '5'))
169
+ # end
170
+
171
+ def test_get_range_count
172
+ @twitter.insert(:Statuses, '2', {'body' => '1'})
173
+ @twitter.insert(:Statuses, '3', {'body' => '1'})
174
+ @twitter.insert(:Statuses, '4', {'body' => '1'})
175
+ @twitter.insert(:Statuses, '5', {'body' => '1'})
176
+ @twitter.insert(:Statuses, '6', {'body' => '1'})
177
+ assert_equal(3, @twitter.get_range(:Statuses, :count => 3).size)
178
+ end
179
+
180
+ def test_multi_get
181
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
182
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
183
+
184
+ expected = OrderedHash[key + '1', {'body' => 'v1', 'user' => 'v1'}, key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}]
185
+ result = @twitter.multi_get(:Users, [key + '1', key + '2', 'bogus'])
186
+ assert_equal expected, result
187
+ assert_equal expected.keys, result.keys
188
+
189
+ expected = OrderedHash[key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}, key + '1', {'body' => 'v1', 'user' => 'v1'}]
190
+ result = @twitter.multi_get(:Users, [key + '2', 'bogus', key + '1'])
191
+ assert_equal expected, result
192
+ assert_equal expected.keys, result.keys
193
+ end
194
+
195
+ def test_remove_key
196
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
197
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, key))
198
+
199
+ @twitter.remove(:Statuses, key)
200
+ assert_equal({}, @twitter.get(:Statuses, key))
201
+ end
202
+
203
+ def test_remove_value
204
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
205
+ @twitter.remove(:Statuses, key, 'body')
206
+ assert_nil @twitter.get(:Statuses, key, 'body')
207
+ end
208
+
209
+ def test_remove_super_key
210
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
211
+ @twitter.remove(:StatusRelationships, key)
212
+ assert_equal({}, @twitter.get(:StatusRelationships, key))
213
+ end
214
+
215
+ def test_remove_super_sub_key
216
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
217
+ @twitter.remove(:StatusRelationships, key, 'user_timelines')
218
+ assert_equal({}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
219
+ end
220
+
221
+ def test_remove_super_value
222
+ columns = {@uuids[1] => 'v1'}
223
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
224
+ @twitter.remove(:StatusRelationships, key, 'user_timelines', columns.keys.first)
225
+ assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first)
226
+ end
227
+
228
+ def test_clear_column_family
229
+ @twitter.insert(:Statuses, key + "1", {'body' => '1'})
230
+ @twitter.insert(:Statuses, key + "2", {'body' => '2'})
231
+ @twitter.insert(:Statuses, key + "3", {'body' => '3'})
232
+ @twitter.clear_column_family!(:Statuses)
233
+ assert_equal 0, @twitter.count_range(:Statuses)
234
+ end
235
+
236
+ def test_insert_key
237
+ @twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
238
+ assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))
239
+ end
240
+
241
+ def test_insert_super_key
242
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
243
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
244
+ assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
245
+ end
246
+
247
+ def test_get_columns
248
+ @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
249
+ assert_equal(['v1' , 'v2'], @twitter.get_columns(:Statuses, key, ['body', 'user']))
250
+ end
251
+
252
+ def test_get_column_values_super
253
+ user_columns, mentions_columns = {@uuids[1] => 'v1'}, {@uuids[2] => 'v2'}
254
+ @twitter.insert(:StatusRelationships, key,
255
+ {'user_timelines' => user_columns, 'mentions_timelines' => mentions_columns})
256
+ assert_equal [user_columns, mentions_columns],
257
+ @twitter.get_columns(:StatusRelationships, key, ['user_timelines', 'mentions_timelines'])
258
+ end
259
+
260
+ def test_get_sub_column_values_super
261
+ user_columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
262
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => user_columns})
263
+ assert_equal ['v1', 'v2'],
264
+ @twitter.get_columns(:StatusRelationships, key, 'user_timelines', @uuids[1..2])
265
+ end
266
+
267
+ def test_multi_get_columns
268
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
269
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
270
+ assert_equal(
271
+ OrderedHash[key + '1', ['v1', 'v1'], key + '2', ['v2', 'v2'], 'bogus', [nil, nil]],
272
+ @twitter.multi_get_columns(:Users, [key + '1', key + '2', 'bogus'], ['body', 'user']))
273
+ assert_equal(
274
+ OrderedHash[key + '2', ['v2', 'v2'], 'bogus', [nil, nil], key + '1', ['v1', 'v1']],
275
+ @twitter.multi_get_columns(:Users, [key + '2', 'bogus', key + '1'], ['body', 'user']))
276
+ end
277
+
278
+ def test_count_keys
279
+ @twitter.insert(:Statuses, key + "1", {'body' => '1'})
280
+ @twitter.insert(:Statuses, key + "2", {'body' => '2'})
281
+ @twitter.insert(:Statuses, key + "3", {'body' => '3'})
282
+ assert_equal 3, @twitter.count_range(:Statuses)
283
+ end
284
+
285
+ def test_count_columns
286
+ @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
287
+ assert_equal 2, @twitter.count_columns(:Statuses, key)
288
+ end
289
+
290
+ def test_count_super_columns
291
+ @twitter.insert(:StatusRelationships, key, {
292
+ 'user_timelines' => {@uuids[1] => 'v1'},
293
+ 'mentions_timelines' => {@uuids[2] => 'v2'}})
294
+ assert_equal 2, @twitter.count_columns(:StatusRelationships, key)
295
+ end
296
+
297
+ def test_count_super_sub_columns
298
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2'}})
299
+ assert_equal 2, @twitter.count_columns(:StatusRelationships, key, 'user_timelines')
300
+ end
301
+
302
+ def test_multi_count_columns
303
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
304
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
305
+ assert_equal(
306
+ OrderedHash[key + '1', 2, key + '2', 2, 'bogus', 0],
307
+ @twitter.multi_count_columns(:Users, [key + '1', key + '2', 'bogus']))
308
+ assert_equal(
309
+ OrderedHash[key + '2', 2, 'bogus', 0, key + '1', 2],
310
+ @twitter.multi_count_columns(:Users, [key + '2', 'bogus', key + '1']))
311
+ end
312
+
313
+ def test_batch_mutate
314
+ k = key
315
+
316
+ @twitter.insert(:Users, k + '1', {'body' => 'v1', 'user' => 'v1'})
317
+
318
+ @twitter.batch do
319
+ @twitter.insert(:Users, k + '2', {'body' => 'v2', 'user' => 'v2'})
320
+ @twitter.insert(:Users, k + '3', {'body' => 'bogus', 'user' => 'v3'})
321
+ @twitter.insert(:Users, k + '3', {'body' => 'v3', 'location' => 'v3'})
322
+ @twitter.insert(:Statuses, k + '3', {'body' => 'v'})
323
+
324
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Written
325
+ assert_equal({}, @twitter.get(:Users, k + '2')) # Not yet written
326
+ assert_equal({}, @twitter.get(:Statuses, k + '3')) # Not yet written
327
+
328
+ @twitter.remove(:Users, k + '1')
329
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
330
+
331
+ @twitter.remove(:Users, k + '4')
332
+ @twitter.insert(:Users, k + '4', {'body' => 'v4', 'user' => 'v4'})
333
+ assert_equal({}, @twitter.get(:Users, k + '4')) # Not yet written
334
+ end
335
+
336
+ assert_equal({'body' => 'v2', 'user' => 'v2'}, @twitter.get(:Users, k + '2')) # Written
337
+ assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}, @twitter.get(:Users, k + '3')) # Written and compacted
338
+ assert_equal({'body' => 'v4', 'user' => 'v4'}, @twitter.get(:Users, k + '4')) # Written
339
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, k + '3')) # Written
340
+ assert_equal({}, @twitter.get(:Users, k + '1')) # Removed
341
+ end
342
+
343
+ def test_complain_about_nil_key
344
+ assert_raises(ArgumentError) do
345
+ @twitter.insert(:Statuses, nil, {'text' => 'crap'})
346
+ end
347
+ end
348
+
349
+ def test_raise_access_error_on_nonexistent_keyspace
350
+ nonexistent = Cassandra.new('Nonexistent')
351
+ assert_raises(Cassandra::AccessError) do
352
+ nonexistent.get "foo", "bar"
353
+ end
354
+ end
355
+
356
+ def test_nil_sub_column_value
357
+ @twitter.insert(:Index, 'asdf', {"thing" => {'jkl' => nil} })
358
+ end
359
+
360
+ def test_disconnect!
361
+ @twitter.disconnect!
362
+ assert_nil @twitter.instance_variable_get(:@client)
363
+ end
364
+
365
+ private
366
+
367
+ def key
368
+ caller.first[/`(.*?)'/, 1]
369
+ end
370
+ end