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