tabular 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +46 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/lib/tabular.rb +10 -0
- data/lib/tabular/column.rb +50 -0
- data/lib/tabular/columns.rb +55 -0
- data/lib/tabular/row.rb +80 -0
- data/lib/tabular/support/object.rb +59 -0
- data/lib/tabular/table.rb +65 -0
- data/tabular.gemspec +63 -0
- data/test/column_test.rb +39 -0
- data/test/columns_test.rb +64 -0
- data/test/fixtures/blank.txt +0 -0
- data/test/fixtures/excel.xls +0 -0
- data/test/fixtures/sample.csv +5 -0
- data/test/helper.rb +9 -0
- data/test/row_test.rb +57 -0
- data/test/table_test.rb +37 -0
- metadata +78 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Scott Willson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
Tabular is a Ruby library for reading, writing, and manipulating CSV, tab-delimited and Excel data.
|
2
|
+
|
3
|
+
I extracted it from production code. Still extracting it, actually. I need to read structured data and manipulate it via a common interface before persisting it with ActiveRecord.
|
4
|
+
|
5
|
+
|
6
|
+
Dependencies
|
7
|
+
------------
|
8
|
+
For tab-delimited data: Ruby standard lib
|
9
|
+
For CSV: FasterCSV
|
10
|
+
For Excel: Spreadsheet gem
|
11
|
+
|
12
|
+
|
13
|
+
Examples
|
14
|
+
--------
|
15
|
+
>> table = Table.read("test/fixtures/sample.csv")
|
16
|
+
>> table.rows.size
|
17
|
+
=> 4
|
18
|
+
|
19
|
+
Access Table Rows by index:
|
20
|
+
>> table[0]
|
21
|
+
|
22
|
+
And Row cells as a Hash:
|
23
|
+
>> table[0][:last_name]
|
24
|
+
=> "Willson"
|
25
|
+
|
26
|
+
|
27
|
+
Usage
|
28
|
+
-----
|
29
|
+
Table.read assumes that .txt files are tab-delimited, .csv files are comma-delimited, and .xls files are Excel. It assumes that the first row is the header row, and normalizes the header to lower-case with underscores. E.g., "Last Name" becomes "last_name".
|
30
|
+
|
31
|
+
Table.new accepts an Array of Arrays.
|
32
|
+
|
33
|
+
Table.new also accepts an options hash. Currently, :columns is the only option. Examples:
|
34
|
+
:city_state => :location -- Maps :city_state column to :location. A column with a "City State" header would be accessed as row[:location]
|
35
|
+
:flyer_approved => { :column_type => :boolean } -- Coerce :flyer_approved column cells to booleans.
|
36
|
+
|
37
|
+
|
38
|
+
Tests
|
39
|
+
-----
|
40
|
+
There's basic test coverage. More comprehensive test coverage needs to be extracted from original projects. Run 'rake test'.
|
41
|
+
|
42
|
+
|
43
|
+
Copyright
|
44
|
+
---------
|
45
|
+
|
46
|
+
Copyright (c) 2009 Scott Willson. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gemspec|
|
7
|
+
gemspec.name = "tabular"
|
8
|
+
gemspec.summary = "Read, write, and manipulate CSV, tab-delimited and Excel data"
|
9
|
+
gemspec.description = "Tabular is a Ruby library for reading, writing, and manipulating CSV, tab-delimited and Excel data."
|
10
|
+
gemspec.email = "scott.willson@gmail.cpm"
|
11
|
+
gemspec.homepage = "http://github.com/scottwillson/tabular"
|
12
|
+
gemspec.authors = ["Scott Willson"]
|
13
|
+
end
|
14
|
+
Jeweler::GemcutterTasks.new
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
task :test => :check_dependencies
|
27
|
+
|
28
|
+
task :default => :test
|
29
|
+
|
30
|
+
require 'rake/rdoctask'
|
31
|
+
Rake::RDocTask.new do |rdoc|
|
32
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
33
|
+
|
34
|
+
rdoc.rdoc_dir = 'rdoc'
|
35
|
+
rdoc.title = "tabular #{version}"
|
36
|
+
rdoc.rdoc_files.include('README*')
|
37
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
38
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/tabular.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Tabular
|
2
|
+
class Column
|
3
|
+
attr_reader :key, :column_type
|
4
|
+
|
5
|
+
def initialize(key = nil, columns_map = {})
|
6
|
+
key = symbolize(key)
|
7
|
+
columns_map = columns_map || {}
|
8
|
+
map_for_key = columns_map[key]
|
9
|
+
|
10
|
+
@column_type = :string
|
11
|
+
case map_for_key
|
12
|
+
when nil
|
13
|
+
@key = key
|
14
|
+
@column_type = :date if key == :date
|
15
|
+
when Symbol
|
16
|
+
@key = map_for_key
|
17
|
+
@column_type = :date if key == :date
|
18
|
+
when Hash
|
19
|
+
@key = key
|
20
|
+
@column_type = map_for_key[:column_type]
|
21
|
+
else
|
22
|
+
raise "Expected Symbol or Hash, but was #{map_for_key.class}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def symbolize(key)
|
27
|
+
return nil if key.blank?
|
28
|
+
|
29
|
+
begin
|
30
|
+
key.to_s.strip.gsub(/::/, '/').
|
31
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
32
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
33
|
+
tr("-", "_").
|
34
|
+
gsub(/ +/, "_").
|
35
|
+
downcase.
|
36
|
+
to_sym
|
37
|
+
rescue
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect
|
43
|
+
"#<Tabular::Column #{key} #{column_type}>"
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
key
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Tabular
|
2
|
+
# The Table's header: a list of Columns.
|
3
|
+
class Columns
|
4
|
+
# +data+ -- array of header names
|
5
|
+
# +columns_map+ -- see Table. Maps column names and type conversion.
|
6
|
+
def initialize(data, columns_map = {})
|
7
|
+
@columns_map = columns_map
|
8
|
+
@column_indexes = {}
|
9
|
+
@columns_by_key = {}
|
10
|
+
index = 0
|
11
|
+
@columns = nil
|
12
|
+
@columns = data.map do |column|
|
13
|
+
new_column = Tabular::Column.new(column, columns_map)
|
14
|
+
unless new_column.key.blank?
|
15
|
+
@column_indexes[new_column.key] = index
|
16
|
+
@columns_by_key[new_column.key] = new_column
|
17
|
+
end
|
18
|
+
index = index + 1
|
19
|
+
new_column
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Is the a Column with this key? Keys are lower-case, underscore symbols.
|
24
|
+
# Example: :postal_code
|
25
|
+
def has_key?(key)
|
26
|
+
@columns.any? { |column| column.key == key }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Column for +key+
|
30
|
+
def [](key)
|
31
|
+
@columns_by_key[key]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Zero-based index of Column for +key+
|
35
|
+
def index(key)
|
36
|
+
@column_indexes[key]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Call +block+ for each Column
|
40
|
+
def each(&block)
|
41
|
+
@columns.each(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Add a new Column with +key+
|
45
|
+
def <<(key)
|
46
|
+
column = Column.new(key, @columns_map)
|
47
|
+
unless column.key.blank?
|
48
|
+
@column_indexes[column.key] = @columns.size
|
49
|
+
@column_indexes[@columns.size] = column
|
50
|
+
@columns_by_key[column.key] = column
|
51
|
+
end
|
52
|
+
@columns << column
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/tabular/row.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Tabular
|
2
|
+
# Associate list of cells. Each Table has a list of Rows. Access Row cells via symbols. Ex: row[:city]
|
3
|
+
class Row
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
# +columns+ -- array of string
|
7
|
+
# +cell+ -- array (not neccessarily Strings)
|
8
|
+
def initialize(columns, cells = [])
|
9
|
+
@columns = columns
|
10
|
+
@array = cells
|
11
|
+
@hash = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
# Cell value by symbol. E.g., row[:phone_number]
|
15
|
+
def [](key)
|
16
|
+
hash[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Set cell value. Adds cell to end of Row and adds new Column if there is no Column fo +key_
|
20
|
+
def []=(key, value)
|
21
|
+
if @columns.has_key?(key)
|
22
|
+
@array[@columns.index(key)] = value
|
23
|
+
else
|
24
|
+
@array << value
|
25
|
+
@columns << key
|
26
|
+
end
|
27
|
+
hash[key] = value
|
28
|
+
end
|
29
|
+
|
30
|
+
# Call +block+ for each cell
|
31
|
+
def each(&block)
|
32
|
+
@array.each(&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# For pretty-printing cell values
|
36
|
+
def join(sep = nil)
|
37
|
+
@array.join(sep)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_hash
|
41
|
+
hash.dup
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect
|
45
|
+
hash.inspect
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
@array.join(", ").to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
|
56
|
+
def hash #:nodoc:
|
57
|
+
unless @hash
|
58
|
+
@hash = Hash.new
|
59
|
+
@columns.each do |column|
|
60
|
+
index = @columns.index(column.key)
|
61
|
+
if index
|
62
|
+
case column.column_type
|
63
|
+
when :boolean
|
64
|
+
@hash[column.key] = [1, "1", true, "true"].include?(@array[index])
|
65
|
+
when :date
|
66
|
+
if @array[index].is_a?(Date) || @array[index].is_a?(DateTime) || @array[index].is_a?(Time)
|
67
|
+
@hash[column.key] = @array[index]
|
68
|
+
else
|
69
|
+
@hash[column.key] = Date.parse(@array[index], true)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
@hash[column.key] = @array[index]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@hash
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copied from Rails' ActiveSupport
|
2
|
+
class Object
|
3
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
4
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
5
|
+
#
|
6
|
+
# This simplifies
|
7
|
+
#
|
8
|
+
# if !address.nil? && !address.empty?
|
9
|
+
#
|
10
|
+
# to
|
11
|
+
#
|
12
|
+
# if !address.blank?
|
13
|
+
def blank?
|
14
|
+
respond_to?(:empty?) ? empty? : !self
|
15
|
+
end
|
16
|
+
|
17
|
+
# An object is present if it's not blank.
|
18
|
+
def present?
|
19
|
+
!blank?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class NilClass #:nodoc:
|
24
|
+
def blank?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class FalseClass #:nodoc:
|
30
|
+
def blank?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TrueClass #:nodoc:
|
36
|
+
def blank?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Array #:nodoc:
|
42
|
+
alias_method :blank?, :empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
class Hash #:nodoc:
|
46
|
+
alias_method :blank?, :empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
class String #:nodoc:
|
50
|
+
def blank?
|
51
|
+
self !~ /\S/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Numeric #:nodoc:
|
56
|
+
def blank?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Tabular
|
2
|
+
# Simple Enumerable list of Hashes. Use Table.read(file_path) to read file.
|
3
|
+
class Table
|
4
|
+
attr_reader :columns, :rows
|
5
|
+
|
6
|
+
# Assumes .txt = tab-delimited, .csv = CSV, .xls = Excel. Assumes first row is the header.
|
7
|
+
# Normalizes column names to lower-case with underscores.
|
8
|
+
def self.read(file_path, *options)
|
9
|
+
raise "Could not find '#{file_path}'" unless File.exists?(file_path)
|
10
|
+
|
11
|
+
case File.extname(file_path)
|
12
|
+
when ".xls", ".xlsx"
|
13
|
+
require "spreadsheet"
|
14
|
+
# Row#to_a coerces Excel data to Strings, but we want Dates and Numbers
|
15
|
+
data = []
|
16
|
+
Spreadsheet.open(file_path).worksheets.first.each do |excel_row|
|
17
|
+
data << excel_row.inject([]) { |row, cell| row << cell; row }
|
18
|
+
end
|
19
|
+
when ".txt"
|
20
|
+
require "csv"
|
21
|
+
data = ::CSV.open(file_path, "r","\t").collect { |row| row }
|
22
|
+
when ".csv"
|
23
|
+
require "fastercsv"
|
24
|
+
data = FasterCSV.read(file_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
Table.new data, options
|
28
|
+
end
|
29
|
+
|
30
|
+
# Pass data in as +rows+. Expects rows to be an Enumerable of Enumerables.
|
31
|
+
# Maps rows to Hash-like Tabular::Rows.
|
32
|
+
#
|
33
|
+
# Options:
|
34
|
+
# :column => { :original_name => :preferred_name, :column_name => { :column_type => :boolean } }
|
35
|
+
def initialize(rows = [], *options)
|
36
|
+
if options
|
37
|
+
options = options.flatten.first || {}
|
38
|
+
else
|
39
|
+
options = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
rows.each do |row|
|
43
|
+
if columns
|
44
|
+
@rows << Tabular::Row.new(columns, row)
|
45
|
+
else
|
46
|
+
@columns = Tabular::Columns.new(row, options[:columns])
|
47
|
+
@rows = []
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return Row at zero-based index, or nil if Row is out of bounds
|
53
|
+
def [](index)
|
54
|
+
rows[index]
|
55
|
+
end
|
56
|
+
|
57
|
+
def inspect
|
58
|
+
rows.map { |row| row.join(",") }.join("\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
"#<#{self.class} #{rows.size}>"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/tabular.gemspec
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{tabular}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Scott Willson"]
|
12
|
+
s.date = %q{2009-12-15}
|
13
|
+
s.description = %q{Tabular is a Ruby library for reading, writing, and manipulating CSV, tab-delimited and Excel data.}
|
14
|
+
s.email = %q{scott.willson@gmail.cpm}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"LICENSE",
|
21
|
+
"README",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"lib/tabular.rb",
|
25
|
+
"lib/tabular/column.rb",
|
26
|
+
"lib/tabular/columns.rb",
|
27
|
+
"lib/tabular/row.rb",
|
28
|
+
"lib/tabular/support/object.rb",
|
29
|
+
"lib/tabular/table.rb",
|
30
|
+
"tabular.gemspec",
|
31
|
+
"test/column_test.rb",
|
32
|
+
"test/columns_test.rb",
|
33
|
+
"test/fixtures/blank.txt",
|
34
|
+
"test/fixtures/excel.xls",
|
35
|
+
"test/fixtures/sample.csv",
|
36
|
+
"test/helper.rb",
|
37
|
+
"test/row_test.rb",
|
38
|
+
"test/table_test.rb"
|
39
|
+
]
|
40
|
+
s.homepage = %q{http://github.com/scottwillson/tabular}
|
41
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
42
|
+
s.require_paths = ["lib"]
|
43
|
+
s.rubygems_version = %q{1.3.5}
|
44
|
+
s.summary = %q{Read, write, and manipulate CSV, tab-delimited and Excel data}
|
45
|
+
s.test_files = [
|
46
|
+
"test/column_test.rb",
|
47
|
+
"test/columns_test.rb",
|
48
|
+
"test/helper.rb",
|
49
|
+
"test/row_test.rb",
|
50
|
+
"test/table_test.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
else
|
59
|
+
end
|
60
|
+
else
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
data/test/column_test.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Tabular
|
4
|
+
class ColumnTest < Test::Unit::TestCase
|
5
|
+
def test_new_nil
|
6
|
+
column = Column.new
|
7
|
+
assert_equal nil, column.to_s, "blank column to_s"
|
8
|
+
assert_equal nil, column.key, "blank column key"
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_new
|
12
|
+
assert_equal :date, Column.new("date").key, "column key"
|
13
|
+
assert_equal :date, Column.new(:date).key, "column key"
|
14
|
+
assert_equal :date, Column.new("Date").key, "column key"
|
15
|
+
assert_equal :date, Column.new(" Date ").key, "column key"
|
16
|
+
assert_equal :date, Column.new("DATE").key, "column key"
|
17
|
+
assert_equal :start_date, Column.new("StartDate").key, "column key"
|
18
|
+
assert_equal :start_date, Column.new("Start Date").key, "column key"
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_mapping
|
22
|
+
assert_equal :city, Column.new(:location, :location => :city).key, "column key"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_type
|
26
|
+
column = Column.new("name")
|
27
|
+
assert_equal :name, column.key, "key"
|
28
|
+
assert_equal :string, column.column_type, "column_type"
|
29
|
+
|
30
|
+
column = Column.new("date")
|
31
|
+
assert_equal :date, column.key, "key"
|
32
|
+
assert_equal :date, column.column_type, "column_type"
|
33
|
+
|
34
|
+
column = Column.new("phone", :phone => { :column_type => :integer })
|
35
|
+
assert_equal :phone, column.key, "key"
|
36
|
+
assert_equal :integer, column.column_type, "column_type"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Tabular
|
4
|
+
class ColumnsTest < Test::Unit::TestCase
|
5
|
+
def test_new_blank
|
6
|
+
columns = Columns.new([])
|
7
|
+
assert_equal false, columns.has_key?(:name), "has_key? :name"
|
8
|
+
assert_equal nil, columns[:name], "[:name]"
|
9
|
+
assert_equal nil, columns.index(nil), "index"
|
10
|
+
assert_equal nil, columns.index(""), "index"
|
11
|
+
assert_equal nil, columns.index(:name), "index"
|
12
|
+
columns.each { |c| c.nil? }
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_new
|
16
|
+
columns = Columns.new(["date", "first name", "LastName"])
|
17
|
+
assert_equal false, columns.has_key?(:location), "has_key? :location"
|
18
|
+
assert_equal true, columns.has_key?(:date), "has_key? :date"
|
19
|
+
assert_equal true, columns.has_key?(:first_name), "has_key? :first_name"
|
20
|
+
assert_equal true, columns.has_key?(:last_name), "has_key? :last_name"
|
21
|
+
assert_equal false, columns.has_key?("first name"), "has_key? 'first name'"
|
22
|
+
|
23
|
+
column = columns[:first_name]
|
24
|
+
assert_equal :first_name, column.key, "column[:first_name] Column key"
|
25
|
+
|
26
|
+
assert_equal 1, columns.index(:first_name), "index of :first_name"
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_columns_map
|
30
|
+
columns = Columns.new(["date"], :start_date => :date)
|
31
|
+
assert_equal true, columns.has_key?(:date), "has_key? :date"
|
32
|
+
assert_equal false, columns.has_key?(:start_date), "has_key? :start_date"
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_each
|
36
|
+
columns = Columns.new(["date", "first name", "LastName"])
|
37
|
+
columns_from_each = []
|
38
|
+
columns.each { |c| columns_from_each << c.key }
|
39
|
+
assert_equal [ :date, :first_name, :last_name ], columns_from_each, "column keys from #each"
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_push_onto_blank
|
43
|
+
columns = Columns.new([])
|
44
|
+
columns << "city state"
|
45
|
+
assert_equal true, columns.has_key?(:city_state), "has_key? :city_state"
|
46
|
+
assert_equal 0, columns.index(:city_state), "index of new column"
|
47
|
+
|
48
|
+
column = columns[:city_state]
|
49
|
+
assert_equal :city_state, column.key, "column[:city_state] Column key"
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_push
|
53
|
+
columns = Columns.new(["first", "second"])
|
54
|
+
columns << "third"
|
55
|
+
assert_equal true, columns.has_key?(:third), "has_key? :third"
|
56
|
+
assert_equal 0, columns.index(:first), "index of existing column"
|
57
|
+
assert_equal 1, columns.index(:second), "index of existing column"
|
58
|
+
assert_equal 2, columns.index(:third), "index of new column"
|
59
|
+
|
60
|
+
column = columns[:third]
|
61
|
+
assert_equal :third, column.key, "column[:third] Column key"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
File without changes
|
Binary file
|
@@ -0,0 +1,5 @@
|
|
1
|
+
"Place ","Number","Last Name","First Name","Team","Category Raced"
|
2
|
+
"1","189","Willson","Scott","Gentle Lover","Senior Men 1/2/3","11",,"11"
|
3
|
+
"2","190","Phinney","Harry","CCCP","Senior Men 1/2/3","9",,
|
4
|
+
"3","10a","Holland","Steve","Huntair","Senior Men 1/2/3",,"3",
|
5
|
+
"dnf","100","Bourcier","Paul","Hutch's","Senior Men 1/2/3",,,"1"
|
data/test/helper.rb
ADDED
data/test/row_test.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Tabular
|
4
|
+
class RowTest < Test::Unit::TestCase
|
5
|
+
def test_new
|
6
|
+
row = Row.new([])
|
7
|
+
assert_equal nil, row[:city], "[]"
|
8
|
+
|
9
|
+
assert_equal "", row.join, "join"
|
10
|
+
assert_equal({}, row.to_hash, "to_hash")
|
11
|
+
assert_equal "{}", row.inspect, "inspect"
|
12
|
+
assert_equal "", row.to_s, "to_s"
|
13
|
+
|
14
|
+
# Test each
|
15
|
+
row.each { |c| c.nil? }
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_set
|
19
|
+
columns = Columns.new([ "planet", "star" ])
|
20
|
+
row = Row.new(columns, [ "Mars", "Sun" ])
|
21
|
+
|
22
|
+
assert_equal "Sun", row[:star], "row[:star]"
|
23
|
+
|
24
|
+
row[:star] = "Solaris"
|
25
|
+
assert_equal "Solaris", row[:star], "row[:star]"
|
26
|
+
|
27
|
+
row[:astronaut] = "Buzz"
|
28
|
+
assert_equal "Buzz", row[:astronaut], "row[:astronaut]"
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_join
|
32
|
+
columns = Columns.new([ "planet", "star" ])
|
33
|
+
row = Row.new(columns, [ "Mars", "Sun" ])
|
34
|
+
assert_equal "MarsSun", row.join, "join"
|
35
|
+
assert_equal "Mars-Sun", row.join("-"), "join '-'"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_to_hash
|
39
|
+
columns = Columns.new([ "planet", "star" ])
|
40
|
+
row = Row.new(columns, [ "Mars", "Sun" ])
|
41
|
+
assert_equal({ :planet => "Mars", :star => "Sun"}, row.to_hash, "to_hash")
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_inspect
|
45
|
+
columns = Columns.new([ "planet", "star" ])
|
46
|
+
row = Row.new(columns, [ "Mars", "Sun" ])
|
47
|
+
assert_equal "{:planet=>\"Mars\", :star=>\"Sun\"}", row.inspect, "inspect"
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_to_s
|
51
|
+
columns = Columns.new([ "planet", "star" ])
|
52
|
+
row = Row.new(columns, [ "Mars", "Sun" ])
|
53
|
+
assert_equal "Mars, Sun", row.to_s, "to_s"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
data/test/table_test.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
module Tabular
|
4
|
+
class TableTest < Test::Unit::TestCase
|
5
|
+
def test_read_from_blank_txt_file
|
6
|
+
table = Table.read(File.expand_path(File.dirname(__FILE__) + "/fixtures/blank.txt"))
|
7
|
+
end
|
8
|
+
|
9
|
+
# "Place ","Number","Last Name","First Name","Team","Category Raced"
|
10
|
+
# "1","189","Willson","Scott","Gentle Lover","Senior Men 1/2/3","11",,"11"
|
11
|
+
# "2","190","Phinney","Harry","CCCP","Senior Men 1/2/3","9",,
|
12
|
+
# "3","10a","Holland","Steve","Huntair","Senior Men 1/2/3",,"3",
|
13
|
+
# "dnf","100","Bourcier","Paul","Hutch's","Senior Men 1/2/3",,,"1"
|
14
|
+
def test_read_from_csv
|
15
|
+
table = Table.read(File.expand_path(File.dirname(__FILE__) + "/fixtures/sample.csv"))
|
16
|
+
assert_equal 4, table.rows.size, "rows"
|
17
|
+
|
18
|
+
assert_equal "1", table[0][:place], "0.0"
|
19
|
+
assert_equal "189", table[0][:number], "0.1"
|
20
|
+
assert_equal "Willson", table[0][:last_name], "0.2"
|
21
|
+
assert_equal "Scott", table[0][:first_name], "0.3"
|
22
|
+
assert_equal "Gentle Lover", table[0][:team], "0.4"
|
23
|
+
|
24
|
+
assert_equal "dnf", table[3][:place], "3.0"
|
25
|
+
assert_equal "100", table[3][:number], "3.1"
|
26
|
+
assert_equal "Bourcier", table[3][:last_name], "3.2"
|
27
|
+
assert_equal "Paul", table[3][:first_name], "3.3"
|
28
|
+
assert_equal "Hutch's", table[3][:team], "3.4"
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_read_from_excel
|
32
|
+
table = Table.read(File.expand_path(File.dirname(__FILE__) + "/fixtures/excel.xls"))
|
33
|
+
assert_equal Date.new(2006, 1, 20), table[0][:date], "0.0"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tabular
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Scott Willson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-15 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Tabular is a Ruby library for reading, writing, and manipulating CSV, tab-delimited and Excel data.
|
17
|
+
email: scott.willson@gmail.cpm
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README
|
25
|
+
files:
|
26
|
+
- LICENSE
|
27
|
+
- README
|
28
|
+
- Rakefile
|
29
|
+
- VERSION
|
30
|
+
- lib/tabular.rb
|
31
|
+
- lib/tabular/column.rb
|
32
|
+
- lib/tabular/columns.rb
|
33
|
+
- lib/tabular/row.rb
|
34
|
+
- lib/tabular/support/object.rb
|
35
|
+
- lib/tabular/table.rb
|
36
|
+
- tabular.gemspec
|
37
|
+
- test/column_test.rb
|
38
|
+
- test/columns_test.rb
|
39
|
+
- test/fixtures/blank.txt
|
40
|
+
- test/fixtures/excel.xls
|
41
|
+
- test/fixtures/sample.csv
|
42
|
+
- test/helper.rb
|
43
|
+
- test/row_test.rb
|
44
|
+
- test/table_test.rb
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/scottwillson/tabular
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --charset=UTF-8
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.3.5
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Read, write, and manipulate CSV, tab-delimited and Excel data
|
73
|
+
test_files:
|
74
|
+
- test/column_test.rb
|
75
|
+
- test/columns_test.rb
|
76
|
+
- test/helper.rb
|
77
|
+
- test/row_test.rb
|
78
|
+
- test/table_test.rb
|