tabular 0.0.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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