rose 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,650 @@
1
+ require 'spec_helper'
2
+
3
+ class RoseyObject < Struct.new(:petals, :thorns)
4
+ end
5
+
6
+ class Flower < Struct.new(:id, :type, :color, :age)
7
+ end
8
+
9
+ describe Rose, "Object adapter" do
10
+ before do
11
+ Rose.make(:with_direct_attrs) {
12
+ rows do
13
+ column :petals
14
+ column :thorns
15
+ end
16
+ }
17
+
18
+ Rose.make(:with_named_direct_attrs) {
19
+ rows do
20
+ column(:petals => "Petals")
21
+ column(:thorns => "Thorns")
22
+ end
23
+ }
24
+
25
+ Rose.make(:with_named_indirect_attrs) {
26
+ rows do
27
+ column("Petals") { |item| item.petals - 1 }
28
+ column("Thorns", &:thorns)
29
+ end
30
+ }
31
+
32
+ Rose.make(:with_enforced_class, :class => RoseyObject) {
33
+ rows do
34
+ column :petals
35
+ column :thorns
36
+ end
37
+ }
38
+
39
+ Rose.make(:with_summary, :class => Flower) {
40
+ rows do
41
+ column("Type", &:type)
42
+ column(:color => "Color")
43
+ end
44
+ summary("Type") do
45
+ column("Color") { |colors| colors.join(", ") }
46
+ end
47
+ }
48
+
49
+ Rose.make(:with_blank_summary, :class => Flower) {
50
+ rows do
51
+ column("Type", &:type)
52
+ column(:color => "Color")
53
+ end
54
+ summary("Type") do
55
+ end
56
+ }
57
+
58
+ Rose.make(:with_additional_summary, :class => Flower) {
59
+ rows do
60
+ column("Type", &:type)
61
+ column(:color => "Color")
62
+ end
63
+ summary("Type") do
64
+ column("Color") { |colors| colors.uniq.join(", ") }
65
+ column("Count") { |colors| colors.size }
66
+ column("Count 2") { |colors| colors.size }
67
+ end
68
+ }
69
+
70
+ @value_block = value_block = lambda { |rows| rows.map(&:Age).map(&:to_i).inject(0) { |sum,x| sum+x } }
71
+
72
+ Rose.make(:with_pivot, :class => Flower) {
73
+ rows do
74
+ column(:type => "Type")
75
+ column(:color => "Color")
76
+ column(:age => "Age")
77
+ end
78
+ pivot("Color", "Type", &value_block)
79
+ }
80
+
81
+ Rose.make(:with_sort_by_color, :class => Flower) {
82
+ rows do
83
+ column("Type", &:type)
84
+ column(:color => "Color")
85
+ end
86
+ sort("Color")
87
+ }
88
+
89
+ Rose.make(:with_sort_by_color_descending, :class => Flower) {
90
+ rows do
91
+ column("Type", &:type)
92
+ column(:color => "Color")
93
+ end
94
+ sort("Color", :descending)
95
+ }
96
+
97
+ Rose.make(:with_sort_by_age, :class => Flower) {
98
+ rows do
99
+ column(:type => "Type")
100
+ column(:color => "Color")
101
+ column(:age => "Age")
102
+ end
103
+ sort("Age")
104
+ }
105
+
106
+ Rose.make(:with_sort_by_age_descending, :class => Flower) {
107
+ rows do
108
+ column(:type => "Type")
109
+ column(:color => "Color")
110
+ column(:age => "Age")
111
+ end
112
+ sort("Age", :descending)
113
+ }
114
+
115
+ @negative_sort_block = negative_sort_block = lambda { |v| v.to_i * -1 }
116
+ @positive_sort_block = positive_sort_block = lambda { |v| v.to_i }
117
+
118
+ Rose.make(:with_sort_by_block_negative, :class => Flower) {
119
+ rows do
120
+ column(:type => "Type")
121
+ column(:age => "Age")
122
+ end
123
+ sort("Age", :descending, &negative_sort_block)
124
+ }
125
+
126
+ Rose.make(:with_sort_by_block_positive, :class => Flower) {
127
+ rows do
128
+ column(:type => "Type")
129
+ column(:age => "Age")
130
+ end
131
+ sort("Age", :descending, &positive_sort_block)
132
+ }
133
+
134
+ Rose.make(:with_ordered_execution_asc, :class => Flower) {
135
+ rows do
136
+ column("Type", &:type)
137
+ column(:color => "Color")
138
+ end
139
+ sort("Color")
140
+ summary("Type") do
141
+ column("Color") { |colors| colors.join(", ") }
142
+ end
143
+ }
144
+
145
+ Rose.make(:with_ordered_execution_desc, :class => Flower) {
146
+ rows do
147
+ column("Type", &:type)
148
+ column(:color => "Color")
149
+ end
150
+ sort("Color", :descending)
151
+ summary("Type") do
152
+ column("Color") { |colors| colors.join(", ") }
153
+ end
154
+ }
155
+
156
+ @filter_block = filter_block = lambda { |row| row["Color"] != "blue" }
157
+
158
+ Rose.make(:with_filter, :class => Flower) {
159
+ rows do
160
+ column(:type => "Type")
161
+ column(:color => "Color")
162
+ column(:age => "Age")
163
+ end
164
+ filter(&filter_block)
165
+ }
166
+
167
+ @find_block = find_block = lambda { |items, idy|
168
+ items.find { |item| item.id.to_s == idy }
169
+ }
170
+ @update_block = update_block = lambda { |item, updates| item.color = updates["Color"] }
171
+
172
+ Rose.make(:with_find_and_update) do
173
+ rows do
174
+ identity(:id => "ID")
175
+ column(:type => "Type")
176
+ column(:color => "Color")
177
+ column(:age => "Age")
178
+ end
179
+ roots do
180
+ find(&find_block)
181
+ update(&update_block)
182
+ end
183
+ end
184
+
185
+ Rose.make(:with_update) do
186
+ rows do
187
+ identity(:id => "ID")
188
+ column(:type => "Type")
189
+ column(:color => "Color")
190
+ column(:age => "Age")
191
+ end
192
+ roots do
193
+ update(&update_block)
194
+ end
195
+ end
196
+
197
+ @preview_update_block = preview_update_block = lambda { |item, updates| item.preview(true); item.color = updates["Color"] }
198
+
199
+ Rose.make(:with_preview_update) do
200
+ rows do
201
+ identity(:id => "ID")
202
+ column(:type => "Type")
203
+ column(:color => "Color")
204
+ column(:age => "Age")
205
+ end
206
+ roots do
207
+ preview_update(&preview_update_block)
208
+ update { raise Exception, "you shouldn't be calling me" }
209
+ end
210
+ end
211
+
212
+ @created = created = []
213
+ @create_block = create_block = lambda { |idy, updates| created << [idy, updates] }
214
+
215
+ Rose.make(:with_create) do
216
+ rows do
217
+ identity(:id => "ID")
218
+ column(:type => "Type")
219
+ column(:color => "Color")
220
+ column(:age => "Age")
221
+ end
222
+ roots do
223
+ create(&create_block)
224
+ update {}
225
+ end
226
+ end
227
+
228
+ @preview_created = preview_created = []
229
+ @preview_create_block = preview_create_block = lambda { |idy, updates| preview_created << [idy, updates] }
230
+
231
+ Rose.make(:with_preview_create) do
232
+ rows do
233
+ identity(:id => "ID")
234
+ column(:type => "Type")
235
+ column(:color => "Color")
236
+ column(:age => "Age")
237
+ end
238
+ roots do
239
+ preview_create(&preview_create_block)
240
+ create { raise "Not me!"}
241
+ update {}
242
+ end
243
+ end
244
+ end
245
+
246
+ after do
247
+ Rose.seedlings.clear
248
+ end
249
+
250
+ context "make report" do
251
+ it "should support bloom and photosynthesize" do
252
+ Rose(:with_direct_attrs).tap do |rose|
253
+ rose.should be_kind_of(Rose::Shell)
254
+ rose.should respond_to(:bloom)
255
+ rose.should respond_to(:photosynthesize)
256
+ end
257
+ end
258
+
259
+ it "should support direct attributes" do
260
+ Rose(:with_direct_attrs).tap do |report|
261
+ report.row.attributes.map(&:column_name).should == [:petals, :thorns]
262
+ report.row.attributes.map(&:method_name).should == [:petals, :thorns]
263
+ end
264
+ end
265
+
266
+ it "should support direct attributes with column name" do
267
+ Rose(:with_named_direct_attrs).tap do |report|
268
+ report.row.attributes.map(&:column_name).should == ["Petals", "Thorns"]
269
+ report.row.attributes.map(&:method_name).should == [:petals, :thorns]
270
+ end
271
+ end
272
+
273
+ it "should support indirect (dynamic) attributes with column name" do
274
+ Rose(:with_named_indirect_attrs).tap do |report|
275
+ report.row.attributes.map(&:column_name).should == ["Petals", "Thorns"]
276
+ report.row.attributes.map(&:method_name).should == ["Petals", "Thorns"]
277
+ end
278
+ end
279
+
280
+ it "should support :class option" do
281
+ Rose(:with_enforced_class).tap do |report|
282
+ report.options[:class].should == RoseyObject
283
+ end
284
+ end
285
+
286
+ it "should support sort by block" do
287
+ Rose(:with_sort_by_block_negative).tap do |report|
288
+ report.options[:sort].column_name.should == "Age"
289
+ report.options[:sort].order.should == :descending
290
+ report.options[:sort].sort_block.should == @negative_sort_block
291
+ end
292
+ end
293
+
294
+ it "should support summary" do
295
+ Rose(:with_summary).tap do |report|
296
+ report.options[:summary].attributes.map(&:column_name).should == ["Color"]
297
+ report.options[:summary].attributes.map(&:method_name).should == ["Color"]
298
+ end
299
+ end
300
+
301
+ it "should support pivoting" do
302
+ Rose(:with_pivot).tap do |report|
303
+ report.options[:pivot].group_column.should == "Color"
304
+ report.options[:pivot].pivot_column.should == "Type"
305
+ report.options[:pivot].value_block.should == @value_block
306
+ end
307
+ end
308
+
309
+ it "should support filtering" do
310
+ Rose(:with_filter).tap do |report|
311
+ report.options[:filter].value_block.should == @filter_block
312
+ end
313
+ end
314
+
315
+ it "should support identity" do
316
+ Rose(:with_update).tap do |report|
317
+ report.row.identity_attribute.column_name.should == "ID"
318
+ end
319
+ end
320
+
321
+ it "should support find and update" do
322
+ Rose(:with_find_and_update).tap do |report|
323
+ report.root.finder.should == @find_block
324
+ report.root.updater.should == @update_block
325
+ end
326
+ end
327
+
328
+ it "should support preview" do
329
+ Rose(:with_preview_update).tap do |report|
330
+ report.root.update_previewer.should == @preview_update_block
331
+ end
332
+ end
333
+
334
+ it "should support create" do
335
+ Rose(:with_create).tap do |report|
336
+ report.root.creator.should == @create_block
337
+ end
338
+ end
339
+
340
+ it "should support preview create" do
341
+ Rose(:with_preview_create).tap do |report|
342
+ report.root.create_previewer.should == @preview_create_block
343
+ end
344
+ end
345
+ end
346
+
347
+ context "run report" do
348
+ before do
349
+ @arr = [RoseyObject.new(30, 10)]
350
+ @flowers = [
351
+ Flower.new(0, :roses, :red, 1),
352
+ Flower.new(1, :violets, :blue, 2),
353
+ Flower.new(2, :roses, :red, 3)
354
+ ]
355
+ end
356
+
357
+ it "should run with direct attributes" do
358
+ Rose(:with_direct_attrs).bloom(@arr).should match_table <<-eo_table.gsub(%r{^ }, '')
359
+ +-----------------+
360
+ | petals | thorns |
361
+ +-----------------+
362
+ | 30 | 10 |
363
+ +-----------------+
364
+ eo_table
365
+ end
366
+
367
+ it "should run with direct attributes with column name" do
368
+ Rose(:with_named_direct_attrs).bloom(@arr).should match_table <<-eo_table.gsub(%r{^ }, '')
369
+ +-----------------+
370
+ | Petals | Thorns |
371
+ +-----------------+
372
+ | 30 | 10 |
373
+ +-----------------+
374
+ eo_table
375
+ end
376
+
377
+ it "should run with indirect (dynamic) attributes with column name" do
378
+ Rose(:with_named_indirect_attrs).bloom(@arr).should match_table <<-eo_table.gsub(%r{^ }, '')
379
+ +-----------------+
380
+ | Petals | Thorns |
381
+ +-----------------+
382
+ | 29 | 10 |
383
+ +-----------------+
384
+ eo_table
385
+ end
386
+
387
+ it "should not run with wrong class" do
388
+ lambda {
389
+ Rose(:with_enforced_class).bloom([mock('Something else')])
390
+ }.should raise_error(TypeError, "Expected RoseyObject, got Mocha::Mock")
391
+ end
392
+
393
+ it "should sort by color (ascending)" do
394
+ Rose(:with_sort_by_color).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
395
+ +-----------------+
396
+ | Type | Color |
397
+ +-----------------+
398
+ | violets | blue |
399
+ | roses | red |
400
+ | roses | red |
401
+ +-----------------+
402
+ eo_table
403
+ end
404
+
405
+ it "should sort by color (descending)" do
406
+ Rose(:with_sort_by_color_descending).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
407
+ +-----------------+
408
+ | Type | Color |
409
+ +-----------------+
410
+ | roses | red |
411
+ | roses | red |
412
+ | violets | blue |
413
+ +-----------------+
414
+ eo_table
415
+ end
416
+
417
+ it "should sort by age (ascending)" do
418
+ Rose(:with_sort_by_age).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
419
+ +-----------------------+
420
+ | Type | Color | Age |
421
+ +-----------------------+
422
+ | roses | red | 1 |
423
+ | violets | blue | 2 |
424
+ | roses | red | 3 |
425
+ +-----------------------+
426
+ eo_table
427
+ end
428
+
429
+ it "should sort by age (descending)" do
430
+ Rose(:with_sort_by_age_descending).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
431
+ +-----------------------+
432
+ | Type | Color | Age |
433
+ +-----------------------+
434
+ | roses | red | 3 |
435
+ | violets | blue | 2 |
436
+ | roses | red | 1 |
437
+ +-----------------------+
438
+ eo_table
439
+ end
440
+
441
+ it "should sort by block (-)" do
442
+ Rose(:with_sort_by_block_negative).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
443
+ +---------------+
444
+ | Type | Age |
445
+ +---------------+
446
+ | roses | 1 |
447
+ | violets | 2 |
448
+ | roses | 3 |
449
+ +---------------+
450
+ eo_table
451
+ end
452
+
453
+ it "should sort by block (+)" do
454
+ Rose(:with_sort_by_block_positive).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
455
+ +---------------+
456
+ | Type | Age |
457
+ +---------------+
458
+ | roses | 3 |
459
+ | violets | 2 |
460
+ | roses | 1 |
461
+ +---------------+
462
+ eo_table
463
+ end
464
+
465
+ it "should summarize columns" do
466
+ Rose(:with_summary).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
467
+ +--------------------+
468
+ | Type | Color |
469
+ +--------------------+
470
+ | roses | red, red |
471
+ | violets | blue |
472
+ +--------------------+
473
+ eo_table
474
+ end
475
+
476
+ it "should summarize columns omitting columns" do
477
+ Rose(:with_blank_summary).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
478
+ +---------+
479
+ | Type |
480
+ +---------+
481
+ | roses |
482
+ | violets |
483
+ +---------+
484
+ eo_table
485
+ end
486
+
487
+ it "should summarize columns adding additional columns" do
488
+ Rose(:with_additional_summary).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
489
+ +-----------------------------------+
490
+ | Type | Color | Count | Count 2 |
491
+ +-----------------------------------+
492
+ | roses | red | 2 | 2 |
493
+ | violets | blue | 1 | 1 |
494
+ +-----------------------------------+
495
+ eo_table
496
+ end
497
+
498
+ it "should pivot table" do
499
+ Rose(:with_pivot).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
500
+ +-------------------------+
501
+ | Color | roses | violets |
502
+ +-------------------------+
503
+ | red | 4 | 0 |
504
+ | blue | 0 | 2 |
505
+ +-------------------------+
506
+ eo_table
507
+ end
508
+
509
+ it "should run in order" do
510
+ @flowers << Flower.new(3, :roses, :maroon, 3)
511
+ Rose(:with_ordered_execution_asc).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
512
+ +----------------------------+
513
+ | Type | Color |
514
+ +----------------------------+
515
+ | violets | blue |
516
+ | roses | maroon, red, red |
517
+ +----------------------------+
518
+ eo_table
519
+
520
+ Rose(:with_ordered_execution_desc).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
521
+ +----------------------------+
522
+ | Type | Color |
523
+ +----------------------------+
524
+ | roses | red, red, maroon |
525
+ | violets | blue |
526
+ +----------------------------+
527
+ eo_table
528
+ end
529
+
530
+ it "should filter rows" do
531
+ Rose(:with_filter).bloom(@flowers).should match_table <<-eo_table.gsub(%r{^ }, '')
532
+ +---------------------+
533
+ | Type | Color | Age |
534
+ +---------------------+
535
+ | roses | red | 1 |
536
+ | roses | red | 3 |
537
+ +---------------------+
538
+ eo_table
539
+ end
540
+
541
+ it "should find and update" do
542
+ Rose(:with_find_and_update).photosynthesize(@flowers, {
543
+ :with => {
544
+ "0" => { "Color" => "blue" }
545
+ }
546
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
547
+ +----------------------------+
548
+ | ID | Type | Color | Age |
549
+ +----------------------------+
550
+ | 0 | roses | blue | 1 |
551
+ | 1 | violets | blue | 2 |
552
+ | 2 | roses | red | 3 |
553
+ +----------------------------+
554
+ eo_table
555
+ end
556
+
557
+ it "should update" do
558
+ Rose(:with_update).photosynthesize(@flowers, {
559
+ :with => {
560
+ "0" => { "Color" => "blue" }
561
+ }
562
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
563
+ +----------------------------+
564
+ | ID | Type | Color | Age |
565
+ +----------------------------+
566
+ | 0 | roses | blue | 1 |
567
+ | 1 | violets | blue | 2 |
568
+ | 2 | roses | red | 3 |
569
+ +----------------------------+
570
+ eo_table
571
+ end
572
+
573
+ it "should update from CSV" do
574
+ Rose(:with_update).photosynthesize(@flowers, {
575
+ :with => "spec/examples/update_flowers.csv"
576
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
577
+ +----------------------------+
578
+ | ID | Type | Color | Age |
579
+ +----------------------------+
580
+ | 0 | roses | blue | 1 |
581
+ | 1 | violets | red | 2 |
582
+ | 2 | roses | green | 3 |
583
+ +----------------------------+
584
+ eo_table
585
+ end
586
+
587
+ it "should preview" do
588
+ @flowers.each do |flower|
589
+ flower.expects(:preview).with(true)
590
+ end
591
+
592
+ Rose(:with_preview_update).photosynthesize(@flowers, {
593
+ :with => {
594
+ "0" => { "Color" => "blue" },
595
+ "1" => { "Color" => "blue" },
596
+ "2" => { "Color" => "blue" }
597
+ },
598
+ :preview => true
599
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
600
+ +----------------------------+
601
+ | ID | Type | Color | Age |
602
+ +----------------------------+
603
+ | 0 | roses | blue | 1 |
604
+ | 1 | violets | blue | 2 |
605
+ | 2 | roses | blue | 3 |
606
+ +----------------------------+
607
+ eo_table
608
+ end
609
+
610
+ it "should preview from CSV" do
611
+ @flowers.each do |flower|
612
+ flower.expects(:preview).with(true)
613
+ end
614
+
615
+ Rose(:with_preview_update).photosynthesize(@flowers, {
616
+ :with => "spec/examples/update_flowers.csv",
617
+ :preview => true
618
+ }).should match_table <<-eo_table.gsub(%r{^ }, '')
619
+ +----------------------------+
620
+ | ID | Type | Color | Age |
621
+ +----------------------------+
622
+ | 0 | roses | blue | 1 |
623
+ | 1 | violets | red | 2 |
624
+ | 2 | roses | green | 3 |
625
+ +----------------------------+
626
+ eo_table
627
+ end
628
+
629
+ it "should create" do
630
+ Rose(:with_create).photosynthesize(@flowers, {
631
+ :with => {
632
+ "9" => { "Color" => "blue" }
633
+ }
634
+ })
635
+
636
+ @created.should == [["9", { "Color" => "blue" }]]
637
+ end
638
+
639
+ it "should preview create" do
640
+ Rose(:with_preview_create).photosynthesize(@flowers, {
641
+ :with => {
642
+ "9" => { "Color" => "blue" }
643
+ },
644
+ :preview => true
645
+ })
646
+
647
+ @preview_created.should == [["9", { "Color" => "blue" }]]
648
+ end
649
+ end
650
+ end