hyper_record 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+