cequel 2.1.0 → 3.0.0

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