cequel 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +8 -0
  5. data/lib/cequel/errors.rb +2 -0
  6. data/lib/cequel/metal/new_relic_instrumentation.rb +2 -1
  7. data/lib/cequel/record.rb +8 -0
  8. data/lib/cequel/record/schema.rb +30 -23
  9. data/lib/cequel/schema.rb +3 -2
  10. data/lib/cequel/schema/column.rb +11 -1
  11. data/lib/cequel/schema/keyspace.rb +18 -7
  12. data/lib/cequel/schema/patch.rb +152 -0
  13. data/lib/cequel/schema/table.rb +55 -137
  14. data/lib/cequel/schema/table_desc_dsl.rb +196 -0
  15. data/lib/cequel/schema/table_differ.rb +112 -0
  16. data/lib/cequel/schema/table_property.rb +14 -0
  17. data/lib/cequel/schema/table_reader.rb +81 -85
  18. data/lib/cequel/schema/table_updater.rb +0 -17
  19. data/lib/cequel/schema/table_writer.rb +10 -9
  20. data/lib/cequel/version.rb +1 -1
  21. data/spec/examples/metal/data_set_spec.rb +156 -153
  22. data/spec/examples/metal/keyspace_spec.rb +4 -4
  23. data/spec/examples/record/associations_spec.rb +6 -0
  24. data/spec/examples/record/mass_assignment_spec.rb +2 -2
  25. data/spec/examples/record/properties_spec.rb +1 -0
  26. data/spec/examples/record/record_set_spec.rb +1 -1
  27. data/spec/examples/schema/patch_spec.rb +190 -0
  28. data/spec/examples/schema/table_differ_spec.rb +280 -0
  29. data/spec/examples/schema/table_reader_spec.rb +379 -354
  30. data/spec/examples/schema/table_updater_spec.rb +0 -12
  31. data/spec/examples/spec_helper.rb +5 -5
  32. data/spec/examples/spec_support/preparation_spec.rb +4 -0
  33. data/spec/support/helpers.rb +23 -0
  34. metadata +9 -6
  35. data/lib/cequel/schema/create_table_dsl.rb +0 -88
  36. data/lib/cequel/schema/table_synchronizer.rb +0 -180
  37. data/spec/examples/schema/table_synchronizer_spec.rb +0 -200
@@ -83,18 +83,6 @@ describe Cequel::Schema::TableUpdater do
83
83
  end
84
84
  end
85
85
 
86
- describe '#change_column' do
87
- before do
88
- cequel.schema.alter_table(table_name) do
89
- change_column :title, :text
90
- end
91
- end
92
-
93
- it 'should change the type' do
94
- expect(table.data_column(:title).type).to eq(Cequel::Type[:text])
95
- end
96
- end
97
-
98
86
  describe '#rename_column' do
99
87
  before do
100
88
  cequel.schema.alter_table(table_name) do
@@ -17,11 +17,11 @@ RSpec.configure do |config|
17
17
 
18
18
  {
19
19
  rails: ActiveSupport::VERSION::STRING,
20
- cassandra: ENV.fetch('CASSANDRA_VERSION', '3.0'),
21
- }.each do |tag, version|
22
- config.filter_run_excluding tag => ->(requirement) {
23
- !Gem::Requirement.new(requirement).
24
- satisfied_by?(Gem::Version.new(version))
20
+ cql: Cequel::SpecSupport::Helpers.cql_version,
21
+ }.each do |tag, actual_version|
22
+ config.filter_run_excluding tag => ->(required_version) {
23
+ !Gem::Requirement.new(required_version).
24
+ satisfied_by?(Gem::Version.new(actual_version))
25
25
  }
26
26
  end
27
27
 
@@ -74,6 +74,10 @@ describe Cequel::SpecSupport::Preparation do
74
74
 
75
75
  # background
76
76
 
77
+ before(:each) do
78
+ Cequel::Record.forget_all_descendants!
79
+ end
80
+
77
81
  after(:each) do
78
82
  begin
79
83
  Cequel::Record.connection.clear_active_connections!
@@ -51,6 +51,14 @@ module Cequel
51
51
 
52
52
  module Helpers
53
53
 
54
+ def self.cql_version
55
+ Cequel.connect(host: host,
56
+ port: port,
57
+ keyspace: "system")
58
+ .execute("SELECT cql_version FROM system.local")
59
+ .first["cql_version"]
60
+ end
61
+
54
62
  def self.cequel
55
63
  @cequel ||= Cequel.connect(
56
64
  host: host,
@@ -151,6 +159,21 @@ module Cequel
151
159
  0.001
152
160
  end
153
161
 
162
+ def example_slug(example, max_length=1000)
163
+ example.description.downcase.gsub(/[^a-z]+/, '_')[/.{1,#{max_length}}$/]
164
+ end
165
+
166
+ # figures a table name starting with `base_name` that is unique to the
167
+ # specified example
168
+ #
169
+ # Examples
170
+ #
171
+ # let(:table_name) { |ex| unique_table_name("posts", ex) }
172
+ #
173
+ def unique_table_name(base_name, example)
174
+ max_suffix = 45 - base_name.size
175
+ :"#{base_name}_#{example_slug(example, max_suffix)}"
176
+ end
154
177
  end
155
178
  end
156
179
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Brown
@@ -29,7 +29,7 @@ authors:
29
29
  autorequire:
30
30
  bindir: bin
31
31
  cert_chain: []
32
- date: 2017-01-26 00:00:00.000000000 Z
32
+ date: 2017-03-15 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: activemodel
@@ -269,13 +269,14 @@ files:
269
269
  - lib/cequel/record/validations.rb
270
270
  - lib/cequel/schema.rb
271
271
  - lib/cequel/schema/column.rb
272
- - lib/cequel/schema/create_table_dsl.rb
273
272
  - lib/cequel/schema/keyspace.rb
274
273
  - lib/cequel/schema/migration_validator.rb
274
+ - lib/cequel/schema/patch.rb
275
275
  - lib/cequel/schema/table.rb
276
+ - lib/cequel/schema/table_desc_dsl.rb
277
+ - lib/cequel/schema/table_differ.rb
276
278
  - lib/cequel/schema/table_property.rb
277
279
  - lib/cequel/schema/table_reader.rb
278
- - lib/cequel/schema/table_synchronizer.rb
279
280
  - lib/cequel/schema/table_updater.rb
280
281
  - lib/cequel/schema/table_writer.rb
281
282
  - lib/cequel/schema/update_table_dsl.rb
@@ -308,8 +309,9 @@ files:
308
309
  - spec/examples/record/timestamps_spec.rb
309
310
  - spec/examples/record/validations_spec.rb
310
311
  - spec/examples/schema/keyspace_spec.rb
312
+ - spec/examples/schema/patch_spec.rb
313
+ - spec/examples/schema/table_differ_spec.rb
311
314
  - spec/examples/schema/table_reader_spec.rb
312
- - spec/examples/schema/table_synchronizer_spec.rb
313
315
  - spec/examples/schema/table_updater_spec.rb
314
316
  - spec/examples/schema/table_writer_spec.rb
315
317
  - spec/examples/spec_helper.rb
@@ -368,8 +370,9 @@ test_files:
368
370
  - spec/examples/record/timestamps_spec.rb
369
371
  - spec/examples/record/validations_spec.rb
370
372
  - spec/examples/schema/keyspace_spec.rb
373
+ - spec/examples/schema/patch_spec.rb
374
+ - spec/examples/schema/table_differ_spec.rb
371
375
  - spec/examples/schema/table_reader_spec.rb
372
- - spec/examples/schema/table_synchronizer_spec.rb
373
376
  - spec/examples/schema/table_updater_spec.rb
374
377
  - spec/examples/schema/table_writer_spec.rb
375
378
  - spec/examples/spec_helper.rb
@@ -1,88 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- module Cequel
3
- module Schema
4
- #
5
- # Implements a DSL that can be used to define a table schema
6
- #
7
- # @see Keyspace#create_table
8
- #
9
- class CreateTableDSL < BasicObject
10
- extend ::Cequel::Util::Forwardable
11
- #
12
- # Evaluate `block` in the context of this DSL, and apply directives to
13
- # `table`
14
- #
15
- # @param table [Table] a table
16
- # @yield block evaluated in the context of the create table DSL
17
- # @return [void]
18
- #
19
- # @api private
20
- #
21
- def self.apply(table, &block)
22
- dsl = new(table)
23
- dsl.instance_eval(&block)
24
- end
25
-
26
- #
27
- # @param table [Table] table to apply directives to
28
- #
29
- # @api private
30
- #
31
- def initialize(table)
32
- @table = table
33
- end
34
-
35
- #
36
- # @!method partition_key(name, type)
37
- # (see Cequel::Schema::Table#add_partition_key)
38
- #
39
- def_delegator :@table, :add_partition_key, :partition_key
40
-
41
- #
42
- # @!method key(name, type, clustering_order = nil)
43
- # (see Cequel::Schema::Table#add_key)
44
- #
45
- def_delegator :@table, :add_key, :key
46
-
47
- #
48
- # @!method column(name, type, options = {})
49
- # (see Cequel::Schema::Table#add_data_column)
50
- #
51
- def_delegator :@table, :add_data_column, :column
52
-
53
- #
54
- # @!method list(name, type)
55
- # (see Cequel::Schema::Table#add_list)
56
- #
57
- def_delegator :@table, :add_list, :list
58
-
59
- #
60
- # @!method set(name, type)
61
- # (see Cequel::Schema::Table#add_set)
62
- #
63
- def_delegator :@table, :add_set, :set
64
-
65
- #
66
- # @!method map(name, key_type, value_type)
67
- # (see Cequel::Schema::Table#add_map)
68
- #
69
- def_delegator :@table, :add_map, :map
70
-
71
- #
72
- # @!method with(name, value)
73
- # (see Cequel::Schema::Table#add_property)
74
- #
75
- def_delegator :@table, :add_property, :with
76
-
77
- #
78
- # Direct that this table use "compact storage". This is primarily useful
79
- # for backwards compatibility with legacy CQL2 table schemas.
80
- #
81
- # @return [void]
82
- #
83
- def compact_storage
84
- @table.compact_storage = true
85
- end
86
- end
87
- end
88
- end
@@ -1,180 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- module Cequel
3
- module Schema
4
- #
5
- # Synchronize a table schema in the database with a desired table schema
6
- #
7
- # @see .apply
8
- # @see Keyspace#synchronize_table
9
- #
10
- class TableSynchronizer
11
- # @return [Table] table as it is currently defined
12
- # @api private
13
- attr_reader :existing
14
- # @return [Table] table schema as it is desired
15
- # @api private
16
- attr_reader :updated
17
- #
18
- # Takes an existing table schema read from the database, and a desired
19
- # schema for that table. Modifies the table schema in the database to
20
- # match the desired schema, or creates the table as specified if it does
21
- # not yet exist
22
- #
23
- # @param keyspace [Metal::Keyspace] keyspace that contains table
24
- # @param existing [Table] table schema as it is currently defined
25
- # @param updated [Table] table schema as it is desired
26
- # @return [void]
27
- # @raise (see #apply)
28
- #
29
- def self.apply(keyspace, existing, updated)
30
- if existing
31
- TableUpdater.apply(keyspace, existing.name) do |updater|
32
- new(updater, existing, updated).apply
33
- end
34
- else
35
- TableWriter.apply(keyspace, updated)
36
- end
37
- end
38
-
39
- #
40
- # @param updater [TableUpdater] table updater to hold schema
41
- # modifications
42
- # @param existing [Table] table schema as it is currently defined
43
- # @param updated [Table] table schema as it is desired
44
- # @return [void]
45
- # @private
46
- #
47
- def initialize(updater, existing, updated)
48
- @updater, @existing, @updated = updater, existing, updated
49
- end
50
- private_class_method :new
51
-
52
- #
53
- # Apply the changes needed to synchronize the schema in the database with
54
- # the desired schema
55
- #
56
- # @return [void]
57
- # @raise (see MigrationValidator#validate!)
58
- #
59
- # @api private
60
- #
61
- def apply
62
- validate!
63
- update_keys
64
- update_columns
65
- update_properties
66
- end
67
-
68
- #
69
- # Iterate over pairs of (old_key, new_key)
70
- #
71
- # @yieldparam old_key [Column] key in existing schema
72
- # @yieldparam new_key [Column] corresponding key in updated schema
73
- # @return [void]
74
- #
75
- # @api private
76
- #
77
- def each_key_pair(&block)
78
- existing.key_columns.zip(updated.key_columns, &block)
79
- end
80
-
81
- #
82
- # Iterate over pairs of (old_column, new_column)
83
- #
84
- # @yieldparam old_column [Column] column in existing schema
85
- # @yieldparam new_column [Column] corresponding column in updated schema
86
- # @return [void]
87
- #
88
- # @api private
89
- #
90
- def each_data_column_pair(&block)
91
- if existing.compact_storage? && existing.clustering_columns.any?
92
- yield existing.data_columns.first, updated.data_columns.first
93
- else
94
- old_columns = existing.data_columns.index_by { |col| col.name }
95
- new_columns = updated.data_columns.index_by { |col| col.name }
96
- all_column_names = (old_columns.keys + new_columns.keys).tap(&:uniq!)
97
- all_column_names.each do |name|
98
- yield old_columns[name], new_columns[name]
99
- end
100
- end
101
- end
102
-
103
- #
104
- # Iterate over pairs of (old_clustering_column, new_clustering_column)
105
- #
106
- # @yieldparam old_clustering_column [Column] key in existing schema
107
- # @yieldparam new_clustering_column [Column] corresponding key in updated
108
- # schema
109
- # @return [void]
110
- #
111
- # @api private
112
- #
113
- def each_clustering_column_pair(&block)
114
- existing.clustering_columns.zip(updated.clustering_columns, &block)
115
- end
116
-
117
- protected
118
-
119
- attr_reader :updater
120
-
121
- private
122
-
123
- def update_keys
124
- each_key_pair do |old_key, new_key|
125
- if old_key.name != new_key.name
126
- updater.rename_column(old_key.name || :column1, new_key.name)
127
- end
128
- end
129
- end
130
-
131
- def update_columns
132
- each_data_column_pair do |old_column, new_column|
133
- if old_column.nil?
134
- add_column(new_column)
135
- elsif new_column
136
- update_column(old_column, new_column)
137
- update_index(old_column, new_column)
138
- end
139
- end
140
- end
141
-
142
- def add_column(column)
143
- updater.add_data_column(column)
144
- updater.create_index(column.name, column.index_name) if column.indexed?
145
- end
146
-
147
- def update_column(old_column, new_column)
148
- if old_column.name != new_column.name
149
- updater.rename_column(old_column.name || :value, new_column.name)
150
- end
151
- if old_column.type != new_column.type
152
- updater.change_column(new_column.name, new_column.type)
153
- end
154
- end
155
-
156
- def update_index(old_column, new_column)
157
- if !old_column.indexed? && new_column.indexed?
158
- updater.create_index(new_column.name, new_column.index_name)
159
- elsif old_column.indexed? && !new_column.indexed?
160
- updater.drop_index(old_column.index_name)
161
- end
162
- end
163
-
164
- def update_properties
165
- changes = {}
166
- updated.properties.each_pair do |name, new_property|
167
- old_property = existing.property(name)
168
- if old_property != new_property.value
169
- changes[name] = new_property.value
170
- end
171
- end
172
- updater.change_properties(changes) if changes.any?
173
- end
174
-
175
- def validate!
176
- MigrationValidator.validate!(self)
177
- end
178
- end
179
- end
180
- end
@@ -1,200 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require File.expand_path('../../spec_helper', __FILE__)
3
-
4
- describe Cequel::Schema::TableSynchronizer do
5
- let(:table_name) { :"posts_#{SecureRandom.hex(4)}" }
6
-
7
- let(:table) { cequel.schema.read_table(table_name) }
8
-
9
- context 'with no existing table' do
10
- before do
11
- cequel.schema.sync_table table_name do
12
- key :blog_subdomain, :text
13
- key :permalink, :text
14
- column :title, :text
15
- column :body, :text
16
- column :created_at, :timestamp
17
- set :author_names, :text
18
- with :comment, 'Test Table'
19
- end
20
- end
21
-
22
- after { cequel.schema.drop_table(table_name) }
23
-
24
- it 'should create table' do
25
- expect(table.column(:title).type).to eq(Cequel::Type[:text]) #etc.
26
- end
27
- end
28
-
29
- context 'with an existing table' do
30
- before do
31
- cequel.schema.create_table table_name do
32
- key :blog_subdomain, :text
33
- key :permalink, :text
34
- column :title, :ascii, :index => true
35
- column :body, :ascii
36
- column :created_at, :timestamp
37
- set :author_names, :text
38
- with :comment, 'Test Table'
39
- end
40
- end
41
-
42
- after { cequel.schema.drop_table(table_name) }
43
-
44
- context 'with valid changes' do
45
-
46
- before do
47
- cequel.schema.sync_table table_name do
48
- key :blog_subdomain, :text
49
- key :post_permalink, :text
50
- column :title, :ascii
51
- column :body, :text
52
- column :primary_author_id, :uuid, :index => true
53
- column :created_at, :timestamp, :index => true
54
- column :published_at, :timestamp
55
- set :author_names, :text
56
- list :categories, :text
57
- with :comment, 'Test Table 2.0'
58
- end
59
- end
60
-
61
- it 'should rename keys' do
62
- expect(table.clustering_columns.first.name).to eq(:post_permalink)
63
- end
64
-
65
- it 'should add new columns' do
66
- expect(table.column(:published_at).type).to eq(Cequel::Type[:timestamp])
67
- end
68
-
69
- it 'should add new collections' do
70
- expect(table.column(:categories)).to be_a(Cequel::Schema::List)
71
- end
72
-
73
- it 'should add new column with index' do
74
- expect(table.column(:primary_author_id)).to be_indexed
75
- end
76
-
77
- it 'should add index to existing columns' do
78
- expect(table.column(:created_at)).to be_indexed
79
- end
80
-
81
- it 'should drop index from existing columns' do
82
- expect(table.column(:title)).not_to be_indexed
83
- end
84
-
85
- it 'should change column type' do
86
- expect(table.column(:body).type).to eq(Cequel::Type[:text])
87
- end
88
-
89
- it 'should change properties' do
90
- expect(table.property(:comment)).to eq('Test Table 2.0')
91
- end
92
-
93
- end
94
-
95
- context 'invalid migrations' do
96
-
97
- it 'should not allow changing type of key' do
98
- expect {
99
- cequel.schema.sync_table table_name do
100
- key :blog_subdomain, :text
101
- key :permalink, :ascii
102
- column :title, :ascii
103
- column :body, :text
104
- column :created_at, :timestamp
105
- set :author_names, :text
106
- with :comment, 'Test Table'
107
- end
108
- }.to raise_error(Cequel::InvalidSchemaMigration)
109
- end
110
-
111
- it 'should not allow adding a key' do
112
- expect {
113
- cequel.schema.sync_table table_name do
114
- key :blog_subdomain, :text
115
- key :permalink, :text
116
- key :year, :int
117
- column :title, :ascii
118
- column :body, :text
119
- column :created_at, :timestamp
120
- set :author_names, :text
121
- with :comment, 'Test Table'
122
- end
123
- }.to raise_error(Cequel::InvalidSchemaMigration)
124
- end
125
-
126
- it 'should not allow removing a key' do
127
- expect {
128
- cequel.schema.sync_table table_name do
129
- key :blog_subdomain, :text
130
- column :title, :ascii
131
- column :body, :text
132
- column :created_at, :timestamp
133
- set :author_names, :text
134
- with :comment, 'Test Table'
135
- end
136
- }.to raise_error(Cequel::InvalidSchemaMigration)
137
- end
138
-
139
- it 'should not allow changing the partition status of a key' do
140
- expect {
141
- cequel.schema.sync_table table_name do
142
- key :blog_subdomain, :text
143
- partition_key :permalink, :text
144
- column :title, :ascii
145
- column :body, :text
146
- column :created_at, :timestamp
147
- set :author_names, :text
148
- with :comment, 'Test Table'
149
- end
150
- }.to raise_error(Cequel::InvalidSchemaMigration)
151
- end
152
-
153
- it 'should not allow changing the data structure of a column' do
154
- expect {
155
- cequel.schema.sync_table table_name do
156
- key :blog_subdomain, :text
157
- key :permalink, :text
158
- column :title, :ascii
159
- column :body, :text
160
- column :created_at, :timestamp
161
- list :author_names, :text
162
- with :comment, 'Test Table'
163
- end
164
- }.to raise_error(Cequel::InvalidSchemaMigration)
165
- end
166
-
167
- it 'should not allow invalid type transitions of a data column' do
168
- expect {
169
- cequel.schema.sync_table table_name do
170
- key :blog_subdomain, :text
171
- key :permalink, :text
172
- column :title, :ascii, :index => true
173
- column :body, :int
174
- column :created_at, :timestamp
175
- set :author_names, :text
176
- with :comment, 'Test Table'
177
- end
178
- }.to raise_error(Cequel::InvalidSchemaMigration)
179
- end
180
-
181
- it 'should not allow changing clustering order' do
182
- expect {
183
- cequel.schema.sync_table table_name do
184
- key :blog_subdomain, :text
185
- key :permalink, :text, :desc
186
- column :title, :ascii, :index => true
187
- column :body, :ascii
188
- column :created_at, :timestamp
189
- set :author_names, :text
190
- with :comment, 'Test Table'
191
- end
192
- }.to raise_error(Cequel::InvalidSchemaMigration)
193
- end
194
-
195
- end
196
-
197
-
198
- end
199
-
200
- end