hyper_record 0.2.8

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.
@@ -0,0 +1,948 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
+
3
+ module ActiveRecord
4
+ module HyperRecord
5
+ describe HyperBase, '.describe_table' do
6
+ fixtures :pages
7
+
8
+ it "should return a string describing the table schema" do
9
+ table_description = Page.connection.describe_table(Page.table_name)
10
+ table_description.should_not be_empty
11
+ table_description.should include("name")
12
+ table_description.should include("url")
13
+ end
14
+ end
15
+
16
+ describe HyperBase, '.table_exists?' do
17
+ fixtures :pages
18
+
19
+ it "should return true if the underlying table exists" do
20
+ Page.table_exists?.should be_true
21
+ end
22
+
23
+ it "should return false if the underlying table does not exists" do
24
+ Dummy.table_exists?.should be_false
25
+ end
26
+ end
27
+
28
+ describe HyperBase, '.drop_table' do
29
+ fixtures :pages
30
+
31
+ it "should remove a table from hypertable" do
32
+ Page.table_exists?.should be_true
33
+ Page.drop_table
34
+ Page.table_exists?.should be_false
35
+ end
36
+ end
37
+
38
+ describe HyperBase, '.columns' do
39
+ fixtures :pages
40
+
41
+ it "should return an array of columns within the table" do
42
+ table_columns = Page.columns
43
+ table_columns.should_not be_empty
44
+ # column list include the special ROW key.
45
+ table_columns.map{|c| c.name}.should == ['ROW', 'name', 'url']
46
+ end
47
+ end
48
+
49
+ describe HyperBase, '.column_families_without_row_key' do
50
+ fixtures :pages
51
+
52
+ it "should return an array of columns within the table but does not include the row key" do
53
+ columns_without_row_key = Page.column_families_without_row_key
54
+ columns_without_row_key.should_not be_empty
55
+ # column list does not include the special ROW key.
56
+ columns_without_row_key.map{|c| c.name}.should == ['name', 'url']
57
+ end
58
+ end
59
+
60
+ describe HyperBase, '.qualified_column_names_without_row_key' do
61
+ fixtures :pages
62
+
63
+ it "should return an array of column names where column families are replaced by fully qualified columns" do
64
+ cols = QualifiedPage.qualified_column_names_without_row_key
65
+ cols.should_not be_empty
66
+ cols.should == ['misc:name', 'misc:url', 'misc2:foo', 'misc2:bar']
67
+ end
68
+ end
69
+
70
+ describe HyperBase, '.qualified_columns' do
71
+ it "should include qualified columns in the regular column list" do
72
+ columns = QualifiedPage.columns
73
+ columns.should_not be_empty
74
+ columns.map{|c| c.name}.should == ['ROW', 'misc', 'misc2']
75
+ end
76
+ end
77
+
78
+ describe HyperBase, '.find_by_hql' do
79
+ fixtures :pages
80
+
81
+ it "should return the cells matching the hql specified" do
82
+ pages = Page.find_by_hql("SELECT * FROM pages LIMIT=1")
83
+ pages.length.should == 1
84
+ page = pages.first
85
+ page.class.should == Page
86
+ page.name.should == "LOLcats and more"
87
+ page.url.should == "http://www.icanhascheezburger.com"
88
+ end
89
+
90
+ it "should respond to the find_by_sql alias" do
91
+ pages = Page.find_by_hql("SELECT * FROM pages LIMIT=1")
92
+ pages.length.should == 1
93
+ page = pages.first
94
+ page.class.should == Page
95
+ page.name.should == "LOLcats and more"
96
+ page.url.should == "http://www.icanhascheezburger.com"
97
+ end
98
+ end
99
+
100
+ describe HyperBase, '.find_initial' do
101
+ fixtures :pages
102
+
103
+ it "should return the first row in the table" do
104
+ page = Page.find_initial({})
105
+ page.class.should == Page
106
+ page.name.should == "LOLcats and more"
107
+ page.url.should == "http://www.icanhascheezburger.com"
108
+ end
109
+ end
110
+
111
+ describe HyperBase, '.find_one' do
112
+ fixtures :pages
113
+
114
+ it "should return the requested row from the table" do
115
+ page = Page.find_one('page_1', {})
116
+ page.class.should == Page
117
+ page.name.should == "LOLcats and more"
118
+ page.url.should == "http://www.icanhascheezburger.com"
119
+ end
120
+ end
121
+
122
+ describe HyperBase, '.find_some' do
123
+ fixtures :pages
124
+
125
+ it "should return the requested rows from the table" do
126
+ row_keys = Page.find(:all).map{|p| p.ROW}
127
+ record_count = row_keys.length
128
+ record_count.should == 2
129
+ pages = Page.find(row_keys)
130
+ pages.length.should == record_count
131
+ end
132
+ end
133
+
134
+ describe HyperBase, '.find' do
135
+ fixtures :pages, :qualified_pages
136
+
137
+ it "should return the declared list of qualified columns by default" do
138
+ qp = QualifiedPage.new
139
+ qp.new_record?.should be_true
140
+ qp.misc['name'] = 'new page'
141
+ qp.misc['url']= 'new.com'
142
+ qp.ROW = 'new_qualified_page'
143
+ qp.save.should be_true
144
+
145
+ qp = QualifiedPage.find('new_qualified_page')
146
+ qp.misc.keys.sort.should == ['name', 'url']
147
+ end
148
+
149
+ it "should support the limit option" do
150
+ p = Page.new({:ROW => 'row key', :name => 'new entry'})
151
+ p.save.should be_true
152
+ Page.find(:all).length.should == 3
153
+ pages = Page.find(:all, :limit => 2)
154
+ pages.length.should == 2
155
+ end
156
+
157
+ it "should support the start_row and end_row option" do
158
+ p = Page.new({:ROW => 'row key', :name => 'new entry'})
159
+ p.save.should be_true
160
+ pages = Page.find(:all)
161
+ pages.length.should == 3
162
+ start_row = pages[1].ROW
163
+ end_row = pages[2].ROW
164
+
165
+ pages_2 = Page.find(:all, :start_row => start_row, :end_row => end_row)
166
+ pages_2.length.should == 2
167
+ pages_2[0].ROW.should == start_row
168
+ pages_2[1].ROW.should == end_row
169
+ end
170
+
171
+ it "should support the row_keys option" do
172
+ p = Page.new({:ROW => 'row key', :name => 'new entry'})
173
+ p.save.should be_true
174
+ pages = Page.find(:all)
175
+ pages.length.should == 3
176
+ row_key_1 = pages[1].ROW
177
+ row_key_2 = pages[2].ROW
178
+
179
+ pages_2 = Page.find(:all, :row_keys => [row_key_1, row_key_2])
180
+ pages_2.length.should == 2
181
+ pages_2[0].ROW.should == row_key_1
182
+ pages_2[1].ROW.should == row_key_2
183
+ end
184
+
185
+ it "should support the row_intervals option" do
186
+ p = Page.new({:ROW => 'row key', :name => 'new entry'})
187
+ p.save.should be_true
188
+ pages = Page.find(:all)
189
+ pages.length.should == 3
190
+ row_key_1 = pages[1].ROW
191
+ row_key_2 = pages[2].ROW
192
+
193
+ pages_2 = Page.find(:all, :row_intervals => [[row_key_1, row_key_2]])
194
+ pages_2.length.should == 2
195
+ pages_2[0].ROW.should == row_key_1
196
+ pages_2[1].ROW.should == row_key_2
197
+ end
198
+
199
+ it "should not support finder conditions not in Hash format" do
200
+ lambda {Page.find(:all, :conditions => "value = 1")}.should raise_error
201
+ end
202
+
203
+ it "should not support finder conditions in Hash format" do
204
+ # NOTE: will be supported in the future when Hypertable supports
205
+ # efficient lookup on arbitrary columns
206
+ lambda {
207
+ pages = Page.find(:all, :conditions => {:name => 'ESPN'})
208
+ pages.length.should == 1
209
+ p = pages.first
210
+ p.name.should == 'ESPN'
211
+ p.ROW.should == 'page_2'
212
+ }.should raise_error
213
+ end
214
+
215
+ it "should not support finder conditions in Hash format when the value is an array" do
216
+ # NOTE: will be supported in the future when Hypertable supports
217
+ # efficient lookup on arbitrary columns
218
+ lambda {
219
+ all_pages = Page.find(:all)
220
+ all_pages.length.should == 2
221
+ name_values = all_pages.map{|p| p.name}
222
+ pages = Page.find(:all, :conditions => {:name => name_values})
223
+ pages.length.should == 2
224
+ }.should raise_error
225
+ end
226
+
227
+ it "should return a specific list of qualifiers when requested explicitly in finder options" do
228
+ qp = QualifiedPage.new
229
+ qp.new_record?.should be_true
230
+ qp.misc['name'] = 'new page'
231
+ qp.misc['url']= 'new.com'
232
+ qp.ROW = 'new_qualified_page'
233
+ qp.save.should be_true
234
+
235
+ qp = QualifiedPage.find('new_qualified_page', :select => 'misc:url')
236
+ # NOTE: will be supported in the future when Hypertable supports
237
+ # efficient lookup on arbitrary columns
238
+ # qp.misc.keys.sort.should == ['url']
239
+ # For now, it returns all columns
240
+ qp.misc.keys.sort.should == ['name', 'url']
241
+ end
242
+
243
+ describe ':select option' do
244
+ before(:each) do
245
+ @qpweq = QualifiedPageWithoutExplicitQualifiers.new
246
+ @qpweq.new_record?.should be_true
247
+ @qpweq.misc['name'] = 'new page'
248
+ @qpweq.misc['url'] = 'new.com'
249
+ @qpweq.ROW = 'new_qualified_page'
250
+ @qpweq.save.should be_true
251
+ end
252
+
253
+ it "should return an empty hash for all qualified columns even if none are explicitly listed in qualifiers" do
254
+ @qpweq2 = QualifiedPageWithoutExplicitQualifiers.find(@qpweq.ROW)
255
+ @qpweq2.misc.should == {}
256
+ @qpweq2.misc2.should == ""
257
+ end
258
+
259
+ it "should return correct values for qualified columns named in select list using comma separated string" do
260
+ qpweq2 = QualifiedPageWithoutExplicitQualifiers.find(@qpweq.ROW, :select => "misc,misc2")
261
+ qpweq2.misc.should == {"name"=>"new page", "url"=>"new.com"}
262
+ qpweq2.misc2.should == ""
263
+ end
264
+
265
+ it "should return correct values for qualified columns named in select list using array" do
266
+ qpweq2 = QualifiedPageWithoutExplicitQualifiers.find(@qpweq.ROW, :select => ["misc", "misc2"])
267
+ qpweq2.misc.should == {"name"=>"new page", "url"=>"new.com"}
268
+ qpweq2.misc2.should == ""
269
+ end
270
+ end
271
+
272
+ it "should instantiate the object with empty hashes for qualified columns when no explicit select list is supplied" do
273
+ qp = QualifiedPage.new
274
+ qp.new_record?.should be_true
275
+ qp.ROW = 'new_qualified_page'
276
+ qp.misc2['name'] = 'test'
277
+ qp.save.should be_true
278
+
279
+ qp = QualifiedPage.find(:first)
280
+ qp.misc.should == {}
281
+ end
282
+
283
+ it "should only instantiate requested columns when option set" do
284
+ p = Page.find("page_1")
285
+ p.name.should == "LOLcats and more"
286
+ p.url.should == "http://www.icanhascheezburger.com"
287
+
288
+ p = Page.find("page_1",
289
+ :select => 'name',
290
+ :instantiate_only_requested_columns => true)
291
+
292
+ p.name.should == "LOLcats and more"
293
+ lambda {p.url}.should raise_error(::ActiveRecord::MissingAttributeError)
294
+ end
295
+
296
+ it "should allow user to specify ROW key as part of initialize attributes" do
297
+ p = Page.new({:ROW => 'row key'})
298
+ p.ROW.should == 'row key'
299
+ end
300
+
301
+ it "should not have any residual state between calls to new" do
302
+ qp = QualifiedPage.new
303
+ qp.new_record?.should be_true
304
+ qp.misc['name'] = 'new page'
305
+ qp.misc['url']= 'new.com'
306
+ qp.ROW = 'new_qualified_page'
307
+ qp.save.should be_true
308
+
309
+ qp2 = QualifiedPage.new
310
+ qp2.misc.should == {}
311
+ qp2.misc2.should == {}
312
+ qp.misc.object_id.should_not == qp2.misc.object_id
313
+ end
314
+ end
315
+
316
+ describe HyperBase, '.table_exists?' do
317
+ fixtures :pages
318
+
319
+ it "should return true for a table that does exist" do
320
+ Page.table_exists?.should be_true
321
+ end
322
+
323
+ it "should return false for a table that does exist" do
324
+ Dummy.table_exists?.should be_false
325
+ end
326
+ end
327
+
328
+ describe HyperBase, '.primary_key' do
329
+ it "should always return the special ROW key" do
330
+ Dummy.primary_key.should == 'ROW'
331
+ end
332
+ end
333
+
334
+ describe HyperBase, '.new' do
335
+ fixtures :pages, :qualified_pages
336
+
337
+ it "should an object of correct class" do
338
+ p = Page.new
339
+ p.new_record?.should be_true
340
+ p.class.should == Page
341
+ p.class.should < ActiveRecord::HyperBase
342
+ p.attributes.keys.sort.should == ['name', 'url']
343
+ end
344
+
345
+ it "should not allow an object to be saved without a row key" do
346
+ page_count = Page.find(:all).length
347
+ p = Page.new
348
+ p.new_record?.should be_true
349
+ p.name = "new page"
350
+ p.url = "new.com"
351
+ p.valid?.should be_false
352
+ p.save.should be_false
353
+ p.new_record?.should be_true
354
+ p.ROW = "new_page"
355
+ p.valid?.should be_true
356
+ p.save.should be_true
357
+ p.new_record?.should be_false
358
+ Page.find(:all).length.should == page_count + 1
359
+ end
360
+
361
+ it "should save a table with qualified columns correctly" do
362
+ qp = QualifiedPage.new
363
+ qp.new_record?.should be_true
364
+ qp.misc['name'] = 'new page'
365
+ qp.misc['url']= 'new.com'
366
+ qp.ROW = 'new_qualified_page'
367
+ qp.save.should be_true
368
+ qp.new_record?.should be_false
369
+ qp.reload.should == qp
370
+ qp.misc['name'].should == 'new page'
371
+ qp.misc['url'].should == 'new.com'
372
+ qp.misc.keys.sort.should == ['name', 'url']
373
+ end
374
+ end
375
+
376
+ describe HyperBase, '.reload' do
377
+ fixtures :pages
378
+
379
+ it "should reload an object and revert any changed state" do
380
+ p = Page.find(:first)
381
+ p.class.should == Page
382
+ original_url = p.url.clone
383
+ p.url = "new url"
384
+ p.reload.should == p
385
+ p.url.should == original_url
386
+ end
387
+ end
388
+
389
+ describe HyperBase, '.save' do
390
+ fixtures :pages, :qualified_pages
391
+
392
+ it "should update an object in hypertable" do
393
+ p = Page.find(:first)
394
+ p.class.should == Page
395
+ original_url = p.url.clone
396
+ p.url = "new url"
397
+ p.save.should be_true
398
+ p.url.should == "new url"
399
+ p.reload.should == p
400
+ p.url.should == "new url"
401
+ end
402
+
403
+ it "should allow undeclared qualified columns to be saved, provided that the column family is declared" do
404
+ qp = QualifiedPage.new
405
+ qp.new_record?.should be_true
406
+ qp.misc['name'] = 'new page'
407
+ qp.misc['url'] = 'new.com'
408
+ qp.misc['new_column'] = 'value'
409
+ qp.ROW = 'new_qualified_page'
410
+ qp.save.should be_true
411
+ qp.new_record?.should be_false
412
+ qp.reload.should == qp
413
+ qp.misc['name'].should == 'new page'
414
+ qp.misc['url'].should == 'new.com'
415
+ qp.misc['new_column'].should == 'value'
416
+ end
417
+ end
418
+
419
+ describe HyperBase, '.save_with_mutator' do
420
+ fixtures :pages
421
+
422
+ it "should successfully save an object with mutator" do
423
+ m = Page.open_mutator
424
+ p1 = Page.new({:ROW => 'created_with_mutator_1', :url => 'url_1'})
425
+ p1.save_with_mutator!(m)
426
+
427
+ p2 = Page.new({:ROW => 'created_with_mutator_2', :url => 'url_2'})
428
+ p2.save_with_mutator!(m)
429
+
430
+ Page.close_mutator(m)
431
+
432
+ new_page_1 = Page.find('created_with_mutator_1')
433
+ new_page_1.url.should == 'url_1'
434
+
435
+ new_page_2 = Page.find('created_with_mutator_2')
436
+ new_page_2.url.should == 'url_2'
437
+ end
438
+
439
+ it "should still flush the mutator and create objects when flush is not requested on close mutator" do
440
+ # As of release 0.9.2.5, Hypertable now auto-flushes the
441
+ # mutator on close.
442
+
443
+ m = Page.open_mutator
444
+ p1 = Page.new({:ROW => 'created_with_mutator_1', :url => 'url_1'})
445
+ p1.save_with_mutator!(m)
446
+ Page.close_mutator(m, 0)
447
+
448
+ page = Page.find('created_with_mutator_1')
449
+ page.should_not be_nil
450
+ end
451
+
452
+ it "should support explicit flushing of the mutator" do
453
+ m = Page.open_mutator
454
+ p1 = Page.new({:ROW => 'created_with_mutator_1', :url => 'url_1'})
455
+ p1.save_with_mutator!(m)
456
+ Page.flush_mutator(m)
457
+ Page.close_mutator(m, 0)
458
+
459
+ new_page_1 = Page.find('created_with_mutator_1')
460
+ new_page_1.url.should == 'url_1'
461
+ end
462
+
463
+ it "should support periodic flushing" do
464
+ m = Page.open_mutator(0, 500)
465
+ p1 = Page.new({:ROW => 'created_with_mutator_1', :url => 'url_1'})
466
+ p1.save_with_mutator!(m)
467
+
468
+ lambda {p1.reload}.should raise_error(::ActiveRecord::RecordNotFound)
469
+ sleep 1
470
+ lambda {p1.reload}.should_not raise_error(::ActiveRecord::RecordNotFound)
471
+ end
472
+ end
473
+
474
+ describe HyperBase, '.update' do
475
+ fixtures :pages
476
+
477
+ it "should update an object in hypertable" do
478
+ p = Page.find(:first)
479
+ p.class.should == Page
480
+ original_url = p.url.clone
481
+ p.url = "new url"
482
+ p.update.should be_true
483
+ p.url.should == "new url"
484
+ p.reload.should == p
485
+ p.url.should == "new url"
486
+ end
487
+ end
488
+
489
+ describe HyperBase, '.destroy' do
490
+ fixtures :pages
491
+
492
+ it "should remove an object from hypertable" do
493
+ p = Page.find(:first)
494
+ p.reload.should == p
495
+ p.destroy
496
+ lambda {p.reload}.should raise_error(::ActiveRecord::RecordNotFound)
497
+ end
498
+
499
+ it "should remove an object from hypertable based on the id" do
500
+ p = Page.find(:first)
501
+ p.reload.should == p
502
+ Page.destroy(p.ROW)
503
+ lambda {p.reload}.should raise_error(::ActiveRecord::RecordNotFound)
504
+ end
505
+
506
+ it "should remove multiple objects from hypertable based on ids" do
507
+ pages = Page.find(:all)
508
+ pages.length.should == 2
509
+ Page.destroy(pages.map{|p| p.ROW})
510
+ pages = Page.find(:all)
511
+ pages.length.should == 0
512
+ end
513
+ end
514
+
515
+ describe HyperBase, '.delete' do
516
+ fixtures :pages
517
+
518
+ it "should remove an object from hypertable based on the id" do
519
+ p = Page.find(:first)
520
+ p.reload.should == p
521
+ Page.delete(p.ROW)
522
+ lambda {p.reload}.should raise_error(::ActiveRecord::RecordNotFound)
523
+ end
524
+
525
+ it "should remove multiple objects from hypertable based on ids" do
526
+ pages = Page.find(:all)
527
+ pages.length.should == 2
528
+ Page.delete(pages.map{|p| p.ROW})
529
+ pages = Page.find(:all)
530
+ pages.length.should == 0
531
+ end
532
+ end
533
+
534
+ describe HyperBase, '.exists' do
535
+ fixtures :pages
536
+
537
+ it "should confirm that a record exists" do
538
+ p = Page.find(:first)
539
+ Page.exists?(p.ROW).should be_true
540
+ end
541
+
542
+ it "should refute that a record does not exists" do
543
+ Page.exists?('foofooofoofoofoofoomonkey').should be_false
544
+ end
545
+
546
+ it "should not support arguments that are not numbers, strings or hashes" do
547
+ lambda {Page.exists?([1])}.should raise_error
548
+ end
549
+
550
+ it "should not allow a Hash argument for conditions" do
551
+ lambda {
552
+ Page.exists?(:name => 'ESPN').should be_true
553
+ }.should raise_error
554
+
555
+
556
+ lambda {
557
+ Page.find(:first, :conditions => {:name => 'ESPN'}).should_not be_nil
558
+ }.should raise_error
559
+
560
+ lambda {
561
+ Page.exists?(:name => 'foofoofoofoofoo').should be_false
562
+ }.should raise_error
563
+
564
+ lambda {
565
+ Page.find(:first, :conditions => {:name => 'foofoofoofoofoo'}).should be_nil
566
+ }.should raise_error
567
+ end
568
+ end
569
+
570
+ describe HyperBase, '.increment' do
571
+ fixtures :pages
572
+
573
+ it "should increment an integer value" do
574
+ p = Page.find(:first)
575
+ p.name = 7
576
+ p.save
577
+ p.reload
578
+ p.increment('name')
579
+ p.name.should == 8
580
+ p.save
581
+ p.reload
582
+ p.name.should == "8"
583
+ p.increment('name', 2)
584
+ p.save
585
+ p.reload
586
+ p.name.should == "10"
587
+ end
588
+ end
589
+
590
+ describe HyperBase, '.increment!' do
591
+ fixtures :pages
592
+
593
+ it "should increment an integer value and save" do
594
+ p = Page.find(:first)
595
+ p.name = 7
596
+ p.increment!('name')
597
+ p.name.should == 8
598
+ p.reload
599
+ p.name.should == "8"
600
+ p.increment!('name', 2)
601
+ p.reload
602
+ p.name.should == "10"
603
+ end
604
+ end
605
+
606
+ describe HyperBase, '.decrement' do
607
+ fixtures :pages
608
+
609
+ it "should decrement an integer value" do
610
+ p = Page.find(:first)
611
+ p.name = 7
612
+ p.save
613
+ p.reload
614
+ p.decrement('name')
615
+ p.name.should == 6
616
+ p.save
617
+ p.reload
618
+ p.name.should == "6"
619
+ p.decrement('name', 2)
620
+ p.save
621
+ p.reload
622
+ p.name.should == "4"
623
+ end
624
+ end
625
+
626
+ describe HyperBase, '.decrement!' do
627
+ fixtures :pages
628
+
629
+ it "should decrement an integer value and save" do
630
+ p = Page.find(:first)
631
+ p.name = 7
632
+ p.decrement!('name')
633
+ p.name.should == 6
634
+ p.reload
635
+ p.name.should == "6"
636
+ p.decrement!('name', 2)
637
+ p.reload
638
+ p.name.should == "4"
639
+ end
640
+ end
641
+
642
+ describe HyperBase, '.update_attribute' do
643
+ fixtures :pages
644
+
645
+ it "should allow a single attribute to be updated" do
646
+ p = Page.find(:first)
647
+ p.update_attribute(:name, 'new name value')
648
+ p.name.should == 'new name value'
649
+ p.reload
650
+ p.name.should == 'new name value'
651
+ end
652
+
653
+ it "should save changes to more than the named column because that's the way activerecord works" do
654
+ p = Page.find(:first)
655
+ p.name = "name"
656
+ p.url = "url"
657
+ p.save!
658
+ p.url = "new url"
659
+ p.update_attribute(:name, 'new name value')
660
+ p.name.should == 'new name value'
661
+ p.url.should == 'new url'
662
+ p.reload
663
+ p.name.should == 'new name value'
664
+ p.url.should == 'new url'
665
+ end
666
+ end
667
+
668
+ describe HyperBase, '.update_attributes' do
669
+ fixtures :pages
670
+
671
+ it "should allow multiple attributes to be updated" do
672
+ p = Page.find(:first)
673
+ p.update_attributes({:name => 'new name value', :url => 'http://new/'})
674
+ p.name.should == 'new name value'
675
+ p.url.should == 'http://new/'
676
+ p.reload
677
+ p.name.should == 'new name value'
678
+ p.url.should == 'http://new/'
679
+ end
680
+ end
681
+
682
+ describe HyperBase, '.attributes' do
683
+ fixtures :pages
684
+
685
+ describe '.attributes_with_quotes' do
686
+ it "should return attributes in expected format" do
687
+ p = Page.find(:first)
688
+ attrs = p.attributes_with_quotes
689
+ attrs.keys.sort.should == ["ROW", "name", "url"]
690
+ attrs['ROW'].should == 'page_1'
691
+ attrs['name'].should == 'LOLcats and more'
692
+ attrs['url'].should == 'http://www.icanhascheezburger.com'
693
+ end
694
+ end
695
+
696
+ describe '.attributes_from_column_definition' do
697
+ fixtures :pages, :qualified_pages
698
+
699
+ it "should return attributes in expected format for scalar columns" do
700
+ p = Page.find(:first)
701
+ attrs = p.send(:attributes_from_column_definition)
702
+ attrs.should == {"name"=>"", "url"=>""}
703
+ end
704
+
705
+ it "should return attributes in expected format for qualified columns" do
706
+ qp = QualifiedPage.new
707
+ qp.new_record?.should be_true
708
+ qp.misc['name'] = 'new page'
709
+ qp.misc['url']= 'new.com'
710
+ qp.ROW = 'new_qualified_page'
711
+ qp.save.should be_true
712
+ qp.reload
713
+ attrs = qp.send(:attributes_from_column_definition)
714
+ attrs.should == {"misc"=>{}, "misc2"=>{}}
715
+ end
716
+ end
717
+
718
+ describe '.attributes_from_column_definition' do
719
+ fixtures :pages, :qualified_pages
720
+
721
+ it "should accept hash assignment to qualified columns" do
722
+ qp = QualifiedPage.new
723
+ qp.ROW = 'new_page'
724
+ qp.new_record?.should be_true
725
+ value = {'name' => 'new page', 'url' => 'new.com'}
726
+ qp.misc = value
727
+ qp.misc.should == value
728
+ qp.save.should be_true
729
+ qp.reload
730
+ qp.misc.should == value
731
+ qp.misc['another_key'] = "1"
732
+ qp.misc.should == value.merge({'another_key' => "1"})
733
+ qp.save.should be_true
734
+ qp.reload
735
+ qp.misc.should == value.merge({'another_key' => "1"})
736
+ end
737
+ end
738
+ end
739
+
740
+ describe HyperBase, '.scanner' do
741
+ fixtures :pages
742
+
743
+ it "should return a scanner object from open_scanner" do
744
+ scan_spec = Hypertable::ThriftGen::ScanSpec.new
745
+ scanner = Page.open_scanner(scan_spec)
746
+ scanner.class.should == Fixnum
747
+ Page.close_scanner(scanner)
748
+ end
749
+
750
+ it "should yield a scanner object from with_scanner" do
751
+ scan_spec = Hypertable::ThriftGen::ScanSpec.new
752
+ Page.with_scanner(scan_spec) do |scanner|
753
+ scanner.is_a?(Fixnum).should be_true
754
+ end
755
+ end
756
+
757
+ it "should yield a scanner object from with_scanner" do
758
+ scan_spec = Hypertable::ThriftGen::ScanSpec.new
759
+ Page.with_scanner(scan_spec) do |scanner|
760
+ scanner.is_a?(Fixnum).should be_true
761
+ end
762
+ end
763
+
764
+ it "should support native each_cell scanner method" do
765
+ scan_spec = Hypertable::ThriftGen::ScanSpec.new
766
+ cell_count = 0
767
+ Page.with_scanner(scan_spec) do |scanner|
768
+ Page.each_cell(scanner) do |cell|
769
+ cell.is_a?(Hypertable::ThriftGen::Cell).should be_true
770
+ cell_count += 1
771
+ end
772
+ end
773
+ cell_count.should == 4
774
+ end
775
+
776
+ it "should support native each_cell_as_arrays scanner method" do
777
+ scan_spec = Hypertable::ThriftGen::ScanSpec.new
778
+ cell_count = 0
779
+ Page.with_scanner(scan_spec) do |scanner|
780
+ Page.each_cell_as_arrays(scanner) do |cell|
781
+ cell.is_a?(Array).should be_true
782
+ cell_count += 1
783
+ end
784
+ end
785
+ cell_count.should == 4
786
+ end
787
+
788
+ it "should return a scan spec from find_to_scan_spec" do
789
+ scan_spec = Page.find_to_scan_spec(:all, :limit => 1)
790
+ scan_spec.is_a?(Hypertable::ThriftGen::ScanSpec).should be_true
791
+ scan_spec.row_limit.should == 1
792
+ end
793
+
794
+ it "should yield a scanner to a block from find_with_scanner" do
795
+ cell_count = 0
796
+ Page.find_with_scanner(:all, :limit => 1) do |scanner|
797
+ scanner.is_a?(Fixnum).should be_true
798
+ Page.each_cell_as_arrays(scanner) do |cell|
799
+ cell.is_a?(Array).should be_true
800
+ cell_count += 1
801
+ end
802
+ end
803
+ cell_count.should == 2
804
+ end
805
+
806
+ it "should yield each row when calling find_each_row_as_arrays" do
807
+ cell_count = 0
808
+ row_count = 0
809
+
810
+ Page.find_each_row_as_arrays(:all) do |row|
811
+ row.is_a?(Array).should be_true
812
+ row_count += 1
813
+ cell_count += row.length
814
+ end
815
+
816
+ row_count.should == 2
817
+ cell_count.should == 4
818
+ end
819
+
820
+ it "should convert an array of cells into a hash" do
821
+ Page.find_each_row_as_arrays(:all, :limit => 1) do |row|
822
+ page_hash = Page.convert_cells_to_hashes(row).first
823
+ page_hash.is_a?(Hash).should be_true
824
+ page_hash['ROW'].should == "page_1"
825
+ page_hash['name'].should == "LOLcats and more"
826
+ page_hash['url'].should == "http://www.icanhascheezburger.com"
827
+ end
828
+ end
829
+
830
+ it "should yield each row as a HyperRecord object when calling find_each_row" do
831
+ row_count = 0
832
+
833
+ Page.find_each_row(:all) do |row|
834
+ row.is_a?(Page).should be_true
835
+ row_count += 1
836
+ end
837
+
838
+ row_count.should == 2
839
+ end
840
+
841
+ it "should support native each_row scanner method"
842
+ it "should support native each_row_as_arrays scanner method"
843
+ end
844
+
845
+ describe HyperBase, '.row_key_attributes' do
846
+ it "should assemble a row key in the order that matches row key attributes" do
847
+ Page.class_eval do
848
+ row_key_attributes :regex => /^(\w+)_(\d+)_(\d{4}-\d{2}-\d{2}_\d{2}:\d{2})$/, :attribute_names => [:type_prefix, :identifier, :timestamp]
849
+ end
850
+
851
+ Page.assemble_row_key_from_attributes({
852
+ :type_prefix => 'prefix',
853
+ :identifier => 12,
854
+ :timestamp => '2009-11-05_00:00'
855
+ }).should == 'prefix_12_2009-11-05_00:00'
856
+
857
+ Page.class_eval do
858
+ row_key_attributes :regex => /^(\w+)_(\d{4}-\d{2}-\d{2}_\d{2}:\d{2})_(\d+)$/, :attribute_names => [:type_prefix, :timestamp, :identifier]
859
+ end
860
+
861
+ Page.assemble_row_key_from_attributes({
862
+ :type_prefix => 'prefix',
863
+ :identifier => 12,
864
+ :timestamp => '2009-11-05_00:00'
865
+ }).should == 'prefix_2009-11-05_00:00_12'
866
+ end
867
+
868
+ it "should extract attributes out of the row key" do
869
+ Page.class_eval do
870
+ row_key_attributes :regex => /_(\d{4}-\d{2}-\d{2}_\d{2}:\d{2})$/, :attribute_names => [:timestamp]
871
+ end
872
+
873
+ p = Page.new
874
+ p.ROW = "apikey_1066_2008-12-25_03:00"
875
+ p.timestamp.should == '2008-12-25_03:00'
876
+ end
877
+
878
+ it "should return empty string if regex doesn't match row key" do
879
+ Page.class_eval do
880
+ row_key_attributes :regex => /will_not_match/, :attribute_names => [:foo]
881
+ end
882
+
883
+ p = Page.new
884
+ p.ROW = "row key"
885
+ p.foo.should == ''
886
+ end
887
+
888
+ it "should allow multiple attributes to be extracted from row key" do
889
+ Page.class_eval do
890
+ row_key_attributes :regex => /^sponsorship_([a-z0-9]+)_(\d{4}-\d{2}-\d{2}_\d{2}:\d{2})_(\d+)$/, :attribute_names => [:sponsorship_id, :timestamp, :partner_id]
891
+ end
892
+
893
+ p = Page.new
894
+ p.ROW = "sponsorship_61066_2009-04-12_07:00_166"
895
+ p.sponsorship_id.should == '61066'
896
+ p.timestamp.should == '2009-04-12_07:00'
897
+ p.partner_id.should == '166'
898
+ end
899
+
900
+ it "should return empty string on partial match" do
901
+ Page.class_eval do
902
+ row_key_attributes :regex => /^sponsorship_([a-z0-9]+)_(\d{4}-\d{2}-\d{2}_\d{2}:\d{2})_?(\d+)?$/, :attribute_names => [:sponsorship_id, :timestamp, :partner_id]
903
+ end
904
+
905
+ p = Page.new
906
+ p.ROW = "sponsorship_61066_2009-04-12_07:00"
907
+ p.sponsorship_id.should == '61066'
908
+ p.timestamp.should == '2009-04-12_07:00'
909
+ p.partner_id.should == ''
910
+ end
911
+
912
+ it "should return empty string on partial match in middle" do
913
+ Page.class_eval do
914
+ row_key_attributes :regex => /^sponsorship_([a-z0-9]+)_?(\d{4}-\d{2}-\d{2}_\d{2}:\d{2})?_(\d+)$/, :attribute_names => [:sponsorship_id, :timestamp, :partner_id]
915
+ end
916
+
917
+ p = Page.new
918
+ p.ROW = "sponsorship_61066_166"
919
+ p.sponsorship_id.should == '61066'
920
+ p.timestamp.should == ''
921
+ p.partner_id.should == '166'
922
+ end
923
+
924
+ it "should return empty string on nil ROW key" do
925
+ Page.class_eval do
926
+ row_key_attributes :regex => /will_not_match/, :attribute_names => [:foo]
927
+ end
928
+
929
+ p = Page.new
930
+ p.ROW.should be_nil
931
+ p.foo.should == ''
932
+ end
933
+
934
+ it "should return correct value even if the ROW key is changed" do
935
+ Page.class_eval do
936
+ row_key_attributes :regex => /_(\d{4}-\d{2}-\d{2}_\d{2}:\d{2})$/, :attribute_names => [:timestamp]
937
+ end
938
+
939
+ p = Page.new
940
+ p.ROW = "apikey_1066_2008-12-25_03:00"
941
+ p.timestamp.should == '2008-12-25_03:00'
942
+ p.ROW = "apikey_1066_2008-12-25_12:00"
943
+ p.timestamp.should == '2008-12-25_12:00'
944
+ end
945
+ end
946
+ end
947
+ end
948
+