cequel 0.5.6 → 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cequel.rb +5 -8
  3. data/lib/cequel/errors.rb +1 -0
  4. data/lib/cequel/metal.rb +17 -0
  5. data/lib/cequel/metal/batch.rb +62 -0
  6. data/lib/cequel/metal/cql_row_specification.rb +26 -0
  7. data/lib/cequel/metal/data_set.rb +461 -0
  8. data/lib/cequel/metal/deleter.rb +47 -0
  9. data/lib/cequel/metal/incrementer.rb +35 -0
  10. data/lib/cequel/metal/inserter.rb +53 -0
  11. data/lib/cequel/metal/keyspace.rb +213 -0
  12. data/lib/cequel/metal/row.rb +48 -0
  13. data/lib/cequel/metal/row_specification.rb +37 -0
  14. data/lib/cequel/metal/statement.rb +30 -0
  15. data/lib/cequel/metal/updater.rb +65 -0
  16. data/lib/cequel/metal/writer.rb +73 -0
  17. data/lib/cequel/model.rb +12 -84
  18. data/lib/cequel/model/association_collection.rb +23 -0
  19. data/lib/cequel/model/associations.rb +84 -80
  20. data/lib/cequel/model/base.rb +74 -0
  21. data/lib/cequel/model/belongs_to_association.rb +31 -0
  22. data/lib/cequel/model/callbacks.rb +14 -10
  23. data/lib/cequel/model/collection.rb +255 -0
  24. data/lib/cequel/model/errors.rb +6 -6
  25. data/lib/cequel/model/has_many_association.rb +26 -0
  26. data/lib/cequel/model/mass_assignment.rb +31 -0
  27. data/lib/cequel/model/persistence.rb +119 -115
  28. data/lib/cequel/model/properties.rb +89 -87
  29. data/lib/cequel/model/railtie.rb +21 -14
  30. data/lib/cequel/model/record_set.rb +285 -0
  31. data/lib/cequel/model/schema.rb +33 -0
  32. data/lib/cequel/model/scoped.rb +5 -48
  33. data/lib/cequel/model/validations.rb +18 -18
  34. data/lib/cequel/schema.rb +15 -0
  35. data/lib/cequel/schema/column.rb +135 -0
  36. data/lib/cequel/schema/create_table_dsl.rb +56 -0
  37. data/lib/cequel/schema/keyspace.rb +50 -0
  38. data/lib/cequel/schema/table.rb +120 -0
  39. data/lib/cequel/schema/table_property.rb +67 -0
  40. data/lib/cequel/schema/table_reader.rb +139 -0
  41. data/lib/cequel/schema/table_synchronizer.rb +114 -0
  42. data/lib/cequel/schema/table_updater.rb +83 -0
  43. data/lib/cequel/schema/table_writer.rb +80 -0
  44. data/lib/cequel/schema/update_table_dsl.rb +60 -0
  45. data/lib/cequel/type.rb +232 -0
  46. data/lib/cequel/version.rb +1 -1
  47. data/spec/environment.rb +5 -1
  48. data/spec/examples/metal/data_set_spec.rb +608 -0
  49. data/spec/examples/model/associations_spec.rb +84 -74
  50. data/spec/examples/model/callbacks_spec.rb +66 -59
  51. data/spec/examples/model/list_spec.rb +393 -0
  52. data/spec/examples/model/map_spec.rb +229 -0
  53. data/spec/examples/model/mass_assignment_spec.rb +55 -0
  54. data/spec/examples/model/naming_spec.rb +11 -4
  55. data/spec/examples/model/persistence_spec.rb +140 -150
  56. data/spec/examples/model/properties_spec.rb +122 -75
  57. data/spec/examples/model/record_set_spec.rb +285 -0
  58. data/spec/examples/model/schema_spec.rb +44 -0
  59. data/spec/examples/model/serialization_spec.rb +20 -14
  60. data/spec/examples/model/set_spec.rb +133 -0
  61. data/spec/examples/model/spec_helper.rb +0 -10
  62. data/spec/examples/model/validations_spec.rb +51 -38
  63. data/spec/examples/schema/table_reader_spec.rb +328 -0
  64. data/spec/examples/schema/table_synchronizer_spec.rb +172 -0
  65. data/spec/examples/schema/table_updater_spec.rb +157 -0
  66. data/spec/examples/schema/table_writer_spec.rb +225 -0
  67. data/spec/examples/spec_helper.rb +29 -0
  68. data/spec/examples/type_spec.rb +204 -0
  69. data/spec/support/helpers.rb +67 -8
  70. metadata +121 -152
  71. data/lib/cequel/batch.rb +0 -58
  72. data/lib/cequel/cql_row_specification.rb +0 -22
  73. data/lib/cequel/data_set.rb +0 -371
  74. data/lib/cequel/keyspace.rb +0 -205
  75. data/lib/cequel/model/class_internals.rb +0 -49
  76. data/lib/cequel/model/column.rb +0 -20
  77. data/lib/cequel/model/counter.rb +0 -35
  78. data/lib/cequel/model/dictionary.rb +0 -126
  79. data/lib/cequel/model/dirty.rb +0 -53
  80. data/lib/cequel/model/dynamic.rb +0 -31
  81. data/lib/cequel/model/inheritable.rb +0 -48
  82. data/lib/cequel/model/instance_internals.rb +0 -23
  83. data/lib/cequel/model/local_association.rb +0 -42
  84. data/lib/cequel/model/magic.rb +0 -79
  85. data/lib/cequel/model/mass_assignment_security.rb +0 -21
  86. data/lib/cequel/model/naming.rb +0 -17
  87. data/lib/cequel/model/observer.rb +0 -42
  88. data/lib/cequel/model/readable_dictionary.rb +0 -182
  89. data/lib/cequel/model/remote_association.rb +0 -40
  90. data/lib/cequel/model/scope.rb +0 -362
  91. data/lib/cequel/model/subclass_internals.rb +0 -45
  92. data/lib/cequel/model/timestamps.rb +0 -52
  93. data/lib/cequel/model/translation.rb +0 -17
  94. data/lib/cequel/row_specification.rb +0 -63
  95. data/lib/cequel/statement.rb +0 -23
  96. data/spec/examples/data_set_spec.rb +0 -444
  97. data/spec/examples/keyspace_spec.rb +0 -84
  98. data/spec/examples/model/counter_spec.rb +0 -94
  99. data/spec/examples/model/dictionary_spec.rb +0 -301
  100. data/spec/examples/model/dirty_spec.rb +0 -39
  101. data/spec/examples/model/dynamic_spec.rb +0 -41
  102. data/spec/examples/model/inheritable_spec.rb +0 -45
  103. data/spec/examples/model/magic_spec.rb +0 -199
  104. data/spec/examples/model/mass_assignment_security_spec.rb +0 -13
  105. data/spec/examples/model/observer_spec.rb +0 -86
  106. data/spec/examples/model/scope_spec.rb +0 -677
  107. data/spec/examples/model/timestamps_spec.rb +0 -52
  108. data/spec/examples/model/translation_spec.rb +0 -23
@@ -0,0 +1,44 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe Cequel::Model::Schema do
4
+ after { cequel.schema.drop_table(:posts) }
5
+ subject { cequel.schema.read_table(:posts) }
6
+
7
+ let(:model) do
8
+ Class.new(Cequel::Model::Base) do
9
+ self.table_name = 'posts'
10
+
11
+ key :permalink, :text
12
+ column :title, :text
13
+ list :categories, :text
14
+ set :tags, :text
15
+ map :trackbacks, :timestamp, :text
16
+ table_property :comment, 'Blog Posts'
17
+ end
18
+ end
19
+
20
+ context 'new model with simple primary key' do
21
+ before { model.synchronize_schema }
22
+
23
+ its(:partition_keys) { should == [Cequel::Schema::Column.new(:permalink, :text)] }
24
+ its(:data_columns) { should include(Cequel::Schema::Column.new(:title, :text)) }
25
+ its(:data_columns) { should include(Cequel::Schema::List.new(:categories, :text)) }
26
+ its(:data_columns) { should include(Cequel::Schema::Set.new(:tags, :text)) }
27
+ its(:data_columns) { should include(Cequel::Schema::Map.new(:trackbacks, :timestamp, :text)) }
28
+ specify { subject.property(:comment).should == 'Blog Posts' }
29
+ end
30
+
31
+ context 'existing model with additional attribute' do
32
+ before do
33
+ cequel.schema.create_table :posts do
34
+ key :permalink, :text
35
+ column :title, :text
36
+ list :categories, :text
37
+ set :tags, :text
38
+ end
39
+ model.synchronize_schema
40
+ end
41
+
42
+ its(:data_columns) { should include(Cequel::Schema::Map.new(:trackbacks, :timestamp, :text)) }
43
+ end
44
+ end
@@ -1,20 +1,26 @@
1
- require File.expand_path('../spec_helper', __FILE__)
1
+ require_relative 'spec_helper'
2
2
 
3
- describe 'Serialization' do
4
- let(:post) { Post.new(:id => 1, :title => 'Cequel') }
3
+ describe 'serialization' do
4
+ model :Post do
5
+ key :blog_subdomain, :text
6
+ key :id, :uuid
7
+ column :title, :text
8
+ column :body, :text
9
+ end
10
+
11
+ uuid :id
5
12
 
6
- it 'should serialize to JSON' do
7
- post.to_json.should ==
8
- {'post' => {'id' => 1, 'title' => 'Cequel'}}.to_json
13
+ let(:attributes) do
14
+ {
15
+ blog_subdomain: 'big-data',
16
+ id: id,
17
+ title: 'Cequel',
18
+ }
9
19
  end
10
20
 
11
- it 'should serialize to XML' do
12
- post.to_xml.should == <<XML
13
- <?xml version="1.0" encoding="UTF-8"?>
14
- <post>
15
- <id type="integer">1</id>
16
- <title>Cequel</title>
17
- </post>
18
- XML
21
+ it 'should provide JSON serialization' do
22
+ Post.include_root_in_json = false
23
+ Post.new(attributes).as_json.symbolize_keys.
24
+ should == attributes.merge(body: nil)
19
25
  end
20
26
  end
@@ -0,0 +1,133 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe Cequel::Model::Set do
4
+ model :Post do
5
+ key :permalink, :text
6
+ column :title, :text
7
+ set :tags, :text
8
+ end
9
+
10
+ let(:scope) { cequel[:posts].where(:permalink => 'cequel') }
11
+ subject { scope.first }
12
+
13
+ let! :post do
14
+ Post.new do |post|
15
+ post.permalink = 'cequel'
16
+ post.tags = Set['one', 'two']
17
+ end.tap(&:save)
18
+ end
19
+
20
+ let! :unloaded_post do
21
+ Post['cequel']
22
+ end
23
+
24
+ context 'new record' do
25
+ it 'should save set as-is' do
26
+ subject[:tags].should == Set['one', 'two']
27
+ end
28
+ end
29
+
30
+ describe 'atomic modification' do
31
+ before { scope.set_add(:tags, 'three') }
32
+
33
+ describe '#add' do
34
+ it 'should add atomically' do
35
+ post.tags.add('four')
36
+ post.save
37
+ subject[:tags].should == Set['one', 'two', 'three', 'four']
38
+ post.tags.should == Set['one', 'two', 'four']
39
+ end
40
+
41
+ it 'should add without reading' do
42
+ max_statements! 2
43
+ unloaded_post.tags.add('four')
44
+ unloaded_post.save
45
+ subject[:tags].should == Set['one', 'two', 'three', 'four']
46
+ end
47
+
48
+ it 'should apply add post-hoc' do
49
+ unloaded_post.tags.add('four')
50
+ unloaded_post.tags.should == Set['one', 'two', 'three', 'four']
51
+ end
52
+ end
53
+
54
+ describe '#clear' do
55
+ it 'should clear atomically' do
56
+ post.tags.clear
57
+ post.save
58
+ subject[:tags].should be_blank
59
+ post.tags.should == Set[]
60
+ end
61
+
62
+ it 'should clear without reading' do
63
+ max_statements! 2
64
+ unloaded_post.tags.clear
65
+ unloaded_post.save
66
+ subject[:tags].should be_blank
67
+ end
68
+
69
+ it 'should apply clear post-hoc' do
70
+ unloaded_post.tags.clear
71
+ unloaded_post.tags.should == Set[]
72
+ end
73
+ end
74
+
75
+ describe '#delete' do
76
+ it 'should delete atomically' do
77
+ post.tags.delete('two')
78
+ post.save
79
+ subject[:tags].should == Set['one', 'three']
80
+ post.tags.should == Set['one']
81
+ end
82
+
83
+ it 'should delete without reading' do
84
+ max_statements! 2
85
+ unloaded_post.tags.delete('two')
86
+ unloaded_post.save
87
+ subject[:tags].should == Set['one', 'three']
88
+ end
89
+
90
+ it 'should apply delete post-hoc' do
91
+ unloaded_post.tags.delete('two')
92
+ unloaded_post.tags.should == Set['one', 'three']
93
+ end
94
+ end
95
+
96
+ describe '#replace' do
97
+ it 'should replace atomically' do
98
+ post.tags.replace(Set['a', 'b'])
99
+ post.save
100
+ subject[:tags].should == Set['a', 'b']
101
+ post.tags.should == Set['a', 'b']
102
+ end
103
+
104
+ it 'should replace without reading' do
105
+ max_statements! 2
106
+ unloaded_post.tags.replace(Set['a', 'b'])
107
+ unloaded_post.save
108
+ subject[:tags].should == Set['a', 'b']
109
+ end
110
+
111
+ it 'should apply delete post-hoc' do
112
+ unloaded_post.tags.replace(Set['a', 'b'])
113
+ unloaded_post.tags.should == Set['a', 'b']
114
+ end
115
+ end
116
+
117
+ specify { expect { post.tags.add?('three') }.to raise_error(NoMethodError) }
118
+ specify { expect { post.tags.collect!(&:upcase) }.
119
+ to raise_error(NoMethodError) }
120
+ specify { expect { post.tags.delete?('two') }.to raise_error(NoMethodError) }
121
+ specify { expect { post.tags.delete_if { |s| s.starts_with?('t') }}.
122
+ to raise_error(NoMethodError) }
123
+ specify { expect { post.tags.flatten! }.to raise_error(NoMethodError) }
124
+ specify { expect { post.tags.keep_if { |s| s.starts_with?('t') }}.
125
+ to raise_error(NoMethodError) }
126
+ specify { expect { post.tags.map!(&:upcase) }.
127
+ to raise_error(NoMethodError) }
128
+ specify { expect { post.tags.reject! { |s| s.starts_with?('t') }}.
129
+ to raise_error(NoMethodError) }
130
+ specify { expect { post.tags.select! { |s| s.starts_with?('t') }}.
131
+ to raise_error(NoMethodError) }
132
+ end
133
+ end
@@ -1,12 +1,2 @@
1
1
  require File.expand_path('../../spec_helper', __FILE__)
2
2
  require 'cequel/model'
3
-
4
- Dir.glob(File.join(File.dirname(__FILE__), '../../models/**/*.rb')).each do |file|
5
- require file
6
- end
7
-
8
- RSpec.configure do |config|
9
- config.before :each do
10
- Cequel::Model.keyspace.connection = connection
11
- end
12
- end
@@ -1,86 +1,99 @@
1
- require File.expand_path('../spec_helper', __FILE__)
1
+ require_relative 'spec_helper'
2
2
 
3
3
  describe Cequel::Model::Validations do
4
+ model :Post do
5
+ key :permalink, :text
6
+ column :title, :text
7
+ column :body, :text
8
+
9
+ validates :title, :presence => true
10
+ before_validation { |post| post.called_validate_callback = true }
11
+
12
+ attr_accessor :called_validate_callback
13
+ end
14
+
15
+ let(:invalid_post) do
16
+ Post.new do |post|
17
+ post.permalink = 'invalid'
18
+ post.body = 'This is an invalid post.'
19
+ end
20
+ end
21
+ let(:valid_post) do
22
+ Post.new do |post|
23
+ post.permalink = 'valid'
24
+ post.title = 'Valid Post'
25
+ end
26
+ end
27
+ let(:unloaded_post) { Post['unloaded'] }
28
+
4
29
  describe '#valid?' do
5
30
  it 'should be false if model is not valid' do
6
- Post.new(:id => 1, :require_title => true).should_not be_valid
31
+ invalid_post.should_not be_valid
7
32
  end
8
33
 
9
34
  it 'should be true if model is valid' do
10
- Post.new(:id => 1, :require_title => true, :title => 'Cequel').should be_valid
35
+ valid_post.should be_valid
11
36
  end
12
37
  end
13
38
 
14
39
  describe '#save' do
15
40
  it 'should return false and not persist model if invalid' do
16
- Post.new(:id => 1, :body => 'Cequel', :require_title => true).save.should be_false
41
+ invalid_post.save.should be_false
17
42
  end
18
43
 
19
44
  it 'should return true and persist model if valid' do
20
- connection.should_receive(:execute).
21
- with "INSERT INTO posts (?) VALUES (?)", ['id', 'title'], [1, 'Cequel']
45
+ valid_post.save.should be_true
46
+ Post.find('valid').title.should == 'Valid Post'
47
+ end
22
48
 
23
- Post.new(:id => 1, :title => 'Cequel', :require_title => true).save.should be_true
49
+ it 'should bypass validations if :validate => false is passed' do
50
+ invalid_post.save(:validate => false).should be_true
51
+ Post.find('invalid').body.should == 'This is an invalid post.'
24
52
  end
25
53
  end
26
54
 
27
55
  describe '#save!' do
28
56
  it 'should raise error and not persist model if invalid' do
29
- expect do
30
- Post.new(:id => 1, :body => 'Cequel', :require_title => true).save!
31
- end.to raise_error(Cequel::Model::RecordInvalid)
57
+ expect { invalid_post.save! }.
58
+ to raise_error(Cequel::Model::RecordInvalid)
32
59
  end
33
60
 
34
61
  it 'should persist model and return self if valid' do
35
- connection.should_receive(:execute).
36
- with "INSERT INTO posts (?) VALUES (?)", ['id', 'title'], [1, 'Cequel']
37
-
38
- post = Post.new(:id => 1, :title => 'Cequel', :require_title => true)
39
- post.save!.should == post
62
+ expect { valid_post.save! }.to_not raise_error
63
+ Post.find(valid_post.permalink).title.should == 'Valid Post'
40
64
  end
41
65
  end
42
66
 
43
67
  describe '#update_attributes!' do
44
- let(:post) do
45
- connection.stub(:execute).with("SELECT * FROM posts WHERE ? = ? LIMIT 1", :id, 1).
46
- and_return result_stub(:id => 1, :blog_id => 1, :title => 'Cequel')
47
- Post.find(1)
48
- end
49
-
50
- it 'should change attributes and save them if valid' do
51
- connection.should_receive(:execute).
52
- with "UPDATE posts SET ? = ? WHERE ? = ?", 'body', 'Cequel cequel', :id, 1
53
- post.update_attributes!(:body => 'Cequel cequel')
68
+ it 'should raise error and not update data in the database' do
69
+ expect { invalid_post.update_attributes!(:body => 'My Post') }.
70
+ to raise_error(Cequel::Model::RecordInvalid)
54
71
  end
55
72
 
56
- it 'should raise error if not valid' do
57
- post.require_title = true
58
- expect { post.update_attributes!(:title => nil) }.
59
- to raise_error(Cequel::Model::RecordInvalid)
73
+ it 'should return successfully and update data in the database if valid' do
74
+ invalid_post.update_attributes!(:title => 'My Post')
75
+ Post.find(invalid_post.permalink).title.should == 'My Post'
60
76
  end
61
77
  end
62
78
 
63
79
  describe '::create!' do
64
80
  it 'should raise RecordInvalid and not persist model if invalid' do
65
81
  expect do
66
- Post.create!(:id => 1, :body => 'Cequel', :require_title => true)
82
+ Post.create!(:permalink => 'cequel', :body => 'Cequel')
67
83
  end.to raise_error(Cequel::Model::RecordInvalid)
68
84
  end
69
85
 
70
- it 'should and return model if valid' do
71
- connection.should_receive(:execute).
72
- with "INSERT INTO posts (?) VALUES (?)", ['id', 'title'], [1, 'Cequel']
73
-
74
- Post.create!(:id => 1, :title => 'Cequel', :require_title => true).
75
- title.should == 'Cequel'
86
+ it 'should persist record to database if valid' do
87
+ Post.create!(:permalink => 'cequel', :title => 'Cequel')
88
+ Post.find('cequel').title.should == 'Cequel'
76
89
  end
77
90
  end
78
91
 
79
92
  describe 'callbacks' do
80
93
  it 'should call validation callbacks' do
81
- post = Post.new(:id => 1)
94
+ post = Post.new(:title => 'cequel')
82
95
  post.valid?
83
- post.should have_callback(:validation)
96
+ post.called_validate_callback.should be_true
84
97
  end
85
98
  end
86
99
  end
@@ -0,0 +1,328 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe Cequel::Schema::TableReader do
4
+
5
+ after do
6
+ cequel.schema.drop_table(:posts)
7
+ end
8
+
9
+ let(:table) { cequel.schema.read_table(:posts) }
10
+
11
+ describe 'reading simple key' do
12
+ before do
13
+ cequel.execute("CREATE TABLE posts (permalink text PRIMARY KEY)")
14
+ end
15
+
16
+ it 'should read name correctly' do
17
+ table.partition_keys.first.name.should == :permalink
18
+ end
19
+
20
+ it 'should read type correctly' do
21
+ table.partition_keys.first.type.should be_a(Cequel::Type::Text)
22
+ end
23
+
24
+ it 'should have no nonpartition keys' do
25
+ table.clustering_columns.should be_empty
26
+ end
27
+ end # describe 'reading simple key'
28
+
29
+ describe 'reading single non-partition key' do
30
+ before do
31
+ cequel.execute <<-CQL
32
+ CREATE TABLE posts (
33
+ blog_subdomain text,
34
+ permalink ascii,
35
+ PRIMARY KEY (blog_subdomain, permalink)
36
+ )
37
+ CQL
38
+ end
39
+
40
+ it 'should read partition key name' do
41
+ table.partition_keys.map(&:name).should == [:blog_subdomain]
42
+ end
43
+
44
+ it 'should read partition key type' do
45
+ table.partition_keys.map(&:type).should == [Cequel::Type::Text.instance]
46
+ end
47
+
48
+ it 'should read non-partition key name' do
49
+ table.clustering_columns.map(&:name).should == [:permalink]
50
+ end
51
+
52
+ it 'should read non-partition key type' do
53
+ table.clustering_columns.map(&:type).
54
+ should == [Cequel::Type::Ascii.instance]
55
+ end
56
+
57
+ it 'should default clustering order to asc' do
58
+ table.clustering_columns.map(&:clustering_order).should == [:asc]
59
+ end
60
+ end # describe 'reading single non-partition key'
61
+
62
+ describe 'reading reverse-ordered non-partition key' do
63
+ before do
64
+ cequel.execute <<-CQL
65
+ CREATE TABLE posts (
66
+ blog_subdomain text,
67
+ permalink ascii,
68
+ PRIMARY KEY (blog_subdomain, permalink)
69
+ )
70
+ WITH CLUSTERING ORDER BY (permalink DESC)
71
+ CQL
72
+ end
73
+
74
+ it 'should read non-partition key name' do
75
+ table.clustering_columns.map(&:name).should == [:permalink]
76
+ end
77
+
78
+ it 'should read non-partition key type' do
79
+ table.clustering_columns.map(&:type).
80
+ should == [Cequel::Type::Ascii.instance]
81
+ end
82
+
83
+ it 'should recognize reversed clustering order' do
84
+ table.clustering_columns.map(&:clustering_order).should == [:desc]
85
+ end
86
+ end # describe 'reading reverse-ordered non-partition key'
87
+
88
+ describe 'reading compound non-partition key' do
89
+ before do
90
+ cequel.execute <<-CQL
91
+ CREATE TABLE posts (
92
+ blog_subdomain text,
93
+ permalink ascii,
94
+ author_id uuid,
95
+ PRIMARY KEY (blog_subdomain, permalink, author_id)
96
+ )
97
+ WITH CLUSTERING ORDER BY (permalink DESC, author_id ASC)
98
+ CQL
99
+ end
100
+
101
+ it 'should read non-partition key names' do
102
+ table.clustering_columns.map(&:name).should == [:permalink, :author_id]
103
+ end
104
+
105
+ it 'should read non-partition key types' do
106
+ table.clustering_columns.map(&:type).
107
+ should == [Cequel::Type::Ascii.instance, Cequel::Type::Uuid.instance]
108
+ end
109
+
110
+ it 'should read heterogeneous clustering orders' do
111
+ table.clustering_columns.map(&:clustering_order).should == [:desc, :asc]
112
+ end
113
+ end # describe 'reading compound non-partition key'
114
+
115
+ describe 'reading compound partition key' do
116
+ before do
117
+ cequel.execute <<-CQL
118
+ CREATE TABLE posts (
119
+ blog_subdomain text,
120
+ permalink ascii,
121
+ PRIMARY KEY ((blog_subdomain, permalink))
122
+ )
123
+ CQL
124
+ end
125
+
126
+ it 'should read partition key names' do
127
+ table.partition_keys.map(&:name).should == [:blog_subdomain, :permalink]
128
+ end
129
+
130
+ it 'should read partition key types' do
131
+ table.partition_keys.map(&:type).
132
+ should == [Cequel::Type::Text.instance, Cequel::Type::Ascii.instance]
133
+ end
134
+
135
+ it 'should have empty nonpartition keys' do
136
+ table.clustering_columns.should be_empty
137
+ end
138
+
139
+ end # describe 'reading compound partition key'
140
+
141
+ describe 'reading compound partition and non-partition keys' do
142
+ before do
143
+ cequel.execute <<-CQL
144
+ CREATE TABLE posts (
145
+ blog_subdomain text,
146
+ permalink ascii,
147
+ author_id uuid,
148
+ published_at timestamp,
149
+ PRIMARY KEY ((blog_subdomain, permalink), author_id, published_at)
150
+ )
151
+ WITH CLUSTERING ORDER BY (author_id ASC, published_at DESC)
152
+ CQL
153
+ end
154
+
155
+ it 'should read partition key names' do
156
+ table.partition_keys.map(&:name).should == [:blog_subdomain, :permalink]
157
+ end
158
+
159
+ it 'should read partition key types' do
160
+ table.partition_keys.map(&:type).
161
+ should == [Cequel::Type::Text.instance, Cequel::Type::Ascii.instance]
162
+ end
163
+
164
+ it 'should read non-partition key names' do
165
+ table.clustering_columns.map(&:name).
166
+ should == [:author_id, :published_at]
167
+ end
168
+
169
+ it 'should read non-partition key types' do
170
+ table.clustering_columns.map(&:type).should ==
171
+ [Cequel::Type::Uuid.instance, Cequel::Type::Timestamp.instance]
172
+ end
173
+
174
+ it 'should read clustering order' do
175
+ table.clustering_columns.map(&:clustering_order).should == [:asc, :desc]
176
+ end
177
+
178
+ end # describe 'reading compound partition and non-partition keys'
179
+
180
+ describe 'reading data columns' do
181
+
182
+ before do
183
+ cequel.execute <<-CQL
184
+ CREATE TABLE posts (
185
+ blog_subdomain text,
186
+ permalink ascii,
187
+ title text,
188
+ author_id uuid,
189
+ categories LIST <text>,
190
+ tags SET <text>,
191
+ trackbacks MAP <timestamp,ascii>,
192
+ PRIMARY KEY (blog_subdomain, permalink)
193
+ )
194
+ CQL
195
+ cequel.execute('CREATE INDEX ON posts (author_id)')
196
+ end
197
+
198
+ it 'should read types of scalar data columns' do
199
+ table.data_columns.find { |column| column.name == :title }.type.
200
+ should == Cequel::Type[:text]
201
+ table.data_columns.find { |column| column.name == :author_id }.type.
202
+ should == Cequel::Type[:uuid]
203
+ end
204
+
205
+ it 'should read index attributes' do
206
+ table.data_columns.find { |column| column.name == :author_id }.index_name.
207
+ should == :posts_author_id_idx
208
+ end
209
+
210
+ it 'should leave nil index for non-indexed columns' do
211
+ table.data_columns.find { |column| column.name == :title }.index_name.
212
+ should be_nil
213
+ end
214
+
215
+ it 'should read list columns' do
216
+ table.data_columns.find { |column| column.name == :categories }.
217
+ should be_a(Cequel::Schema::List)
218
+ end
219
+
220
+ it 'should read list column type' do
221
+ table.data_columns.find { |column| column.name == :categories }.type.
222
+ should == Cequel::Type[:text]
223
+ end
224
+
225
+ it 'should read set columns' do
226
+ table.data_columns.find { |column| column.name == :tags }.
227
+ should be_a(Cequel::Schema::Set)
228
+ end
229
+
230
+ it 'should read set column type' do
231
+ table.data_columns.find { |column| column.name == :tags }.type.
232
+ should == Cequel::Type[:text]
233
+ end
234
+
235
+ it 'should read map columns' do
236
+ table.data_columns.find { |column| column.name == :trackbacks }.
237
+ should be_a(Cequel::Schema::Map)
238
+ end
239
+
240
+ it 'should read map column key type' do
241
+ table.data_columns.find { |column| column.name == :trackbacks }.key_type.
242
+ should == Cequel::Type[:timestamp]
243
+ end
244
+
245
+ it 'should read map column value type' do
246
+ table.data_columns.find { |column| column.name == :trackbacks }.
247
+ value_type.should == Cequel::Type[:ascii]
248
+ end
249
+
250
+ end # describe 'reading data columns'
251
+
252
+ describe 'reading storage properties' do
253
+
254
+ before do
255
+ cequel.execute <<-CQL
256
+ CREATE TABLE posts (permalink text PRIMARY KEY)
257
+ WITH bloom_filter_fp_chance = 0.02
258
+ AND comment = 'Posts table'
259
+ AND compaction = {
260
+ 'class' : 'SizeTieredCompactionStrategy',
261
+ 'bucket_high' : 1.8,
262
+ 'max_threshold' : 64,
263
+ 'min_sstable_size' : 50,
264
+ 'tombstone_compaction_interval' : 2
265
+ } AND compression = {
266
+ 'sstable_compression' : 'DeflateCompressor',
267
+ 'chunk_length_kb' : 128,
268
+ 'crc_check_chance' : 0.5
269
+ }
270
+ CQL
271
+ end
272
+
273
+ it 'should read float properties' do
274
+ table.property(:bloom_filter_fp_chance).should == 0.02
275
+ end
276
+
277
+ it 'should read string properties' do
278
+ table.property(:comment).should == 'Posts table'
279
+ end
280
+
281
+ it 'should read and simplify compaction class' do
282
+ table.property(:compaction)[:class].
283
+ should == 'SizeTieredCompactionStrategy'
284
+ end
285
+
286
+ it 'should read float properties from compaction hash' do
287
+ table.property(:compaction)[:bucket_high].should == 1.8
288
+ end
289
+
290
+ it 'should read integer properties from compaction hash' do
291
+ table.property(:compaction)[:max_threshold].should == 64
292
+ end
293
+
294
+ it 'should read and simplify compression class' do
295
+ table.property(:compression)[:sstable_compression].
296
+ should == 'DeflateCompressor'
297
+ end
298
+
299
+ it 'should read integer properties from compression class' do
300
+ table.property(:compression)[:chunk_length_kb].should == 128
301
+ end
302
+
303
+ it 'should read float properties from compression class' do
304
+ table.property(:compression)[:crc_check_chance].should == 0.5
305
+ end
306
+
307
+ end # describe 'reading storage properties'
308
+
309
+ describe 'compact storage' do
310
+
311
+ it 'should read non-compact storage status' do
312
+ cequel.execute <<-CQL
313
+ CREATE TABLE posts (permalink text PRIMARY KEY, body text)
314
+ CQL
315
+ table.should_not be_compact_storage
316
+ end
317
+
318
+ it 'should read compact storage status' do
319
+ cequel.execute <<-CQL
320
+ CREATE TABLE posts (permalink text PRIMARY KEY, body text)
321
+ WITH COMPACT STORAGE
322
+ CQL
323
+ table.should be_compact_storage
324
+ end
325
+
326
+ end
327
+
328
+ end