kbaum-munger 0.1.4
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.
- data/.gitignore +5 -0
- data/README +90 -0
- data/Rakefile +21 -0
- data/VERSION +1 -0
- data/examples/column_add.rb +30 -0
- data/examples/development.log +2 -0
- data/examples/example_helper.rb +23 -0
- data/examples/sinatra_app.rb +100 -0
- data/examples/test.html +0 -0
- data/lib/munger.rb +16 -0
- data/lib/munger/data.rb +232 -0
- data/lib/munger/item.rb +50 -0
- data/lib/munger/render.rb +22 -0
- data/lib/munger/render/csv.rb +31 -0
- data/lib/munger/render/html.rb +89 -0
- data/lib/munger/render/sortable_html.rb +133 -0
- data/lib/munger/render/text.rb +54 -0
- data/lib/munger/report.rb +349 -0
- data/spec/munger/data/new_spec.rb +18 -0
- data/spec/munger/data_spec.rb +140 -0
- data/spec/munger/item_spec.rb +37 -0
- data/spec/munger/render/csv_spec.rb +21 -0
- data/spec/munger/render/html_spec.rb +75 -0
- data/spec/munger/render/text_spec.rb +22 -0
- data/spec/munger/render_spec.rb +28 -0
- data/spec/munger/report_spec.rb +148 -0
- data/spec/spec_helper.rb +76 -0
- metadata +92 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe Munger::Data do
|
4
|
+
|
5
|
+
describe '.new' do
|
6
|
+
|
7
|
+
it 'initializes the data attribute to the :data value' do
|
8
|
+
data = [{:foo => '1'}, {:foo => 2}]
|
9
|
+
Munger::Data.new(:data => data).data.should == data
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'yields itself to the given block' do
|
13
|
+
Munger::Data.new { |data| data.should be_kind_of(Munger::Data) }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Munger::Data do
|
4
|
+
include MungerSpecHelper
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@data = Munger::Data.new(:data => test_data)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should accept an array of hashes" do
|
11
|
+
Munger::Data.new(:data => test_data).should be_valid
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be able to set data after init" do
|
15
|
+
m = Munger::Data.new
|
16
|
+
m.data = test_data
|
17
|
+
m.should be_valid
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be able to set data in init block" do
|
21
|
+
m = Munger::Data.new do |d|
|
22
|
+
d.data = test_data
|
23
|
+
end
|
24
|
+
m.should be_valid
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be able to extract columns from data" do
|
28
|
+
@titles = @data.columns
|
29
|
+
@titles.should have(4).items
|
30
|
+
@titles.should include(:name, :score, :age)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should be able to add a new column with a default value" do
|
34
|
+
@data.add_column('new_column', :default => 1)
|
35
|
+
@data.data.first['new_column'].should eql(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be able to add a new column with a block" do
|
39
|
+
@data.add_column('new_column') { |c| c.age + 1 }
|
40
|
+
@data.data.first['new_column'].should eql(24)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be able to add multiple new columns with defaults" do
|
44
|
+
@data.add_column(['col1', 'col2'], :default => [1, 2])
|
45
|
+
@data.data.first['col1'].should eql(1)
|
46
|
+
@data.data.first['col2'].should eql(2)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be able to add multiple new columns with a block" do
|
50
|
+
@data.add_column(['col1', 'col2']) { |c| [c.age * 2, c.score * 3]}
|
51
|
+
@data.data.first['col1'].should eql(46)
|
52
|
+
@data.data.first['col2'].should eql(36)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should work with add_columns, too" do
|
56
|
+
@data.add_columns(['col1', 'col2'], :default => [1, 2])
|
57
|
+
@data.data.first['col1'].should eql(1)
|
58
|
+
@data.data.first['col2'].should eql(2)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be able to transform a column" do
|
62
|
+
@data.data.first[:age].should eql(23)
|
63
|
+
@data.transform_column(:age) { |c| c.age * 2 }
|
64
|
+
@data.data.first[:age].should eql(46)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should be able to transform multiple rows" do
|
68
|
+
@data.data.first[:age].should eql(23)
|
69
|
+
@data.data.first[:score].should eql(12)
|
70
|
+
@data.transform_columns([:age, :score]) { |c| [c.age * 2, c.score * 3] }
|
71
|
+
@data.data.first[:age].should eql(46)
|
72
|
+
@data.data.first[:score].should eql(36)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should be able to filter the data down" do
|
76
|
+
orig_size = @data.size
|
77
|
+
@data.filter_rows { |r| r.age < 30 }
|
78
|
+
@data.size.should < orig_size
|
79
|
+
@data.size.should eql(4)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should be able to pivot the data (1 column)" do
|
83
|
+
orig_size = @data.size
|
84
|
+
new_keys = @data.pivot(:day, :name, :score)
|
85
|
+
@data.size.should < orig_size
|
86
|
+
new_keys.should include(1, 2)
|
87
|
+
scott = @data.data.select { |r| r.name == 'Scott' }.first
|
88
|
+
scott[1].should eql(43)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should be able to pivot the data with average aggregation" do
|
92
|
+
new_keys = @data.pivot(:day, :name, :score, :average)
|
93
|
+
new_keys.should include(1, 2)
|
94
|
+
scott = @data.data.select { |r| r.name == 'Scott' }.first
|
95
|
+
scott[1].should eql(21)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should be able to pivot the data with count aggregation" do
|
99
|
+
new_keys = @data.pivot(:day, :name, :score, :count)
|
100
|
+
scott = @data.data.select { |r| r.name == 'Scott' }.first
|
101
|
+
scott[1].should eql(2)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should be able to pivot the data in three dimensions (1 col, 2 row)" do
|
105
|
+
new_keys = @data.pivot(:name, [:score, :age], :score, :count)
|
106
|
+
alice = @data.data.select { |r| r.name == 'Alice' }.first
|
107
|
+
alice.Alice.should eql(2)
|
108
|
+
end
|
109
|
+
|
110
|
+
# like sql group command, give aggregation block
|
111
|
+
it "should be able to group the data like sql" do
|
112
|
+
@data.group(:name)
|
113
|
+
@data.size.should eql(6)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should be able to group on multiple columns" do
|
117
|
+
@data.group([:age, :score], :count => :day, :sum => :day, :average => :score)
|
118
|
+
alice = @data.data.select { |r| (r.score == 12) && (r.age == 33)}.first
|
119
|
+
alice.count_day.should eql(2)
|
120
|
+
alice.sum_day.should eql(3)
|
121
|
+
alice.average_day.should eql(nil)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should be able to group with a proc aggregation" do
|
125
|
+
pr = Proc.new {|arr| arr.inject(0) { |a,b| a + (b*2) }}
|
126
|
+
@data.group([:age, :score], :sum => :day, ['test', pr] => :age)
|
127
|
+
alice = @data.data.select { |r| (r.score == 12) && (r.age == 33)}.first
|
128
|
+
alice.test_age.should eql(132)
|
129
|
+
alice.sum_day.should eql(3)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should be able to pivot the data in three dimensions (2 col, 1 row)"
|
133
|
+
|
134
|
+
it "should be able to pivot the data in four dimensions (2 col, 2 row)"
|
135
|
+
|
136
|
+
it "should be able to add two Munger::Datas together if they have the same columns"
|
137
|
+
|
138
|
+
it "(maybe) should be able to zip two Munger::Datas together given a unique key column in each"
|
139
|
+
|
140
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Munger::Item do
|
4
|
+
include MungerSpecHelper
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
@data = Munger::Data.new(:data => test_data)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should accept a hash with symbols" do
|
11
|
+
hash = {'key1' => 'value1', 'key2' => 'value2'}
|
12
|
+
item = Munger::Item.ensure(hash)
|
13
|
+
item.key1.should eql('value1')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should accept a hash with strings" do
|
17
|
+
hash = {:key1 => 'value1', :key2 => 'value2'}
|
18
|
+
item = Munger::Item.ensure(hash)
|
19
|
+
item.key1.should eql('value1')
|
20
|
+
item.key2.should_not eql('value1')
|
21
|
+
item.key3.should be(nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should accept mixed types" do
|
25
|
+
hash = {:key1 => 'value1', 'key2' => 'value2'}
|
26
|
+
item = Munger::Item.ensure(hash)
|
27
|
+
item.key1.should eql('value1')
|
28
|
+
item.key2.should eql('value2')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should be able to access hash values indifferently" do
|
32
|
+
hash = {:key1 => 'value1', 'key2' => 'value2'}
|
33
|
+
item = Munger::Item.ensure(hash)
|
34
|
+
item['key1'].should eql('value1')
|
35
|
+
item[:key2].should eql('value2')
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper"
|
2
|
+
|
3
|
+
describe Munger::Render::CSV do
|
4
|
+
include MungerSpecHelper
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@data = Munger::Data.new(:data => test_data)
|
8
|
+
@report = Munger::Report.new(:data => @data)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept a Munger::Report object" do
|
12
|
+
Munger::Render::Text.new(@report.process).should be_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should render a basic text table" do
|
16
|
+
@render = Munger::Render::CSV.new(@report.process)
|
17
|
+
count = @report.rows
|
18
|
+
text = @render.render
|
19
|
+
text.split("\n").should have_at_least(count).items
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper"
|
2
|
+
|
3
|
+
describe Munger::Render::Html do
|
4
|
+
include MungerSpecHelper
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@data = Munger::Data.new(:data => test_data)
|
8
|
+
@report = Munger::Report.new(:data => @data)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept a Munger::Report object" do
|
12
|
+
Munger::Render::Html.new(@report.process).should be_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should render a basic html table" do
|
16
|
+
@render = Munger::Render::Html.new(@report.process)
|
17
|
+
count = @report.rows
|
18
|
+
|
19
|
+
html = @render.render
|
20
|
+
html.should have_tag('table')
|
21
|
+
html.should have_tag('tr', :count => count + 1) # rows plus header
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should accept a custom table class" do
|
25
|
+
rep = Munger::Render::Html.new(@report.process, :classes => {:table => 'helloClass'})
|
26
|
+
html = rep.render
|
27
|
+
html.should have_tag('table.helloClass')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should use aliased column titles" do
|
31
|
+
@report = @report.columns([:age, :name, :score])
|
32
|
+
@report.column_titles = {:age => "The Age", :name => "The Name"}
|
33
|
+
html = Munger::Render::Html.new(@report.process).render
|
34
|
+
html.should match(/The Age/)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should render columns in the right order" do
|
38
|
+
@report = @report.columns([:age, :name]).process
|
39
|
+
html = Munger::Render::Html.new(@report).render
|
40
|
+
html.should have_tag('th', :count => 2) # rows plus header
|
41
|
+
html.should match(/age(.*?)name/)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should render groups" do
|
45
|
+
@report = @report.subgroup(:age).aggregate(:sum => :score).process
|
46
|
+
html = Munger::Render::Html.new(@report).render
|
47
|
+
html.should match(/151/) # only in the aggregate group
|
48
|
+
html.should have_tag('tr.group0', :count => 1)
|
49
|
+
html.should have_tag('tr.group1', :count => 9)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should render group headers" do
|
53
|
+
@report = @report.subgroup(:age, :with_headers => true).process
|
54
|
+
html = Munger::Render::Html.new(@report).render
|
55
|
+
html.should have_tag('tr.groupHeader1', :count => 9)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should render cell styles" do
|
59
|
+
@report.process.style_rows('over_thirty') { |row| row.age > 29 }
|
60
|
+
|
61
|
+
html = Munger::Render::Html.new(@report).render
|
62
|
+
html.should have_tag('tr.over_thirty', :count => 6)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should render row styles" do
|
66
|
+
@report.process.style_cells('highlight', :only => :age) { |c, r| c == 32 }
|
67
|
+
html = Munger::Render::Html.new(@report).render
|
68
|
+
html.should have_tag('td.highlight')
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should render column styles"
|
72
|
+
|
73
|
+
it "should render default css if you ask"
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../../spec_helper"
|
2
|
+
|
3
|
+
describe Munger::Render::Text do
|
4
|
+
include MungerSpecHelper
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@data = Munger::Data.new(:data => test_data)
|
8
|
+
@report = Munger::Report.new(:data => @data)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept a Munger::Report object" do
|
12
|
+
Munger::Render::Text.new(@report.process).should be_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should render a basic text table" do
|
16
|
+
@render = Munger::Render::Text.new(@report.process)
|
17
|
+
count = @report.rows
|
18
|
+
text = @render.render
|
19
|
+
text.split("\n").should have_at_least(count).items
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Munger::Render do
|
4
|
+
include MungerSpecHelper
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@data = Munger::Data.new(:data => test_data)
|
8
|
+
@report = Munger::Report.new(:data => @data).process
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should render html" do
|
12
|
+
html = Munger::Render.to_html(@report)
|
13
|
+
html.should have_tag('table')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should render text" do
|
17
|
+
text = Munger::Render.to_text(@report)
|
18
|
+
text.should_not have_tag('table')
|
19
|
+
text.split("\n").should have_at_least(5).items
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should render xls"
|
23
|
+
|
24
|
+
it "should render csv"
|
25
|
+
|
26
|
+
it "should render pdf"
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Munger::Report do
|
4
|
+
include MungerSpecHelper
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@data = Munger::Data.new(:data => test_data)
|
8
|
+
@report = Munger::Report.new(:data => @data)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should accept a Munger::Data object" do
|
12
|
+
Munger::Report.new(:data => @data).should be_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should accept a array of hashes" do
|
16
|
+
Munger::Report.new(:data => test_data).should be_valid
|
17
|
+
Munger::Report.new(:data => invalid_test_data).should_not be_valid
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be able to sort fields by array" do
|
21
|
+
@report.sort = 'name'
|
22
|
+
data = @report.process.process_data
|
23
|
+
data.map { |a| a[:data].name }[0, 4].join(',').should eql('Alice,Alice,Alice,Chaz')
|
24
|
+
|
25
|
+
@report.sort = ['name', 'age']
|
26
|
+
data = @report.process.process_data
|
27
|
+
data.map { |a| a[:data].age }[0, 4].join(',').should eql('33,33,34,28')
|
28
|
+
|
29
|
+
@report.sort = [['name', :asc], ['age', :desc]]
|
30
|
+
data = @report.process.process_data
|
31
|
+
data.map { |a| a[:data].age }[0, 4].join(',').should eql('34,33,33,28')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be able to custom sort fields" do
|
35
|
+
@report.sort = [['name', Proc.new {|a, b| a[2] <=> b[2]} ]]
|
36
|
+
data = @report.process.process_data
|
37
|
+
data.map { |a| a[:data].name }[0, 4].join(',').should eql('Chaz,Rich,Alice,Alice')
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should be able to order columns" do
|
41
|
+
@report.columns([:name, :age, :score])
|
42
|
+
@report.columns.should eql([:name, :age, :score])
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be able to alias column titles" do
|
46
|
+
titles = {:name => 'My Name', :age => 'The Age', :score => 'Super Score'}
|
47
|
+
@report.column_titles = titles
|
48
|
+
@report.column_titles.should eql(titles)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should default to all columns" do
|
52
|
+
@report.columns.map { |c| c.to_s }.sort.join(',').should eql('age,day,name,score')
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
it "should be able to subgroup data" do
|
57
|
+
@report.sort('name').subgroup('name').process
|
58
|
+
@report.get_subgroup_rows.should have(6).items
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be able to add subgroup headers" do
|
62
|
+
@report.sort('score').subgroup('score', :with_headers => true)
|
63
|
+
@report.aggregate(:sum => :score).process
|
64
|
+
puts Munger::Render.to_text(@report)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should add the grouping name on the group line somewhere"
|
68
|
+
|
69
|
+
it "should be able to subgroup in multiple dimensions"
|
70
|
+
|
71
|
+
it "should be able to aggregate columns into subgroup rows" do
|
72
|
+
@report.sort('name').subgroup('name').aggregate(:sum => :score).process
|
73
|
+
@report.get_subgroup_rows(1).should have(6).items
|
74
|
+
@report.get_subgroup_rows(0).should have(1).items
|
75
|
+
@report.get_subgroup_rows(0).first[:data][:score].should eql(151)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should be able to aggregate multiple columns into subgroup rows" do
|
79
|
+
@report.sort('name').subgroup('name').aggregate(:sum => [:score, :age]).process
|
80
|
+
data = @report.get_subgroup_rows(0).first[:data]
|
81
|
+
data[:score].should eql(151)
|
82
|
+
data[:age].should eql(294)
|
83
|
+
|
84
|
+
@report.sort('name').subgroup('name').aggregate(:sum => :score, :average => :age).process
|
85
|
+
data = @report.get_subgroup_rows(0).first[:data]
|
86
|
+
data[:score].should eql(151)
|
87
|
+
data[:age].should eql(29)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should be able to aggregate with :average" do
|
91
|
+
@report.sort('name').subgroup('name').aggregate(:average => :score).process
|
92
|
+
@report.get_subgroup_rows(0).first[:data][:score].should eql(15)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should be able to aggregate with :custom" do
|
96
|
+
@report.sort('name').subgroup('name')
|
97
|
+
@report.aggregate(Proc.new { |d| d.inject { |t, a| 2 * (t + a) } } => :score).process
|
98
|
+
@report.get_subgroup_rows(0).first[:data][:score].should eql(19508)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should be able to style cells" do
|
102
|
+
@report.process
|
103
|
+
@report.style_cells('highlight') { |c, r| c == 32 }
|
104
|
+
styles = @report.process_data.select { |r| r[:meta][:cell_styles] }
|
105
|
+
styles.should have(2).items
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should be able to style cells in certain columns" do
|
109
|
+
@report.process
|
110
|
+
@report.style_cells('highlight', :only => :age) { |c, r| c == 32 }
|
111
|
+
@report.style_cells('big', :except => [:name, :day]) { |c, r| c.size > 2 }
|
112
|
+
styles = @report.process_data.select { |r| r[:meta][:cell_styles] }
|
113
|
+
styles.should have(10).items
|
114
|
+
|
115
|
+
janet = @report.process_data.select { |r| r[:data].name == 'Janet' }.first
|
116
|
+
jstyles = janet[:meta][:cell_styles]
|
117
|
+
|
118
|
+
jstyles[:age].sort.join(',').should eql('big,highlight')
|
119
|
+
jstyles[:score].should eql(["big"])
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should be able to style rows" do
|
123
|
+
@report.process
|
124
|
+
@report.style_rows('over_thirty') { |row| row.age > 29 }
|
125
|
+
@report.style_cells('highlight', :only => :age) { |c, r| c == 32 }
|
126
|
+
|
127
|
+
janet = @report.process_data.select { |r| r[:data].name == 'Janet' }.first[:meta]
|
128
|
+
janet[:row_styles].should eql(["over_thirty"])
|
129
|
+
janet[:cell_styles].should have(1).item
|
130
|
+
janet[:cell_styles][:age].should eql(["highlight"])
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should know when it is processed" do
|
134
|
+
@report.should_not be_processed
|
135
|
+
@report.process
|
136
|
+
@report.should be_processed
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should be able to style columns"
|
140
|
+
|
141
|
+
it "should be able to attach formatting independent of content"
|
142
|
+
# so can format numbers without hurting ability to aggregate correctly
|
143
|
+
# or add hyperlinks using data from columns not being shown
|
144
|
+
|
145
|
+
it "should be able to aggregate rows into new column"
|
146
|
+
|
147
|
+
|
148
|
+
end
|