table_transform 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +35 -0
- data/lib/table_transform/excel_creator.rb +18 -7
- data/lib/table_transform/formula_helper.rb +36 -0
- data/lib/table_transform/table.rb +19 -1
- data/lib/table_transform/version.rb +1 -1
- data/lib/table_transform.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9eeed52b5a7aa9db9eea7f0e0f27a40ff2b932be
|
4
|
+
data.tar.gz: bad8620c79b09929f105d54894b4df931a8b778b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7837639dace38af311b85547f997b138812c3bba8d969a356936896f0b1a0be9e12ec22e7b86fbaa705f93a6efd55122d6a739339ad732a619965dfb6d058eaf
|
7
|
+
data.tar.gz: aa40106dd2c8e0c18129a60e50965a1e4e09eaf3d8305bb367aeab79166d6dc540636e9d0b771e9a4fb6f4e4c1c812593338a4309a2f10251a03f7f9d5b794a2
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,11 @@ TableTransform is still in pre-release state. This means that its APIs and behav
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.4.0][] (2016-03-05)
|
10
|
+
* Supports formulas in columns
|
11
|
+
* Helper functions to create formulas
|
12
|
+
* Column width estimates format size in calculation
|
13
|
+
|
9
14
|
## [0.3.0][] (2016-02-29)
|
10
15
|
* Added format capability for columns when published as Excel
|
11
16
|
* CodeClimate and test coverage added
|
@@ -21,5 +26,6 @@ TableTransform is still in pre-release state. This means that its APIs and behav
|
|
21
26
|
|
22
27
|
[Semver]: http://semver.org
|
23
28
|
[Unreleased]: https://github.com/jonas-lantto/table_transform/compare/v0.3.0...HEAD
|
29
|
+
[0.4.0]: https://github.com/jonas-lantto/table_transform/compare/v0.3.0...v0.4.0
|
24
30
|
[0.3.0]: https://github.com/jonas-lantto/table_transform/compare/v0.2.0...v0.3.0
|
25
31
|
[0.2.0]: https://github.com/jonas-lantto/table_transform/compare/v0.1.0...v0.2.0
|
data/README.md
CHANGED
@@ -86,6 +86,41 @@ A row can access its values by column name e.g. `row['Name']`
|
|
86
86
|
# Add meta data during add_column
|
87
87
|
t.add_column('Tax', {format: '0.0%'}){|row| 0.25}
|
88
88
|
|
89
|
+
### Formula
|
90
|
+
Formulas can be added to columns. They have to be in US locale.
|
91
|
+
Column values will be nil if inspected
|
92
|
+
|
93
|
+
# Add formula to column
|
94
|
+
t.add_column_formula('OnePlusOne', '1+1')
|
95
|
+
|
96
|
+
# Add formula to column with meta data
|
97
|
+
t.add_column_formula('OnePlusOne', '1+1' {format: '0.0'})
|
98
|
+
|
99
|
+
#### Formula helper
|
100
|
+
To aid when creating formulas there are a few helpers available
|
101
|
+
|
102
|
+
f = TableTransform::FormulaHelper # Namespace alias
|
103
|
+
|
104
|
+
# Column
|
105
|
+
f::column('price') # <=> [price]
|
106
|
+
t.add_column_formula('Total value', "#{f::column('price')}*f::column('volume')}")
|
107
|
+
|
108
|
+
# Table
|
109
|
+
f::table('Table1') # <=> Table1[]
|
110
|
+
|
111
|
+
# Text (will avoid the clutter of protecting " character)
|
112
|
+
f::text('No') # <=> "No"
|
113
|
+
|
114
|
+
# VLOOKUP, convenience function to extract values from another table
|
115
|
+
# Finds the street name in table Address for name specified in column Name
|
116
|
+
f::vlookup(f::column('Name'), 'Address', 'Street'))
|
117
|
+
f::vlookup('[Name]', 'Address', 'Street'))
|
118
|
+
|
119
|
+
# Finds the number of pets in Table Pets for name specified in column Name. If name does not exist it defaults to 0
|
120
|
+
f::vlookup(f::column('Name'), 'Pets', 'nPets', 0))
|
121
|
+
f::vlookup('[Name]', 'Pets', 'nPets', 0))
|
122
|
+
|
123
|
+
|
89
124
|
### Publish
|
90
125
|
# Export as array
|
91
126
|
t.to_a
|
@@ -23,13 +23,22 @@ module TableTransform
|
|
23
23
|
#################################
|
24
24
|
private
|
25
25
|
|
26
|
+
# estimated column width of format
|
27
|
+
def self.format_column_size(format)
|
28
|
+
return 0 if format.nil?
|
29
|
+
f = format.gsub(/\[.*?\]/, '').split(';')
|
30
|
+
f.map{|x| x.size}.max
|
31
|
+
end
|
32
|
+
|
26
33
|
# @return array with column width per column
|
27
|
-
def self.column_width(
|
28
|
-
return []
|
34
|
+
def self.column_width(table, auto_filter_correct = true, max_width = 100)
|
35
|
+
return [] unless table.is_a? TableTransform::Table
|
36
|
+
data = table.to_a
|
29
37
|
|
30
38
|
auto_filter_size_correction = auto_filter_correct ? 3 : 0
|
31
|
-
|
32
|
-
|
39
|
+
res = Array.new(data.first.map { |name|
|
40
|
+
[name.to_s.size + auto_filter_size_correction, format_column_size(table.metadata[name][:format])].max
|
41
|
+
})
|
33
42
|
data.each { |row|
|
34
43
|
row.each_with_index { |cell, column_no|
|
35
44
|
res[column_no] = [cell.to_s.size, res[column_no]].max
|
@@ -45,11 +54,13 @@ module TableTransform
|
|
45
54
|
}
|
46
55
|
end
|
47
56
|
|
48
|
-
def create_column_metadata(metadata, formats)
|
57
|
+
def create_column_metadata(metadata, formats, formulas)
|
49
58
|
res = []
|
50
59
|
metadata.each{ |header_name, data|
|
51
60
|
data_dup = data.dup
|
52
61
|
data_dup[:format] = formats[data_dup[:format]] unless data_dup[:format].nil? #replace str format with excel representation
|
62
|
+
formula = formulas[header_name]
|
63
|
+
data_dup.merge!({formula: formula}) unless formula.nil?
|
53
64
|
res << {header: header_name}.merge(data_dup)
|
54
65
|
}
|
55
66
|
res
|
@@ -61,7 +72,7 @@ module TableTransform
|
|
61
72
|
return if data.nil? or data.empty? # Create empty worksheet if no data
|
62
73
|
|
63
74
|
auto_filter = true
|
64
|
-
col_width = ExcelCreator::column_width(
|
75
|
+
col_width = ExcelCreator::column_width(table, auto_filter)
|
65
76
|
|
66
77
|
header = data.shift
|
67
78
|
data << [nil] * header.count if data.empty? # Add extra row if empty
|
@@ -72,7 +83,7 @@ module TableTransform
|
|
72
83
|
:name => name.tr(' ', '_'),
|
73
84
|
:data => data,
|
74
85
|
:autofilter => auto_filter ? 1 : 0,
|
75
|
-
:columns => create_column_metadata(table.metadata, @formats)
|
86
|
+
:columns => create_column_metadata(table.metadata, @formats, table.formulas)
|
76
87
|
}
|
77
88
|
)
|
78
89
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module TableTransform
|
2
|
+
|
3
|
+
# Help functions to create formulas
|
4
|
+
module FormulaHelper
|
5
|
+
# Reference a table
|
6
|
+
def self.table(name)
|
7
|
+
"#{name}[]"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Reference a column in same table
|
11
|
+
def self.column(name)
|
12
|
+
"[#{name}]"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Quotes text to be used inside formulas
|
16
|
+
def self.text(txt)
|
17
|
+
"\"#{txt}\""
|
18
|
+
end
|
19
|
+
|
20
|
+
# vlookup helper, search for a value in another table with return column specified by name
|
21
|
+
# Use other help functions to create an excel expression
|
22
|
+
#
|
23
|
+
# @param [excel expression] search_value, value to lookup
|
24
|
+
# @param [string] table_name, name of the table to search in
|
25
|
+
# @param [string] return_col_name, name of the return column in given table
|
26
|
+
# @param [excel expression] default, value if nothing was found, otherwise Excel will show N/A
|
27
|
+
def self.vlookup(search_value, table_name, return_col_name, default = nil)
|
28
|
+
vlookup = "VLOOKUP(#{search_value},#{table(table_name)},COLUMN(#{table_name}[[#Headers],#{column(return_col_name)}]),FALSE)"
|
29
|
+
|
30
|
+
# Workaround
|
31
|
+
# Should be possible to write "IFNA(#{vlookup},#{default})"
|
32
|
+
# but Excel will error with #Name? until formula is updated by hand
|
33
|
+
default.nil? ? vlookup : "IF(ISNA(#{vlookup}),#{default},#{vlookup})"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -8,6 +8,8 @@ module TableTransform
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class Table
|
11
|
+
attr_reader :formulas
|
12
|
+
|
11
13
|
def self.create_from_file(file_name, sep = ',')
|
12
14
|
rows = CSV.read(file_name, { :col_sep => sep })
|
13
15
|
raise "'#{file_name}' contains no data" if rows.empty?
|
@@ -30,6 +32,7 @@ module TableTransform
|
|
30
32
|
header = @data_rows.shift
|
31
33
|
@column_indexes = create_column_name_binding(header)
|
32
34
|
@metadata = header.zip( Array.new(header.size){{}} ).to_h
|
35
|
+
@formulas = {}
|
33
36
|
|
34
37
|
validate_header_uniqueness(header)
|
35
38
|
validate_column_size
|
@@ -50,6 +53,12 @@ module TableTransform
|
|
50
53
|
@metadata.clone
|
51
54
|
end
|
52
55
|
|
56
|
+
def add_column_formula(column, formula, metadata = {})
|
57
|
+
add_column(column, metadata){nil}
|
58
|
+
@formulas[column] = formula
|
59
|
+
self # self chaining
|
60
|
+
end
|
61
|
+
|
53
62
|
def << (hash_values)
|
54
63
|
@data_rows << create_row(hash_values)
|
55
64
|
self
|
@@ -83,6 +92,7 @@ module TableTransform
|
|
83
92
|
selected_cols = @column_indexes.values_at(*header)
|
84
93
|
t = Table.new( @data_rows.inject([header]) {|res, row| (res << row.values_at(*selected_cols))} )
|
85
94
|
t.metadata = t.metadata.keys.zip(@metadata.values_at(*header)).to_h
|
95
|
+
t.formulas = header.zip(@formulas.values_at(*header)).to_h
|
86
96
|
t
|
87
97
|
end
|
88
98
|
|
@@ -105,6 +115,7 @@ module TableTransform
|
|
105
115
|
end
|
106
116
|
|
107
117
|
def change_column(name)
|
118
|
+
raise "Column with formula('#{name}') cannot be changed" if @formulas[name]
|
108
119
|
index = Util::get_col_index(name, @column_indexes)
|
109
120
|
@data_rows.each{|r|
|
110
121
|
r[index] = yield Row.new(@column_indexes, r)
|
@@ -115,7 +126,10 @@ module TableTransform
|
|
115
126
|
|
116
127
|
def delete_column(*names)
|
117
128
|
validate_column_names(*names)
|
118
|
-
names.each{|n|
|
129
|
+
names.each{|n|
|
130
|
+
@metadata.delete(n)
|
131
|
+
@formulas.delete(n)
|
132
|
+
}
|
119
133
|
|
120
134
|
selected_cols = @column_indexes.values_at(*@metadata.keys)
|
121
135
|
@data_rows.map!{|row| row.values_at(*selected_cols)}
|
@@ -124,6 +138,8 @@ module TableTransform
|
|
124
138
|
self
|
125
139
|
end
|
126
140
|
|
141
|
+
# Table row
|
142
|
+
# Columns within row can be referenced by name, e.g. row['name']
|
127
143
|
class Row
|
128
144
|
|
129
145
|
def initialize(cols, row)
|
@@ -140,6 +156,7 @@ module TableTransform
|
|
140
156
|
|
141
157
|
end
|
142
158
|
|
159
|
+
# Cell within Table::Row
|
143
160
|
class Cell < String
|
144
161
|
# @returns true if this cell includes any of the given values in list
|
145
162
|
def include_any?(list)
|
@@ -149,6 +166,7 @@ module TableTransform
|
|
149
166
|
|
150
167
|
protected
|
151
168
|
attr_writer :metadata
|
169
|
+
attr_writer :formulas
|
152
170
|
|
153
171
|
private
|
154
172
|
def create_column_name_binding(header_row)
|
data/lib/table_transform.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_transform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Lantto
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- Rakefile
|
102
102
|
- lib/table_transform.rb
|
103
103
|
- lib/table_transform/excel_creator.rb
|
104
|
+
- lib/table_transform/formula_helper.rb
|
104
105
|
- lib/table_transform/table.rb
|
105
106
|
- lib/table_transform/version.rb
|
106
107
|
- table_transform.gemspec
|