goldmine 0.9.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Dependency Status](https://gemnasium.com/hopsoft/goldmine.png)](https://gemnasium.com/hopsoft/goldmine)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/hopsoft/goldmine.png)](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:
|