tabular 0.0.7 → 0.2.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.
data/lib/tabular/table.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  module Tabular
2
2
  # Simple Enumerable list of Hashes. Use Table.read(file_path) to read file.
3
3
  class Table
4
- attr_reader :rows
5
-
4
+ include Tabular::Keys
5
+
6
+ attr_reader :options, :rows
7
+ attr_accessor :row_mapper
8
+
6
9
  # +file+ : file path as String or File
7
10
  # Assumes .txt = tab-delimited, .csv = CSV, .xls = Excel. Assumes first row is the header.
8
11
  # Normalizes column names to lower-case with underscores.
@@ -13,16 +16,16 @@ module Tabular
13
16
  else
14
17
  file
15
18
  end
16
-
19
+
17
20
  raise "Could not find '#{file_path}'" unless File.exists?(file_path)
18
21
  options = extract_options(options)
19
-
22
+
20
23
  format = self.format_from(options.delete(:as), file_path)
21
24
  data = read_file(file_path, format)
22
-
25
+
23
26
  Table.new data, options
24
27
  end
25
-
28
+
26
29
  # +format+ : :csv, :txt, or :xls
27
30
  # Returns Array of Arrays
28
31
  def self.read_file(file_path, format)
@@ -54,49 +57,116 @@ module Tabular
54
57
  raise "Cannot read '#{format}' format. Expected :xls, :xlsx, :txt, or :csv"
55
58
  end
56
59
  end
57
-
58
- # Pass data in as +rows+. Expects rows to be an Enumerable of Enumerables.
60
+
61
+ # Pass data in as +rows+. Expects rows to be an Enumerable of Enumerables.
59
62
  # Maps rows to Hash-like Tabular::Rows.
60
63
  #
61
64
  # Options:
62
65
  # :columns => { :original_name => :preferred_name, :column_name => { :column_type => :boolean } }
63
66
  def initialize(rows = [], *options)
64
- options = Table.extract_options(options)
65
- @rows = []
67
+ @options = Table.extract_options(options)
68
+ self.rows = rows
69
+ end
70
+
71
+ def rows
72
+ @rows ||= []
73
+ end
74
+
75
+ def rows=(source_rows = [])
76
+ return [] unless source_rows
66
77
 
67
- rows.each do |row|
68
- if @columns
69
- self << row
70
- else
71
- @columns = Tabular::Columns.new(row, options[:columns])
72
- end
78
+ source_rows.each do |row|
79
+ self.<< row
73
80
  end
81
+
82
+ rows
74
83
  end
75
-
84
+
76
85
  # Return Row at zero-based index, or nil if Row is out of bounds
77
86
  def [](index)
78
87
  rows[index]
79
88
  end
80
-
89
+
81
90
  def <<(row)
82
- @rows << Tabular::Row.new(self, row)
91
+ if row_mapper
92
+ cells = row_mapper.map(row)
93
+ else
94
+ cells = row
95
+ end
96
+
97
+ if @columns.nil? && !cells.respond_to?(:keys)
98
+ @columns = Tabular::Columns.new(self, cells, options[:columns])
99
+ return columns
100
+ end
101
+
102
+ _row = Tabular::Row.new(self, cells, row)
103
+ _row.keys.each do |key|
104
+ columns << key
105
+ end
106
+ rows << _row
107
+ _row
83
108
  end
84
-
109
+
85
110
  def inspect
86
111
  rows.map { |row| row.join(",") }.join("\n")
87
112
  end
88
-
113
+
89
114
  def columns
90
- @columns || Tabular::Columns.new([])
115
+ @columns ||= Tabular::Columns.new(self, [])
116
+ end
117
+
118
+ def delete_blank_columns!
119
+ columns.map(&:key).each do |key|
120
+ if rows.all? { |row| row[key].blank? || row[key].zero? }
121
+ delete_column key
122
+ end
123
+ end
124
+ end
125
+
126
+ def delete_homogenous_columns!
127
+ return if rows.size < 2
128
+
129
+ columns.map(&:key).each do |key|
130
+ value = rows.first[key]
131
+ if rows.all? { |row| row[key] == value }
132
+ delete_column key
133
+ end
134
+ end
135
+ end
136
+
137
+ def strip!
138
+ rows.each do |row|
139
+ columns.each do |column|
140
+ value = row[column.key]
141
+ if value.respond_to?(:strip)
142
+ row[column.key] = value.strip
143
+ end
144
+ end
145
+ end
91
146
  end
92
147
 
148
+ def delete_column(key)
149
+ rows.each do |row|
150
+ row.delete key
151
+ end
152
+ columns.delete key
153
+ end
154
+
155
+ def renderer=(value)
156
+ columns.renderer = value
157
+ end
158
+
159
+ def renderers
160
+ columns.renderers
161
+ end
162
+
93
163
  def to_s
94
164
  "#<#{self.class} #{rows.size}>"
95
165
  end
96
166
 
97
167
 
98
168
  private
99
-
169
+
100
170
  def self.extract_options(options)
101
171
  if options
102
172
  options.flatten.first || {}
@@ -104,7 +174,7 @@ module Tabular
104
174
  {}
105
175
  end
106
176
  end
107
-
177
+
108
178
  def self.format_from(as_option, file_path)
109
179
  if as_option.present?
110
180
  as_option
data/tabular.gemspec CHANGED
@@ -1,66 +1,71 @@
1
1
  # Generated by jeweler
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{tabular}
8
- s.version = "0.0.7"
7
+ s.name = "tabular"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Scott Willson"]
12
- s.date = %q{2010-10-19}
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}
12
+ s.date = "2013-05-05"
13
+ s.description = "Tabular is a Ruby library for reading, writing, and manipulating CSV, tab-delimited and Excel data."
14
+ s.email = "scott.willson@gmail.cpm"
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README"
17
+ "README"
18
18
  ]
19
19
  s.files = [
20
- ".gitignore",
21
- "LICENSE",
22
- "README",
23
- "Rakefile",
24
- "VERSION",
25
- "lib/tabular.rb",
26
- "lib/tabular/column.rb",
27
- "lib/tabular/columns.rb",
28
- "lib/tabular/row.rb",
29
- "lib/tabular/support/object.rb",
30
- "lib/tabular/table.rb",
31
- "tabular.gemspec",
32
- "test/column_test.rb",
33
- "test/columns_test.rb",
34
- "test/fixtures/blank.txt",
35
- "test/fixtures/excel.xls",
36
- "test/fixtures/quoted.txt",
37
- "test/fixtures/sample.csv",
38
- "test/fixtures/sample.lif",
39
- "test/helper.rb",
40
- "test/row_test.rb",
41
- "test/table_test.rb"
42
- ]
43
- s.homepage = %q{http://github.com/scottwillson/tabular}
44
- s.rdoc_options = ["--charset=UTF-8"]
45
- s.require_paths = ["lib"]
46
- s.rubygems_version = %q{1.3.7}
47
- s.summary = %q{Read, write, and manipulate CSV, tab-delimited and Excel data}
48
- s.test_files = [
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENSE",
23
+ "README",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/tabular.rb",
27
+ "lib/tabular/column.rb",
28
+ "lib/tabular/columns.rb",
29
+ "lib/tabular/keys.rb",
30
+ "lib/tabular/renderer.rb",
31
+ "lib/tabular/row.rb",
32
+ "lib/tabular/support/object.rb",
33
+ "lib/tabular/support/zero.rb",
34
+ "lib/tabular/table.rb",
35
+ "tabular.gemspec",
49
36
  "test/column_test.rb",
50
- "test/columns_test.rb",
51
- "test/helper.rb",
52
- "test/row_test.rb",
53
- "test/table_test.rb"
37
+ "test/columns_test.rb",
38
+ "test/fixtures/blank.txt",
39
+ "test/fixtures/excel.xls",
40
+ "test/fixtures/quoted.txt",
41
+ "test/fixtures/sample.csv",
42
+ "test/fixtures/sample.lif",
43
+ "test/helper.rb",
44
+ "test/row_test.rb",
45
+ "test/table_test.rb",
46
+ "test/zero_test.rb"
54
47
  ]
48
+ s.homepage = "http://github.com/scottwillson/tabular"
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = "1.8.25"
51
+ s.summary = "Read, write, and manipulate CSV, tab-delimited and Excel data"
55
52
 
56
53
  if s.respond_to? :specification_version then
57
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
58
54
  s.specification_version = 3
59
55
 
60
56
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<ruby-ole>, [">= 0"])
58
+ s.add_runtime_dependency(%q<spreadsheet>, [">= 0"])
59
+ s.add_runtime_dependency(%q<jeweler>, [">= 0"])
61
60
  else
61
+ s.add_dependency(%q<ruby-ole>, [">= 0"])
62
+ s.add_dependency(%q<spreadsheet>, [">= 0"])
63
+ s.add_dependency(%q<jeweler>, [">= 0"])
62
64
  end
63
65
  else
66
+ s.add_dependency(%q<ruby-ole>, [">= 0"])
67
+ s.add_dependency(%q<spreadsheet>, [">= 0"])
68
+ s.add_dependency(%q<jeweler>, [">= 0"])
64
69
  end
65
70
  end
66
71
 
data/test/column_test.rb CHANGED
@@ -3,37 +3,78 @@ require "helper"
3
3
  module Tabular
4
4
  class ColumnTest < Test::Unit::TestCase
5
5
  def test_new_nil
6
- column = Column.new
7
- assert_equal nil, column.to_s, "blank column to_s"
6
+ column = Column.new(nil, nil)
7
+ assert_equal "", column.to_s, "blank column to_s"
8
8
  assert_equal nil, column.key, "blank column key"
9
9
  end
10
-
10
+
11
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
-
12
+ assert_equal :date, Column.new(nil, nil, "date").key, "column key"
13
+ assert_equal :date, Column.new(nil, nil, :date).key, "column key"
14
+ assert_equal :date, Column.new(nil, nil, "Date").key, "column key"
15
+ assert_equal :date, Column.new(nil, nil, " Date ").key, "column key"
16
+ assert_equal :date, Column.new(nil, nil, "DATE").key, "column key"
17
+ assert_equal :start_date, Column.new(nil, nil, "StartDate").key, "column key"
18
+ assert_equal :start_date, Column.new(nil, nil, "Start Date").key, "column key"
19
+ end
20
+
21
21
  def test_mapping
22
- assert_equal :city, Column.new(:location, :location => :city).key, "column key"
22
+ assert_equal :city, Column.new(nil, nil, :location, :location => :city).key, "column key"
23
23
  end
24
-
24
+
25
25
  def test_type
26
- column = Column.new("name")
26
+ column = Column.new(nil, nil, "name")
27
27
  assert_equal :name, column.key, "key"
28
28
  assert_equal :string, column.column_type, "column_type"
29
-
30
- column = Column.new("date")
29
+
30
+ column = Column.new(nil, nil, "date")
31
31
  assert_equal :date, column.key, "key"
32
32
  assert_equal :date, column.column_type, "column_type"
33
33
 
34
- column = Column.new("phone", :phone => { :column_type => :integer })
34
+ column = Column.new(nil, nil, "phone", :phone => { :column_type => :integer })
35
35
  assert_equal :phone, column.key, "key"
36
36
  assert_equal :integer, column.column_type, "column_type"
37
37
  end
38
+
39
+ def test_cells
40
+ data = [
41
+ { :place => "1", :name => "Bernard Hinault" },
42
+ { :place => "2", :name => "Greg Lemond" }
43
+ ]
44
+ table = Table.new(data)
45
+ column = table.columns[:place]
46
+ assert_equal [ "1", "2" ], column.cells
47
+ end
48
+
49
+ def test_max
50
+ data = [
51
+ { :place => "1", :name => "Bernard Hinault" },
52
+ { :place => "2", :name => "Greg Lemond" }
53
+ ]
54
+ table = Table.new(data)
55
+
56
+ assert_equal "2", table.columns[:place].max
57
+ assert_equal "Greg Lemond", table.columns[:name].max
58
+ end
59
+
60
+ def test_precision
61
+ data = [
62
+ { :place => "1", :age => 22, :points => 10.75 },
63
+ { :place => "2", :age => 30, :points => 12.000 }
64
+ ]
65
+ table = Table.new(data)
66
+
67
+ assert_equal 0, table.columns[:place].precision
68
+ assert_equal 0, table.columns[:age].precision
69
+ assert_equal 2, table.columns[:points].precision
70
+ end
71
+
72
+ def test_precision_with_mixed_zeros
73
+ data = [
74
+ { :place => "1", :age => 22, :points => 12.001 }
75
+ ]
76
+ table = Table.new(data)
77
+ assert_equal 3, table.columns[:points].precision
78
+ end
38
79
  end
39
80
  end
data/test/columns_test.rb CHANGED
@@ -3,7 +3,7 @@ require "helper"
3
3
  module Tabular
4
4
  class ColumnsTest < Test::Unit::TestCase
5
5
  def test_new_blank
6
- columns = Columns.new([])
6
+ columns = Columns.new(nil, [])
7
7
  assert_equal false, columns.has_key?(:name), "has_key? :name"
8
8
  assert_equal nil, columns[:name], "[:name]"
9
9
  assert_equal nil, columns.index(nil), "index"
@@ -11,36 +11,54 @@ module Tabular
11
11
  assert_equal nil, columns.index(:name), "index"
12
12
  columns.each { |c| c.nil? }
13
13
  end
14
-
14
+
15
15
  def test_new
16
- columns = Columns.new(["date", "first name", "LastName"])
16
+ columns = Columns.new(nil, ["date", "first name", "LastName"])
17
17
  assert_equal false, columns.has_key?(:location), "has_key? :location"
18
18
  assert_equal true, columns.has_key?(:date), "has_key? :date"
19
19
  assert_equal true, columns.has_key?(:first_name), "has_key? :first_name"
20
20
  assert_equal true, columns.has_key?(:last_name), "has_key? :last_name"
21
21
  assert_equal false, columns.has_key?("first name"), "has_key? 'first name'"
22
-
22
+
23
23
  column = columns[:first_name]
24
24
  assert_equal :first_name, column.key, "column[:first_name] Column key"
25
25
 
26
26
  assert_equal 1, columns.index(:first_name), "index of :first_name"
27
27
  end
28
-
28
+
29
29
  def test_columns_map
30
- columns = Columns.new(["date"], :start_date => :date)
30
+ columns = Columns.new(nil, ["date"], :start_date => :date)
31
31
  assert_equal true, columns.has_key?(:date), "has_key? :date"
32
32
  assert_equal false, columns.has_key?(:start_date), "has_key? :start_date"
33
33
  end
34
-
35
- def test_each
36
- columns = Columns.new(["date", "first name", "LastName"])
34
+
35
+ def test_render
36
+ columns = Columns.new(nil, ["date", "first name", "LastName"])
37
+ assert_equal "date", columns.first.render
38
+ end
39
+
40
+ def test_renderer
41
+ columns = Columns.new(nil, ["date", "first name", "LastName"])
42
+ columns.renderer = TestRenderer
43
+ assert_equal "Date", columns.first.render
44
+ end
45
+
46
+ def test_delete
47
+ columns = Columns.new(nil, ["date", "first name", "LastName"])
48
+ columns.delete :date
49
+
37
50
  columns_from_each = []
38
51
  columns.each { |c| columns_from_each << c.key }
39
- assert_equal [ :date, :first_name, :last_name ], columns_from_each, "column keys from #each"
52
+ assert_equal [ :first_name, :last_name ], columns_from_each, "column keys from #each"
53
+
54
+ assert_equal false, columns.has_key?(:date), "has_key? :date"
55
+ assert_equal true, columns.has_key?(:first_name), "has_key? :first_name"
56
+ assert_equal 0, columns.index(:first_name), "index of :first_name"
57
+ assert_equal 1, columns.index(:last_name), "index of :last_name"
40
58
  end
41
-
59
+
42
60
  def test_push_onto_blank
43
- columns = Columns.new([])
61
+ columns = Columns.new(nil, [])
44
62
  columns << "city state"
45
63
  assert_equal true, columns.has_key?(:city_state), "has_key? :city_state"
46
64
  assert_equal 0, columns.index(:city_state), "index of new column"
@@ -48,9 +66,9 @@ module Tabular
48
66
  column = columns[:city_state]
49
67
  assert_equal :city_state, column.key, "column[:city_state] Column key"
50
68
  end
51
-
69
+
52
70
  def test_push
53
- columns = Columns.new(["first", "second"])
71
+ columns = Columns.new(nil, ["first", "second"])
54
72
  columns << "third"
55
73
  assert_equal true, columns.has_key?(:third), "has_key? :third"
56
74
  assert_equal 0, columns.index(:first), "index of existing column"
@@ -60,5 +78,12 @@ module Tabular
60
78
  column = columns[:third]
61
79
  assert_equal :third, column.key, "column[:third] Column key"
62
80
  end
81
+
82
+ class TestRenderer
83
+ def self.render_header(column)
84
+ key = column.key.to_s
85
+ (key.slice(0) || key.chars('')).upcase + (key.slice(1..-1) || key.chars('')).downcase
86
+ end
87
+ end
63
88
  end
64
89
  end