tabular 0.0.1
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.
- 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
|