cequel 1.0.0.rc1 → 1.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/cequel.rb +18 -0
  3. data/lib/cequel/errors.rb +8 -4
  4. data/lib/cequel/metal.rb +14 -0
  5. data/lib/cequel/metal/batch.rb +21 -11
  6. data/lib/cequel/metal/batch_manager.rb +74 -0
  7. data/lib/cequel/metal/cql_row_specification.rb +19 -6
  8. data/lib/cequel/metal/data_set.rb +400 -163
  9. data/lib/cequel/metal/deleter.rb +45 -11
  10. data/lib/cequel/metal/incrementer.rb +23 -10
  11. data/lib/cequel/metal/inserter.rb +19 -6
  12. data/lib/cequel/metal/keyspace.rb +82 -159
  13. data/lib/cequel/metal/logger.rb +71 -0
  14. data/lib/cequel/metal/logging.rb +47 -0
  15. data/lib/cequel/metal/new_relic_instrumentation.rb +26 -0
  16. data/lib/cequel/metal/row.rb +36 -10
  17. data/lib/cequel/metal/row_specification.rb +21 -8
  18. data/lib/cequel/metal/statement.rb +30 -6
  19. data/lib/cequel/metal/updater.rb +89 -12
  20. data/lib/cequel/metal/writer.rb +23 -14
  21. data/lib/cequel/record.rb +52 -6
  22. data/lib/cequel/record/association_collection.rb +13 -6
  23. data/lib/cequel/record/associations.rb +146 -54
  24. data/lib/cequel/record/belongs_to_association.rb +34 -7
  25. data/lib/cequel/record/bound.rb +69 -12
  26. data/lib/cequel/record/bulk_writes.rb +29 -1
  27. data/lib/cequel/record/callbacks.rb +22 -6
  28. data/lib/cequel/record/collection.rb +273 -36
  29. data/lib/cequel/record/configuration_generator.rb +5 -0
  30. data/lib/cequel/record/data_set_builder.rb +86 -0
  31. data/lib/cequel/record/dirty.rb +11 -8
  32. data/lib/cequel/record/errors.rb +38 -4
  33. data/lib/cequel/record/has_many_association.rb +42 -9
  34. data/lib/cequel/record/lazy_record_collection.rb +39 -10
  35. data/lib/cequel/record/mass_assignment.rb +14 -6
  36. data/lib/cequel/record/persistence.rb +157 -20
  37. data/lib/cequel/record/properties.rb +147 -24
  38. data/lib/cequel/record/railtie.rb +15 -2
  39. data/lib/cequel/record/record_set.rb +504 -75
  40. data/lib/cequel/record/schema.rb +77 -13
  41. data/lib/cequel/record/scoped.rb +16 -11
  42. data/lib/cequel/record/secondary_indexes.rb +42 -6
  43. data/lib/cequel/record/tasks.rb +2 -1
  44. data/lib/cequel/record/validations.rb +51 -11
  45. data/lib/cequel/schema.rb +9 -0
  46. data/lib/cequel/schema/column.rb +172 -33
  47. data/lib/cequel/schema/create_table_dsl.rb +62 -31
  48. data/lib/cequel/schema/keyspace.rb +106 -7
  49. data/lib/cequel/schema/migration_validator.rb +128 -0
  50. data/lib/cequel/schema/table.rb +183 -20
  51. data/lib/cequel/schema/table_property.rb +92 -34
  52. data/lib/cequel/schema/table_reader.rb +45 -15
  53. data/lib/cequel/schema/table_synchronizer.rb +101 -43
  54. data/lib/cequel/schema/table_updater.rb +114 -19
  55. data/lib/cequel/schema/table_writer.rb +31 -13
  56. data/lib/cequel/schema/update_table_dsl.rb +71 -40
  57. data/lib/cequel/type.rb +214 -53
  58. data/lib/cequel/util.rb +6 -9
  59. data/lib/cequel/version.rb +2 -1
  60. data/spec/examples/record/associations_spec.rb +12 -12
  61. data/spec/examples/record/persistence_spec.rb +5 -5
  62. data/spec/examples/record/record_set_spec.rb +62 -50
  63. data/spec/examples/schema/table_synchronizer_spec.rb +37 -11
  64. data/spec/examples/schema/table_updater_spec.rb +3 -3
  65. data/spec/examples/spec_helper.rb +2 -11
  66. data/spec/examples/type_spec.rb +3 -3
  67. metadata +23 -4
  68. data/lib/cequel/new_relic_instrumentation.rb +0 -22
data/lib/cequel/util.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  module Cequel
2
-
3
2
  module Util
4
-
3
+ #
4
+ # @api private
5
+ #
5
6
  module HashAccessors
6
-
7
7
  def hattr_reader(hash, *attributes)
8
8
  attributes.each do |attribute|
9
- module_eval <<-RUBY, __FILE__, __LINE__+1
9
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
10
10
  def #{attribute}
11
11
  #{hash}[#{attribute.to_sym.inspect}]
12
12
  end
@@ -16,7 +16,7 @@ module Cequel
16
16
 
17
17
  def hattr_inquirer(hash, *attributes)
18
18
  attributes.each do |attribute|
19
- module_eval <<-RUBY, __FILE__, __LINE__+1
19
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
20
20
  def #{attribute}?
21
21
  !!#{hash}[#{attribute.to_sym.inspect}]
22
22
  end
@@ -26,7 +26,7 @@ module Cequel
26
26
 
27
27
  def hattr_writer(hash, *attributes)
28
28
  attributes.each do |attribute|
29
- module_eval <<-RUBY, __FILE__, __LINE__+1
29
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
30
30
  def #{attribute}=(value)
31
31
  #{hash}[#{attribute.to_sym.inspect}] = value
32
32
  end
@@ -38,9 +38,6 @@ module Cequel
38
38
  hattr_reader(hash, *attributes)
39
39
  hattr_writer(hash, *attributes)
40
40
  end
41
-
42
41
  end
43
-
44
42
  end
45
-
46
43
  end
@@ -1,3 +1,4 @@
1
1
  module Cequel
2
- VERSION = '1.0.0.rc1'
2
+ # The current version of the library
3
+ VERSION = '1.0.0.rc2'
3
4
  end
@@ -27,12 +27,20 @@ describe Cequel::Record::Associations do
27
27
  belongs_to :post
28
28
  key :id, :uuid, auto: true
29
29
  column :content, :text
30
+
31
+ cattr_accessor :callback_count
32
+ self.callback_count = 0
33
+ before_destroy { self.class.callback_count += 1 }
30
34
  end
31
35
 
32
36
  model :Attachment do
33
37
  belongs_to :post
34
38
  key :id, :uuid, auto: true
35
39
  column :caption, :text
40
+
41
+ cattr_accessor :callback_count
42
+ self.callback_count = 0
43
+ before_destroy { self.class.callback_count += 1 }
36
44
  end
37
45
 
38
46
  describe '::belongs_to' do
@@ -156,10 +164,6 @@ describe Cequel::Record::Associations do
156
164
  comment.post = post_with_comments
157
165
  end.tap(&:save)
158
166
  end
159
- @callback_count = 0
160
- Comment.any_instance.stub(:run_callbacks).with(:destroy) do
161
- @callback_count += 1
162
- end
163
167
  end
164
168
 
165
169
  it "deletes all children when destroying the parent" do
@@ -171,11 +175,11 @@ describe Cequel::Record::Associations do
171
175
  it "executes :destroy callbacks on the children" do
172
176
  expect {
173
177
  post_with_comments.destroy
174
- }.to change { @callback_count }.by(2)
178
+ }.to change { Comment.callback_count }.by(2)
175
179
  end
176
180
  end
177
181
 
178
- context "with dependent => delete" do
182
+ context "with :dependent => :delete" do
179
183
  let(:post_with_attachments) { posts.first }
180
184
 
181
185
  before :each do
@@ -185,10 +189,6 @@ describe Cequel::Record::Associations do
185
189
  comment.post = post_with_attachments
186
190
  end.tap(&:save)
187
191
  end
188
- @callback_count = 0
189
- Attachment.any_instance.stub(:run_callbacks).with(:destroy) do
190
- @callback_count += 1
191
- end
192
192
  end
193
193
 
194
194
  it "deletes all children when destroying the parent" do
@@ -197,10 +197,10 @@ describe Cequel::Record::Associations do
197
197
  }.to change { Attachment.count }.by(-2)
198
198
  end
199
199
 
200
- it "executes :destroy callbacks on the children" do
200
+ it "does not execute :destroy callbacks on the children" do
201
201
  expect {
202
202
  post_with_attachments.destroy
203
- }.to change { @callback_count }.by(0)
203
+ }.not_to change { Attachment.callback_count }
204
204
  end
205
205
  end
206
206
  end
@@ -40,7 +40,7 @@ describe Cequel::Record::Persistence do
40
40
  it 'should fail fast if keys are missing' do
41
41
  expect {
42
42
  Blog.new.save
43
- }.to raise_error(Cequel::MissingKeyError)
43
+ }.to raise_error(Cequel::Record::MissingKeyError)
44
44
  end
45
45
  end
46
46
 
@@ -99,7 +99,7 @@ describe Cequel::Record::Persistence do
99
99
  Blog.create do |blog|
100
100
  blog.name = 'Big Data'
101
101
  end
102
- }.to raise_error(Cequel::MissingKeyError)
102
+ }.to raise_error(Cequel::Record::MissingKeyError)
103
103
  end
104
104
  end
105
105
 
@@ -119,7 +119,7 @@ describe Cequel::Record::Persistence do
119
119
  it 'should fail fast if keys are missing' do
120
120
  expect {
121
121
  Blog.create(:name => 'Big Data')
122
- }.to raise_error(Cequel::MissingKeyError)
122
+ }.to raise_error(Cequel::Record::MissingKeyError)
123
123
  end
124
124
  end
125
125
  end
@@ -189,7 +189,7 @@ describe Cequel::Record::Persistence do
189
189
  post.permalink = 'cequel'
190
190
  post.title = 'Cequel'
191
191
  end.tap(&:save)
192
- }.to raise_error(Cequel::MissingKeyError)
192
+ }.to raise_error(Cequel::Record::MissingKeyError)
193
193
  end
194
194
 
195
195
  it 'should fail fast if row keys are missing' do
@@ -198,7 +198,7 @@ describe Cequel::Record::Persistence do
198
198
  post.blog_subdomain = 'cassandra'
199
199
  post.title = 'Cequel'
200
200
  end.tap(&:save)
201
- }.to raise_error(Cequel::MissingKeyError)
201
+ }.to raise_error(Cequel::Record::MissingKeyError)
202
202
  end
203
203
  end
204
204
 
@@ -192,30 +192,6 @@ describe Cequel::Record::RecordSet do
192
192
  end
193
193
  end
194
194
 
195
- context 'multiple simple primary keys' do
196
- let(:records) { blogs }
197
- subject { Blog['blog-0', 'blog-1'] }
198
-
199
- it 'should return both specified records' do
200
- subject.map(&:subdomain).should =~ %w(blog-0 blog-1)
201
- end
202
-
203
- it 'should not query the database' do
204
- disallow_queries!
205
- subject.map(&:subdomain)
206
- end
207
-
208
- it 'should load value lazily' do
209
- subject.first.name.should == 'Blog 0'
210
- end
211
-
212
- it 'should load values for all referenced records on first access' do
213
- max_statements! 1
214
- subject.first.name.should == 'Blog 0'
215
- subject.last.name.should == 'Blog 1'
216
- end
217
- end
218
-
219
195
  context 'fully specified compound primary key' do
220
196
  let(:records) { posts }
221
197
  subject { Post['cassandra']['cequel0'] }
@@ -242,39 +218,43 @@ describe Cequel::Record::RecordSet do
242
218
  end
243
219
  end
244
220
 
245
- context 'fully specified compound primary key with multiple clustering columns' do
221
+ context 'partially specified compound primary key' do
246
222
  let(:records) { posts }
247
- subject { Post['cassandra']['cequel0', 'cequel1'] }
223
+ it 'should create partial collection if not all keys specified' do
224
+ Post['cassandra'].find_each(:batch_size => 2).map(&:title).
225
+ should == (0...5).map { |i| "Cequel #{i}" }
226
+ end
227
+ end
228
+ end
248
229
 
249
- it 'should combine partition key with each clustering column' do
250
- disallow_queries!
251
- subject.map(&:key_values).
252
- should == [['cassandra', 'cequel0'], ['cassandra', 'cequel1']]
230
+ describe '#values_at' do
231
+ context 'multiple simple primary keys' do
232
+ let(:records) { blogs }
233
+ subject { Blog.values_at('blog-0', 'blog-1') }
234
+
235
+ it 'should return both specified records' do
236
+ subject.map(&:subdomain).should =~ %w(blog-0 blog-1)
253
237
  end
254
238
 
255
- it 'should lazily load all records when one record accessed' do
256
- max_statements! 1
257
- subject.first.title.should == 'Cequel 0'
258
- subject.second.title.should == 'Cequel 1'
239
+ it 'should not query the database' do
240
+ disallow_queries!
241
+ subject.map(&:subdomain)
259
242
  end
260
243
 
261
- it 'should not allow collection columns to be selected' do
262
- expect { Post.select(:tags)['cassandra']['cequel0', 'cequel1'] }.
263
- to raise_error(ArgumentError)
244
+ it 'should load value lazily' do
245
+ subject.first.name.should == 'Blog 0'
264
246
  end
265
- end
266
247
 
267
- context 'partially specified compound primary key' do
268
- let(:records) { posts }
269
- it 'should create partial collection if not all keys specified' do
270
- Post['cassandra'].find_each(:batch_size => 2).map(&:title).
271
- should == (0...5).map { |i| "Cequel #{i}" }
248
+ it 'should load values for all referenced records on first access' do
249
+ max_statements! 1
250
+ subject.first.name.should == 'Blog 0'
251
+ subject.last.name.should == 'Blog 1'
272
252
  end
273
253
  end
274
254
 
275
255
  context 'partially specified compound primary key with multiple partition keys' do
276
256
  let(:records) { posts }
277
- subject { Post['cassandra', 'postgres'] }
257
+ subject { Post.values_at('cassandra', 'postgres') }
278
258
 
279
259
  it 'should return scope to keys' do
280
260
  subject.map { |post| post.title }.should =~ (0...5).
@@ -285,7 +265,7 @@ describe Cequel::Record::RecordSet do
285
265
  context 'fully specified compound primary key with multiple partition keys' do
286
266
  let(:records) { [posts, orm_posts] }
287
267
 
288
- subject { Post['cassandra', 'orms']['cequel0'] }
268
+ subject { Post.values_at('cassandra', 'orms')['cequel0'] }
289
269
 
290
270
  it 'should return collection of unloaded models' do
291
271
  disallow_queries!
@@ -299,6 +279,38 @@ describe Cequel::Record::RecordSet do
299
279
  subject.second.title.should == 'Cequel ORM 0'
300
280
  end
301
281
  end
282
+
283
+ context 'fully specified compound primary key with multiple clustering columns' do
284
+ let(:records) { posts }
285
+ subject { Post['cassandra'].values_at('cequel0', 'cequel1') }
286
+
287
+ it 'should combine partition key with each clustering column' do
288
+ disallow_queries!
289
+ subject.map(&:key_values).
290
+ should == [['cassandra', 'cequel0'], ['cassandra', 'cequel1']]
291
+ end
292
+
293
+ it 'should lazily load all records when one record accessed' do
294
+ max_statements! 1
295
+ subject.first.title.should == 'Cequel 0'
296
+ subject.second.title.should == 'Cequel 1'
297
+ end
298
+
299
+ it 'should not allow collection columns to be selected' do
300
+ expect { Post.select(:tags)['cassandra'].values_at('cequel0', 'cequel1') }.
301
+ to raise_error(ArgumentError)
302
+ end
303
+ end
304
+
305
+ context 'non-final clustering column' do
306
+ let(:records) { [] }
307
+
308
+ it 'should raise IllegalQuery' do
309
+ disallow_queries!
310
+ expect { Comment['cassandra'].values_at('cequel0') }.
311
+ to raise_error(Cequel::Record::IllegalQuery)
312
+ end
313
+ end
302
314
  end
303
315
 
304
316
  describe '#all' do
@@ -608,11 +620,11 @@ describe Cequel::Record::RecordSet do
608
620
  end
609
621
 
610
622
  it 'should update fully specified collection' do
611
- Post['cassandra']['cequel0', 'cequel1', 'cequel2'].
623
+ Post['cassandra'].values_at('cequel0', 'cequel1', 'cequel2').
612
624
  update_all(title: 'Same Title')
613
- Post['cassandra']['cequel0', 'cequel1', 'cequel2'].map(&:title).
625
+ Post['cassandra'].values_at('cequel0', 'cequel1', 'cequel2').map(&:title).
614
626
  should == Array.new(3) { 'Same Title' }
615
- Post['cassandra']['cequel3', 'cequel4'].map(&:title).
627
+ Post['cassandra'].values_at('cequel3', 'cequel4').map(&:title).
616
628
  should == cassandra_posts.drop(3).map(&:title)
617
629
  end
618
630
  end
@@ -632,7 +644,7 @@ describe Cequel::Record::RecordSet do
632
644
  end
633
645
 
634
646
  it 'should be able to delete fully specified collection' do
635
- Post['postgres']['sequel0', 'sequel1'].delete_all
647
+ Post['postgres'].values_at('sequel0', 'sequel1').delete_all
636
648
  Post['postgres'].map(&:permalink).
637
649
  should == postgres_posts.drop(2).map(&:permalink)
638
650
  end
@@ -653,7 +665,7 @@ describe Cequel::Record::RecordSet do
653
665
  end
654
666
 
655
667
  it 'should be able to delete fully specified collection' do
656
- Post['postgres']['sequel0', 'sequel1'].destroy_all
668
+ Post['postgres'].values_at('sequel0', 'sequel1').destroy_all
657
669
  Post['postgres'].map(&:permalink).
658
670
  should == postgres_posts.drop(2).map(&:permalink)
659
671
  end
@@ -29,8 +29,8 @@ describe Cequel::Schema::TableSynchronizer do
29
29
  cequel.schema.create_table :posts do
30
30
  key :blog_subdomain, :text
31
31
  key :permalink, :text
32
- column :title, :text, :index => true
33
- column :body, :text
32
+ column :title, :ascii, :index => true
33
+ column :body, :ascii
34
34
  column :created_at, :timestamp
35
35
  set :author_names, :text
36
36
  with :comment, 'Test Table'
@@ -45,8 +45,8 @@ describe Cequel::Schema::TableSynchronizer do
45
45
  cequel.schema.sync_table :posts do
46
46
  key :blog_subdomain, :text
47
47
  key :post_permalink, :text
48
- column :title, :text
49
- column :body, :ascii
48
+ column :title, :ascii
49
+ column :body, :text
50
50
  column :primary_author_id, :uuid, :index => true
51
51
  column :created_at, :timestamp, :index => true
52
52
  column :published_at, :timestamp
@@ -81,7 +81,7 @@ describe Cequel::Schema::TableSynchronizer do
81
81
  end
82
82
 
83
83
  it 'should change column type' do
84
- table.column(:body).type.should == Cequel::Type[:ascii]
84
+ table.column(:body).type.should == Cequel::Type[:text]
85
85
  end
86
86
 
87
87
  it 'should change properties' do
@@ -97,7 +97,7 @@ describe Cequel::Schema::TableSynchronizer do
97
97
  cequel.schema.sync_table :posts do
98
98
  key :blog_subdomain, :text
99
99
  key :permalink, :ascii
100
- column :title, :text
100
+ column :title, :ascii
101
101
  column :body, :text
102
102
  column :created_at, :timestamp
103
103
  set :author_names, :text
@@ -112,7 +112,7 @@ describe Cequel::Schema::TableSynchronizer do
112
112
  key :blog_subdomain, :text
113
113
  key :permalink, :text
114
114
  key :year, :int
115
- column :title, :text
115
+ column :title, :ascii
116
116
  column :body, :text
117
117
  column :created_at, :timestamp
118
118
  set :author_names, :text
@@ -125,7 +125,7 @@ describe Cequel::Schema::TableSynchronizer do
125
125
  expect {
126
126
  cequel.schema.sync_table :posts do
127
127
  key :blog_subdomain, :text
128
- column :title, :text
128
+ column :title, :ascii
129
129
  column :body, :text
130
130
  column :created_at, :timestamp
131
131
  set :author_names, :text
@@ -139,7 +139,7 @@ describe Cequel::Schema::TableSynchronizer do
139
139
  cequel.schema.sync_table :posts do
140
140
  key :blog_subdomain, :text
141
141
  partition_key :permalink, :text
142
- column :title, :text
142
+ column :title, :ascii
143
143
  column :body, :text
144
144
  column :created_at, :timestamp
145
145
  set :author_names, :text
@@ -153,7 +153,7 @@ describe Cequel::Schema::TableSynchronizer do
153
153
  cequel.schema.sync_table :posts do
154
154
  key :blog_subdomain, :text
155
155
  key :permalink, :text
156
- column :title, :text
156
+ column :title, :ascii
157
157
  column :body, :text
158
158
  column :created_at, :timestamp
159
159
  list :author_names, :text
@@ -162,7 +162,33 @@ describe Cequel::Schema::TableSynchronizer do
162
162
  }.to raise_error(Cequel::InvalidSchemaMigration)
163
163
  end
164
164
 
165
- it 'should not allow changing of clustering order'
165
+ it 'should not allow invalid type transitions of a data column' do
166
+ expect {
167
+ cequel.schema.sync_table :posts do
168
+ key :blog_subdomain, :text
169
+ key :permalink, :text
170
+ column :title, :ascii, :index => true
171
+ column :body, :int
172
+ column :created_at, :timestamp
173
+ set :author_names, :text
174
+ with :comment, 'Test Table'
175
+ end
176
+ }.to raise_error(Cequel::InvalidSchemaMigration)
177
+ end
178
+
179
+ it 'should not allow changing clustering order' do
180
+ expect {
181
+ cequel.schema.sync_table :posts do
182
+ key :blog_subdomain, :text
183
+ key :permalink, :text, :desc
184
+ column :title, :ascii, :index => true
185
+ column :body, :ascii
186
+ column :created_at, :timestamp
187
+ set :author_names, :text
188
+ with :comment, 'Test Table'
189
+ end
190
+ }.to raise_error(Cequel::InvalidSchemaMigration)
191
+ end
166
192
 
167
193
  end
168
194
 
@@ -5,7 +5,7 @@ describe Cequel::Schema::TableUpdater do
5
5
  cequel.schema.create_table(:posts) do
6
6
  key :blog_subdomain, :text
7
7
  key :permalink, :text
8
- column :title, :text
8
+ column :title, :ascii
9
9
  column :body, :text
10
10
  end
11
11
  end
@@ -83,12 +83,12 @@ describe Cequel::Schema::TableUpdater do
83
83
  describe '#change_column' do
84
84
  before do
85
85
  cequel.schema.alter_table(:posts) do
86
- change_column :title, :ascii
86
+ change_column :title, :text
87
87
  end
88
88
  end
89
89
 
90
90
  it 'should change the type' do
91
- table.data_column(:title).type.should == Cequel::Type[:ascii]
91
+ table.data_column(:title).type.should == Cequel::Type[:text]
92
92
  end
93
93
  end
94
94