goldmine 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2de5d730c5afa6d7b115594a6a3b426d69cc7901
4
- data.tar.gz: d3d9768c962046f97c223cca2d6bf1825ddcebdb
3
+ metadata.gz: 98cf471fea798a03ff6b2d00fd66f46a8e7314bc
4
+ data.tar.gz: 9f3504f492b1fe77c6e6a6be0c25ca8c15320a1b
5
5
  SHA512:
6
- metadata.gz: 31343963f3b95dcfa233194f8663706f8a863f5b50660bc123c8f69df34a847453655ef31ff41320fff9544aea515ddc2b084bfaa9df0ea98d3f5fccb73262ab
7
- data.tar.gz: 0fd12420956bcd9a9910b2006ead91b89a0df0dd90e59a798a7b35d0e0f9e7e2764804f776eda6fce21bd26981b95d7797832f8949514ce6b41c87a2c3617a18
6
+ metadata.gz: 5d8eb6c77e8a16834ad6c045040abb56d77fbd0b4fff6c5921cb96b17875de0152deaf874b85269836f7e18f6c194783a865d9dc4fb75c2eb65ed977e62a09c6
7
+ data.tar.gz: 869e2679c6f914fba82726834056420f00f7aee3597d514623b15d233b826936d29600729609e2e3ff6ad07d042f84cab0a5e8195f5b4a4b1e004c0fcc2f863c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- goldmine (1.2.0)
4
+ goldmine (1.2.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Lines of Code](http://img.shields.io/badge/lines_of_code-89-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
1
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-132-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
2
2
  [![Code Status](http://img.shields.io/codeclimate/github/hopsoft/goldmine.svg?style=flat)](https://codeclimate.com/github/hopsoft/goldmine)
3
3
  [![Dependency Status](http://img.shields.io/gemnasium/hopsoft/goldmine.svg?style=flat)](https://gemnasium.com/hopsoft/goldmine)
4
4
  [![Build Status](http://img.shields.io/travis/hopsoft/goldmine.svg?style=flat)](https://travis-ci.org/hopsoft/goldmine)
@@ -14,14 +14,14 @@ Think of __Goldmine__ as `Enumerable#group_by` on steroids.
14
14
 
15
15
  - Data mining
16
16
  - Data transformation
17
+ - Data blending
18
+ - Data visualization prep
17
19
  - CSV report generation
18
- - Prep for data visualization
19
- - Fact table creation
20
20
 
21
21
  ---
22
22
 
23
23
  The [demo project](http://hopsoft.github.io/goldmine/) demonstrates some of Goldmine's uses.
24
- `TODO: update the demo project`
24
+ `TODO: update the demo project to use the latest features`
25
25
 
26
26
  ---
27
27
 
@@ -143,64 +143,52 @@ end
143
143
  }
144
144
  ```
145
145
 
146
- ## Rollups
146
+ ## Rollups, Tabular, & CSV
147
147
 
148
- Sometimes it's useful to roll pivots into a summary.
148
+ Rollups provide a clean way to aggregate pivoted data.
149
+
150
+ _NOTE: Rollups can also be chained._
149
151
 
150
152
  ```ruby
151
153
  list = [1,2,3,4,5,6,7,8,9]
152
154
  list = Goldmine::ArrayMiner.new(list)
153
- pivoted = list.pivot(:less_than_5) { |i| i < 5 }.pivot(:even) { |i| i % 2 == 0 }
154
- pivoted.rollup { |values| values.size }
155
+ pivoted = list
156
+ .pivot(:less_than_5) { |i| i < 5 }
157
+ .pivot(:even) { |i| i % 2 == 0 }
155
158
  # result:
156
159
  {
157
- { :less_than_5 => true, :even => false} => 2,
158
- { :less_than_5 => true, :even => true} => 2,
159
- { :less_than_5 => false, :even => false} => 3,
160
- { :less_than_5 => false, :even => true} => 2
160
+ { :less_than_5 => true, :even => false } => [1, 3],
161
+ { :less_than_5 => true, :even => true } => [2, 4],
162
+ { :less_than_5 => false, :even => false} => [5, 7, 9],
163
+ { :less_than_5 => false, :even => true } => [6, 8]
161
164
  }
162
- ```
163
-
164
- ## Tabular data
165
165
 
166
- Tabular data provides a more streamlined summary view of a pivot.
166
+ rollup = pivoted.rollup(:count) { |matched| matched.size }
167
+ # result:
168
+ {
169
+ {:less_than_5=>true, :even=>false}=>{:count=>2},
170
+ {:less_than_5=>true, :even=>true}=>{:count=>2},
171
+ {:less_than_5=>false, :even=>false}=>{:count=>3},
172
+ {:less_than_5=>false, :even=>true}=>{:count=>2}
173
+ }
167
174
 
168
- ```ruby
169
- list = [1,2,3,4,5,6,7,8,9]
170
- list = Goldmine::ArrayMiner.new(list)
171
- pivoted = list.pivot(:less_than_5) { |i| i < 5 }.pivot(:even) { |i| i % 2 == 0 }
172
- pivoted.to_tabular
175
+ rollup.to_tabular
173
176
  # result:
174
177
  [
175
- ["less_than_5", "even", "percent", "count"],
176
- [true, false, 0.22, 2],
177
- [true, true, 0.22, 2],
178
- [false, false, 0.33, 3],
179
- [false, true, 0.22, 2]
178
+ ["less_than_5", "even", "count"],
179
+ [true, false, 2],
180
+ [true, true, 2],
181
+ [false, false, 3],
182
+ [false, true, 2]
180
183
  ]
181
- ```
182
184
 
183
- ## CSV table
184
-
185
- CSV tables are a formalized version of tabular data.
186
- They simplify the complexity of working with tabular data.
187
-
188
- ```ruby
189
- list = [1,2,3,4,5,6,7,8,9]
190
- list = Goldmine::ArrayMiner.new(list)
191
- pivoted = list.pivot(:less_than_5) { |i| i < 5 }.pivot(:even) { |i| i % 2 == 0 }
192
- csv = pivoted.to_csv
193
-
194
- csv.headers # => ["less_than_5", "even", "percent", "count"]
195
-
196
- csv.each do |row|
197
- puts row["less_than_5"]
198
- puts row["even"]
199
- end
185
+ rollup.to_csv_table
186
+ # result:
187
+ #<CSV::Table mode:col_or_row row_count:5>
200
188
 
201
- csv.to_csv
189
+ rollup.to_csv_table.to_csv
202
190
  # result:
203
- "less_than_5,even,percent,count\ntrue,false,0.22,2\ntrue,true,0.22,2\nfalse,false,0.33,3\nfalse,true,0.22,2\n"
191
+ "less_than_5,even,count\ntrue,false,2\ntrue,true,2\nfalse,false,3\nfalse,true,2\n"
204
192
  ```
205
193
 
206
194
  ## Summary
data/lib/goldmine.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  $:.unshift File.join(File.dirname(__FILE__), "goldmine")
2
+ require "hash_rollup"
2
3
  require "array_miner"
3
4
  require "hash_miner"
4
5
 
@@ -2,10 +2,7 @@ require "delegate"
2
2
 
3
3
  module Goldmine
4
4
  class ArrayMiner < SimpleDelegator
5
- attr_reader :source_data
6
-
7
- def initialize(array=[], source_data: [])
8
- @source_data = source_data
5
+ def initialize(array=[])
9
6
  super array
10
7
  end
11
8
 
@@ -48,7 +45,7 @@ module Goldmine
48
45
  # @yield [Object] Yields once for each item in the Array
49
46
  # @return [Hash] The pivoted Hash of data.
50
47
  def pivot(name=nil, &block)
51
- reduce(HashMiner.new(source_data: self)) do |memo, item|
48
+ reduce(HashMiner.new) do |memo, item|
52
49
  value = yield(item)
53
50
 
54
51
  if value.is_a?(Array)
@@ -1,13 +1,9 @@
1
1
  require "delegate"
2
- require "csv"
3
2
 
4
3
  module Goldmine
5
4
  class HashMiner < SimpleDelegator
6
- attr_reader :source_data
7
-
8
- def initialize(hash={}, source_data: [])
9
- @source_data = source_data
10
- super hash
5
+ def initialize(hash={})
6
+ super @hash = hash
11
7
  end
12
8
 
13
9
  attr_accessor :goldmine
@@ -35,7 +31,7 @@ module Goldmine
35
31
  def pivot(name=nil, &block)
36
32
  return self unless goldmine
37
33
 
38
- reduce(HashMiner.new(source_data: source_data)) do |memo, item|
34
+ reduce(HashMiner.new) do |memo, item|
39
35
  key = item.first
40
36
  value = Goldmine.miner(item.last)
41
37
  value.pivot(name, &block).each do |k, v|
@@ -52,45 +48,12 @@ module Goldmine
52
48
  end
53
49
  end
54
50
 
55
- # Returns a new "rolled up" Hash based on the return value of the yield.
51
+ # Returns a HashRollup with summarized data.
56
52
  #
57
- # @yield [Object] Yields once for each pivoted group.
53
+ # @yield [Object] Yields once for each pivoted grouping of values.
58
54
  # @return [Hash] The rollup Hash of data.
59
- def rollup
60
- each_with_object({}) do |pair, memo|
61
- memo[pair.first] = yield(pair.last)
62
- end
63
- end
64
-
65
- # Returns a tabular representation of the pivot.
66
- # Useful for building CSVs & data visualizations.
67
- #
68
- # @param percent_column_name [String] The name of the percent column (percent of total)
69
- # @param count_column_name [String] The name of the count column (number of objects)
70
- # @return [Array] The tabular representation of the data.
71
- def to_tabular(percent_column_name: "percent", count_column_name: "count")
72
- [].tap do |rows|
73
- rows << tabular_header_from_key(first.first) + [percent_column_name, count_column_name]
74
- rolled = rollup { |row| row.size }
75
- rolled.each do |key, value|
76
- tabular_row_from_key(key).tap do |row|
77
- rows << row + [calculate_percentage(value, source_data.size), value]
78
- end
79
- end
80
- end
81
- end
82
-
83
- # Returns an in memory CSV table representation of the pivot.
84
- # Useful for working with data & building data visualizations.
85
- #
86
- # @param percent_column_name [String] The name of the percent column (percent of total)
87
- # @param count_column_name [String] The name of the count column (number of objects)
88
- # @return [CSV::Table] The CSV representation of the data.
89
- def to_csv(percent_column_name: "percent", count_column_name: "count")
90
- tabular = to_tabular(percent_column_name: percent_column_name, count_column_name: count_column_name)
91
- header = tabular.shift
92
- rows = tabular.map { |row| CSV::Row.new(header, row) }
93
- CSV::Table.new rows
55
+ def rollup(name, &block)
56
+ HashRollup.new(@hash).rollup(name, &block)
94
57
  end
95
58
 
96
59
  # Assigns a key/value pair to the Hash.
@@ -113,24 +76,5 @@ module Goldmine
113
76
  goldmine_key ||= key
114
77
  end
115
78
 
116
- private
117
-
118
- def calculate_percentage(count, total)
119
- return 0.0 unless total > 0
120
- sprintf("%.2f", count / total.to_f).to_f
121
- end
122
-
123
- def tabular_header_from_key(key)
124
- return key.keys.map(&:to_s) if key.is_a?(Hash)
125
- key = [key] unless key.is_a?(Array)
126
- (0..key.size-1).map { |i| "column#{i}" }
127
- end
128
-
129
- def tabular_row_from_key(key)
130
- return key.dup if key.is_a?(Array)
131
- return [key] unless key.is_a?(Hash)
132
- key.values.dup
133
- end
134
-
135
79
  end
136
80
  end
@@ -0,0 +1,65 @@
1
+ require "delegate"
2
+ require "csv"
3
+
4
+ module Goldmine
5
+ class HashRollup < SimpleDelegator
6
+ def initialize(pivoted)
7
+ @pivoted = pivoted
8
+ super @rolled = {}
9
+ end
10
+
11
+ def rollup(name)
12
+ pivoted.each do |key, value|
13
+ rolled[key] ||= {}
14
+ rolled[key][name] = yield(value)
15
+ end
16
+ self
17
+ end
18
+
19
+ def to_tabular
20
+ [].tap do |rows|
21
+ rows << tabular_header
22
+ rows.concat tabular_rows
23
+ end
24
+ end
25
+
26
+ def to_csv_table
27
+ header = tabular_header
28
+ rows = tabular_rows.map { |row| CSV::Row.new(header, row) }
29
+ CSV::Table.new rows
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :pivoted, :rolled
35
+
36
+ def tabular_header
37
+ return [] if empty?
38
+ to_tabular_header keys.first, values.first
39
+ end
40
+
41
+ def tabular_rows
42
+ map do |pair|
43
+ to_tabular_row pair.first, pair.last
44
+ end
45
+ end
46
+
47
+ def to_tabular_row(key, value)
48
+ row = key.values.dup if key.is_a?(Hash)
49
+ row ||= as_array(key).dup
50
+ row.concat value.values
51
+ end
52
+
53
+ def to_tabular_header(key, value)
54
+ header = key.keys.map(&:to_s) if key.is_a?(Hash)
55
+ header ||= (1..as_array(key).size).map { |i| "column#{i}" }
56
+ header.concat value.keys.map(&:to_s)
57
+ end
58
+
59
+ def as_array(value)
60
+ return value if value.is_a?(Array)
61
+ [value]
62
+ end
63
+
64
+ end
65
+ end
@@ -1,3 +1,3 @@
1
1
  module Goldmine
2
- VERSION = "1.2.1"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -34,38 +34,54 @@ class TestGoldmine < PryTest::Test
34
34
  list = [1,2,3,4,5,6,7,8,9]
35
35
  list = Goldmine::ArrayMiner.new(list)
36
36
  data = list.pivot { |i| i < 5 }
37
- rolled = data.rollup { |row| row.size }
37
+ rolled = data.rollup(:count) { |items| items.size }
38
38
 
39
39
  expected = {
40
- true => 4,
41
- false => 5
40
+ true => { count: 4 },
41
+ false => { count: 5 }
42
42
  }
43
43
 
44
44
  assert rolled == expected
45
45
  end
46
46
 
47
- test "simple pivot to_tabular" do
47
+ test "pivot with chained rollup" do
48
48
  list = [1,2,3,4,5,6,7,8,9]
49
49
  list = Goldmine::ArrayMiner.new(list)
50
50
  data = list.pivot { |i| i < 5 }
51
+ rolled = data
52
+ .rollup(:count) { |items| items.size }
53
+ .rollup(:div_by_3) { |items| items.keep_if { |i| i % 3 == 0 }.size }
54
+
55
+ expected = {
56
+ true => {:count=>4, :div_by_3=>1},
57
+ false => {:count=>5, :div_by_3=>2}
58
+ }
59
+
60
+ assert rolled == expected
61
+ end
62
+
63
+ test "simple pivot rollup to_tabular" do
64
+ list = [1,2,3,4,5,6,7,8,9]
65
+ list = Goldmine::ArrayMiner.new(list)
66
+ rolled = list.pivot { |i| i < 5 }.rollup(:count, &:size)
51
67
 
52
68
  expected = [
53
- ["column0", "percent", "count"],
54
- [true, 0.44, 4],
55
- [false, 0.56, 5]
69
+ ["column1", "count"],
70
+ [true, 4],
71
+ [false, 5]
56
72
  ]
57
73
 
58
- assert data.to_tabular == expected
74
+ assert rolled.to_tabular == expected
59
75
  end
60
76
 
61
- test "simple pivot to_csv" do
77
+ test "simple pivot rollup to_csv_table" do
62
78
  list = [1,2,3,4,5,6,7,8,9]
63
79
  list = Goldmine::ArrayMiner.new(list)
64
- data = list.pivot { |i| i < 5 }
65
- csv = data.to_csv
80
+ rolled = list.pivot { |i| i < 5 }.rollup(:count, &:size)
81
+ csv = rolled.to_csv_table
66
82
 
67
- assert csv.headers == ["column0", "percent", "count"]
68
- assert csv.to_a == [["column0", "percent", "count"], [true, 0.44, 4], [false, 0.56, 5]]
83
+ assert csv.headers == ["column1", "count"]
84
+ assert csv.to_a == [["column1", "count"], [true, 4], [false, 5]]
69
85
  end
70
86
 
71
87
  test "named pivot" do
@@ -85,28 +101,28 @@ class TestGoldmine < PryTest::Test
85
101
  list = [1,2,3,4,5,6,7,8,9]
86
102
  list = Goldmine::ArrayMiner.new(list)
87
103
  data = list.pivot("less than 5") { |i| i < 5 }
88
- rolled = data.rollup { |row| row.size }
104
+ rolled = data.rollup(:count) { |row| row.size }
89
105
 
90
106
  expected = {
91
- { "less than 5" => true } => 4,
92
- { "less than 5" => false } => 5
107
+ { "less than 5" => true } => { count: 4 },
108
+ { "less than 5" => false } => { count: 5 }
93
109
  }
94
110
 
95
111
  assert rolled == expected
96
112
  end
97
113
 
98
- test "named pivot to_tabular" do
114
+ test "named pivot rollup to_tabular" do
99
115
  list = [1,2,3,4,5,6,7,8,9]
100
116
  list = Goldmine::ArrayMiner.new(list)
101
- data = list.pivot("less than 5") { |i| i < 5 }
117
+ rolled = list.pivot("less than 5") { |i| i < 5 }.rollup(:count, &:size)
102
118
 
103
119
  expected = [
104
- ["less than 5", "percent", "count"],
105
- [true, 0.44, 4],
106
- [false, 0.56, 5]
120
+ ["less than 5", "count"],
121
+ [true, 4],
122
+ [false, 5]
107
123
  ]
108
124
 
109
- assert data.to_tabular == expected
125
+ assert rolled.to_tabular == expected
110
126
  end
111
127
 
112
128
  test "pivot of list values" do
@@ -182,32 +198,32 @@ class TestGoldmine < PryTest::Test
182
198
  list = [1,2,3,4,5,6,7,8,9]
183
199
  list = Goldmine::ArrayMiner.new(list)
184
200
  data = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
185
- rolled = data.rollup { |row| row.size }
201
+ rolled = data.rollup(:count) { |row| row.size }
186
202
 
187
203
  expected = {
188
- [true, false] => 2,
189
- [true, true] => 2,
190
- [false, false] => 3,
191
- [false, true] => 2
204
+ [true, false] => { count: 2 },
205
+ [true, true] => { count: 2 },
206
+ [false, false] => { count: 3 },
207
+ [false, true] => { count: 2 }
192
208
  }
193
209
 
194
210
  assert rolled == expected
195
211
  end
196
212
 
197
- test "chained pivots to_tabular" do
213
+ test "chained pivots rollup to_tabular" do
198
214
  list = [1,2,3,4,5,6,7,8,9]
199
215
  list = Goldmine::ArrayMiner.new(list)
200
- data = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
216
+ rolled = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }.rollup(:count, &:size)
201
217
 
202
218
  expected = [
203
- ["column0", "column1", "percent", "count"],
204
- [true, false, 0.22, 2],
205
- [true, true, 0.22, 2],
206
- [false, false, 0.33, 3],
207
- [false, true, 0.22, 2]
219
+ ["column1", "column2", "count"],
220
+ [true, false, 2],
221
+ [true, true, 2],
222
+ [false, false, 3],
223
+ [false, true, 2]
208
224
  ]
209
225
 
210
- assert data.to_tabular == expected
226
+ assert rolled.to_tabular == expected
211
227
  end
212
228
 
213
229
  test "deep chained pivots" do
@@ -274,49 +290,48 @@ class TestGoldmine < PryTest::Test
274
290
  list = [1,2,3,4,5,6,7,8,9]
275
291
  list = Goldmine::ArrayMiner.new(list)
276
292
  data = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }
277
- rolled = data.rollup { |row| row.size }
293
+ rolled = data.rollup(:count) { |row| row.size }
278
294
 
279
295
  expected = {
280
- { "less than 5" => true, "divisible by 2" => false } => 2,
281
- { "less than 5" => true, "divisible by 2" => true } => 2,
282
- { "less than 5" => false, "divisible by 2" => false } => 3,
283
- { "less than 5" => false, "divisible by 2" => true } => 2
296
+ { "less than 5" => true, "divisible by 2" => false } => { count: 2 },
297
+ { "less than 5" => true, "divisible by 2" => true } => { count: 2 },
298
+ { "less than 5" => false, "divisible by 2" => false } => { count: 3 },
299
+ { "less than 5" => false, "divisible by 2" => true } => { count: 2 }
284
300
  }
285
301
 
286
302
  assert rolled == expected
287
303
  end
288
304
 
289
- test "named chained pivots to tabular" do
305
+ test "named chained pivots rollup to tabular" do
290
306
  list = [1,2,3,4,5,6,7,8,9]
291
307
  list = Goldmine::ArrayMiner.new(list)
292
- data = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }
308
+ rolled = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }.rollup(:count, &:size)
293
309
 
294
310
  expected = [
295
- ["less than 5", "divisible by 2", "percent", "count"],
296
- [true, false, 0.22, 2],
297
- [true, true, 0.22, 2],
298
- [false, false, 0.33, 3],
299
- [false, true, 0.22, 2]
311
+ ["less than 5", "divisible by 2", "count"],
312
+ [true, false, 2],
313
+ [true, true, 2],
314
+ [false, false, 3],
315
+ [false, true, 2]
300
316
  ]
301
317
 
302
- assert data.to_tabular == expected
318
+ assert rolled.to_tabular == expected
303
319
  end
304
320
 
305
- test "named chained pivots to csv" do
321
+ test "named chained pivots rollup to_csv_table" do
306
322
  list = [1,2,3,4,5,6,7,8,9]
307
323
  list = Goldmine::ArrayMiner.new(list)
308
- data = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }
309
- csv = data.to_csv
324
+ rolled = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }.rollup(:count, &:size)
325
+ csv = rolled.to_csv_table
310
326
 
311
- assert csv.to_a == data.to_tabular
327
+ assert csv.to_a == rolled.to_tabular
312
328
 
313
- expected = ["less than 5", "divisible by 2", "percent", "count"]
329
+ expected = ["less than 5", "divisible by 2", "count"]
314
330
  assert csv.headers == expected
315
331
 
316
332
  row = csv.first
317
333
  assert row["less than 5"] == true
318
334
  assert row["divisible by 2"] == false
319
- assert row["percent"] == 0.22
320
335
  assert row ["count"] == 2
321
336
  end
322
337
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goldmine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Hopkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-01 00:00:00.000000000 Z
11
+ date: 2015-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -67,6 +67,7 @@ files:
67
67
  - lib/goldmine.rb
68
68
  - lib/goldmine/array_miner.rb
69
69
  - lib/goldmine/hash_miner.rb
70
+ - lib/goldmine/hash_rollup.rb
70
71
  - lib/goldmine/version.rb
71
72
  - license.md
72
73
  - test/test_goldmine.rb