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/Gemfile +5 -0
- data/Gemfile.lock +34 -0
- data/Rakefile +1 -3
- data/VERSION +1 -1
- data/lib/tabular.rb +8 -4
- data/lib/tabular/column.rb +35 -8
- data/lib/tabular/columns.rb +49 -19
- data/lib/tabular/keys.rb +14 -0
- data/lib/tabular/renderer.rb +11 -0
- data/lib/tabular/row.rb +53 -20
- data/lib/tabular/support/zero.rb +29 -0
- data/lib/tabular/table.rb +94 -24
- data/tabular.gemspec +46 -41
- data/test/column_test.rb +59 -18
- data/test/columns_test.rb +39 -14
- data/test/row_test.rb +72 -13
- data/test/table_test.rb +140 -6
- data/test/zero_test.rb +21 -0
- metadata +82 -47
- data/.gitignore +0 -2
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/scottwillson/ruby-ole.git
|
3
|
+
revision: 6b9d2530b12259ca21af75ed2dcb26ba745a6115
|
4
|
+
specs:
|
5
|
+
ruby-ole (1.2.11.3)
|
6
|
+
|
7
|
+
GIT
|
8
|
+
remote: git://github.com/scottwillson/spreadsheet.git
|
9
|
+
revision: e5d97bc43f3db66600a70ace5de638f41a4a700b
|
10
|
+
specs:
|
11
|
+
spreadsheet (0.6.6)
|
12
|
+
ruby-ole
|
13
|
+
|
14
|
+
GEM
|
15
|
+
remote: https://rubygems.org/
|
16
|
+
specs:
|
17
|
+
git (1.2.5)
|
18
|
+
jeweler (1.8.4)
|
19
|
+
bundler (~> 1.0)
|
20
|
+
git (>= 1.2.5)
|
21
|
+
rake
|
22
|
+
rdoc
|
23
|
+
json (1.7.7)
|
24
|
+
rake (10.0.4)
|
25
|
+
rdoc (4.0.1)
|
26
|
+
json (~> 1.4)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
jeweler
|
33
|
+
ruby-ole!
|
34
|
+
spreadsheet!
|
data/Rakefile
CHANGED
@@ -23,11 +23,9 @@ Rake::TestTask.new(:test) do |test|
|
|
23
23
|
test.verbose = true
|
24
24
|
end
|
25
25
|
|
26
|
-
task :test => :check_dependencies
|
27
|
-
|
28
26
|
task :default => :test
|
29
27
|
|
30
|
-
require '
|
28
|
+
require 'rdoc/task'
|
31
29
|
Rake::RDocTask.new do |rdoc|
|
32
30
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
33
31
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.2.0
|
data/lib/tabular.rb
CHANGED
@@ -2,9 +2,13 @@ require "rubygems"
|
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib")
|
4
4
|
|
5
|
-
require "tabular/
|
6
|
-
|
7
|
-
require "tabular/
|
8
|
-
require "tabular/
|
5
|
+
require "tabular/keys"
|
6
|
+
|
7
|
+
require "tabular/column"
|
8
|
+
require "tabular/columns"
|
9
|
+
require "tabular/renderer"
|
10
|
+
require "tabular/row"
|
11
|
+
require "tabular/table"
|
9
12
|
|
10
13
|
require "tabular/support/object"
|
14
|
+
require "tabular/support/zero"
|
data/lib/tabular/column.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
module Tabular
|
2
2
|
class Column
|
3
3
|
attr_reader :key, :column_type
|
4
|
-
|
5
|
-
def initialize(key = nil, columns_map = {})
|
4
|
+
|
5
|
+
def initialize(table, columns, key = nil, columns_map = {})
|
6
|
+
@columns = columns
|
7
|
+
@table = table
|
8
|
+
|
6
9
|
key = symbolize(key)
|
7
10
|
columns_map = columns_map || {}
|
8
11
|
map_for_key = columns_map[key]
|
9
|
-
|
12
|
+
|
10
13
|
@column_type = :string
|
11
14
|
case map_for_key
|
12
15
|
when nil
|
@@ -17,7 +20,7 @@ module Tabular
|
|
17
20
|
@column_type = :date if key == :date
|
18
21
|
when Hash
|
19
22
|
@key = key
|
20
|
-
@column_type = map_for_key[:column_type]
|
23
|
+
@column_type = map_for_key[:column_type]
|
21
24
|
else
|
22
25
|
raise "Expected Symbol or Hash, but was #{map_for_key.class}"
|
23
26
|
end
|
@@ -25,7 +28,7 @@ module Tabular
|
|
25
28
|
|
26
29
|
def symbolize(key)
|
27
30
|
return nil if key.blank?
|
28
|
-
|
31
|
+
|
29
32
|
begin
|
30
33
|
key.to_s.strip.gsub(/::/, '/').
|
31
34
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
@@ -38,13 +41,37 @@ module Tabular
|
|
38
41
|
nil
|
39
42
|
end
|
40
43
|
end
|
41
|
-
|
44
|
+
|
45
|
+
def rows
|
46
|
+
@table.rows
|
47
|
+
end
|
48
|
+
|
49
|
+
def cells
|
50
|
+
rows.map { |r| r[key] }
|
51
|
+
end
|
52
|
+
|
53
|
+
def max
|
54
|
+
cells.compact.max
|
55
|
+
end
|
56
|
+
|
57
|
+
def precision
|
58
|
+
@precision || cells.map(&:to_f).map {|n| n.round(3) }.map {|n| n.to_s.split(".").last.gsub(/0+$/, "").length }.max
|
59
|
+
end
|
60
|
+
|
61
|
+
def render
|
62
|
+
renderer.render_header self
|
63
|
+
end
|
64
|
+
|
65
|
+
def renderer
|
66
|
+
@columns.renderer(key)
|
67
|
+
end
|
68
|
+
|
42
69
|
def inspect
|
43
70
|
"#<Tabular::Column #{key} #{column_type}>"
|
44
71
|
end
|
45
|
-
|
72
|
+
|
46
73
|
def to_s
|
47
|
-
key
|
74
|
+
key.to_s
|
48
75
|
end
|
49
76
|
end
|
50
77
|
end
|
data/lib/tabular/columns.rb
CHANGED
@@ -2,18 +2,22 @@ module Tabular
|
|
2
2
|
# The Table's header: a list of Columns.
|
3
3
|
class Columns
|
4
4
|
include Enumerable
|
5
|
-
|
5
|
+
include Tabular::Keys
|
6
|
+
|
7
|
+
attr_accessor :renderer
|
8
|
+
|
6
9
|
# +data+ -- array of header names
|
7
10
|
# +columns_map+ -- see Table. Maps column names and type conversion.
|
8
|
-
def initialize(
|
11
|
+
def initialize(table, names, columns_map = {})
|
12
|
+
@table = table
|
9
13
|
columns_map ||= {}
|
10
14
|
@columns_map = normalize_columns_map(columns_map)
|
11
15
|
@column_indexes = {}
|
12
16
|
@columns_by_key = {}
|
13
17
|
index = 0
|
14
18
|
@columns = nil
|
15
|
-
@columns =
|
16
|
-
new_column = Tabular::Column.new(column, @columns_map)
|
19
|
+
@columns = names.map do |column|
|
20
|
+
new_column = Tabular::Column.new(table, self, column, @columns_map)
|
17
21
|
unless new_column.key.blank?
|
18
22
|
@column_indexes[new_column.key] = index
|
19
23
|
@columns_by_key[new_column.key] = new_column
|
@@ -22,50 +26,76 @@ module Tabular
|
|
22
26
|
new_column
|
23
27
|
end
|
24
28
|
end
|
25
|
-
|
29
|
+
|
26
30
|
# Is the a Column with this key? Keys are lower-case, underscore symbols.
|
27
31
|
# Example: :postal_code
|
28
32
|
def has_key?(key)
|
29
33
|
@columns.any? { |column| column.key == key }
|
30
34
|
end
|
31
|
-
|
35
|
+
|
32
36
|
# Column for +key+
|
33
37
|
def [](key)
|
34
|
-
@columns_by_key[key]
|
38
|
+
@columns_by_key[key_to_sym(key)]
|
35
39
|
end
|
36
|
-
|
40
|
+
|
37
41
|
# Zero-based index of Column for +key+
|
38
42
|
def index(key)
|
39
43
|
@column_indexes[key]
|
40
44
|
end
|
41
|
-
|
45
|
+
|
42
46
|
# Call +block+ for each Column
|
43
47
|
def each(&block)
|
44
48
|
@columns.each(&block)
|
45
49
|
end
|
46
|
-
|
50
|
+
|
47
51
|
# Add a new Column with +key+
|
48
52
|
def <<(key)
|
49
|
-
column = Column.new(key, @columns_map)
|
50
|
-
unless column.key.blank?
|
53
|
+
column = Column.new(@table, self, key, @columns_map)
|
54
|
+
unless column.key.blank? || has_key?(key)
|
51
55
|
@column_indexes[column.key] = @columns.size
|
52
56
|
@column_indexes[@columns.size] = column
|
53
57
|
@columns_by_key[column.key] = column
|
58
|
+
@columns << column
|
54
59
|
end
|
55
|
-
@columns << column
|
56
60
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
|
62
|
+
def delete(key)
|
63
|
+
@columns.delete_if { |column| column.key == key }
|
64
|
+
@columns_by_key.delete key
|
65
|
+
@column_indexes.delete key
|
66
|
+
|
67
|
+
@columns.each.with_index do |column, index|
|
68
|
+
@column_indexes[column.key] = index
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def size
|
73
|
+
@columns.size
|
74
|
+
end
|
75
|
+
|
76
|
+
def max
|
77
|
+
cells.compact.max
|
78
|
+
end
|
79
|
+
|
80
|
+
def renderer(key)
|
81
|
+
renderers[key] || @renderer || Renderer
|
82
|
+
end
|
83
|
+
|
84
|
+
def renderers
|
85
|
+
@renderers ||= {}
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
private
|
90
|
+
|
61
91
|
def normalize_columns_map(columns_map)
|
62
92
|
normalized_columns_map = {}
|
63
93
|
columns_map.each do |key, value|
|
64
94
|
case value
|
65
95
|
when Hash, Symbol
|
66
|
-
normalized_columns_map[key
|
96
|
+
normalized_columns_map[key_to_sym(key)] = value
|
67
97
|
else
|
68
|
-
normalized_columns_map[key
|
98
|
+
normalized_columns_map[key_to_sym(key)] = value.to_sym
|
69
99
|
end
|
70
100
|
end
|
71
101
|
normalized_columns_map
|
data/lib/tabular/keys.rb
ADDED
data/lib/tabular/row.rb
CHANGED
@@ -4,23 +4,37 @@ module Tabular
|
|
4
4
|
# Associate list of cells. Each Table has a list of Rows. Access Row cells via symbols. Ex: row[:city]
|
5
5
|
class Row
|
6
6
|
include Enumerable
|
7
|
-
|
7
|
+
include Tabular::Keys
|
8
|
+
|
8
9
|
attr_reader :index
|
9
|
-
|
10
|
+
attr_reader :source
|
11
|
+
|
10
12
|
# +table+ -- Table
|
11
|
-
# +
|
12
|
-
|
13
|
+
# +cells+ -- array (not neccessarily Strings)
|
14
|
+
# +source+ -- original data before mapped to Hash or Array (optional)
|
15
|
+
def initialize(table, cells = [], source = nil)
|
13
16
|
@table = table
|
14
|
-
@
|
15
|
-
|
17
|
+
@source = source || cells
|
18
|
+
|
19
|
+
if cells.respond_to?(:keys)
|
20
|
+
@array = cells.values
|
21
|
+
@hash = {}
|
22
|
+
cells.each do |key, value|
|
23
|
+
@hash[key_to_sym(key)] = value
|
24
|
+
end
|
25
|
+
else
|
26
|
+
@array = cells
|
27
|
+
@hash = nil
|
28
|
+
end
|
29
|
+
|
16
30
|
@index = table.rows.size
|
17
31
|
end
|
18
|
-
|
32
|
+
|
19
33
|
# Cell value by symbol. E.g., row[:phone_number]
|
20
34
|
def [](key)
|
21
35
|
hash[key]
|
22
36
|
end
|
23
|
-
|
37
|
+
|
24
38
|
# Set cell value. Adds cell to end of Row and adds new Column if there is no Column for +key_
|
25
39
|
def []=(key, value)
|
26
40
|
if columns.has_key?(key)
|
@@ -31,42 +45,61 @@ module Tabular
|
|
31
45
|
end
|
32
46
|
hash[key] = value
|
33
47
|
end
|
34
|
-
|
48
|
+
|
35
49
|
# Call +block+ for each cell
|
36
50
|
def each(&block)
|
37
51
|
@array.each(&block)
|
38
52
|
end
|
39
53
|
|
54
|
+
# Call +block+ for each cell
|
55
|
+
def each_with_key(&block)
|
56
|
+
hash.each(&block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def keys
|
60
|
+
hash.keys
|
61
|
+
end
|
62
|
+
|
40
63
|
# For pretty-printing cell values
|
41
64
|
def join(sep = nil)
|
42
65
|
@array.join(sep)
|
43
66
|
end
|
44
|
-
|
67
|
+
|
68
|
+
def delete(key)
|
69
|
+
@array.delete key
|
70
|
+
hash.delete key
|
71
|
+
end
|
72
|
+
|
45
73
|
def previous
|
46
74
|
if index > 0
|
47
75
|
@table.rows[index - 1]
|
48
76
|
end
|
49
77
|
end
|
50
|
-
|
78
|
+
|
51
79
|
def columns
|
52
80
|
@table.columns
|
53
81
|
end
|
54
|
-
|
82
|
+
|
83
|
+
def render(key)
|
84
|
+
column = columns[key]
|
85
|
+
column.renderer.render column, self
|
86
|
+
end
|
87
|
+
|
55
88
|
def to_hash
|
56
89
|
hash.dup
|
57
90
|
end
|
58
|
-
|
91
|
+
|
59
92
|
def inspect
|
60
93
|
hash.inspect
|
61
94
|
end
|
62
|
-
|
95
|
+
|
63
96
|
def to_s
|
64
97
|
@array.join(", ").to_s
|
65
98
|
end
|
66
99
|
|
67
100
|
|
68
101
|
protected
|
69
|
-
|
102
|
+
|
70
103
|
def hash #:nodoc:
|
71
104
|
unless @hash
|
72
105
|
@hash = Hash.new
|
@@ -103,14 +136,14 @@ module Tabular
|
|
103
136
|
end
|
104
137
|
@hash
|
105
138
|
end
|
106
|
-
|
107
|
-
|
139
|
+
|
140
|
+
|
108
141
|
private
|
109
|
-
|
142
|
+
|
110
143
|
# Handle common m/d/yy case that Date.parse dislikes
|
111
144
|
def parse_invalid_date(value)
|
112
145
|
return unless value
|
113
|
-
|
146
|
+
|
114
147
|
parts = value.split("/")
|
115
148
|
return unless parts.size == 3
|
116
149
|
|
@@ -128,7 +161,7 @@ module Tabular
|
|
128
161
|
elsif year < 1900 || year > 2050
|
129
162
|
return nil
|
130
163
|
end
|
131
|
-
|
164
|
+
|
132
165
|
Date.new(year, month, day)
|
133
166
|
end
|
134
167
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Object
|
2
|
+
def zero?
|
3
|
+
false
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class NilClass #:nodoc:
|
8
|
+
def zero?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class FalseClass #:nodoc:
|
14
|
+
def zero?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class TrueClass #:nodoc:
|
20
|
+
def zero?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class String #:nodoc:
|
26
|
+
def zero?
|
27
|
+
self == "0" || self[/^0+\.0+$/]
|
28
|
+
end
|
29
|
+
end
|