tabular 0.2.0 → 0.2.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/README CHANGED
@@ -2,6 +2,12 @@ Tabular is a Ruby library for reading, writing, and manipulating CSV, tab-delimi
2
2
 
3
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
4
 
5
+ Tabular is also handy for display table-like data. For example, I want to display a bike race's results in HTML. I need to drop empty columns: sometimes there are points or times; sometimes not. I need find the most precise time to format all the times in the results correctly.
6
+
7
+ Much of the API is a copy of FasterCSV without the focus on CSV.
8
+
9
+ Import and display can be configured with Mappers and Renderers. It's a OOP-heavy design that is fast and test-able.
10
+
5
11
  Install
6
12
  -------
7
13
  sudo gem install tabular
@@ -38,7 +44,7 @@ Usage
38
44
  -----
39
45
  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".
40
46
 
41
- Table.new accepts an Array of Arrays.
47
+ Table.new accepts an Array of Arrays or an Array of Hashes.
42
48
 
43
49
  Table.new also accepts an options hash.
44
50
 
@@ -56,6 +62,25 @@ There's basic test coverage. More comprehensive test coverage needs to be extrac
56
62
 
57
63
  Changes
58
64
  -------
65
+ 0.2.1 Documentation!
66
+ 0.2.0 Add several new features that break previous API
67
+ * New public accessors for Table, Columns, Row, and Column
68
+ * Mapper to translate source data to Rows
69
+ * Renderer to control display of Row cells and Column headers
70
+ * Table#delete_blank_columns! to delete columns that are blank. Zero is considered blank.
71
+ * Table#delete_homogenous_columns! to delete columns that are all the same value. E.g.,
72
+ A | B | C
73
+ =========
74
+ 1 | 2 | 3
75
+ 1 | 6 |
76
+ 1 | * | 5
77
+
78
+ Column A would be deleted
79
+ * Table#strip! to remove whitespace around cell values. By default, Tabular::Table preserves cell whitespace.
80
+ * Column#max
81
+ * Column#precision
82
+ * Ruby 1.8 support is deprcated
83
+
59
84
  0.0.5 Parse 'invalid' m/d/yy dates
60
85
 
61
86
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
@@ -2,6 +2,9 @@ module Tabular
2
2
  class Column
3
3
  attr_reader :key, :column_type
4
4
 
5
+ # +table+ -- parent Table
6
+ # +column+ -- parent Columns
7
+ # +key+ is normalized to a downcase, underscored symbol
5
8
  def initialize(table, columns, key = nil, columns_map = {})
6
9
  @columns = columns
7
10
  @table = table
@@ -26,42 +29,31 @@ module Tabular
26
29
  end
27
30
  end
28
31
 
29
- def symbolize(key)
30
- return nil if key.blank?
31
-
32
- begin
33
- key.to_s.strip.gsub(/::/, '/').
34
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
35
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
36
- tr("-", "_").
37
- gsub(/ +/, "_").
38
- downcase.
39
- to_sym
40
- rescue
41
- nil
42
- end
43
- end
44
-
45
32
  def rows
46
33
  @table.rows
47
34
  end
48
35
 
36
+ # All cells value under this Column
49
37
  def cells
50
38
  rows.map { |r| r[key] }
51
39
  end
52
40
 
41
+ # Maximum value for cells in the Column. Determine with Ruby #max
53
42
  def max
54
43
  cells.compact.max
55
44
  end
56
45
 
46
+ # Number of zeros to the right of the decimal point. Useful for formtting time data.
57
47
  def precision
58
48
  @precision || cells.map(&:to_f).map {|n| n.round(3) }.map {|n| n.to_s.split(".").last.gsub(/0+$/, "").length }.max
59
49
  end
60
50
 
51
+ # Human-friendly header string. Delegate to +renderer+'s render_header method.
61
52
  def render
62
53
  renderer.render_header self
63
54
  end
64
55
 
56
+ # Renderer
65
57
  def renderer
66
58
  @columns.renderer(key)
67
59
  end
@@ -73,5 +65,23 @@ module Tabular
73
65
  def to_s
74
66
  key.to_s
75
67
  end
68
+
69
+ private
70
+
71
+ def symbolize(key)
72
+ return nil if key.blank?
73
+
74
+ begin
75
+ key.to_s.strip.gsub(/::/, '/').
76
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
77
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
78
+ tr("-", "_").
79
+ gsub(/ +/, "_").
80
+ downcase.
81
+ to_sym
82
+ rescue
83
+ nil
84
+ end
85
+ end
76
86
  end
77
87
  end
@@ -6,6 +6,7 @@ module Tabular
6
6
 
7
7
  attr_accessor :renderer
8
8
 
9
+ # +table+ -- Table
9
10
  # +data+ -- array of header names
10
11
  # +columns_map+ -- see Table. Maps column names and type conversion.
11
12
  def initialize(table, names, columns_map = {})
@@ -69,18 +70,17 @@ module Tabular
69
70
  end
70
71
  end
71
72
 
73
+ # Count of Columns#columns
72
74
  def size
73
75
  @columns.size
74
76
  end
75
77
 
76
- def max
77
- cells.compact.max
78
- end
79
-
78
+ # Renderer for Column +key+. Default to Table#renderer.
80
79
  def renderer(key)
81
80
  renderers[key] || @renderer || Renderer
82
81
  end
83
82
 
83
+ # List of Renderers
84
84
  def renderers
85
85
  @renderers ||= {}
86
86
  end
@@ -1,5 +1,6 @@
1
1
  module Tabular
2
2
  module Keys
3
+ # Return Symbol for +key+. Translate Column and String. Return +key+ unmodified for anything else.
3
4
  def key_to_sym(key)
4
5
  _key = case key
5
6
  when Column
@@ -1,4 +1,8 @@
1
1
  module Tabular
2
+ # Custom display of cells. By default, return to_s.
3
+ #
4
+ # Create your own Renders by implementing a class that responds to render(column, row) for cells
5
+ # and/or render_header(column) for Column headers.
2
6
  class Renderer
3
7
  def self.render(column, row)
4
8
  row[column.key]
@@ -56,6 +56,7 @@ module Tabular
56
56
  hash.each(&block)
57
57
  end
58
58
 
59
+ # Keys for all columns
59
60
  def keys
60
61
  hash.keys
61
62
  end
@@ -70,16 +71,19 @@ module Tabular
70
71
  hash.delete key
71
72
  end
72
73
 
74
+ # Previous Row
73
75
  def previous
74
76
  if index > 0
75
77
  @table.rows[index - 1]
76
78
  end
77
79
  end
78
80
 
81
+ # Tabluar::Columns
79
82
  def columns
80
83
  @table.columns
81
84
  end
82
85
 
86
+ # By default, return self[key]. Customize by setting Table#renderer or Column#renderers[key]
83
87
  def render(key)
84
88
  column = columns[key]
85
89
  column.renderer.render column, self
@@ -1,5 +1,6 @@
1
1
  module Tabular
2
- # Simple Enumerable list of Hashes. Use Table.read(file_path) to read file.
2
+ # Simple Enumerable list of Hashes. Use Table.read(file_path) to read file. Can also create a Table with Table.new. Either
3
+ # pass in data or set options and then call row=.
3
4
  class Table
4
5
  include Tabular::Keys
5
6
 
@@ -63,6 +64,8 @@ module Tabular
63
64
  #
64
65
  # Options:
65
66
  # :columns => { :original_name => :preferred_name, :column_name => { :column_type => :boolean } }
67
+ #
68
+ # The :columns option will likely be deprecated and options for mappers and renderers added
66
69
  def initialize(rows = [], *options)
67
70
  @options = Table.extract_options(options)
68
71
  self.rows = rows
@@ -72,6 +75,7 @@ module Tabular
72
75
  @rows ||= []
73
76
  end
74
77
 
78
+ # Set table rows. Calls row <<, which creates columns and links the source rows to Row#source.
75
79
  def rows=(source_rows = [])
76
80
  return [] unless source_rows
77
81
 
@@ -87,6 +91,9 @@ module Tabular
87
91
  rows[index]
88
92
  end
89
93
 
94
+ # Add row to end of table. Create missing columns and link the source row to Row#source.
95
+ # To control how source data is added to the Table, use Table#mapper= to set a class that
96
+ # implements map(row) and returns a Hash.
90
97
  def <<(row)
91
98
  if row_mapper
92
99
  cells = row_mapper.map(row)
@@ -111,10 +118,12 @@ module Tabular
111
118
  rows.map { |row| row.join(",") }.join("\n")
112
119
  end
113
120
 
121
+ # Instance of Tabular::Columns
114
122
  def columns
115
123
  @columns ||= Tabular::Columns.new(self, [])
116
124
  end
117
125
 
126
+ # Remove all columns that only contain a blank string, zero, or nil
118
127
  def delete_blank_columns!
119
128
  columns.map(&:key).each do |key|
120
129
  if rows.all? { |row| row[key].blank? || row[key].zero? }
@@ -123,6 +132,7 @@ module Tabular
123
132
  end
124
133
  end
125
134
 
135
+ # Remove all columns that contain the same value in all rows
126
136
  def delete_homogenous_columns!
127
137
  return if rows.size < 2
128
138
 
@@ -134,6 +144,8 @@ module Tabular
134
144
  end
135
145
  end
136
146
 
147
+ # Remove preceding and trailing whitespace from all cells. By default, Table does not
148
+ # strip whitespace from cells.
137
149
  def strip!
138
150
  rows.each do |row|
139
151
  columns.each do |column|
@@ -152,10 +164,12 @@ module Tabular
152
164
  columns.delete key
153
165
  end
154
166
 
167
+ # Set default Renderer. If present, will be used for all cells and Column headers.
155
168
  def renderer=(value)
156
169
  columns.renderer = value
157
170
  end
158
171
 
172
+ # List of Renderers
159
173
  def renderers
160
174
  columns.renderers
161
175
  end
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "tabular"
8
- s.version = "0.2.0"
8
+ s.version = "0.2.1"
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"]
@@ -33,7 +33,7 @@ module Tabular
33
33
  end
34
34
 
35
35
  def test_render
36
- columns = Columns.new(nil, ["date", "first name", "LastName"])
36
+ columns = Columns.new(Table.new, ["date", "first name", "LastName"])
37
37
  assert_equal "date", columns.first.render
38
38
  end
39
39
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: tabular
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.2.0
5
+ version: 0.2.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Scott Willson
@@ -105,7 +105,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - ! '>='
107
107
  - !ruby/object:Gem::Version
108
- hash: -1721544850789134153
108
+ hash: -2217103212522653909
109
109
  segments:
110
110
  - 0
111
111
  version: '0'