goldmine 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,16 +1,10 @@
1
- $:.unshift File.join(File.dirname(__FILE__), "goldmine")
2
- require "cache"
3
- require "rollup_context"
4
- require "hash_rollup"
5
- require "array_miner"
6
- require "hash_miner"
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "goldmine")
2
+ require "version"
3
+ require "miner"
7
4
 
8
5
  module Goldmine
9
- class << self
10
- def miner(object)
11
- return ArrayMiner.new(object) if object.is_a?(Array)
12
- return HashMiner.new(object) if object.is_a?(Hash)
13
- nil
14
- end
15
- end
6
+ end
7
+
8
+ def Goldmine(list)
9
+ Goldmine::Miner.new(list)
16
10
  end
@@ -0,0 +1,20 @@
1
+ require "forwardable"
2
+ require "pivot"
3
+
4
+ module Goldmine
5
+ class Miner
6
+ extend Forwardable
7
+ include Enumerable
8
+ def_delegators :@array, :each, :to_a
9
+ attr_reader :pivots
10
+
11
+ def initialize(array=[])
12
+ @pivots = []
13
+ @array = array.to_a
14
+ end
15
+
16
+ def pivot(name, &block)
17
+ Pivot.new(name, self, block)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ require "pivot_result"
2
+
3
+ module Goldmine
4
+ class Pivot
5
+ attr_reader :name, :proc
6
+
7
+ def initialize(name, array_miner, block)
8
+ @array_miner = array_miner
9
+ @name = name
10
+ @proc = block
11
+ array_miner.pivots << self
12
+ end
13
+
14
+ def pivot(name, &block)
15
+ self.class.new(name, array_miner, block)
16
+ end
17
+
18
+ def result
19
+ PivotResult.new.tap do |pivot_result|
20
+ array_miner.each do |item|
21
+ key_data = array_miner.pivots.each_with_object(key: [], keys: []) do |pivot, memo|
22
+ value = pivot.proc.call(item)
23
+ if value.is_a?(Array)
24
+ if value.empty?
25
+ memo[:key] << key_for(pivot.name, nil)
26
+ else
27
+ value.each { |v| memo[:keys] << key_for(pivot.name, v) }
28
+ end
29
+ else
30
+ memo[:key] << key_for(pivot.name, value)
31
+ end
32
+ end
33
+ (pivot_result[key_data[:key]] ||= []) << item unless key_data[:key].empty?
34
+ key_data[:keys].each do |key|
35
+ (pivot_result[key] ||= []) << item
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :array_miner
44
+
45
+ def key_for(name, value)
46
+ Array.new(2).tap do |key|
47
+ key[0] = name
48
+ key[1] = value
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ require "forwardable"
2
+ require "rollup"
3
+
4
+ module Goldmine
5
+ class PivotResult
6
+ extend Forwardable
7
+ include Enumerable
8
+ def_delegators :@hash, :[], :[]=, :each, :to_h
9
+ attr_reader :rollups, :cache
10
+
11
+ def initialize(hash={})
12
+ @rollups = []
13
+ @hash = hash
14
+ @cache = {}
15
+ end
16
+
17
+ def rollup(name, &block)
18
+ Rollup.new(name, self, block)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require "rollup_clean_room"
2
+ require "rollup_result"
3
+
4
+ module Goldmine
5
+ class Rollup
6
+ attr_reader :name, :proc
7
+
8
+ def initialize(name, pivot_result, block)
9
+ @name = name
10
+ @pivot_result = pivot_result
11
+ @proc = block
12
+ pivot_result.rollups << self
13
+ end
14
+
15
+ def rollup(name, &block)
16
+ self.class.new(name, pivot_result, block)
17
+ end
18
+
19
+ def result(cache: false)
20
+ RollupResult.new.tap do |rollup_result|
21
+ pivot_result.each do |pivot_key, pivoted_list|
22
+ stash = {} if cache
23
+ pivot_result.rollups.each do |rollup|
24
+ Array.new(2).tap do |computed_value|
25
+ key = rollup.name
26
+ value = RollupCleanRoom.new(key, stash).rollup(pivoted_list, &rollup.proc) if cache
27
+ value ||= rollup.proc.call(pivoted_list)
28
+ computed_value[0] = key
29
+ computed_value[1] = value
30
+ (rollup_result[pivot_key] ||= []) << computed_value
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :pivot_result
40
+ end
41
+ end
@@ -0,0 +1,21 @@
1
+ module Goldmine
2
+ class RollupCleanRoom
3
+ attr_reader :name, :cache
4
+
5
+ def initialize(name, cache={})
6
+ @name = name
7
+ @cache = cache
8
+ end
9
+
10
+ def eigen
11
+ class << self
12
+ self
13
+ end
14
+ end
15
+
16
+ def rollup(pivoted_list, &block)
17
+ eigen.instance_eval { define_method(:do_rollup, &block) }
18
+ @cache[name] = do_rollup(pivoted_list)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,45 @@
1
+ require "forwardable"
2
+ require "csv"
3
+
4
+ module Goldmine
5
+ class RollupResult
6
+ extend Forwardable
7
+ include Enumerable
8
+ def_delegators :@pivot_result, :[], :[]=, :each, :to_h
9
+
10
+ def initialize(pivot_result={})
11
+ @pivot_result = pivot_result
12
+ end
13
+
14
+ def to_rows
15
+ pivot_result.each_with_object([]) do |pair, memo|
16
+ memo << pair.first + pair.last
17
+ end
18
+ end
19
+
20
+ def to_hash_rows
21
+ pivot_result.each_with_object([]) do |pair, memo|
22
+ memo << (pair.first + pair.last).to_h
23
+ end
24
+ end
25
+
26
+ def to_tabular
27
+ rows = to_rows
28
+ values = rows.map { |r| r.map(&:last) }
29
+ sample = rows.first || []
30
+ values.unshift sample.map(&:first)
31
+ values
32
+ end
33
+
34
+ def to_csv_table
35
+ rows = to_tabular
36
+ header = rows.shift
37
+ csv_rows = rows.map { |row| CSV::Row.new(header, row) }
38
+ CSV::Table.new csv_rows
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :pivot_result
44
+ end
45
+ end
@@ -1,3 +1,3 @@
1
1
  module Goldmine
2
- VERSION = "2.1.0"
2
+ VERSION = "3.0.0".freeze
3
3
  end
@@ -6,124 +6,58 @@ require File.expand_path("../../lib/goldmine", __FILE__)
6
6
 
7
7
  class TestGoldmine < PryTest::Test
8
8
 
9
- test "miner from array" do
10
- assert Goldmine.miner([]).is_a?(Goldmine::ArrayMiner)
11
- end
12
-
13
- test "miner from hash" do
14
- assert Goldmine.miner({}).is_a?(Goldmine::HashMiner)
15
- end
16
-
17
- test "miner from usupported object" do
18
- assert Goldmine.miner(Object.new).nil?
19
- end
9
+ # pivots .....................................................................
20
10
 
21
11
  test "simple pivot" do
22
12
  list = [1,2,3,4,5,6,7,8,9]
23
- list = Goldmine::ArrayMiner.new(list)
24
- data = list.pivot { |i| i < 5 }
25
-
26
- expected = {
27
- true => [1, 2, 3, 4],
28
- false => [5, 6, 7, 8, 9]
29
- }
30
-
31
- assert data == expected
32
- end
33
-
34
- test "simple pivot rollup" do
35
- list = [1,2,3,4,5,6,7,8,9]
36
- list = Goldmine::ArrayMiner.new(list)
37
- data = list.pivot { |i| i < 5 }
38
- rolled = data.rollup(:count) { |items| items.size }
39
-
40
- expected = {
41
- true => { count: 4 },
42
- false => { count: 5 }
43
- }
44
-
45
- assert rolled == expected
46
- end
47
-
48
- test "pivot with chained rollup" do
49
- list = [1,2,3,4,5,6,7,8,9]
50
- list = Goldmine::ArrayMiner.new(list)
51
- data = list.pivot { |i| i < 5 }
52
- rolled = data
53
- .rollup(:count) { |items| items.size }
54
- .rollup(:div_by_3) { |items| items.keep_if { |i| i % 3 == 0 }.size }
13
+ pivot = Goldmine(list).pivot("< 5") { |i| i < 5 }
55
14
 
56
15
  expected = {
57
- true => {:count=>4, :div_by_3=>1},
58
- false => {:count=>5, :div_by_3=>2}
16
+ [["< 5", true]] => [1, 2, 3, 4],
17
+ [["< 5", false]] => [5, 6, 7, 8, 9]
59
18
  }
60
19
 
61
- assert rolled == expected
20
+ assert pivot.result.to_h == expected
62
21
  end
63
22
 
64
- test "simple pivot rollup to_tabular" do
65
- list = [1,2,3,4,5,6,7,8,9]
66
- list = Goldmine::ArrayMiner.new(list)
67
- rolled = list.pivot { |i| i < 5 }.rollup(:count, &:size)
68
-
69
- expected = [
70
- ["column1", "count"],
71
- [true, 4],
72
- [false, 5]
73
- ]
74
-
75
- assert rolled.to_tabular == expected
76
- end
77
-
78
- test "simple pivot rollup to_csv_table" do
79
- list = [1,2,3,4,5,6,7,8,9]
80
- list = Goldmine::ArrayMiner.new(list)
81
- rolled = list.pivot { |i| i < 5 }.rollup(:count, &:size)
82
- csv = rolled.to_csv_table
83
-
84
- assert csv.headers == ["column1", "count"]
85
- assert csv.to_a == [["column1", "count"], [true, 4], [false, 5]]
86
- end
87
-
88
- test "named pivot" do
23
+ test "chained pivots" do
89
24
  list = [1,2,3,4,5,6,7,8,9]
90
- list = Goldmine::ArrayMiner.new(list)
91
- data = list.pivot("less than 5") { |i| i < 5 }
25
+ pivot = Goldmine(list)
26
+ .pivot("< 5") { |i| i < 5 }
27
+ .pivot("even") { |i| i % 2 == 0 }
92
28
 
93
29
  expected = {
94
- { "less than 5" => true } => [1, 2, 3, 4],
95
- { "less than 5" => false } => [5, 6, 7, 8, 9]
30
+ [["< 5", true], ["even", false]] => [1, 3],
31
+ [["< 5", true], ["even", true]] => [2, 4],
32
+ [["< 5", false], ["even", false]] => [5, 7, 9],
33
+ [["< 5", false], ["even", true]] => [6, 8]
96
34
  }
97
35
 
98
- assert data == expected
36
+ assert pivot.result.to_h == expected
99
37
  end
100
38
 
101
- test "named pivot rollup" do
39
+ test "deep chained pivots" do
102
40
  list = [1,2,3,4,5,6,7,8,9]
103
- list = Goldmine::ArrayMiner.new(list)
104
- data = list.pivot("less than 5") { |i| i < 5 }
105
- rolled = data.rollup(:count) { |row| row.size }
41
+ pivot = Goldmine(list)
42
+ .pivot("< 3") { |i| i < 3 }
43
+ .pivot("< 6") { |i| i < 6 }
44
+ .pivot("< 9") { |i| i < 9 }
45
+ .pivot("even") { |i| i % 2 == 0 }
46
+ .pivot("odd") { |i| i % 3 == 0 || i == 1 }
106
47
 
107
48
  expected = {
108
- { "less than 5" => true } => { count: 4 },
109
- { "less than 5" => false } => { count: 5 }
49
+ [["< 3", true], ["< 6", true], ["< 9", true], ["even", false], ["odd", true]] => [1],
50
+ [["< 3", true], ["< 6", true], ["< 9", true], ["even", true], ["odd", false]] => [2],
51
+ [["< 3", false], ["< 6", true], ["< 9", true], ["even", false], ["odd", true]] => [3],
52
+ [["< 3", false], ["< 6", true], ["< 9", true], ["even", true], ["odd", false]] => [4],
53
+ [["< 3", false], ["< 6", true], ["< 9", true], ["even", false], ["odd", false]] => [5],
54
+ [["< 3", false], ["< 6", false], ["< 9", true], ["even", true], ["odd", true]] => [6],
55
+ [["< 3", false], ["< 6", false], ["< 9", true], ["even", false], ["odd", false]] => [7],
56
+ [["< 3", false], ["< 6", false], ["< 9", true], ["even", true], ["odd", false]] => [8],
57
+ [["< 3", false], ["< 6", false], ["< 9", false], ["even", false], ["odd", true]] => [9]
110
58
  }
111
59
 
112
- assert rolled == expected
113
- end
114
-
115
- test "named pivot rollup to_tabular" do
116
- list = [1,2,3,4,5,6,7,8,9]
117
- list = Goldmine::ArrayMiner.new(list)
118
- rolled = list.pivot("less than 5") { |i| i < 5 }.rollup(:count, &:size)
119
-
120
- expected = [
121
- ["less than 5", "count"],
122
- [true, 4],
123
- [false, 5]
124
- ]
125
-
126
- assert rolled.to_tabular == expected
60
+ assert pivot.result.to_h == expected
127
61
  end
128
62
 
129
63
  test "pivot of list values" do
@@ -133,23 +67,17 @@ class TestGoldmine < PryTest::Test
133
67
  { :name => "three", :list => [1, 2, 3] },
134
68
  { :name => "four", :list => [1, 2, 3, 4] },
135
69
  ]
136
- list = Goldmine::ArrayMiner.new(list)
137
- data = list.pivot { |record| record[:list] }
70
+ pivot = Goldmine(list)
71
+ .pivot("list value") { |record| record[:list] }
138
72
 
139
73
  expected = {
140
- 1 => [ { :name => "one", :list => [1] },
141
- { :name => "two", :list => [1, 2] },
142
- { :name => "three", :list => [1, 2, 3] },
143
- { :name => "four", :list => [1, 2, 3, 4] } ],
144
- 2 => [ { :name => "two", :list => [1, 2] },
145
- { :name => "three", :list => [1, 2, 3] },
146
- { :name => "four", :list => [1, 2, 3, 4] } ],
147
- 3 => [ { :name => "three", :list => [1, 2, 3] },
148
- { :name => "four", :list => [1, 2, 3, 4] } ],
149
- 4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
74
+ ["list value", 1] => [{:name=>"one", :list=>[1]}, {:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
75
+ ["list value", 2] => [{:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
76
+ ["list value", 3] => [{:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
77
+ ["list value", 4] => [{:name=>"four", :list=>[1, 2, 3, 4]}]
150
78
  }
151
79
 
152
- assert data == expected
80
+ assert pivot.result.to_h == expected
153
81
  end
154
82
 
155
83
  test "pivot of list values with empty list" do
@@ -160,243 +88,187 @@ class TestGoldmine < PryTest::Test
160
88
  { :name => "three", :list => [1, 2, 3] },
161
89
  { :name => "four", :list => [1, 2, 3, 4] },
162
90
  ]
163
- list = Goldmine::ArrayMiner.new(list)
164
- data = list.pivot { |record| record[:list] }
91
+ pivot = Goldmine(list)
92
+ .pivot("list value") { |record| record[:list] }
165
93
 
166
94
  expected = {
167
- nil => [ {:name => "empty", :list => [] } ],
168
- 1 => [ { :name => "one", :list => [1] },
169
- { :name => "two", :list => [1, 2] },
170
- { :name => "three", :list => [1, 2, 3] },
171
- { :name => "four", :list => [1, 2, 3, 4] } ],
172
- 2 => [ { :name => "two", :list => [1, 2] },
173
- { :name => "three", :list => [1, 2, 3] },
174
- { :name => "four", :list => [1, 2, 3, 4] } ],
175
- 3 => [ { :name => "three", :list => [1, 2, 3] },
176
- { :name => "four", :list => [1, 2, 3, 4] } ],
177
- 4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
95
+ [["list value", nil]] => [{:name=>"empty", :list=>[]}],
96
+ ["list value", 1] => [{:name=>"one", :list=>[1]}, {:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
97
+ ["list value", 2] => [{:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
98
+ ["list value", 3] => [{:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
99
+ ["list value", 4] => [{:name=>"four", :list=>[1, 2, 3, 4]}]
178
100
  }
179
101
 
180
- assert data == expected
102
+ assert pivot.result.to_h == expected
181
103
  end
182
104
 
183
- test "chained pivots" do
105
+ # rollups ...................................................................
106
+
107
+ test "simple pivot rollup" do
184
108
  list = [1,2,3,4,5,6,7,8,9]
185
- list = Goldmine::ArrayMiner.new(list)
186
- data = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
109
+ rollup = Goldmine(list)
110
+ .pivot("< 5") { |i| i < 5 }
111
+ .result
112
+ .rollup(:count) { |items| items.size }
187
113
 
188
114
  expected = {
189
- [true, false] => [1, 3],
190
- [true, true] => [2, 4],
191
- [false, false] => [5, 7, 9],
192
- [false, true] => [6, 8]
115
+ [["< 5", true]] => [[:count, 4]],
116
+ [["< 5", false]] => [[:count, 5]]
193
117
  }
194
118
 
195
- assert data == expected
119
+ assert rollup.result.to_h == expected
196
120
  end
197
121
 
198
122
  test "chained pivots rollup" do
199
123
  list = [1,2,3,4,5,6,7,8,9]
200
- list = Goldmine::ArrayMiner.new(list)
201
- data = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
202
- rolled = data.rollup(:count) { |row| row.size }
124
+ rollup = Goldmine(list)
125
+ .pivot("< 5") { |i| i < 5 }
126
+ .pivot("even") { |i| i % 2 == 0 }
127
+ .result
128
+ .rollup(:count) { |row| row.size }
203
129
 
204
130
  expected = {
205
- [true, false] => { count: 2 },
206
- [true, true] => { count: 2 },
207
- [false, false] => { count: 3 },
208
- [false, true] => { count: 2 }
131
+ [["< 5", true], ["even", false]] => [[:count, 2]],
132
+ [["< 5", true], ["even", true]] => [[:count, 2]],
133
+ [["< 5", false], ["even", false]] => [[:count, 3]],
134
+ [["< 5", false], ["even", true]] => [[:count, 2]]
209
135
  }
210
136
 
211
- assert rolled == expected
212
- end
213
-
214
- test "chained pivots rollup to_tabular" do
215
- list = [1,2,3,4,5,6,7,8,9]
216
- list = Goldmine::ArrayMiner.new(list)
217
- rolled = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }.rollup(:count, &:size)
218
-
219
- expected = [
220
- ["column1", "column2", "count"],
221
- [true, false, 2],
222
- [true, true, 2],
223
- [false, false, 3],
224
- [false, true, 2]
225
- ]
226
-
227
- assert rolled.to_tabular == expected
137
+ assert rollup.result.to_h == expected
228
138
  end
229
139
 
230
- test "deep chained pivots" do
140
+ test "pivot with chained rollup" do
231
141
  list = [1,2,3,4,5,6,7,8,9]
232
- list = Goldmine::ArrayMiner.new(list)
233
- data = list
234
- .pivot { |i| i < 3 }
235
- .pivot { |i| i < 6 }
236
- .pivot { |i| i < 9 }
237
- .pivot { |i| i % 2 == 0 }
238
- .pivot { |i| i % 3 == 0 }
142
+ list = Goldmine(list)
143
+ rollup = list
144
+ .pivot("< 5") { |i| i < 5 }
145
+ .result
146
+ .rollup(:count) { |items| items.size }
147
+ .rollup(:div_by_3) { |items| items.keep_if { |i| i % 3 == 0 }.size }
239
148
 
240
149
  expected = {
241
- [true, true, true, false, false] => [1],
242
- [true, true, true, true, false] => [2],
243
- [false, true, true, false, true] => [3],
244
- [false, true, true, false, false] => [5],
245
- [false, true, true, true, false] => [4],
246
- [false, false, true, true, true] => [6],
247
- [false, false, true, true, false] => [8],
248
- [false, false, true, false, false] => [7],
249
- [false, false, false, false, true] => [9]
150
+ [["< 5", true]] => [[:count, 4], [:div_by_3, 1]],
151
+ [["< 5", false]] => [[:count, 5], [:div_by_3, 2]]
250
152
  }
251
153
 
252
- assert data == expected
154
+ assert rollup.result.to_h == expected
253
155
  end
254
156
 
255
- test "named deep chained pivots" do
256
- list = [1,2,3,4,5,6,7,8,9]
257
- list = Goldmine::ArrayMiner.new(list)
258
- data = list.pivot("a") { |i| i < 3 }.pivot("b") { |i| i < 6 }.pivot("c") { |i| i < 9 }.pivot("d") { |i| i % 2 == 0 }.pivot("e") { |i| i % 3 == 0 }
259
-
260
- expected = {
261
- {"a"=>true, "b"=>true, "c"=>true, "d"=>false, "e"=>false} => [1],
262
- {"a"=>true, "b"=>true, "c"=>true, "d"=>true, "e"=>false} => [2],
263
- {"a"=>false, "b"=>true, "c"=>true, "d"=>false, "e"=>true} => [3],
264
- {"a"=>false, "b"=>true, "c"=>true, "d"=>false, "e"=>false} => [5],
265
- {"a"=>false, "b"=>true, "c"=>true, "d"=>true, "e"=>false} => [4],
266
- {"a"=>false, "b"=>false, "c"=>true, "d"=>true, "e"=>true} => [6],
267
- {"a"=>false, "b"=>false, "c"=>true, "d"=>true, "e"=>false} => [8],
268
- {"a"=>false, "b"=>false, "c"=>true, "d"=>false, "e"=>false} => [7],
269
- {"a"=>false, "b"=>false, "c"=>false, "d"=>false, "e"=>true} => [9]
270
- }
157
+ # to_rows ...................................................................
271
158
 
272
- assert data == expected
273
- end
274
-
275
- test "named chained pivots" do
159
+ test "simple pivot rollup to_rows" do
276
160
  list = [1,2,3,4,5,6,7,8,9]
277
- list = Goldmine::ArrayMiner.new(list)
278
- data = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }
161
+ rollup = Goldmine(list)
162
+ .pivot("< 5") { |i| i < 5 }
163
+ .result
164
+ .rollup(:count) { |items| items.size }
279
165
 
280
- expected = {
281
- { "less than 5" => true, "divisible by 2" => false } => [1, 3],
282
- { "less than 5" => true, "divisible by 2" => true} => [2, 4],
283
- { "less than 5" => false, "divisible by 2" => false} => [5, 7, 9],
284
- { "less than 5" => false, "divisible by 2" => true} => [6, 8]
285
- }
166
+ expected = [
167
+ [["< 5", true], [:count, 4]],
168
+ [["< 5", false], [:count, 5]]
169
+ ]
286
170
 
287
- assert data == expected
171
+ assert rollup.result.to_rows == expected
288
172
  end
289
173
 
290
- test "named chained pivots rollup" do
174
+ test "chained pivots rollup to_rows" do
291
175
  list = [1,2,3,4,5,6,7,8,9]
292
- list = Goldmine::ArrayMiner.new(list)
293
- data = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }
294
- rolled = data.rollup(:count) { |row| row.size }
176
+ rollup = Goldmine(list)
177
+ .pivot("< 5") { |i| i < 5 }
178
+ .pivot("even") { |i| i % 2 == 0 }
179
+ .result
180
+ .rollup(:count) { |row| row.size }
295
181
 
296
- expected = {
297
- { "less than 5" => true, "divisible by 2" => false } => { count: 2 },
298
- { "less than 5" => true, "divisible by 2" => true } => { count: 2 },
299
- { "less than 5" => false, "divisible by 2" => false } => { count: 3 },
300
- { "less than 5" => false, "divisible by 2" => true } => { count: 2 }
301
- }
182
+ expected = [
183
+ [["< 5", true], ["even", false], [:count, 2]],
184
+ [["< 5", true], ["even", true], [:count, 2]],
185
+ [["< 5", false], ["even", false], [:count, 3]],
186
+ [["< 5", false], ["even", true], [:count, 2]]
187
+ ]
302
188
 
303
- assert rolled == expected
189
+ assert rollup.result.to_rows == expected
304
190
  end
305
191
 
306
- test "named chained pivots rollup to tabular" do
192
+ test "simple pivot rollup to_hash_rows" do
307
193
  list = [1,2,3,4,5,6,7,8,9]
308
- list = Goldmine::ArrayMiner.new(list)
309
- rolled = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }.rollup(:count, &:size)
194
+ rollup = Goldmine(list)
195
+ .pivot("< 5") { |i| i < 5 }
196
+ .result
197
+ .rollup(:count) { |items| items.size }
310
198
 
311
199
  expected = [
312
- ["less than 5", "divisible by 2", "count"],
313
- [true, false, 2],
314
- [true, true, 2],
315
- [false, false, 3],
316
- [false, true, 2]
200
+ {"< 5" => true, :count => 4},
201
+ {"< 5" => false, :count => 5}
317
202
  ]
318
203
 
319
- assert rolled.to_tabular == expected
204
+ assert rollup.result.to_hash_rows == expected
320
205
  end
321
206
 
322
- test "named & chained pivots with rollup to_csv_table" do
323
- list = [1,2,3,4,5,6,7,8,9]
324
- list = Goldmine::ArrayMiner.new(list)
325
- rolled = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }.rollup(:count, &:size)
326
- csv = rolled.to_csv_table
327
-
328
- assert csv.to_a == rolled.to_tabular
329
-
330
- expected = ["less than 5", "divisible by 2", "count"]
331
- assert csv.headers == expected
332
-
333
- row = csv.first
334
- assert row["less than 5"] == true
335
- assert row["divisible by 2"] == false
336
- assert row ["count"] == 2
337
- end
207
+ # to_tabular ................................................................
338
208
 
339
- test "unnamed & chained pivots with rollup to rows" do
209
+ test "simple pivot rollup to_tabular" do
340
210
  list = [1,2,3,4,5,6,7,8,9]
341
- list = Goldmine::ArrayMiner.new(list)
342
- rolled = list
343
- .pivot { |i| i < 5 }
211
+ rollup = Goldmine(list)
212
+ .pivot("< 5") { |i| i < 5 }
213
+ .result
344
214
  .rollup(:count, &:size)
345
- .rollup(:evens) { |l| l.select { |i| i % 2 == 0 }.size }
346
- .rollup(:even_percentage) { |l| computed(:evens).for(l) / computed(:count).for(l).to_f }
347
215
 
348
216
  expected = [
349
- {"column1"=>true, "count"=>4, "evens"=>2, "even_percentage"=>0.5},
350
- {"column1"=>false, "count"=>5, "evens"=>2, "even_percentage"=>0.4}
217
+ ["< 5", :count],
218
+ [true, 4],
219
+ [false, 5]
351
220
  ]
352
221
 
353
- assert rolled.to_rows == expected
222
+ assert rollup.result.to_tabular == expected
354
223
  end
355
224
 
356
- test "named & chained pivots with rollup to rows" do
225
+ test "chained pivots rollup to_tabular" do
357
226
  list = [1,2,3,4,5,6,7,8,9]
358
- list = Goldmine::ArrayMiner.new(list)
359
- rolled = list
360
- .pivot(:less_than_5) { |i| i < 5 }
227
+ rollup = Goldmine(list)
228
+ .pivot("< 5") { |i| i < 5 }
229
+ .pivot(:even) { |i| i % 2 == 0 }
230
+ .result
361
231
  .rollup(:count, &:size)
362
- .rollup(:evens) { |l| l.select { |i| i % 2 == 0 }.size }
363
- .rollup(:even_percentage) { |l| computed(:evens).for(l) / computed(:count).for(l).to_f }
364
232
 
365
233
  expected = [
366
- {"less_than_5"=>true, "count"=>4, "evens"=>2, "even_percentage"=>0.5},
367
- {"less_than_5"=>false, "count"=>5, "evens"=>2, "even_percentage"=>0.4}
234
+ ["< 5", :even, :count],
235
+ [true, false, 2],
236
+ [true, true, 2],
237
+ [false, false, 3],
238
+ [false, true, 2]
368
239
  ]
369
240
 
370
- assert rolled.to_rows == expected
241
+ assert rollup.result.to_tabular == expected
371
242
  end
372
243
 
373
- test "access to prior-computed rollups" do
244
+ # to_csv_table ..............................................................
245
+
246
+ test "simple pivot rollup to_csv_table" do
374
247
  list = [1,2,3,4,5,6,7,8,9]
375
- list = Goldmine::ArrayMiner.new(list)
376
- rolled = list
377
- .pivot("less than 5") { |i| i < 5 }
248
+ rollup = Goldmine(list)
249
+ .pivot("< 5") { |i| i < 5 }
250
+ .result
378
251
  .rollup(:count, &:size)
379
- .rollup(:evens) { |pivoted_list| pivoted_list.select { |i| i % 2 == 0 }.size }
380
- .rollup(:even_percentage) { |pivoted_list| computed(:evens).for(pivoted_list) / computed(:count).for(pivoted_list).to_f }
381
252
 
382
- expected = [
383
- ["less than 5", "count", "evens", "even_percentage"],
384
- [true, 4, 2, 0.5],
385
- [false, 5, 2, 0.4]
386
- ]
387
- assert rolled.to_tabular == expected
253
+ csv = rollup.result.to_csv_table
254
+ assert csv.to_s == "< 5,count\ntrue,4\nfalse,5\n"
388
255
  end
389
256
 
390
- test "rollup names" do
257
+ # pivot_result cache ..........................................................
258
+
259
+ test "pivot_result cache is available to rollups" do
391
260
  list = [1,2,3,4,5,6,7,8,9]
392
- list = Goldmine::ArrayMiner.new(list)
393
- data = list.pivot { |i| i < 5 }
394
- rolled = data
395
- .rollup(:count) { |items| items.size }
396
- .rollup(:div_by_3) { |items| items.keep_if { |i| i % 3 == 0 }.size }
261
+ cached_counts = []
262
+ Goldmine(list)
263
+ .pivot("< 5") { |i| i < 5 }
264
+ .result
265
+ .rollup(:count, &:size)
266
+ .rollup(:cached_count) { |_| cached_counts << cache[:count] }
267
+ .result(cache: true)
397
268
 
398
- expected = [:count, :div_by_3]
399
- assert rolled.names == expected
269
+ assert cached_counts.size == 2
270
+ assert cached_counts.first == 4
271
+ assert cached_counts.last == 5
400
272
  end
401
273
 
402
274
  end