tablestakes 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: {}
|