goldmine 0.9.2 → 1.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 +7 -0
- data/Gemfile.lock +15 -11
- data/README.md +88 -329
- data/Rakefile +2 -5
- data/lib/goldmine.rb +7 -2
- data/lib/goldmine/array_miner.rb +7 -3
- data/lib/goldmine/hash_miner.rb +9 -4
- data/lib/goldmine/version.rb +1 -1
- data/test/test_goldmine.rb +65 -21
- metadata +18 -44
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 657692c39740a0cf3b3c14851f886387616aa417
|
4
|
+
data.tar.gz: 8a611460c2a55d95d8e2d406dca9b1e72ab50682
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4821192a956bedf128bf1f9013712f12d11ff1b69e7b7decd14655998243bdc449775ae2ec0485f49f39cf7c1701134f46430c8b1e1ddc80e0f89848e6fff51
|
7
|
+
data.tar.gz: 987848b18ed99c765401567937b8ffcc80411248f1e07ccb1d649a8c5a2ccce41b0139168c4f9133a1553475dee1b69b2c434f01e2b7c7c2936dc4b8995afd50
|
data/Gemfile.lock
CHANGED
@@ -1,30 +1,34 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
goldmine (0.
|
4
|
+
goldmine (1.0.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
ansi (1.4.3)
|
10
9
|
coderay (1.0.9)
|
11
|
-
method_source (0.8.
|
12
|
-
|
13
|
-
|
10
|
+
method_source (0.8.2)
|
11
|
+
micro_test (0.4.0)
|
12
|
+
os
|
13
|
+
multi_json (1.8.2)
|
14
|
+
os (0.9.6)
|
15
|
+
pry (0.9.12.2)
|
14
16
|
coderay (~> 1.0.5)
|
15
17
|
method_source (~> 0.8)
|
16
18
|
slop (~> 3.4)
|
17
|
-
rake (10.0
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
rake (10.1.0)
|
20
|
+
simplecov (0.7.1)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
simplecov-html (~> 0.7.1)
|
23
|
+
simplecov-html (0.7.1)
|
24
|
+
slop (3.4.6)
|
21
25
|
|
22
26
|
PLATFORMS
|
23
27
|
ruby
|
24
28
|
|
25
29
|
DEPENDENCIES
|
26
30
|
goldmine!
|
27
|
-
|
31
|
+
micro_test
|
28
32
|
pry
|
29
33
|
rake
|
30
|
-
|
34
|
+
simplecov
|
data/README.md
CHANGED
@@ -4,113 +4,46 @@
|
|
4
4
|
[](https://gemnasium.com/hopsoft/goldmine)
|
5
5
|
[](https://codeclimate.com/github/hopsoft/goldmine)
|
6
6
|
|
7
|
-
|
7
|
+
### Extract a wealth of information from Arrays & Hashes
|
8
8
|
|
9
|
-
|
9
|
+
Think of it as an enhanced `Enumerable#group_by`.
|
10
10
|
|
11
|
-
|
11
|
+
## Uses
|
12
12
|
|
13
|
-
|
13
|
+
- Data mining
|
14
|
+
- Data transformation
|
15
|
+
- CSV report generation
|
16
|
+
- Prep for data visualization
|
17
|
+
- Fact table creation
|
14
18
|
|
15
|
-
|
19
|
+
The [demo project](http://hopsoft.github.io/goldmine/) demonstrates some of Goldmine's uses.
|
16
20
|
|
17
|
-
|
18
|
-
* Easily build OLAP cubes using Ruby
|
19
|
-
* Supports method chaining for deep data mining
|
20
|
-
* Handles values that are lists themselves
|
21
|
+
## Quick Start
|
21
22
|
|
22
|
-
[Why use it?](#putting-it-all-together)
|
23
|
-
|
24
|
-
## Quick start
|
25
|
-
|
26
|
-
Install
|
27
|
-
|
28
|
-
```bash
|
29
|
-
$gem install goldmine
|
30
23
|
```
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
```ruby
|
35
|
-
require "goldmine"
|
36
|
-
[1,2,3,4,5,6,7,8,9].pivot { |i| i < 5 }
|
24
|
+
gem install goldmine
|
25
|
+
irb
|
37
26
|
```
|
38
27
|
|
39
|
-
### Usage examples
|
40
|
-
|
41
|
-
* [Pivot a list](#pivot-a-list-of-numbers-based-on-whether-or-not-they-are-less-than-5)
|
42
|
-
* [Create a named pivot](#explicitly-name-a-pivot)
|
43
|
-
* [Pivot values that are lists themselves](#pivot-values-that-are-lists-themselves)
|
44
|
-
* [Chain pivots](#chain-pivots-together)
|
45
|
-
* [Dig deep and extract meaningful data](#deep-cuts)
|
46
|
-
|
47
|
-
## The Basics
|
48
|
-
|
49
|
-
### Pivot a list of numbers based on whether or not they are less than 5
|
50
|
-
|
51
28
|
```ruby
|
52
|
-
|
29
|
+
require "goldmine"
|
53
30
|
list = [1,2,3,4,5,6,7,8,9]
|
54
|
-
|
55
|
-
|
56
|
-
#
|
31
|
+
list = Goldmine::ArrayMiner.new(list)
|
32
|
+
list.pivot { |i| i < 5 }
|
33
|
+
# result:
|
57
34
|
{
|
58
35
|
true => [1, 2, 3, 4],
|
59
36
|
false => [5, 6, 7, 8, 9]
|
60
37
|
}
|
61
38
|
```
|
62
39
|
|
63
|
-
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
# operation
|
67
|
-
list = [1,2,3,4,5,6,7,8,9]
|
68
|
-
data = list.pivot("less than 5") { |i| i < 5 }
|
69
|
-
|
70
|
-
# resulting data
|
71
|
-
{
|
72
|
-
{ "less than 5" => true } => [1, 2, 3, 4],
|
73
|
-
{ "less than 5" => false } => [5, 6, 7, 8, 9]
|
74
|
-
}
|
75
|
-
```
|
76
|
-
|
77
|
-
## Next Steps
|
78
|
-
|
79
|
-
### Pivot values that are lists themselves
|
80
|
-
|
81
|
-
```ruby
|
82
|
-
# operation
|
83
|
-
list = [
|
84
|
-
{ :name => "one", :list => [1] },
|
85
|
-
{ :name => "two", :list => [1, 2] },
|
86
|
-
{ :name => "three", :list => [1, 2, 3] },
|
87
|
-
{ :name => "four", :list => [1, 2, 3, 4] },
|
88
|
-
]
|
89
|
-
data = list.pivot { |record| record[:list] }
|
90
|
-
|
91
|
-
# resulting data
|
92
|
-
{
|
93
|
-
1 => [ { :name => "one", :list => [1] },
|
94
|
-
{ :name => "two", :list => [1, 2] },
|
95
|
-
{ :name => "three", :list => [1, 2, 3] },
|
96
|
-
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
97
|
-
2 => [ { :name => "two", :list => [1, 2] },
|
98
|
-
{ :name => "three", :list => [1, 2, 3] },
|
99
|
-
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
100
|
-
3 => [ { :name => "three", :list => [1, 2, 3] },
|
101
|
-
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
102
|
-
4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
|
103
|
-
}
|
104
|
-
```
|
105
|
-
|
106
|
-
### Chain pivots together
|
40
|
+
## Chained Pivots
|
107
41
|
|
108
42
|
```ruby
|
109
|
-
# operation
|
110
43
|
list = [1,2,3,4,5,6,7,8,9]
|
111
|
-
|
112
|
-
|
113
|
-
#
|
44
|
+
list = Goldmine::ArrayMiner.new(list)
|
45
|
+
list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
|
46
|
+
# result:
|
114
47
|
{
|
115
48
|
[true, false] => [1, 3],
|
116
49
|
[true, true] => [2, 4],
|
@@ -119,261 +52,87 @@ data = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
|
|
119
52
|
}
|
120
53
|
```
|
121
54
|
|
122
|
-
##
|
123
|
-
|
124
|
-
### Build a moderately complex dataset of Cities
|
55
|
+
## Named Pivots
|
125
56
|
|
126
57
|
```ruby
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
},
|
133
|
-
{
|
134
|
-
|
135
|
-
:state => "CA",
|
136
|
-
:population => 74066,
|
137
|
-
:airlines => [ "SkyWest", "United", "SouthWest" ]
|
138
|
-
},
|
139
|
-
{
|
140
|
-
:name => "Manhattan",
|
141
|
-
:state => "NY",
|
142
|
-
:population => 1586698,
|
143
|
-
:airlines => [ "Delta", "JetBlue", "United" ]
|
144
|
-
},
|
145
|
-
{
|
146
|
-
:name => "Brooklyn",
|
147
|
-
:state => "NY",
|
148
|
-
:population => 2504700,
|
149
|
-
:airlines => [ "Delta", "American", "US Airways" ]
|
150
|
-
},
|
151
|
-
{
|
152
|
-
:name => "Boston",
|
153
|
-
:state => "MA",
|
154
|
-
:population => 617594,
|
155
|
-
:airlines => [ "Delta", "JetBlue", "American" ]
|
156
|
-
},
|
157
|
-
{
|
158
|
-
:name => "Atlanta",
|
159
|
-
:state => "GA",
|
160
|
-
:population => 420003,
|
161
|
-
:airlines => [ "Delta", "United", "SouthWest" ]
|
162
|
-
},
|
163
|
-
{
|
164
|
-
:name => "Dallas",
|
165
|
-
:state => "TX",
|
166
|
-
:population => 1197816,
|
167
|
-
:airlines => [ "Delta", "SouthWest", "Frontier" ]
|
168
|
-
}
|
169
|
-
]
|
58
|
+
list = [1,2,3,4,5,6,7,8,9]
|
59
|
+
list = Goldmine::ArrayMiner.new(list)
|
60
|
+
list.pivot(:less_than_5) { |i| i < 5 }
|
61
|
+
# result:
|
62
|
+
{
|
63
|
+
{ :less_than_5 => true } => [1, 2, 3, 4],
|
64
|
+
{ :less_than_5 => false } => [5, 6, 7, 8, 9]
|
65
|
+
}
|
170
66
|
```
|
171
67
|
|
172
|
-
|
68
|
+
## Value Pivots
|
173
69
|
|
174
70
|
```ruby
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
71
|
+
list = [
|
72
|
+
{ :name => "Sally", :favorite_colors => [:blue] },
|
73
|
+
{ :name => "John", :favorite_colors => [:blue, :green] },
|
74
|
+
{ :name => "Stephen", :favorite_colors => [:red, :pink, :purple] },
|
75
|
+
{ :name => "Emily", :favorite_colors => [:orange, :green] },
|
76
|
+
{ :name => "Joe", :favorite_colors => [:red] }
|
77
|
+
]
|
78
|
+
list = Goldmine::ArrayMiner.new(list)
|
79
|
+
list.pivot { |record| record[:favorite_colors] }
|
80
|
+
# result:
|
181
81
|
{
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
82
|
+
:blue => [
|
83
|
+
{ :name => "Sally", :favorite_colors => [:blue] },
|
84
|
+
{ :name => "John", :favorite_colors => [:blue, :green] }
|
85
|
+
],
|
86
|
+
:green => [
|
87
|
+
{ :name => "John", :favorite_colors => [:blue, :green] },
|
88
|
+
{ :name => "Emily", :favorite_colors => [:orange, :green] }
|
89
|
+
],
|
90
|
+
:red => [
|
91
|
+
{ :name => "Stephen", :favorite_colors => [:red, :pink, :purple] },
|
92
|
+
{ :name => "Joe", :favorite_colors => [:red] }
|
93
|
+
],
|
94
|
+
:pink => [
|
95
|
+
{ :name => "Stephen", :favorite_colors => [:red, :pink, :purple] }
|
96
|
+
],
|
97
|
+
:purple => [
|
98
|
+
{ :name => "Stephen", :favorite_colors => [:red, :pink, :purple] }
|
99
|
+
],
|
100
|
+
:orange => [
|
101
|
+
{ :name => "Emily", :favorite_colors => [:orange, :green] }
|
102
|
+
]
|
188
103
|
}
|
189
104
|
```
|
190
105
|
|
191
|
-
|
192
|
-
|
193
|
-
**The end goal of all this is to support the creation of aggregate reports.**
|
194
|
-
|
195
|
-
*You can think of these reports as individual data cubes.*
|
196
|
-
|
197
|
-
Here is a table view of the pivoted city data from above.
|
198
|
-
|
199
|
-
<table class="table table-bordered table-striped">
|
200
|
-
<thead>
|
201
|
-
<tr>
|
202
|
-
<th>state</th>
|
203
|
-
<th>population >= 750k</th>
|
204
|
-
<th>cities</th>
|
205
|
-
</tr>
|
206
|
-
</thead>
|
207
|
-
<tbody>
|
208
|
-
<tr>
|
209
|
-
<td>CA</td>
|
210
|
-
<td>true</td>
|
211
|
-
<td>1</td>
|
212
|
-
</tr>
|
213
|
-
<tr>
|
214
|
-
<td>CA</td>
|
215
|
-
<td>false</td>
|
216
|
-
<td>1</td>
|
217
|
-
</tr>
|
218
|
-
<tr>
|
219
|
-
<td>NY</td>
|
220
|
-
<td>true</td>
|
221
|
-
<td>2</td>
|
222
|
-
</tr>
|
223
|
-
<tr>
|
224
|
-
<td>MA</td>
|
225
|
-
<td>false</td>
|
226
|
-
<td>1</td>
|
227
|
-
</tr>
|
228
|
-
<tr>
|
229
|
-
<td>GA</td>
|
230
|
-
<td>false</td>
|
231
|
-
<td>1</td>
|
232
|
-
</tr>
|
233
|
-
<tr>
|
234
|
-
<td>TX</td>
|
235
|
-
<td>true</td>
|
236
|
-
<td>1</td>
|
237
|
-
</tr>
|
238
|
-
</tbody>
|
239
|
-
</table>
|
240
|
-
|
241
|
-
Lets try another one.
|
242
|
-
|
243
|
-
### Determine which airlines service cities with fewer than 750k people
|
106
|
+
# Stacked pivots
|
244
107
|
|
245
108
|
```ruby
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
109
|
+
list = [
|
110
|
+
{ :name => "Sally", :age => 21 },
|
111
|
+
{ :name => "John", :age => 28 },
|
112
|
+
{ :name => "Stephen", :age => 37 },
|
113
|
+
{ :name => "Emily", :age => 32 },
|
114
|
+
{ :name => "Joe", :age => 18 }
|
115
|
+
]
|
116
|
+
list = Goldmine::ArrayMiner.new(list)
|
117
|
+
mined = list.pivot("Name has an 'e'") do |record|
|
118
|
+
!!record[:name].match(/e/i)
|
119
|
+
end
|
120
|
+
mined = mined.pivot(">= 21 years old") do |record|
|
121
|
+
record[:age] >= 21
|
122
|
+
end
|
123
|
+
# result:
|
252
124
|
{
|
253
|
-
{ "
|
254
|
-
{ :name => "
|
255
|
-
{ :name => "
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
{ :name => "
|
260
|
-
|
261
|
-
{ "
|
262
|
-
{ :name => "
|
263
|
-
|
264
|
-
{ "airline" => "United", "population < 750k" => true } => [
|
265
|
-
{ :name => "Mountain View", ... },
|
266
|
-
{ :name => "Atlanta", ... }],
|
267
|
-
{ "airline" => "SouthWest", "population < 750k" => false } => [
|
268
|
-
{ :name => "San Francisco", ... },
|
269
|
-
{ :name => "Dallas", ... }],
|
270
|
-
{ "airline" => "SouthWest", "population < 750k" => true } => [
|
271
|
-
{ :name => "Mountain View", ... },
|
272
|
-
{ :name => "Atlanta", ... }],
|
273
|
-
{ "airline" => "SkyWest", "population < 750k" => true } => [
|
274
|
-
{ :name => "Mountain View", ... }],
|
275
|
-
{ "airline" => "JetBlue", "population < 750k" => false } => [
|
276
|
-
{ :name => "Manhattan", ... }],
|
277
|
-
{ "airline" => "JetBlue", "population < 750k" => true } => [
|
278
|
-
{ :name => "Boston", ... }],
|
279
|
-
{ "airline" => "American", "population < 750k" => false } => [
|
280
|
-
{ :name => "Brooklyn", ... }],
|
281
|
-
{ "airline" => "American", "population < 750k" => true } => [
|
282
|
-
{ :name => "Boston", ... }],
|
283
|
-
{ "airline" => "US Airways", "population < 750k" => false } => [
|
284
|
-
{ :name => "Brooklyn", ... }],
|
285
|
-
{ "airline" => "Frontier", "population < 750k" => false } => [
|
286
|
-
{ :name => "Dallas", ... }]
|
125
|
+
{ "Name has an 'e'" => false, ">= 21 years old" => true } => [
|
126
|
+
{ :name => "Sally", :age => 21 },
|
127
|
+
{ :name => "John", :age => 28 }
|
128
|
+
],
|
129
|
+
{ "Name has an 'e'" => true, ">= 21 years old" => true } => [
|
130
|
+
{ :name => "Stephen", :age => 37 },
|
131
|
+
{ :name => "Emily", :age => 32 }
|
132
|
+
],
|
133
|
+
{ "Name has an 'e'" => true, ">= 21 years old" => false } => [
|
134
|
+
{ :name => "Joe", :age => 18 }
|
135
|
+
]
|
287
136
|
}
|
288
137
|
```
|
289
138
|
|
290
|
-
Here is the corresponding table view for the above dataset.
|
291
|
-
|
292
|
-
<table class="table table-bordered table-striped">
|
293
|
-
<thead>
|
294
|
-
<tr>
|
295
|
-
<th>airline</th>
|
296
|
-
<th>population < 750k</th>
|
297
|
-
<th>cities</th>
|
298
|
-
</tr>
|
299
|
-
</thead>
|
300
|
-
<tbody>
|
301
|
-
<tr>
|
302
|
-
<td>Delta</td>
|
303
|
-
<td>false</td>
|
304
|
-
<td>4</td>
|
305
|
-
</tr>
|
306
|
-
<tr>
|
307
|
-
<td>Delta</td>
|
308
|
-
<td>true</td>
|
309
|
-
<td>2</td>
|
310
|
-
</tr>
|
311
|
-
<tr>
|
312
|
-
<td>United</td>
|
313
|
-
<td>false</td>
|
314
|
-
<td>2</td>
|
315
|
-
</tr>
|
316
|
-
<tr>
|
317
|
-
<td>United</td>
|
318
|
-
<td>true</td>
|
319
|
-
<td>2</td>
|
320
|
-
</tr>
|
321
|
-
<tr>
|
322
|
-
<td>SouthWest</td>
|
323
|
-
<td>false</td>
|
324
|
-
<td>2</td>
|
325
|
-
</tr>
|
326
|
-
<tr>
|
327
|
-
<td>SouthWest</td>
|
328
|
-
<td>true</td>
|
329
|
-
<td>2</td>
|
330
|
-
</tr>
|
331
|
-
<tr>
|
332
|
-
<td>SkyWest</td>
|
333
|
-
<td>true</td>
|
334
|
-
<td>1</td>
|
335
|
-
</tr>
|
336
|
-
<tr>
|
337
|
-
<td>JetBlue</td>
|
338
|
-
<td>false</td>
|
339
|
-
<td>1</td>
|
340
|
-
</tr>
|
341
|
-
<tr>
|
342
|
-
<td>JetBlue</td>
|
343
|
-
<td>true</td>
|
344
|
-
<td>1</td>
|
345
|
-
</tr>
|
346
|
-
<tr>
|
347
|
-
<td>American</td>
|
348
|
-
<td>false</td>
|
349
|
-
<td>1</td>
|
350
|
-
</tr>
|
351
|
-
<tr>
|
352
|
-
<td>American</td>
|
353
|
-
<td>true</td>
|
354
|
-
<td>1</td>
|
355
|
-
</tr>
|
356
|
-
<tr>
|
357
|
-
<td>US Airways</td>
|
358
|
-
<td>false</td>
|
359
|
-
<td>1</td>
|
360
|
-
</tr>
|
361
|
-
<tr>
|
362
|
-
<td>Frontier</td>
|
363
|
-
<td>false</td>
|
364
|
-
<td>1</td>
|
365
|
-
</tr>
|
366
|
-
</tbody>
|
367
|
-
</table>
|
368
|
-
|
369
|
-
Hopefully you can see the potential even though the above examples are somewhat contrived.
|
370
|
-
|
371
|
-
## Special thanks
|
372
|
-
|
373
|
-
* [One on One Marketing](http://www.1on1.com/) - for sponsoring the development of Goldmine
|
374
|
-
* [Eric Berry](https://github.com/cavneb/) - for constructive feedback
|
375
|
-
* [Spencer Roan](https://github.com/spencerroan) - for constructive feedback
|
376
|
-
* [Brian Johnson](https://github.com/whap/) - for bringing some sanity to the recursion
|
377
|
-
* [Josh Bowles](https://github.com/jbowles/) - for early adoption and feedback
|
378
|
-
* [Brett Beers](https://github.com/beersbr/) - for early adoption and feedback
|
379
|
-
|
data/Rakefile
CHANGED
data/lib/goldmine.rb
CHANGED
@@ -2,5 +2,10 @@ $:.unshift File.join(File.dirname(__FILE__), "goldmine")
|
|
2
2
|
require "array_miner"
|
3
3
|
require "hash_miner"
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
module Goldmine
|
6
|
+
def self.miner(object)
|
7
|
+
return ArrayMiner.new(object) if object.is_a?(Array)
|
8
|
+
return HashMiner.new(object) if object.is_a?(Hash)
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
end
|
data/lib/goldmine/array_miner.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
1
3
|
module Goldmine
|
4
|
+
class ArrayMiner < SimpleDelegator
|
2
5
|
|
3
|
-
|
4
|
-
|
6
|
+
def initialize(array=[])
|
7
|
+
super array
|
8
|
+
end
|
5
9
|
|
6
10
|
# Pivots the Array into a Hash of mined data.
|
7
11
|
# Think of it as creating a pivot table or perhaps an OLAP cube.
|
@@ -42,7 +46,7 @@ module Goldmine
|
|
42
46
|
# @yield [Object] Yields once for each item in the Array
|
43
47
|
# @return [Hash] The pivoted Hash of data.
|
44
48
|
def pivot(name=nil, &block)
|
45
|
-
reduce(
|
49
|
+
reduce(HashMiner.new) do |memo, item|
|
46
50
|
value = yield(item)
|
47
51
|
|
48
52
|
if value.is_a?(Array)
|
data/lib/goldmine/hash_miner.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
require "delegate"
|
2
|
+
require "hash_miner"
|
3
|
+
|
1
4
|
module Goldmine
|
5
|
+
class HashMiner < SimpleDelegator
|
2
6
|
|
3
|
-
|
4
|
-
|
7
|
+
def initialize(hash={})
|
8
|
+
super hash
|
9
|
+
end
|
5
10
|
|
6
11
|
attr_accessor :goldmine
|
7
12
|
|
@@ -28,9 +33,9 @@ module Goldmine
|
|
28
33
|
def pivot(name=nil, &block)
|
29
34
|
return self unless goldmine
|
30
35
|
|
31
|
-
reduce(
|
36
|
+
reduce(HashMiner.new) do |memo, item|
|
32
37
|
key = item.first
|
33
|
-
value = item.last
|
38
|
+
value = Goldmine.miner(item.last)
|
34
39
|
value.pivot(name, &block).each do |k, v|
|
35
40
|
if key.is_a? Hash
|
36
41
|
k = { block.to_s => k } unless k.is_a?(Hash)
|
data/lib/goldmine/version.rb
CHANGED
data/test/test_goldmine.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
|
+
require "micro_test"
|
1
2
|
require "simplecov"
|
3
|
+
SimpleCov.command_name "MicroTest"
|
2
4
|
SimpleCov.start
|
3
|
-
require "
|
4
|
-
require "turn"
|
5
|
-
require File.join(File.dirname(__FILE__), "..", "lib", "goldmine")
|
5
|
+
require File.expand_path("../../lib/goldmine", __FILE__)
|
6
6
|
|
7
|
-
class TestGoldmine <
|
7
|
+
class TestGoldmine < MicroTest::Test
|
8
8
|
|
9
|
-
|
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
|
20
|
+
|
21
|
+
test "simple pivot" do
|
10
22
|
list = [1,2,3,4,5,6,7,8,9]
|
23
|
+
list = Goldmine::ArrayMiner.new(list)
|
11
24
|
data = list.pivot { |i| i < 5 }
|
12
25
|
|
13
26
|
expected = {
|
@@ -15,11 +28,12 @@ class TestGoldmine < MiniTest::Unit::TestCase
|
|
15
28
|
false => [5, 6, 7, 8, 9]
|
16
29
|
}
|
17
30
|
|
18
|
-
|
31
|
+
assert data == expected
|
19
32
|
end
|
20
33
|
|
21
|
-
|
34
|
+
test "named pivot" do
|
22
35
|
list = [1,2,3,4,5,6,7,8,9]
|
36
|
+
list = Goldmine::ArrayMiner.new(list)
|
23
37
|
data = list.pivot("less than 5") { |i| i < 5 }
|
24
38
|
|
25
39
|
expected = {
|
@@ -27,16 +41,17 @@ class TestGoldmine < MiniTest::Unit::TestCase
|
|
27
41
|
{ "less than 5" => false } => [5, 6, 7, 8, 9]
|
28
42
|
}
|
29
43
|
|
30
|
-
|
44
|
+
assert data == expected
|
31
45
|
end
|
32
46
|
|
33
|
-
|
47
|
+
test "pivot of list values" do
|
34
48
|
list = [
|
35
49
|
{ :name => "one", :list => [1] },
|
36
50
|
{ :name => "two", :list => [1, 2] },
|
37
51
|
{ :name => "three", :list => [1, 2, 3] },
|
38
52
|
{ :name => "four", :list => [1, 2, 3, 4] },
|
39
53
|
]
|
54
|
+
list = Goldmine::ArrayMiner.new(list)
|
40
55
|
data = list.pivot { |record| record[:list] }
|
41
56
|
|
42
57
|
expected = {
|
@@ -52,11 +67,40 @@ class TestGoldmine < MiniTest::Unit::TestCase
|
|
52
67
|
4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
|
53
68
|
}
|
54
69
|
|
55
|
-
|
70
|
+
assert data == expected
|
56
71
|
end
|
57
72
|
|
58
|
-
|
73
|
+
test "pivot of list values with empty list" do
|
74
|
+
list = [
|
75
|
+
{ :name => "empty", :list => [] },
|
76
|
+
{ :name => "one", :list => [1] },
|
77
|
+
{ :name => "two", :list => [1, 2] },
|
78
|
+
{ :name => "three", :list => [1, 2, 3] },
|
79
|
+
{ :name => "four", :list => [1, 2, 3, 4] },
|
80
|
+
]
|
81
|
+
list = Goldmine::ArrayMiner.new(list)
|
82
|
+
data = list.pivot { |record| record[:list] }
|
83
|
+
|
84
|
+
expected = {
|
85
|
+
nil => [ {:name => "empty", :list => [] } ],
|
86
|
+
1 => [ { :name => "one", :list => [1] },
|
87
|
+
{ :name => "two", :list => [1, 2] },
|
88
|
+
{ :name => "three", :list => [1, 2, 3] },
|
89
|
+
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
90
|
+
2 => [ { :name => "two", :list => [1, 2] },
|
91
|
+
{ :name => "three", :list => [1, 2, 3] },
|
92
|
+
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
93
|
+
3 => [ { :name => "three", :list => [1, 2, 3] },
|
94
|
+
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
95
|
+
4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
|
96
|
+
}
|
97
|
+
|
98
|
+
assert data == expected
|
99
|
+
end
|
100
|
+
|
101
|
+
test "chained pivots" do
|
59
102
|
list = [1,2,3,4,5,6,7,8,9]
|
103
|
+
list = Goldmine::ArrayMiner.new(list)
|
60
104
|
data = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
|
61
105
|
|
62
106
|
expected = {
|
@@ -66,12 +110,12 @@ class TestGoldmine < MiniTest::Unit::TestCase
|
|
66
110
|
[false, true] => [6, 8]
|
67
111
|
}
|
68
112
|
|
69
|
-
|
113
|
+
assert data == expected
|
70
114
|
end
|
71
115
|
|
72
|
-
|
116
|
+
test "deep chained pivots" do
|
73
117
|
list = [1,2,3,4,5,6,7,8,9]
|
74
|
-
|
118
|
+
list = Goldmine::ArrayMiner.new(list)
|
75
119
|
data = list
|
76
120
|
.pivot { |i| i < 3 }
|
77
121
|
.pivot { |i| i < 6 }
|
@@ -91,11 +135,12 @@ class TestGoldmine < MiniTest::Unit::TestCase
|
|
91
135
|
[false, false, false, false, true] => [9]
|
92
136
|
}
|
93
137
|
|
94
|
-
|
138
|
+
assert data == expected
|
95
139
|
end
|
96
140
|
|
97
|
-
|
141
|
+
test "named deep chained pivots" do
|
98
142
|
list = [1,2,3,4,5,6,7,8,9]
|
143
|
+
list = Goldmine::ArrayMiner.new(list)
|
99
144
|
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 }
|
100
145
|
|
101
146
|
expected = {
|
@@ -110,11 +155,12 @@ class TestGoldmine < MiniTest::Unit::TestCase
|
|
110
155
|
{"a"=>false, "b"=>false, "c"=>false, "d"=>false, "e"=>true} => [9]
|
111
156
|
}
|
112
157
|
|
113
|
-
|
158
|
+
assert data == expected
|
114
159
|
end
|
115
160
|
|
116
|
-
|
161
|
+
test "named chained pivots" do
|
117
162
|
list = [1,2,3,4,5,6,7,8,9]
|
163
|
+
list = Goldmine::ArrayMiner.new(list)
|
118
164
|
data = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }
|
119
165
|
|
120
166
|
expected = {
|
@@ -124,9 +170,7 @@ class TestGoldmine < MiniTest::Unit::TestCase
|
|
124
170
|
{ "less than 5" => false, "divisible by 2" => true} => [6, 8]
|
125
171
|
}
|
126
172
|
|
127
|
-
|
173
|
+
assert data == expected
|
128
174
|
end
|
129
175
|
|
130
|
-
|
131
|
-
|
132
176
|
end
|
metadata
CHANGED
@@ -1,97 +1,72 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goldmine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Nathan Hopkins
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-10-16 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rake
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
28
|
+
name: micro_test
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: simplecov
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - '>='
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: turn
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ! '>='
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
type: :development
|
71
|
-
prerelease: false
|
72
|
-
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ! '>='
|
52
|
+
- - '>='
|
76
53
|
- !ruby/object:Gem::Version
|
77
54
|
version: '0'
|
78
55
|
- !ruby/object:Gem::Dependency
|
79
56
|
name: pry
|
80
57
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
58
|
requirements:
|
83
|
-
- -
|
59
|
+
- - '>='
|
84
60
|
- !ruby/object:Gem::Version
|
85
61
|
version: '0'
|
86
62
|
type: :development
|
87
63
|
prerelease: false
|
88
64
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
65
|
requirements:
|
91
|
-
- -
|
66
|
+
- - '>='
|
92
67
|
- !ruby/object:Gem::Version
|
93
68
|
version: '0'
|
94
|
-
description:
|
69
|
+
description: Extract a wealth of information from Arrays & Hashes
|
95
70
|
email:
|
96
71
|
- natehop@gmail.com
|
97
72
|
executables: []
|
@@ -110,28 +85,27 @@ files:
|
|
110
85
|
homepage: https://github.com/hopsoft/goldmine
|
111
86
|
licenses:
|
112
87
|
- MIT
|
88
|
+
metadata: {}
|
113
89
|
post_install_message:
|
114
90
|
rdoc_options: []
|
115
91
|
require_paths:
|
116
92
|
- lib
|
117
93
|
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
-
none: false
|
119
94
|
requirements:
|
120
|
-
- -
|
95
|
+
- - '>='
|
121
96
|
- !ruby/object:Gem::Version
|
122
97
|
version: '0'
|
123
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
-
none: false
|
125
99
|
requirements:
|
126
|
-
- -
|
100
|
+
- - '>='
|
127
101
|
- !ruby/object:Gem::Version
|
128
102
|
version: '0'
|
129
103
|
requirements: []
|
130
104
|
rubyforge_project:
|
131
|
-
rubygems_version:
|
105
|
+
rubygems_version: 2.0.3
|
132
106
|
signing_key:
|
133
|
-
specification_version:
|
134
|
-
summary:
|
107
|
+
specification_version: 4
|
108
|
+
summary: Extract a wealth of information from Arrays & Hashes
|
135
109
|
test_files:
|
136
110
|
- test/test_goldmine.rb
|
137
111
|
has_rdoc:
|