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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31fac6cdfcc42c42c1cd03489d11aa97e4791193
4
- data.tar.gz: 5d86e45f0dac711e70936c61ff7a4879095b28d8
3
+ metadata.gz: c970b068b50a1215dc4bf58af07dfc5b1b936214
4
+ data.tar.gz: c7470426d1cac668b2bd698d7f35608835b2bf0a
5
5
  SHA512:
6
- metadata.gz: b4a4696642f07aed6bf5e777b57fd48a9f570eb7ae370e31f8b37d92770874b9d25b0b72d0159dc3501d2d94671e0845f33119895cadc4bbe3498c668ee38fe7
7
- data.tar.gz: 7600517db78e87916d1f40584d21f181388ee91b2c21a73d26db17a598ce5c4bbee627ed26cfab4c33f0a84908bd568ab3e0cd9df67df76eec1eaa4242c5969c
6
+ metadata.gz: 5ce7dfd8aa2d73dc6d6748a789b4cd84ab137fb7c3d98bc70df830c6f64d8fb86a0e4e3fb26de6ce0d0c8167db8fcb8a0a2e3c57799f6847cf239db0ae73e4e0
7
+ data.tar.gz: 886e98099ef58ee431a76f4560e89ad10e3146a0850293671aa8bef45b852fa1bcdab41d2968639ec24dd7bfcba45c516ac88bf1de0f119f3fecf8b15a0eda95
@@ -1,3 +1,9 @@
1
+ ## 3.0.0
2
+ * Drop support for changing the type of cluster keys as it is no longer support by Cassandra.
3
+ * Drop support for non-option based index specification in table schema DSL. For example, `column :author_name, :text, true` must be rewritten as `column :author_name, :text, index: true`.
4
+ * Fix Relic instrumentation for batch statements [PR 361](https://github.com/cequel/cequel/pull/361)
5
+ * Don't set table name when it is already present [PR 364](https://github.com/cequel/cequel/pull/364)
6
+
1
7
  ## 2.1.0
2
8
 
3
9
  * Add ActiveRecord::Enum like support `column :status, :enum, values: { open: 1, closed: 2 }` ([PR 354](https://github.com/cequel/cequel/pull/354))
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cequel (2.1.0)
4
+ cequel (3.0.0)
5
5
  activemodel (>= 4.0)
6
6
  cassandra-driver (~> 3.0)
7
7
 
@@ -357,4 +357,4 @@ DEPENDENCIES
357
357
  yard (~> 0.6)
358
358
 
359
359
  BUNDLED WITH
360
- 1.12.5
360
+ 1.14.6
data/README.md CHANGED
@@ -612,6 +612,14 @@ you require this functionality.
612
612
 
613
613
  ## Breaking API changes
614
614
 
615
+ ### 3.0
616
+
617
+ * Dropped support for changing the type of cluster keys because the ability has
618
+ been removed from Cassandra. Calls to `#change_column` must be removed.
619
+ * Dropped support for previously deprecated signature of the `#column` method
620
+ of schema DSL. Uses like `column :my_column, :text, true` must be rewritten
621
+ as `#column :my_column, :text, indexed: true`
622
+
615
623
  ### 2.0
616
624
 
617
625
  * dropped support for jruby (Due to difficult to work around bugs in jruby. PRs welcome to restore jruby compatibility.)
@@ -10,4 +10,6 @@ module Cequel
10
10
  InvalidSchemaMigration = Class.new(StandardError)
11
11
 
12
12
  NoSuchKeyspaceError = Class.new(StandardError)
13
+
14
+ NoSuchTableError = Class.new(StandardError)
13
15
  end
@@ -16,7 +16,7 @@ module Cequel
16
16
  define_method :execute_with_options_with_newrelic do |statement, options|
17
17
 
18
18
  operation = nil
19
- statement_txt = nil
19
+ statement_txt = nil
20
20
  statement_words = nil
21
21
 
22
22
  if statement.is_a?(::Cequel::Metal::Statement)
@@ -25,6 +25,7 @@ module Cequel
25
25
  operation = statement_words.first.downcase
26
26
  elsif statement.is_a?(::Cassandra::Statements::Batch)
27
27
  operation = "batch"
28
+ statement_txt = 'BEGIN BATCH'
28
29
  end
29
30
 
30
31
  callback = Proc.new do |result, scoped_metric, elapsed|
@@ -136,6 +136,14 @@ module Cequel
136
136
  weak_descendants << WeakRef.new(base)
137
137
  end
138
138
 
139
+ # This is probably not the method you are looking for.
140
+ #
141
+ # Clear descendants list. Useful in tests to ensure bogus record classes
142
+ # are not synced.
143
+ def forget_all_descendants!
144
+ weak_descendants.clear
145
+ end
146
+
139
147
  private
140
148
 
141
149
  def weak_descendants
@@ -20,7 +20,7 @@ module Cequel
20
20
 
21
21
  included do
22
22
  class_attribute :table_name, instance_writer: false
23
- self.table_name = name.demodulize.tableize.to_sym unless name.nil?
23
+ self.table_name = name.demodulize.tableize.to_sym unless name.nil? || self.table_name.present?
24
24
  end
25
25
 
26
26
  #
@@ -80,21 +80,24 @@ module Cequel
80
80
  # @return [void]
81
81
  #
82
82
  def synchronize_schema
83
- reader = get_table_reader
84
- return if reader.materialized_view?
85
- Cequel::Schema::TableSynchronizer
86
- .apply(connection, reader.read, table_schema)
87
- end
88
-
89
- #
90
- # Return a {TableReader} instance
91
- #
92
- # @return [Schema::TableReader] object
93
- #
94
- def get_table_reader
95
83
  fail MissingTableNameError unless table_name
96
84
 
97
- connection.schema.get_table_reader(table_name)
85
+ patch =
86
+ begin
87
+ existing_table_descriptor = Cequel::Schema::TableReader.read(connection,
88
+ table_name)
89
+
90
+ return if existing_table_descriptor.materialized_view?
91
+
92
+ Cequel::Schema::TableDiffer.new(existing_table_descriptor,
93
+ table_schema)
94
+ .call
95
+
96
+ rescue NoSuchTableError
97
+ Cequel::Schema::TableWriter.new(table_schema)
98
+ end
99
+
100
+ patch.statements.each { |stmt| connection.execute(stmt) }
98
101
  end
99
102
 
100
103
  #
@@ -113,46 +116,50 @@ module Cequel
113
116
  # specified in the class definition
114
117
  #
115
118
  def table_schema
116
- @table_schema ||= Cequel::Schema::Table.new(table_name)
119
+ dsl.table
117
120
  end
118
121
 
119
122
  protected
120
123
 
124
+ def dsl
125
+ @dsl ||= Cequel::Schema::TableDescDsl.new(table_name)
126
+ end
127
+
121
128
  def key(name, type, options = {})
122
129
  super
123
130
  if options[:partition]
124
- table_schema.add_partition_key(name, type)
131
+ dsl.partition_key(name, type)
125
132
  else
126
- table_schema.add_key(name, type, options[:order])
133
+ dsl.key(name, type, options[:order])
127
134
  end
128
135
  end
129
136
 
130
137
  def column(name, type, options = {})
131
138
  super
132
- table_schema.add_data_column(name, type, options[:index])
139
+ dsl.column(name, type, options)
133
140
  end
134
141
 
135
142
  def list(name, type, options = {})
136
143
  super
137
- table_schema.add_list(name, type)
144
+ dsl.list(name, type)
138
145
  end
139
146
 
140
147
  def set(name, type, options = {})
141
148
  super
142
- table_schema.add_set(name, type)
149
+ dsl.set(name, type)
143
150
  end
144
151
 
145
152
  def map(name, key_type, value_type, options = {})
146
153
  super
147
- table_schema.add_map(name, key_type, value_type)
154
+ dsl.map(name, key_type, value_type)
148
155
  end
149
156
 
150
157
  def table_property(name, value)
151
- table_schema.add_property(name, value)
158
+ dsl.with(name, value)
152
159
  end
153
160
 
154
161
  def compact_storage
155
- table_schema.compact_storage = true
162
+ dsl.compact_storage
156
163
  end
157
164
  end
158
165
 
@@ -1,12 +1,13 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'cequel/schema/column'
3
- require 'cequel/schema/create_table_dsl'
3
+ require 'cequel/schema/table_desc_dsl'
4
4
  require 'cequel/schema/keyspace'
5
5
  require 'cequel/schema/migration_validator'
6
6
  require 'cequel/schema/table'
7
7
  require 'cequel/schema/table_property'
8
8
  require 'cequel/schema/table_reader'
9
- require 'cequel/schema/table_synchronizer'
9
+ require 'cequel/schema/table_differ'
10
+ require 'cequel/schema/patch'
10
11
  require 'cequel/schema/table_updater'
11
12
  require 'cequel/schema/table_writer'
12
13
  require 'cequel/schema/update_table_dsl'
@@ -71,6 +71,15 @@ module Cequel
71
71
  false
72
72
  end
73
73
 
74
+ # Indicates if this column is indexed. Overridden by subclasses that
75
+ # support indexing.
76
+ #
77
+ # @return [Boolean] true if this column has a secondary index
78
+ #
79
+ def indexed?
80
+ false
81
+ end
82
+
74
83
  #
75
84
  # @param type_in [Symbol,Type] type to check against
76
85
  # @return [Boolean] true if this column has the type given by `type_in`
@@ -96,7 +105,7 @@ module Cequel
96
105
  # @api private
97
106
  #
98
107
  def to_cql
99
- "#{@name} #{@type}"
108
+ %Q|"#{@name}" #{@type}|
100
109
  end
101
110
 
102
111
  #
@@ -107,6 +116,7 @@ module Cequel
107
116
  def ==(other)
108
117
  to_cql == other.to_cql
109
118
  end
119
+ alias_method :eql?, :==
110
120
 
111
121
  #
112
122
  # @return [String] the column's name
@@ -52,7 +52,7 @@ module Cequel
52
52
  }
53
53
 
54
54
  options = options.symbolize_keys
55
- options.reverse_merge!(keyspace.configuration)
55
+ options.reverse_merge!(keyspace.configuration.symbolize_keys)
56
56
  options.reverse_merge!(default_options)
57
57
 
58
58
  if options.key?(:class)
@@ -148,8 +148,7 @@ module Cequel
148
148
  # @see CreateTableDSL
149
149
  #
150
150
  def create_table(name, &block)
151
- table = Table.new(name)
152
- CreateTableDSL.apply(table, &block)
151
+ table = TableDescDsl.new(name).eval(&block)
153
152
  TableWriter.apply(keyspace, table)
154
153
  end
155
154
 
@@ -224,13 +223,25 @@ module Cequel
224
223
  # @see #create_table Example of DSL usage
225
224
  #
226
225
  def sync_table(name, &block)
227
- existing = read_table(name)
228
- updated = Table.new(name)
229
- CreateTableDSL.apply(updated, &block)
230
- TableSynchronizer.apply(keyspace, existing, updated)
226
+ new_table_desc = TableDescDsl.new(name).eval(&block)
227
+ patch = if has_table?(name)
228
+ existing_table_desc = read_table(name)
229
+ TableDiffer.new(existing_table_desc, new_table_desc).call
230
+ else
231
+ TableWriter.new(new_table_desc) # close enough to a patch
232
+ end
233
+
234
+ patch.statements.each{|stmt| keyspace.execute(stmt) }
231
235
  end
232
236
  alias_method :synchronize_table, :sync_table
233
237
 
238
+
239
+ # Returns true iff the specified table name exists in the keyspace.
240
+ #
241
+ def has_table?(table_name)
242
+ !!read_table(table_name)
243
+ end
244
+
234
245
  protected
235
246
 
236
247
  attr_reader :keyspace
@@ -0,0 +1,152 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Cequel
3
+ module Schema
4
+ #
5
+ # The set of changes needed to transform a table from its current form to a
6
+ # desired form. `Patch`es are immutable.
7
+ #
8
+ class Patch
9
+ extend Forwardable
10
+
11
+ protected def initialize(changes)
12
+ @changes = changes
13
+ end
14
+
15
+ attr_reader :changes
16
+
17
+ def_delegator :changes, :empty?
18
+
19
+ def statements
20
+ changes.map(&:to_cql)
21
+ end
22
+
23
+ class AbstractChange
24
+ protected def initialize(table, *post_init_args)
25
+ @table = table
26
+
27
+ post_init(*post_init_args)
28
+ end
29
+
30
+ attr_reader :table
31
+
32
+ def to_cql
33
+ fail NotImplementedError
34
+ end
35
+
36
+ def inspect
37
+ "#<#{self.class.name} #{to_cql}>"
38
+ end
39
+
40
+ def ==(other)
41
+ other.class == self.class &&
42
+ other.table == self.table &&
43
+ subclass_eql?(other)
44
+ end
45
+
46
+ def eql?(other)
47
+ self == other
48
+ end
49
+
50
+ protected
51
+
52
+ def subclass_eql?(other)
53
+ fail NotImplementedError
54
+ end
55
+ end
56
+
57
+ class SetTableProperties < AbstractChange
58
+ protected def post_init()
59
+ end
60
+
61
+ def to_cql
62
+ %Q|ALTER TABLE "#{table.name}" WITH #{properties.map(&:to_cql).join(' AND ')}|
63
+ end
64
+
65
+ def properties
66
+ table.properties.values
67
+ end
68
+
69
+ protected
70
+
71
+ def subclass_eql?(other)
72
+ other.properties == propreties
73
+ end
74
+ end
75
+
76
+ class DropIndex < AbstractChange
77
+ protected def post_init(column_with_obsolete_idx)
78
+ @index_name = column_with_obsolete_idx.index_name
79
+ end
80
+
81
+ attr_reader :index_name
82
+
83
+ def to_cql
84
+ %Q|DROP INDEX IF EXISTS "#{index_name}"|
85
+ end
86
+
87
+ protected
88
+
89
+ def subclass_eql?(other)
90
+ other.index_name == index_name
91
+ end
92
+ end
93
+
94
+ class AddIndex < AbstractChange
95
+ protected def post_init(column)
96
+ @column = column
97
+ @index_name = column.index_name
98
+ end
99
+
100
+ attr_reader :column, :index_name
101
+
102
+ def to_cql
103
+ %Q|CREATE INDEX "#{index_name}" ON "#{table.name}" ("#{column.name}")|
104
+ end
105
+
106
+ protected
107
+
108
+ def subclass_eql?(other)
109
+ other.column == column &&
110
+ other.index_name == index_name
111
+ end
112
+ end
113
+
114
+ class AddColumn < AbstractChange
115
+ protected def post_init(column)
116
+ @column = column
117
+ end
118
+
119
+ attr_reader :column
120
+
121
+ def to_cql
122
+ %Q|ALTER TABLE "#{table.name}" ADD #{column.to_cql}|
123
+ end
124
+
125
+ protected
126
+
127
+ def subclass_eql?(other)
128
+ other.column == column
129
+ end
130
+ end
131
+
132
+ class RenameColumn < AbstractChange
133
+ protected def post_init(old_column, new_column)
134
+ @old_name, @new_name = old_column.name, new_column.name
135
+ end
136
+
137
+ attr_reader :old_name, :new_name
138
+
139
+ def to_cql
140
+ %Q|ALTER TABLE "#{table.name}" RENAME "#{old_name}" TO "#{new_name}"|
141
+ end
142
+
143
+ protected
144
+
145
+ def subclass_eql?(other)
146
+ other.old_name == old_name &&
147
+ other.new_name == new_name
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end