goldmine 1.2.1 → 2.0.0

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