goldmine 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +336 -129
- data/lib/goldmine.rb +8 -9
- metadata +2 -2
data/README.md
CHANGED
@@ -1,180 +1,387 @@
|
|
1
1
|
# Goldmine
|
2
2
|
|
3
|
-
##
|
4
|
-
### Turn any list into a treasure trove.
|
3
|
+
## Pivot tables for the Rubyist
|
5
4
|
|
6
|
-
|
5
|
+
### Pivot any list into a wealth of information.
|
7
6
|
|
8
|
-
|
7
|
+
Goldmine allows you to apply pivot table logic to any list for powerful data mining capabilities.
|
9
8
|
|
10
|
-
|
9
|
+
### Reasons to love it
|
11
10
|
|
12
11
|
* Provides ETL like functionality... but simple and elegant
|
12
|
+
* Easily build OLAP cubes using Ruby
|
13
13
|
* Supports method chaining for deep data mining
|
14
14
|
* Handles values that are lists themselves
|
15
|
-
* Allows you to name your pivots
|
16
15
|
|
17
|
-
|
16
|
+
[Why use it?](#putting-it-all-together)
|
17
|
+
|
18
|
+
## Quick start
|
19
|
+
|
20
|
+
Install
|
21
|
+
|
22
|
+
```
|
23
|
+
$gem install goldmine
|
24
|
+
```
|
25
|
+
|
26
|
+
Use
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
[1,2,3,4,5,6,7,8,9].pivot { |i| i < 5 }
|
30
|
+
```
|
31
|
+
|
32
|
+
### Usage examples
|
33
|
+
|
34
|
+
* [Pivot a list](#pivot-a-list-of-numbers-based-on-whether-or-not-they-are-less-than-5)
|
35
|
+
* [Create a named pivot](#explicitly-name-a-pivot)
|
36
|
+
* [Pivot values that are lists themselves](#pivot-values-that-are-lists-themselves)
|
37
|
+
* [Chain pivots](#chain-pivots-together)
|
38
|
+
* [Chain pivots conditionally](#conditionally-chain-pivots-together)
|
39
|
+
* [Dig deep and extract meaningful data](#deep-cuts)
|
18
40
|
|
19
41
|
## The Basics
|
20
42
|
|
21
|
-
|
43
|
+
### Pivot a list of numbers based on whether or not they are less than 5
|
22
44
|
|
23
45
|
```ruby
|
46
|
+
# operation
|
24
47
|
list = [1,2,3,4,5,6,7,8,9]
|
25
|
-
data = list.
|
48
|
+
data = list.pivot { |i| i < 5 }
|
26
49
|
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
50
|
+
# resulting data
|
51
|
+
{
|
52
|
+
true => [1, 2, 3, 4],
|
53
|
+
false => [5, 6, 7, 8, 9]
|
54
|
+
}
|
31
55
|
```
|
32
56
|
|
33
|
-
|
57
|
+
### Explicitly name a pivot
|
34
58
|
|
35
59
|
```ruby
|
60
|
+
# operation
|
36
61
|
list = [1,2,3,4,5,6,7,8,9]
|
37
|
-
data = list.
|
62
|
+
data = list.pivot("less than 5") { |i| i < 5 }
|
38
63
|
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
64
|
+
# resulting data
|
65
|
+
{
|
66
|
+
{ "less than 5" => true } => [1, 2, 3, 4],
|
67
|
+
{ "less than 5" => false } => [5, 6, 7, 8, 9]
|
68
|
+
}
|
43
69
|
```
|
44
70
|
|
45
71
|
## Next Steps
|
46
72
|
|
47
|
-
|
73
|
+
### Pivot values that are lists themselves
|
48
74
|
|
49
75
|
```ruby
|
76
|
+
# operation
|
77
|
+
list = [
|
78
|
+
{ :name => "one", :list => [1] },
|
79
|
+
{ :name => "two", :list => [1, 2] },
|
80
|
+
{ :name => "three", :list => [1, 2, 3] },
|
81
|
+
{ :name => "four", :list => [1, 2, 3, 4] },
|
82
|
+
]
|
83
|
+
data = list.pivot { |record| record[:list] }
|
84
|
+
|
85
|
+
# resulting data
|
86
|
+
{
|
87
|
+
1 => [ { :name => "one", :list => [1] },
|
88
|
+
{ :name => "two", :list => [1, 2] },
|
89
|
+
{ :name => "three", :list => [1, 2, 3] },
|
90
|
+
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
91
|
+
2 => [ { :name => "two", :list => [1, 2] },
|
92
|
+
{ :name => "three", :list => [1, 2, 3] },
|
93
|
+
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
94
|
+
3 => [ { :name => "three", :list => [1, 2, 3] },
|
95
|
+
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
96
|
+
4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
|
97
|
+
}
|
98
|
+
```
|
99
|
+
|
100
|
+
### Chain pivots together
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# operation
|
50
104
|
list = [1,2,3,4,5,6,7,8,9]
|
51
|
-
data = list.
|
52
|
-
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
105
|
+
data = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }
|
106
|
+
|
107
|
+
# resulting data
|
108
|
+
{
|
109
|
+
[true, false] => [1, 3],
|
110
|
+
[true, true] => [2, 4],
|
111
|
+
[false, false] => [5, 7, 9],
|
112
|
+
[false, true] => [6, 8]
|
113
|
+
}
|
59
114
|
```
|
60
115
|
|
61
|
-
|
116
|
+
### Conditionally chain pivots together
|
62
117
|
|
63
118
|
```ruby
|
119
|
+
# operation
|
120
|
+
params = { :divisible_by_two => false, :next_greater_than_five => true }
|
64
121
|
list = [1,2,3,4,5,6,7,8,9]
|
65
|
-
data = list.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
122
|
+
data = list.pivot("less than 5") { |i| i < 5 }
|
123
|
+
data = data.pivot("divisible by 2") { |i| i % 2 == 0 } if params[:divisible_by_two]
|
124
|
+
data = data.pivot("next greater than 5") { |i| i.next > 5 } if params[:next_greater_than_five]
|
125
|
+
|
126
|
+
# resulting data
|
127
|
+
{
|
128
|
+
{ "less than 5" => true, "next greater than 5" => false } => [1, 2, 3, 4],
|
129
|
+
{ "less than 5" => false, "next greater than 5" => true } => [5, 6, 7, 8, 9]
|
130
|
+
}
|
73
131
|
```
|
74
132
|
|
75
133
|
## Deep Cuts
|
76
134
|
|
77
|
-
|
135
|
+
### Build a moderately complex dataset of Cities
|
78
136
|
|
79
137
|
```ruby
|
80
|
-
|
81
|
-
{ :name => "
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
{
|
138
|
+
cities = [
|
139
|
+
{ :name => "San Francisco",
|
140
|
+
:state => "CA",
|
141
|
+
:population => 805235,
|
142
|
+
:airlines => [ "Delta", "United", "SouthWest" ]
|
143
|
+
},
|
144
|
+
{
|
145
|
+
:name => "Mountain View",
|
146
|
+
:state => "CA",
|
147
|
+
:population => 74066,
|
148
|
+
:airlines => [ "SkyWest", "United", "SouthWest" ]
|
149
|
+
},
|
150
|
+
{
|
151
|
+
:name => "Manhattan",
|
152
|
+
:state => "NY",
|
153
|
+
:population => 1586698,
|
154
|
+
:airlines => [ "Delta", "JetBlue", "United" ]
|
155
|
+
},
|
156
|
+
{
|
157
|
+
:name => "Brooklyn",
|
158
|
+
:state => "NY",
|
159
|
+
:population => 2504700,
|
160
|
+
:airlines => [ "Delta", "American", "US Airways" ]
|
161
|
+
},
|
162
|
+
{
|
163
|
+
:name => "Boston",
|
164
|
+
:state => "MA",
|
165
|
+
:population => 617594,
|
166
|
+
:airlines => [ "Delta", "JetBlue", "American" ]
|
167
|
+
},
|
168
|
+
{
|
169
|
+
:name => "Atlanta",
|
170
|
+
:state => "GA",
|
171
|
+
:population => 420003,
|
172
|
+
:airlines => [ "Delta", "United", "SouthWest" ]
|
173
|
+
},
|
174
|
+
{
|
175
|
+
:name => "Dallas",
|
176
|
+
:state => "TX",
|
177
|
+
:population => 1197816,
|
178
|
+
:airlines => [ "Delta", "SouthWest", "Frontier" ]
|
179
|
+
}
|
87
180
|
]
|
88
|
-
data = list.dig { |record| record[:projects] }
|
89
|
-
|
90
|
-
# {
|
91
|
-
# :a => [ { :name => "Nathan", :projects => [:a, :b] },
|
92
|
-
# { :name => "Eric", :projects => [:a, :d, :g] },
|
93
|
-
# { :name => "Josh", :projects => [:a, :c] } ],
|
94
|
-
# :b => [ { :name => "Nathan", :projects => [:a, :b] },
|
95
|
-
# { :name => "Brian", :projects => [:b, :c, :e, :f] },
|
96
|
-
# { :name => "Matthew", :projects => [:b, :c, :d] } ],
|
97
|
-
# :d => [ { :name => "Eric", :projects => [:a, :d, :g] },
|
98
|
-
# { :name => "Matthew", :projects => [:b, :c, :d] } ],
|
99
|
-
# :g => [ { :name => "Eric", :projects => [:a, :d, :g] },
|
100
|
-
# { :name => "Mark", :projects => [:g] } ],
|
101
|
-
# :c => [ { :name => "Brian", :projects => [:b, :c, :e, :f] },
|
102
|
-
# { :name => "Josh", :projects => [:a, :c] },
|
103
|
-
# { :name => "Matthew", :projects => [:b, :c, :d] } ],
|
104
|
-
# :e => [ { :name => "Brian", :projects => [:b, :c, :e, :f] } ],
|
105
|
-
# :f => [ { :name => "Brian", :projects => [:b, :c, :e, :f] } ]
|
106
|
-
# }
|
107
|
-
|
108
181
|
```
|
109
182
|
|
110
|
-
|
183
|
+
### Pivot cities by state for population over 750k
|
111
184
|
|
112
185
|
```ruby
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
]
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
# ["lang: ruby", "project count: 3"] => [ { :name => "Eric", ... }, { :name => "Matthew", ... } ],
|
128
|
-
# ["lang: ruby", "project count: 4"] => [ { :name => "Brian", ... } ],
|
129
|
-
# ["lang: ruby", "project count: 1"] => [ { :name => "Mark", ... } ],
|
130
|
-
# ["lang: javascript", "project count: 2"] => [ { :name => "Nathan", ... } ],
|
131
|
-
# ["lang: javascript", "project count: 3"] => [ { :name => "Eric", ... } ],
|
132
|
-
# ["lang: javascript", "project count: 4"] => [ { :name => "Brian", ... } ],
|
133
|
-
# ["lang: groovy", "project count: 3"] => [ { :name => "Eric", ... } ],
|
134
|
-
# ["lang: c", "project count: 4"] => [ { :name => "Brian", ... } ],
|
135
|
-
# ["lang: c", "project count: 3"] => [ { :name => "Matthew", ... } ],
|
136
|
-
# ["lang: go", "project count: 4"] => [ { :name => "Brian", ... } ],
|
137
|
-
# ["lang: java", "project count: 1"] => [ { :name => "Mark", ... } ],
|
138
|
-
# ["lang: scala", "project count: 1"] => [ { :name => "Mark", ... } ],
|
139
|
-
# ["lang: lisp", "project count: 2"] => [ { :name => "Josh", ... } ],
|
140
|
-
# ["lang: clojure", "project count: 2"] => [ { :name => "Josh", ... } ],
|
141
|
-
# ["lang: clojure", "project count: 3"] => [ { :name => "Matthew", ... } ]
|
142
|
-
# }
|
186
|
+
# operation
|
187
|
+
data = cities
|
188
|
+
.pivot("state") { |city| city[:state] }
|
189
|
+
.pivot("population >= 750k") { |city| city[:population] >= 750000 }
|
190
|
+
|
191
|
+
# resulting data
|
192
|
+
{
|
193
|
+
{ "state" => "CA", "population >= 750k" => true } => [ { :name => "San Francisco", ... } ],
|
194
|
+
{ "state" => "CA", "population >= 750k" => false } => [ { :name => "Mountain View", ... } ],
|
195
|
+
{ "state" => "NY", "population >= 750k" => true } => [ { :name => "Manhattan", ... }, { :name => "Brooklyn", ... } ],
|
196
|
+
{ "state" => "MA", "population >= 750k" => false } => [ { :name => "Boston", ... } ],
|
197
|
+
{ "state" => "GA", "population >= 750k" => false } => [ { :name => "Atlanta", ... } ],
|
198
|
+
{ "state" => "TX", "population >= 750k" => true } => [ { :name => "Dallas", ... } ]
|
199
|
+
}
|
143
200
|
```
|
144
201
|
|
145
|
-
|
146
|
-
|
147
|
-
|
202
|
+
### Putting it all together
|
203
|
+
|
204
|
+
**The end goal of all this is to support the creation of aggregate reports.**
|
205
|
+
|
206
|
+
*You can think of these reports as individual data cubes.*
|
207
|
+
|
208
|
+
Here is a table view of the pivoted city data from above.
|
209
|
+
|
210
|
+
<table>
|
211
|
+
<thead>
|
212
|
+
<tr>
|
213
|
+
<th>state</th>
|
214
|
+
<th>population >= 750k</th>
|
215
|
+
<th>cities</th>
|
216
|
+
</tr>
|
217
|
+
</thead>
|
218
|
+
<tbody>
|
219
|
+
<tr>
|
220
|
+
<td>CA</td>
|
221
|
+
<td>true</td>
|
222
|
+
<td>1</td>
|
223
|
+
</tr>
|
224
|
+
<tr>
|
225
|
+
<td>CA</td>
|
226
|
+
<td>false</td>
|
227
|
+
<td>1</td>
|
228
|
+
</tr>
|
229
|
+
<tr>
|
230
|
+
<td>NY</td>
|
231
|
+
<td>true</td>
|
232
|
+
<td>2</td>
|
233
|
+
</tr>
|
234
|
+
<tr>
|
235
|
+
<td>MA</td>
|
236
|
+
<td>false</td>
|
237
|
+
<td>1</td>
|
238
|
+
</tr>
|
239
|
+
<tr>
|
240
|
+
<td>GA</td>
|
241
|
+
<td>false</td>
|
242
|
+
<td>1</td>
|
243
|
+
</tr>
|
244
|
+
<tr>
|
245
|
+
<td>TX</td>
|
246
|
+
<td>true</td>
|
247
|
+
<td>1</td>
|
248
|
+
</tr>
|
249
|
+
</tbody>
|
250
|
+
</table>
|
251
|
+
|
252
|
+
Lets try another one.
|
253
|
+
|
254
|
+
### Determine which airlines service cities with fewer than 750k people
|
148
255
|
|
149
256
|
```ruby
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
257
|
+
# operation
|
258
|
+
data = cities
|
259
|
+
.pivot("airline") { |city| city[:airlines] }
|
260
|
+
.pivot("population < 750k") { |city| city[:population] < 750000 }
|
261
|
+
|
262
|
+
# resulting data
|
263
|
+
{
|
264
|
+
{ "airline" => "Delta", "population < 750k" => false } => [
|
265
|
+
{ :name => "San Francisco", ... },
|
266
|
+
{ :name => "Manhattan", ... },
|
267
|
+
{ :name => "Brooklyn", ... },
|
268
|
+
{ :name => "Dallas", ... }],
|
269
|
+
{ "airline" => "Delta", "population < 750k" => true } => [
|
270
|
+
{ :name => "Boston", ... },
|
271
|
+
{ :name => "Atlanta", ... }],
|
272
|
+
{ "airline" => "United", "population < 750k" => false } => [
|
273
|
+
{ :name => "San Francisco", ... },
|
274
|
+
{ :name => "Manhattan", ... }],
|
275
|
+
{ "airline" => "United", "population < 750k" => true } => [
|
276
|
+
{ :name => "Mountain View", ... },
|
277
|
+
{ :name => "Atlanta", ... }],
|
278
|
+
{ "airline" => "SouthWest", "population < 750k" => false } => [
|
279
|
+
{ :name => "San Francisco", ... },
|
280
|
+
{ :name => "Dallas", ... }],
|
281
|
+
{ "airline" => "SouthWest", "population < 750k" => true } => [
|
282
|
+
{ :name => "Mountain View", ... },
|
283
|
+
{ :name => "Atlanta", ... }],
|
284
|
+
{ "airline" => "SkyWest", "population < 750k" => true } => [
|
285
|
+
{ :name => "Mountain View", ... }],
|
286
|
+
{ "airline" => "JetBlue", "population < 750k" => false } => [
|
287
|
+
{ :name => "Manhattan", ... }],
|
288
|
+
{ "airline" => "JetBlue", "population < 750k" => true } => [
|
289
|
+
{ :name => "Boston", ... }],
|
290
|
+
{ "airline" => "American", "population < 750k" => false } => [
|
291
|
+
{ :name => "Brooklyn", ... }],
|
292
|
+
{ "airline" => "American", "population < 750k" => true } => [
|
293
|
+
{ :name => "Boston", ... }],
|
294
|
+
{ "airline" => "US Airways", "population < 750k" => false } => [
|
295
|
+
{ :name => "Brooklyn", ... }],
|
296
|
+
{ "airline" => "Frontier", "population < 750k" => false } => [
|
297
|
+
{ :name => "Dallas", ... }]
|
298
|
+
}
|
180
299
|
```
|
300
|
+
|
301
|
+
Here is the corresponding table view for the above dataset.
|
302
|
+
|
303
|
+
<table>
|
304
|
+
<thead>
|
305
|
+
<tr>
|
306
|
+
<th>airline</th>
|
307
|
+
<th>population < 750k</th>
|
308
|
+
<th>cities</th>
|
309
|
+
</tr>
|
310
|
+
</thead>
|
311
|
+
<tbody>
|
312
|
+
<tr>
|
313
|
+
<td>Delta</td>
|
314
|
+
<td>false</td>
|
315
|
+
<td>4</td>
|
316
|
+
</tr>
|
317
|
+
<tr>
|
318
|
+
<td>Delta</td>
|
319
|
+
<td>true</td>
|
320
|
+
<td>2</td>
|
321
|
+
</tr>
|
322
|
+
<tr>
|
323
|
+
<td>United</td>
|
324
|
+
<td>false</td>
|
325
|
+
<td>2</td>
|
326
|
+
</tr>
|
327
|
+
<tr>
|
328
|
+
<td>United</td>
|
329
|
+
<td>true</td>
|
330
|
+
<td>2</td>
|
331
|
+
</tr>
|
332
|
+
<tr>
|
333
|
+
<td>SouthWest</td>
|
334
|
+
<td>false</td>
|
335
|
+
<td>2</td>
|
336
|
+
</tr>
|
337
|
+
<tr>
|
338
|
+
<td>SouthWest</td>
|
339
|
+
<td>true</td>
|
340
|
+
<td>2</td>
|
341
|
+
</tr>
|
342
|
+
<tr>
|
343
|
+
<td>SkyWest</td>
|
344
|
+
<td>true</td>
|
345
|
+
<td>1</td>
|
346
|
+
</tr>
|
347
|
+
<tr>
|
348
|
+
<td>JetBlue</td>
|
349
|
+
<td>false</td>
|
350
|
+
<td>1</td>
|
351
|
+
</tr>
|
352
|
+
<tr>
|
353
|
+
<td>JetBlue</td>
|
354
|
+
<td>true</td>
|
355
|
+
<td>1</td>
|
356
|
+
</tr>
|
357
|
+
<tr>
|
358
|
+
<td>American</td>
|
359
|
+
<td>false</td>
|
360
|
+
<td>1</td>
|
361
|
+
</tr>
|
362
|
+
<tr>
|
363
|
+
<td>American</td>
|
364
|
+
<td>true</td>
|
365
|
+
<td>1</td>
|
366
|
+
</tr>
|
367
|
+
<tr>
|
368
|
+
<td>US Airways</td>
|
369
|
+
<td>false</td>
|
370
|
+
<td>1</td>
|
371
|
+
</tr>
|
372
|
+
<tr>
|
373
|
+
<td>Frontier</td>
|
374
|
+
<td>false</td>
|
375
|
+
<td>1</td>
|
376
|
+
</tr>
|
377
|
+
</tbody>
|
378
|
+
</table>
|
379
|
+
|
380
|
+
Hopefully you can see the potential even though the above examples are somewhat contrived.
|
381
|
+
|
382
|
+
## Special thanks
|
383
|
+
|
384
|
+
* One on One Marketing - for sponsoring the development of Goldmine
|
385
|
+
* Eric Berry - for constructive feedback
|
386
|
+
* Josh Bowles - for early adoption and feedback
|
387
|
+
* Brett Beers - for early adoption and feedback
|
data/lib/goldmine.rb
CHANGED
@@ -2,7 +2,7 @@ require "rubygems"
|
|
2
2
|
|
3
3
|
module Goldmine
|
4
4
|
module ArrayMiner
|
5
|
-
def
|
5
|
+
def pivot(name=nil, &block)
|
6
6
|
reduce({}) do |memo, item|
|
7
7
|
value = yield(item)
|
8
8
|
|
@@ -24,19 +24,18 @@ module Goldmine
|
|
24
24
|
|
25
25
|
module HashMiner
|
26
26
|
attr_accessor :goldmine
|
27
|
-
def
|
27
|
+
def pivot(name=nil, &block)
|
28
28
|
return self unless goldmine
|
29
29
|
reduce({}) do |memo, item|
|
30
30
|
key = item.first
|
31
31
|
value = item.last
|
32
|
-
value.
|
33
|
-
|
34
|
-
|
35
|
-
new_key =
|
32
|
+
value.pivot(name, &block).each do |k, v|
|
33
|
+
if key.is_a? Hash
|
34
|
+
k = { block.to_s => k } unless k.is_a?(Hash)
|
35
|
+
new_key = key.merge(k)
|
36
36
|
else
|
37
|
-
new_key
|
37
|
+
new_key = [key, k]
|
38
38
|
end
|
39
|
-
new_key << k
|
40
39
|
memo[new_key] = v
|
41
40
|
end
|
42
41
|
memo.goldmine = true
|
@@ -51,7 +50,7 @@ module Goldmine
|
|
51
50
|
end
|
52
51
|
|
53
52
|
def goldmine_key(name, key)
|
54
|
-
mine_key =
|
53
|
+
mine_key = { name => key } if name
|
55
54
|
mine_key ||= key
|
56
55
|
end
|
57
56
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goldmine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -50,5 +50,5 @@ rubyforge_project:
|
|
50
50
|
rubygems_version: 1.8.10
|
51
51
|
signing_key:
|
52
52
|
specification_version: 3
|
53
|
-
summary:
|
53
|
+
summary: Pivot tables for the Rubyist
|
54
54
|
test_files: []
|