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
@@ -86,17 +86,17 @@ describe Cequel::Metal::Keyspace do
86
86
  end
87
87
  end
88
88
 
89
- describe "#drop_table", cassandra: '~> 3.x' do
89
+ describe "#drop_table", cql: "~> 3.1" do
90
90
  it "allows IF EXISTS" do
91
91
  expect { cequel.schema.drop_table(:unknown) }.to raise_error(Cassandra::Errors::InvalidError)
92
- expect { cequel.schema.drop_table(:unknown, exists: true) }.not_to raise_error(Cassandra::Errors::InvalidError)
92
+ expect { cequel.schema.drop_table(:unknown, exists: true) }.not_to raise_error
93
93
  end
94
94
  end
95
95
 
96
- describe "#drop_materialized_view", cassandra: '~> 3.x' do
96
+ describe "#drop_materialized_view", cql: "~> 3.4" do
97
97
  it "allows IF EXISTS" do
98
98
  expect { cequel.schema.drop_materialized_view(:unknown) }.to raise_error(Cassandra::Errors::ConfigurationError)
99
- expect { cequel.schema.drop_materialized_view(:unknown, exists: true) }.not_to raise_error(Cassandra::Errors::ConfigurationError)
99
+ expect { cequel.schema.drop_materialized_view(:unknown, exists: true) }.not_to raise_error
100
100
  end
101
101
  end
102
102
 
@@ -104,6 +104,7 @@ describe Cequel::Record::Associations do
104
104
  expect do
105
105
  Class.new do
106
106
  include Cequel::Record
107
+ self.table_name = "foo"
107
108
  key :permalink, :text
108
109
  belongs_to :blog
109
110
  end
@@ -114,6 +115,7 @@ describe Cequel::Record::Associations do
114
115
  expect do
115
116
  Class.new do
116
117
  include Cequel::Record
118
+ self.table_name = "foo"
117
119
  belongs_to :blog
118
120
  belongs_to :user
119
121
  end
@@ -169,6 +171,8 @@ describe Cequel::Record::Associations do
169
171
  expect do
170
172
  Class.new do
171
173
  include Cequel::Record
174
+ self.table_name = "foo"
175
+
172
176
  key :permalink, :text
173
177
  belongs_to :post, partition: true
174
178
  end
@@ -179,6 +183,8 @@ describe Cequel::Record::Associations do
179
183
  expect do
180
184
  Class.new do
181
185
  include Cequel::Record
186
+ self.table_name = "foo"
187
+
182
188
  belongs_to :post, partition: true
183
189
  belongs_to :user
184
190
  end
@@ -2,7 +2,7 @@
2
2
  require_relative 'spec_helper'
3
3
 
4
4
  describe Cequel::Record::MassAssignment do
5
- context 'with strong parameters', :rails => '~> 4.0' do
5
+ context 'with strong parameters', rails: '>= 4.0' do
6
6
  model :Post do
7
7
  key :permalink, :text
8
8
  column :title, :text
@@ -34,7 +34,7 @@ describe Cequel::Record::MassAssignment do
34
34
  end
35
35
  end
36
36
 
37
- context 'with mass-assignment protection', :rails => '~> 3.1' do
37
+ context 'with mass-assignment protection', rails: '~> 3.0' do
38
38
  model :Post do
39
39
  key :permalink, :text
40
40
  column :title, :text
@@ -203,6 +203,7 @@ describe Cequel::Record::Properties do
203
203
  expect do
204
204
  Class.new do
205
205
  include Cequel::Record
206
+ self.table_name = "foo"
206
207
  key :subdomain, :text, auto: true
207
208
  end
208
209
  end.to raise_error(ArgumentError)
@@ -806,7 +806,7 @@ describe Cequel::Record::RecordSet do
806
806
  end
807
807
  end
808
808
 
809
- context 'allow_filtering!', cassandra: '~> 3.x' do
809
+ context 'allow_filtering!', cql: '~> 3.4' do
810
810
  it 'should allow filtering for none indexed columns' do
811
811
  expect(Post.allow_filtering!.where(title: 'Cequel 0').entries.length).to be(1)
812
812
  end
@@ -0,0 +1,190 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.expand_path('../../spec_helper', __FILE__)
3
+
4
+ module Cequel::Schema
5
+ describe Patch do
6
+
7
+ let(:table_name) { |ex| unique_table_name("posts", ex) }
8
+ let(:table) {
9
+ Table.new(table_name).tap do |t|
10
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
11
+ end
12
+ }
13
+
14
+ describe ".new" do
15
+ it "returns a Patch" do
16
+ expect(
17
+ described_class.new([])
18
+ ).to be_kind_of described_class
19
+ end
20
+ end
21
+
22
+ describe "#changes" do
23
+ context "no changes" do
24
+ subject { described_class.new([]) }
25
+
26
+ it "returns the changes" do
27
+ expect( subject.changes ).to be_empty
28
+ end
29
+ end
30
+
31
+ context "no some changes" do
32
+ let(:change) {
33
+ Patch::AddColumn.new(table, DataColumn.new(:author_name, Cequel::Type[:text]))
34
+ }
35
+
36
+ subject { described_class.new([change]) }
37
+
38
+ it "returns the changes" do
39
+ expect( subject.changes ).to eq [change]
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "#statements" do
45
+ let(:change) {
46
+ Patch::AddColumn.new(table, DataColumn.new(:author_name, Cequel::Type[:text]))
47
+ }
48
+
49
+ subject { described_class.new([change]) }
50
+
51
+ it "returns a statement for each change" do
52
+ expect( subject.statements.count ).to eq subject.changes.count
53
+ expect( subject.statements.count ).to eq 1
54
+ end
55
+ end
56
+ end
57
+
58
+ describe Patch::SetTableProperties do
59
+ let(:table_name) { |ex| unique_table_name("posts", ex) }
60
+ let(:table) {
61
+ Table.new(table_name).tap do |t|
62
+ t.add_property TableProperty.build(:comment, "hello")
63
+ end
64
+ }
65
+
66
+ describe "#new" do
67
+ it "returns #{described_class}" do
68
+ expect( described_class.new(table) ).to be_kind_of described_class
69
+ end
70
+ end
71
+
72
+ subject { described_class.new(table) }
73
+
74
+ describe "#to_cql" do
75
+ it "sets the property" do
76
+ expect( subject.to_cql ).to match /alter +table +"?#{table_name}"? +with/i
77
+ expect( subject.to_cql ).to match /"?comment"? *= *'hello'/i
78
+ end
79
+ end
80
+
81
+ describe "#properties" do
82
+ it "returns collection of Table properties to be set" do
83
+ expect( subject.properties ).to all be_kind_of TableProperty
84
+ expect( subject.properties ).to eq table.properties.values
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ describe Patch::DropIndex do
91
+ let(:table_name) { |ex| unique_table_name("posts", ex) }
92
+ let(:table) { Table.new(table_name) }
93
+ let(:column_with_obsolete_idx) {
94
+ DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
95
+ }
96
+
97
+ describe "#new" do
98
+ it "returns #{described_class}" do
99
+ expect(
100
+ described_class.new(table, column_with_obsolete_idx)
101
+ ).to be_kind_of described_class
102
+ end
103
+ end
104
+
105
+ subject { described_class.new(table, column_with_obsolete_idx) }
106
+
107
+ describe "#to_cql" do
108
+ it "drops index" do
109
+ expect( subject.to_cql ).to match /drop +index/i
110
+ expect( subject.to_cql ).to match /"?#{column_with_obsolete_idx.index_name}"?/i
111
+ end
112
+ end
113
+
114
+ describe "#index_name" do
115
+ it "returns the index name" do
116
+ expect( subject.index_name ).to eq column_with_obsolete_idx.index_name
117
+ end
118
+ end
119
+ end
120
+
121
+ describe Patch::AddIndex do
122
+ let(:table_name) { |ex| unique_table_name("posts", ex) }
123
+ let(:table) { Table.new(table_name) }
124
+ let(:column) {
125
+ DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
126
+ }
127
+
128
+ describe "#new" do
129
+ it "returns #{described_class}" do
130
+ expect(
131
+ described_class.new(table, column)
132
+ ).to be_kind_of described_class
133
+ end
134
+ end
135
+
136
+ subject { described_class.new(table, column) }
137
+
138
+ describe "#to_cql" do
139
+ it "creates the index" do
140
+ expect( subject.to_cql ).to match /create +index/i
141
+ expect( subject.to_cql ).to match /"#{column.index_name}"/i
142
+ expect( subject.to_cql ).to match /( *"#{column.name}" *)/i
143
+ end
144
+ end
145
+
146
+ describe "#index_name" do
147
+ it "returns the index name" do
148
+ expect( subject.index_name ).to eq column.index_name
149
+ end
150
+ end
151
+
152
+ describe "#column" do
153
+ it "returns the column" do
154
+ expect( subject.column ).to eq column
155
+ end
156
+ end
157
+ end
158
+
159
+ describe Patch::AddColumn do
160
+ let(:table_name) { |ex| unique_table_name("posts", ex) }
161
+ let(:table) { Table.new(table_name) }
162
+ let(:column) {
163
+ DataColumn.new(:author_name, Cequel::Type[:text])
164
+ }
165
+
166
+ describe "#new" do
167
+ it "returns #{described_class}" do
168
+ expect(
169
+ described_class.new(table, column)
170
+ ).to be_kind_of described_class
171
+ end
172
+ end
173
+
174
+ subject { described_class.new(table, column) }
175
+
176
+ describe "#to_cql" do
177
+ it "adds the column" do
178
+ expect( subject.to_cql ).to match /alter +table/i
179
+ expect( subject.to_cql ).to match /"#{table_name}"/i
180
+ expect( subject.to_cql ).to match /add +"#{column.name}"/i
181
+ end
182
+ end
183
+
184
+ describe "#column" do
185
+ it "returns the column" do
186
+ expect( subject.column ).to eq column
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,280 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.expand_path('../../spec_helper', __FILE__)
3
+
4
+ module Cequel::Schema
5
+ describe TableDiffer do
6
+
7
+ let(:table_name) { |ex| unique_table_name("posts", ex) }
8
+ let(:orig_table) {
9
+ Table.new(table_name).tap do |t|
10
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
11
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
12
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
13
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
14
+ t.add_property TableProperty.build(:comment, "Orig comment")
15
+ end
16
+ }
17
+
18
+ describe ".new" do
19
+ it "returns a TableDiffer" do
20
+ expect(
21
+ described_class.new(orig_table, orig_table)
22
+ ).to be_kind_of described_class
23
+ end
24
+ end
25
+
26
+ describe ".call" do
27
+ it "returns a Patch" do
28
+ updated_table = Table.new(table_name).tap do |t|
29
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
30
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
31
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
32
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
33
+ t.add_property TableProperty.build(:comment, "Orig comment")
34
+ end
35
+
36
+ expect(
37
+ described_class.new(orig_table, updated_table).call
38
+ ).to be_kind_of Patch
39
+ end
40
+
41
+ it "fails for table name changes" do
42
+ renamed_table = Table.new(:fancy_new_name).tap do |t|
43
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
44
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
45
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
46
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
47
+ t.add_property TableProperty.build(:comment, "Orig comment")
48
+ end
49
+
50
+ expect{
51
+ described_class.new(orig_table, renamed_table).call
52
+ }.to raise_error(Cequel::InvalidSchemaMigration)
53
+ end
54
+
55
+ it "succeed if name difference it immaterial" do
56
+ equiv_table = Table.new(table_name.to_s).tap do |t|
57
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
58
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
59
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
60
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
61
+ t.add_property TableProperty.build(:comment, "Orig comment")
62
+ end
63
+
64
+ expect{
65
+ described_class.new(orig_table, equiv_table).call
66
+ }.not_to raise_error
67
+ end
68
+
69
+ it "fails for type changes" do
70
+ updated_table = Table.new(table_name).tap do |t|
71
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
72
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
73
+ t.add_column DataColumn.new(:body, Cequel::Type[:ascii])
74
+ end
75
+
76
+ expect{
77
+ described_class.new(orig_table, updated_table).call
78
+ }.to raise_error(Cequel::InvalidSchemaMigration)
79
+ end
80
+
81
+ it "fails for partition key changes" do
82
+ updated_table = Table.new(table_name).tap do |t|
83
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
84
+ t.add_column PartitionKey.new(:date, Cequel::Type[:timestamp])
85
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
86
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
87
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
88
+ t.add_property TableProperty.build(:comment, "Orig comment")
89
+ end
90
+
91
+ expect{
92
+ described_class.new(orig_table, updated_table).call
93
+ }.to raise_error(Cequel::InvalidSchemaMigration)
94
+ end
95
+
96
+ it "fails for clustering order changes" do
97
+ updated_table = Table.new(table_name).tap do |t|
98
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
99
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text], :desc)
100
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
101
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
102
+ t.add_property TableProperty.build(:comment, "Orig comment")
103
+ end
104
+
105
+ expect{
106
+ described_class.new(orig_table, updated_table).call
107
+ }.to raise_error(Cequel::InvalidSchemaMigration)
108
+ end
109
+
110
+ it "ignore immaterial changes to clustering order" do
111
+ unchanged_table = Table.new(table_name).tap do |t|
112
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
113
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text], :asc)
114
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
115
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
116
+ t.add_property TableProperty.build(:comment, "Orig comment")
117
+ end
118
+
119
+ expect{
120
+ described_class.new(orig_table, unchanged_table).call
121
+ }.not_to raise_error
122
+ end
123
+
124
+ it "detects a lack of changes changes" do
125
+ unchanged_table = Table.new(table_name).tap do |t|
126
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
127
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
128
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
129
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text], :author_name_idx)
130
+ t.add_property TableProperty.build(:comment, "Orig comment")
131
+ end
132
+
133
+ expect(
134
+ described_class.new(orig_table, unchanged_table).call
135
+ ).to be_empty
136
+ end
137
+
138
+ it "detects new columns" do
139
+ updated_table = Table.new(table_name).tap do |t|
140
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
141
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
142
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
143
+ t.add_column DataColumn.new(:created_at, Cequel::Type[:timestamp])
144
+ end
145
+
146
+ expect(
147
+ described_class.new(orig_table, updated_table).call
148
+ ).to add_column(:created_at).of_type(:timestamp)
149
+ end
150
+
151
+ it "detects added index to existing column" do
152
+ updated_table = Table.new(table_name).tap do |t|
153
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
154
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
155
+ t.add_column DataColumn.new(:body, Cequel::Type[:text], :my_index_name)
156
+ end
157
+
158
+ expect(
159
+ described_class.new(orig_table, updated_table).call
160
+ ).to add_index(:my_index_name).of_column(:body)
161
+ end
162
+
163
+ it "detects dropped index" do
164
+ updated_table = Table.new(table_name).tap do |t|
165
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
166
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
167
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
168
+ t.add_column DataColumn.new(:author_name, Cequel::Type[:text])
169
+ end
170
+
171
+ expect(
172
+ described_class.new(orig_table, updated_table).call
173
+ ).to drop_index(:author_name_idx)
174
+ end
175
+
176
+ it "detects added index to new column" do
177
+ updated_table = Table.new(table_name).tap do |t|
178
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
179
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
180
+ t.add_column DataColumn.new(:category, Cequel::Type[:text], :posts_category_idx)
181
+ end
182
+
183
+ expect(
184
+ described_class.new(orig_table, updated_table).call
185
+ ).to add_index(:posts_category_idx).of_column(:category)
186
+ end
187
+
188
+ it "ignores dropped columns" do
189
+ updated_table = Table.new(table_name).tap do |t|
190
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
191
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
192
+ t.add_column DataColumn.new(:body, Cequel::Type[:text])
193
+ t.add_property TableProperty.build(:comment, "Orig comment")
194
+ end
195
+
196
+ expect(
197
+ described_class.new(orig_table, updated_table).call
198
+ ).to be_empty
199
+ end
200
+
201
+ it "detects new property" do
202
+ updated_table = Table.new(table_name).tap do |t|
203
+ t.add_column PartitionKey.new(:blog_subdomain, Cequel::Type[:text])
204
+ t.add_column ClusteringColumn.new(:slug, Cequel::Type[:text])
205
+ t.add_property TableProperty.build(:comment, "test")
206
+ end
207
+
208
+ expect(
209
+ described_class.new(orig_table, updated_table).call
210
+ ).to set_property(:comment, "test")
211
+ end
212
+ end
213
+
214
+ # Background
215
+
216
+ matcher :set_property do |expected_name, expected_val|
217
+ match do |actual_patch|
218
+ actual_patch.changes
219
+ .any? { |actual_change|
220
+ actual_change.is_a?(Cequel::Schema::Patch::SetTableProperties) &&
221
+ actual_change.properties
222
+ .any?{|p|
223
+ p.name.to_s == expected_name.to_s &&
224
+ expected_val === p.value
225
+ }
226
+ }
227
+ end
228
+ end
229
+
230
+ matcher :drop_index do |index_name|
231
+ match do |actual_patch|
232
+ actual_patch.changes
233
+ .any? { |actual_change|
234
+ actual_change.is_a?(Cequel::Schema::Patch::DropIndex) &&
235
+ actual_change.index_name.to_s == index_name.to_s
236
+ }
237
+ end
238
+ end
239
+
240
+ matcher :add_index do |index_name|
241
+ @column_matcher = ->(_){true}
242
+
243
+ match do |actual_patch|
244
+ actual_patch.changes
245
+ .any? { |actual_change|
246
+ actual_change.is_a?(Cequel::Schema::Patch::AddIndex) &&
247
+ @column_matcher === actual_change.column &&
248
+ actual_change.index_name.to_s == index_name.to_s
249
+ }
250
+ end
251
+
252
+ chain :of_column do |col_name|
253
+ @column_matcher = ->(col) { col.name.to_s == col_name.to_s }
254
+ end
255
+ end
256
+
257
+ matcher :add_column do |name|
258
+ @type_matcher = ->(_){true}
259
+
260
+ match do |actual_patch|
261
+ actual_patch.changes
262
+ .any? { |actual_change|
263
+ actual_change.is_a?(Cequel::Schema::Patch::AddColumn) &&
264
+ actual_change.column.name == name &&
265
+ @type_matcher === actual_change.column.type
266
+ }
267
+ end
268
+
269
+ chain :of_type do |type_or_matcher|
270
+ @type_matcher = if type_or_matcher.respond_to?(:call)
271
+ type_or_matcher
272
+ elsif t = Cequel::Type[type_or_matcher]
273
+ ->(type) { type == t }
274
+ else
275
+ fail "unsupported type matcher: #{type_or_matcher.inspect}"
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end