cequel 1.0.0.rc1 → 1.0.0.rc2

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