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.
@@ -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