tablestakes 0.9.4 → 0.10.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 +4 -4
- data/README.md +2 -1
- data/lib/tablestakes.rb +43 -32
- data/spec/spec_helper.rb +2 -2
- data/spec/table_spec.rb +102 -3
- metadata +15 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bd18629eb73a93ba19287533d8b493f524e0bd7
|
4
|
+
data.tar.gz: 0e18ca987924cc39fd4472a2b82f4c45b2b2c2d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbde53ed596e58e5e95d753b71a0ccb53214d362b152803703a72e154fe462c34d540b5a5f18be099357fa674593d9770dc18dd40e695b6a3d4c2cf5e7b05e17
|
7
|
+
data.tar.gz: 433d41010b0d2661d133cfdff0bf1b681f3927a43ef9b7dc0af0cc8d30e9ce6eb5ff724ebe643bfee9e629d0e14d63088f54cc09194c35fc0bd41cd298ba3950
|
data/README.md
CHANGED
@@ -165,9 +165,10 @@ the `Table#sub` method provides a way to eliminate common garbage from
|
|
165
165
|
your data such as stray characters.
|
166
166
|
|
167
167
|
```ruby
|
168
|
-
cities.sub("2012 land area",
|
168
|
+
cities.sub("2012 land area", /(.*) sq mi/, '\1') # deletes ' sq mi' from the end of the 2012 land area field
|
169
169
|
```
|
170
170
|
|
171
|
+
|
171
172
|
`Table#sub` takes a regular expression and a substitute string, which
|
172
173
|
gives some flexibility in how data is updated. Note that this is
|
173
174
|
a method which modifies the table object.
|
data/lib/tablestakes.rb
CHANGED
@@ -16,7 +16,6 @@
|
|
16
16
|
# serving as the header names.
|
17
17
|
|
18
18
|
class Table
|
19
|
-
include Enumerable
|
20
19
|
|
21
20
|
# The headers attribute contains the table headers used to reference
|
22
21
|
# columns in the +Table+. All headers are represented as +String+ types.
|
@@ -54,6 +53,7 @@ class Table
|
|
54
53
|
# a string, then read_file
|
55
54
|
read_file(input)
|
56
55
|
elsif input.respond_to?(:headers)
|
56
|
+
@headers = input.headers.dup
|
57
57
|
input.each {|row| add_row(row) }
|
58
58
|
end
|
59
59
|
# else create empty +Table+
|
@@ -63,13 +63,23 @@ class Table
|
|
63
63
|
# for its calling block.
|
64
64
|
#
|
65
65
|
def each
|
66
|
-
|
67
|
-
|
68
|
-
@headers.
|
69
|
-
nextrow
|
66
|
+
|
67
|
+
if block_given?
|
68
|
+
@table[@headers.first].each_index do |index|
|
69
|
+
nextrow = []
|
70
|
+
@headers.each do |col|
|
71
|
+
begin
|
72
|
+
nextrow << @table[col][index].clone
|
73
|
+
rescue
|
74
|
+
nextrow << @table[col][index]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
yield nextrow
|
70
78
|
end
|
71
|
-
|
79
|
+
else
|
80
|
+
self.to_enum(:each)
|
72
81
|
end
|
82
|
+
|
73
83
|
end
|
74
84
|
|
75
85
|
# Return a copy of a column from the table, identified by column name.
|
@@ -78,12 +88,7 @@ class Table
|
|
78
88
|
# ==== Attributes
|
79
89
|
# +colname+:: +String+ to identify the name of the column
|
80
90
|
def column(colname)
|
81
|
-
|
82
|
-
unless @table.has_key?(colname)
|
83
|
-
Array.new()
|
84
|
-
else
|
85
|
-
Array(@table[colname])
|
86
|
-
end
|
91
|
+
Array(get_col(colname))
|
87
92
|
end
|
88
93
|
|
89
94
|
# Return a copy of a row from the table as an +Array+, given an index
|
@@ -116,8 +121,6 @@ class Table
|
|
116
121
|
args.flatten!
|
117
122
|
colname = args.shift
|
118
123
|
column_vals = args
|
119
|
-
else
|
120
|
-
raise ArgumentError, "Invalid Arguments to add_column"
|
121
124
|
end
|
122
125
|
# check arguments
|
123
126
|
raise ArgumentError, "Duplicate Column Name!" if @table.has_key?(colname)
|
@@ -186,8 +189,6 @@ class Table
|
|
186
189
|
def add_row(*row)
|
187
190
|
if row.kind_of? Array
|
188
191
|
row = row.flatten
|
189
|
-
else
|
190
|
-
raise ArgumentError, "Invalid Arguments to add_row"
|
191
192
|
end
|
192
193
|
if @headers.empty?
|
193
194
|
@headers = row
|
@@ -245,6 +246,10 @@ class Table
|
|
245
246
|
# +orig_name+:: +String+ current header name
|
246
247
|
# +new_name+:: +String+ indicating new header name
|
247
248
|
def rename_header(orig_name, new_name)
|
249
|
+
raise ArgumentError, "Original Column name type invalid" unless orig_name.kind_of? String
|
250
|
+
raise ArgumentError, "New Column name type invalid" unless new_name.kind_of? String
|
251
|
+
raise ArgumentError, "Column Name does not exist!" unless @headers.include? orig_name
|
252
|
+
|
248
253
|
update_header(orig_name, new_name)
|
249
254
|
return self
|
250
255
|
end
|
@@ -467,7 +472,6 @@ class Table
|
|
467
472
|
raise ArgumentError, "Invalid table!" unless table2.is_a?(Table)
|
468
473
|
raise ArgumentError, "Invalid column name" unless @table.has_key?(colname)
|
469
474
|
raise ArgumentError, "Invalid column name" unless table2.headers.include?(col2name)
|
470
|
-
t2_col_index = table2.headers.index(col2name)
|
471
475
|
|
472
476
|
dedupe_headers(table2, colname)
|
473
477
|
|
@@ -495,27 +499,28 @@ class Table
|
|
495
499
|
# in the given column. Raises ArgumentError if the column is not found.
|
496
500
|
#
|
497
501
|
# ==== Attributes
|
498
|
-
# +colname+:: +String+ to identify the column to
|
499
|
-
# +
|
500
|
-
# +replace+:: OPTIONAL +String+ or +Hash+ to specify the replacement text for the given
|
502
|
+
# +colname+:: +String+ to identify the column to substitute on
|
503
|
+
# +match+:: OPTIONAL +String+ or +Regexp+ to match the value in the selected column
|
504
|
+
# +replace+:: OPTIONAL +String+ or +Hash+ to specify the replacement text for the given match value
|
501
505
|
# +&block+:: OPTIONAL block to execute against matching values
|
502
506
|
#
|
503
507
|
# ==== Examples
|
504
508
|
# cities.sub("Population", /(.*?),(.*?)/, '\1\2') # eliminate commas
|
505
509
|
# capitals.sub("State", /NY/, "New York") # replace acronym with full name
|
510
|
+
# capitals.sub("State", /North|South/, {"North" => "South", "South" => "North"}) # Northern states for Southern and vice-versa
|
506
511
|
# capitals.sub("State") { |state| state.downcase } # Lowercase for all values
|
507
512
|
#
|
508
|
-
def sub(colname,
|
513
|
+
def sub(colname, match=nil, replace=nil, &block)
|
509
514
|
# check arguments
|
510
|
-
raise ArgumentError, "No regular expression to match against" unless
|
515
|
+
raise ArgumentError, "No regular expression to match against" unless match || block_given?
|
511
516
|
raise ArgumentError, "Invalid column name" unless @table.has_key?(colname)
|
512
|
-
|
513
|
-
if
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
517
|
+
|
518
|
+
if ! block_given?
|
519
|
+
if ! (String.try_convert(match) || Regexp.try_convert(match))
|
520
|
+
raise ArgumentError, "Match expression must be String or Regexp"
|
521
|
+
elsif ! (replace.respond_to?(:fetch) || replace.respond_to?(:to_str))
|
522
|
+
raise ArgumentError, "Replacement must be String or Hash"
|
523
|
+
end
|
519
524
|
end
|
520
525
|
|
521
526
|
result = Table.new([@headers])
|
@@ -525,7 +530,7 @@ class Table
|
|
525
530
|
if block_given?
|
526
531
|
row[col_index] = block.call row[col_index]
|
527
532
|
else
|
528
|
-
row[col_index] = row[col_index].sub(
|
533
|
+
row[col_index] = row[col_index].sub(match, replace)
|
529
534
|
end
|
530
535
|
result.add_row(row)
|
531
536
|
end
|
@@ -647,7 +652,8 @@ class Table
|
|
647
652
|
|
648
653
|
def get_row(index)
|
649
654
|
result = []
|
650
|
-
if index >= @table[@headers.first].length
|
655
|
+
if index >= @table[@headers.first].length ||
|
656
|
+
index < -(@table[@headers.first].length)
|
651
657
|
return result
|
652
658
|
end
|
653
659
|
@headers.each { |col| result << @table[col][index].to_s }
|
@@ -662,7 +668,12 @@ class Table
|
|
662
668
|
end
|
663
669
|
|
664
670
|
def get_col(colname)
|
665
|
-
Array
|
671
|
+
# return empty Array if column name not found
|
672
|
+
unless @table.has_key?(colname)
|
673
|
+
Array.new()
|
674
|
+
else
|
675
|
+
Array(@table[colname])
|
676
|
+
end
|
666
677
|
end
|
667
678
|
|
668
679
|
def append_col(colname, column_vals)
|
data/spec/spec_helper.rb
CHANGED
@@ -4,10 +4,10 @@
|
|
4
4
|
require 'simplecov'
|
5
5
|
require 'coveralls'
|
6
6
|
|
7
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
8
8
|
SimpleCov::Formatter::HTMLFormatter,
|
9
9
|
Coveralls::SimpleCov::Formatter
|
10
|
-
]
|
10
|
+
])
|
11
11
|
SimpleCov.start
|
12
12
|
|
13
13
|
require 'factory_girl'
|
data/spec/table_spec.rb
CHANGED
@@ -11,6 +11,7 @@ describe "Table" do
|
|
11
11
|
describe ".new" do
|
12
12
|
let(:t) { Table.new('test.tab') }
|
13
13
|
let(:empty) { Table.new() }
|
14
|
+
let(:copy) { Table.new(Table.new('test.tab')) }
|
14
15
|
|
15
16
|
it "reads a file and create a table" do
|
16
17
|
expect(t).to be_a(Table)
|
@@ -18,12 +19,39 @@ describe "Table" do
|
|
18
19
|
it "creates a table if no file was given" do
|
19
20
|
expect(empty).to be_a(Table)
|
20
21
|
end
|
22
|
+
it "creates a table from another table" do
|
23
|
+
expect(copy).to be_a(Table)
|
24
|
+
expect(copy.headers).to eq(t.headers)
|
25
|
+
expect(copy.count).to eq(t.count)
|
26
|
+
end
|
21
27
|
it "errors when the file is not found" do
|
22
28
|
expect{Table.new('sillyfile.txt')}.to raise_error(Errno::ENOENT)
|
23
29
|
end
|
24
30
|
|
25
31
|
end
|
32
|
+
|
33
|
+
describe ".column" do
|
34
|
+
let(:test) { FactoryGirl.build(:table) }
|
35
|
+
|
36
|
+
it "returns a column when given a valid header" do
|
37
|
+
expect(test.column('Name')).to be_a(Array)
|
38
|
+
end
|
39
|
+
it "returns an empty Array when given an invalid header" do
|
40
|
+
expect(test.column('NotName')).to eq(Array.new)
|
41
|
+
end
|
42
|
+
end
|
26
43
|
|
44
|
+
describe ".row" do
|
45
|
+
let(:test) { FactoryGirl.build(:table) }
|
46
|
+
|
47
|
+
it "returns a row when given a valid index" do
|
48
|
+
expect(test.row(2)).to be_a(Array)
|
49
|
+
end
|
50
|
+
it "returns an empty Array when given an invalid index" do
|
51
|
+
expect(test.row(4)).to eq(Array.new)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
27
55
|
describe ".empty?" do
|
28
56
|
let(:test) { Table.new('test.tab') }
|
29
57
|
let(:empty) { Table.new() }
|
@@ -36,6 +64,28 @@ describe "Table" do
|
|
36
64
|
end
|
37
65
|
end
|
38
66
|
|
67
|
+
describe ".each" do
|
68
|
+
let (:test) { FactoryGirl.build(:table) }
|
69
|
+
let (:test_fixnum) {
|
70
|
+
Table.new('test.tab') << ["Phil", "567 Vine", "567-432-1234", 3 ]
|
71
|
+
}
|
72
|
+
|
73
|
+
it "returns an Enumerator if not given a block" do
|
74
|
+
expect(test.each).to be_a(Enumerator)
|
75
|
+
end
|
76
|
+
it "returns a clone of a row" do
|
77
|
+
h = test.each.first
|
78
|
+
expect(h).not_to equal(test.row 0)
|
79
|
+
expect(h).to eq(test.row 0)
|
80
|
+
end
|
81
|
+
it "returns a row that can't be cloned" do
|
82
|
+
a = test_fixnum.each
|
83
|
+
a.next
|
84
|
+
a.next
|
85
|
+
expect(a.next).to eq(test_fixnum.row(2))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
39
89
|
describe ".add_column" do
|
40
90
|
let(:test) { FactoryGirl.build(:table) }
|
41
91
|
let(:newcol) { ["A", "B", "C"] }
|
@@ -55,7 +105,7 @@ describe "Table" do
|
|
55
105
|
expect { test.add_column("Name", newcol) }.to raise_error(ArgumentError)
|
56
106
|
end
|
57
107
|
it "raises an ArgumentError when given a column with the wrong length" do
|
58
|
-
expect { test.add_column("
|
108
|
+
expect { test.add_column("NewName", newcol << "D") }.to raise_error(ArgumentError)
|
59
109
|
end
|
60
110
|
it "adds a column when given an Array" do
|
61
111
|
expect(test.add_column(headercol).headers).to include("TestCol")
|
@@ -96,6 +146,9 @@ describe "Table" do
|
|
96
146
|
it "returns itself when appending an empty table" do
|
97
147
|
expect(test1.append(empty).count).to eq(3)
|
98
148
|
end
|
149
|
+
it "raises an ArgumentError when not given a table" do
|
150
|
+
expect { test1.append('') }.to raise_error(ArgumentError)
|
151
|
+
end
|
99
152
|
it "raises an ArgumentError when given a table with the wrong headers" do
|
100
153
|
expect { test1.append(cities) }.to raise_error(ArgumentError)
|
101
154
|
end
|
@@ -150,6 +203,38 @@ describe "Table" do
|
|
150
203
|
end
|
151
204
|
end
|
152
205
|
|
206
|
+
describe ".rename_header" do
|
207
|
+
let (:test) { FactoryGirl.build(:table) }
|
208
|
+
|
209
|
+
it "raises an ArgumentError when given a column name with invalid type" do
|
210
|
+
expect { test.rename_header(:Name, "FirstName") }.to raise_error(ArgumentError)
|
211
|
+
end
|
212
|
+
it "raises an ArgumentError when given a new name with invalid type" do
|
213
|
+
expect { test.rename_header("Name", :FirstName) }.to raise_error(ArgumentError)
|
214
|
+
end
|
215
|
+
it "raises an ArgumentError when given an invalid column name" do
|
216
|
+
expect { test.rename_header("NName", "FirstName") }.to raise_error(ArgumentError)
|
217
|
+
end
|
218
|
+
it "returns a table with an updated header" do
|
219
|
+
expect(test.rename_header("Name", "FirstName").headers).to include("FirstName")
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
describe ".to_s" do
|
225
|
+
let (:test) { FactoryGirl.build(:table) }
|
226
|
+
|
227
|
+
it "returns a String" do
|
228
|
+
expect(test.to_s).to be_a(String)
|
229
|
+
end
|
230
|
+
it "returns a String with the same number of rows" do
|
231
|
+
expect(test.to_s.split("\n").count).to eq(test.count + 1)
|
232
|
+
end
|
233
|
+
it "returns a String with the same number of columns" do
|
234
|
+
expect(test.to_s.split("\n")[0].split("\t").count).to eq(test.headers.count)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
153
238
|
describe ".count" do
|
154
239
|
let(:t) { FactoryGirl.build(:table) }
|
155
240
|
let(:empty) { Table.new() }
|
@@ -260,12 +345,23 @@ describe "Table" do
|
|
260
345
|
|
261
346
|
describe ".sub" do
|
262
347
|
let (:cities) { Table.new('cities.txt') }
|
348
|
+
let (:capitals) { Table.new('capitals.txt') }
|
263
349
|
|
264
350
|
it "returns an instance of Table" do
|
265
351
|
expect(cities.sub("State", /Jersey/, "York")).to be_a(Table)
|
266
352
|
end
|
267
|
-
it "substitutes the values in a given field" do
|
268
|
-
expect(cities.sub("State", /Jersey/, "York").column("State")).
|
353
|
+
it "substitutes the values in a given field when matching Regexp" do
|
354
|
+
expect(cities.sub("State", /Jersey/, "York").column("State")).not_to include("New Jersey")
|
355
|
+
end
|
356
|
+
it "substitutes the values in a given field when matching String" do
|
357
|
+
expect(cities.sub("State", "Jersey", "York").column("State")).not_to include("New Jersey")
|
358
|
+
end
|
359
|
+
it "substitutes the values in a given field when provided with a block" do
|
360
|
+
expect(cities.sub("State") {|state| state.upcase}.column("State")).to include("NEW JERSEY")
|
361
|
+
end
|
362
|
+
it "substitutes the values in a given field when provided with a replacement Hash" do
|
363
|
+
expect(capitals.sub("State", /North|South|East|West/, {"North"=>"South",
|
364
|
+
"South"=>"North", "West" => "East", "East"=>"West" }).column("State")).to include("East Virginia")
|
269
365
|
end
|
270
366
|
it "raises ArgumentError when the given arguments don't match a column" do
|
271
367
|
expect {cities.sub("Silly", /NJ/, "NY") }.to raise_error(ArgumentError)
|
@@ -273,6 +369,9 @@ describe "Table" do
|
|
273
369
|
it "raises ArgumentError when not given a Match string" do
|
274
370
|
expect {cities.sub("State") }.to raise_error(ArgumentError)
|
275
371
|
end
|
372
|
+
it "raises ArgumentError when Match expression is not a String or Regexp" do
|
373
|
+
expect {cities.sub("State",:New, "Old") }.to raise_error(ArgumentError)
|
374
|
+
end
|
276
375
|
it "raises ArgumentError when replacement is not a String or Hash" do
|
277
376
|
expect {cities.sub("State", /New/, 9)}.to raise_error(ArgumentError)
|
278
377
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tablestakes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J.B. Folkerts
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '3.
|
19
|
+
version: '3.5'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '3.
|
26
|
+
version: '3.5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec-its
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,70 +44,70 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
47
|
+
version: '3.5'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
54
|
+
version: '3.5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec-expectations
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
61
|
+
version: '3.5'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '3.
|
68
|
+
version: '3.5'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: factory_girl
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '4.
|
75
|
+
version: '4.7'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '4.
|
82
|
+
version: '4.7'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: simplecov
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0.
|
89
|
+
version: '0.12'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0.
|
96
|
+
version: '0.12'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: coveralls
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 0.8.15
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 0.8.15
|
111
111
|
description: A simple implementation of Tables, for use in summing, joining, slicing
|
112
112
|
and dicing data tables
|
113
113
|
email: jbf@pentambic.com
|
@@ -161,7 +161,7 @@ files:
|
|
161
161
|
- spec/spec_helper.rb
|
162
162
|
- spec/table_spec.rb
|
163
163
|
- test.tab
|
164
|
-
homepage:
|
164
|
+
homepage: https://rubygems.org/gems/tablestakes
|
165
165
|
licenses:
|
166
166
|
- MIT
|
167
167
|
metadata: {}
|