table_transform 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/CHANGELOG.md +10 -8
- data/README.md +31 -1
- data/lib/table_transform/excel_creator.rb +24 -3
- data/lib/table_transform/table.rb +71 -29
- data/lib/table_transform/version.rb +1 -1
- data/table_transform.gemspec +9 -7
- metadata +20 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91c96009ab39b8d65825644044ebd64e75b85994
|
4
|
+
data.tar.gz: 7f6c16447953380214f627ce735bbe90bedcd1dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cf48bcd2ed716bc0c0c2c00d43f8d6d2bd6165f790c75bfb20e496a2aa7afe223012f1a91495d0d77c36973c42c9f6ccfcb5620efc5e7f79a72ae57a1427bf3
|
7
|
+
data.tar.gz: f3e61219d7987ef67a8070cd6e11228c0ab138a7475d2890e20a12e2c03737bf0fca5522b44804ab21f22505d0ad805b40f951d672cf2853217ced602481764c
|
data/.travis.yml
CHANGED
@@ -1,3 +1,8 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
|
3
|
+
- 2.1.2
|
4
|
+
- 2.2.2
|
5
|
+
addons:
|
6
|
+
code_climate:
|
7
|
+
repo_token:
|
8
|
+
secure: G4bpiNUwVTTksk5nncmXcjMWh84uB3YbZwZsCqBROpjHOwaWcHDDID7OPWbl8Yp3x23Q9tj5oEI1i8+zei+jsk1AM5nB30XgAbIVQtv0qcahnDLrMNpWs4ubvBEw3yjwKRslqCD7WDC/lk8eYYmjJ6GyaIDeW8rJbITIkHTk4tKCjXFQRinUClWFfgg+rJSTyUqNqo3+aPaxDUvSeermOKK3jTQp9y1IsRFEbwtLUqliZ3rCgvhj43H4pQ0gDqsmtDqnuHgKBikm9D7+Gf6SbFyAMfy+XpHWlEXLjwwUVlTcSASB6cQ/v0ilJlLSzgaGoTgb6nBbLA3q/cvWTS3O5QEYfWnhzIlmoDBZXVRHWTcR1WpIXF10J5KT/UH+nHCOFGIefriMPWr6LZou1LMZrjY5hJLCeXxgM5xBF3MTAgiQd9cLlzzirCGzN9GGBOZsdDENG74IZPGpmKUPBm7OXjsrLc7rRIoPg92bMuDlBejU0RUWMDTjmrLvgLJM6R6ZdWQ6Ahf9YkLM7apDtVj3DFjOBX2oQuynq9ZuZ9dfhu49MME/7qeIQZD6HQqZixq9Zn8n2tFFvAMrIbeZKc5lZz5TumHiukouh6Jmq9e4issca9J4hV/hFlQ30Ds2plabbgzliISQlQEGh0oUBog3sW1prNnxkcfN+gO7uCHTBaI=
|
data/CHANGELOG.md
CHANGED
@@ -6,18 +6,20 @@ TableTransform is still in pre-release state. This means that its APIs and behav
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
-
## [0.
|
9
|
+
## [0.3.0][] (2016-02-29)
|
10
|
+
* Added format capability for columns when published as Excel
|
11
|
+
* CodeClimate and test coverage added
|
12
|
+
* Ruby >= 2.1 verified with TravisCI
|
13
|
+
* Improved documentation
|
10
14
|
|
15
|
+
## [0.2.0][] (2016-02-16)
|
11
16
|
* Added ability to publish Tables in *Microsoft Excel* format
|
12
|
-
|
13
|
-
Fixes:
|
14
|
-
|
15
|
-
* Require at least non empty header row to be present in Table
|
17
|
+
* [FIX] Require at least non empty header row to be present in Table
|
16
18
|
|
17
19
|
## 0.1.0 (2016-02-10)
|
18
|
-
|
19
20
|
* Initial release including Table
|
20
21
|
|
21
22
|
[Semver]: http://semver.org
|
22
|
-
[Unreleased]: https://github.com/jonas-lantto/table_transform/compare/v0.
|
23
|
-
[0.
|
23
|
+
[Unreleased]: https://github.com/jonas-lantto/table_transform/compare/v0.3.0...HEAD
|
24
|
+
[0.3.0]: https://github.com/jonas-lantto/table_transform/compare/v0.2.0...v0.3.0
|
25
|
+
[0.2.0]: https://github.com/jonas-lantto/table_transform/compare/v0.1.0...v0.2.0
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
[](http://badge.fury.io/rb/table_transform)
|
3
3
|
[](https://travis-ci.org/jonas-lantto/table_transform)
|
4
4
|
[](https://codeclimate.com/github/jonas-lantto/table_transform)
|
5
|
+
[](https://codeclimate.com/github/jonas-lantto/table_transform/coverage)
|
6
|
+
[](https://codeclimate.com/github/jonas-lantto/table_transform)
|
5
7
|
|
6
8
|
Utility to work with csv type data in a name safe environment with utilities to transform data
|
7
9
|
|
@@ -54,8 +56,36 @@ Or install it yourself as:
|
|
54
56
|
t1 = TableTransform::Table::create_empty(%w(Col1 Col2))
|
55
57
|
t2 = TableTransform::Table::create_empty(%w(Col1 Col2))
|
56
58
|
t3 = t1 + t2
|
57
|
-
|
59
|
+
|
60
|
+
### Inspect and traverse
|
61
|
+
# Loop through all rows
|
62
|
+
t.each_row{|row| puts row['Rebate'].to_f * row['Price'].to_f}
|
63
|
+
|
64
|
+
#### Table::Row
|
65
|
+
`Table::each_row` return a `Table::Row`<br/>
|
66
|
+
A row can access its values by column name e.g. `row['Name']`
|
67
|
+
|
68
|
+
#### Table:Cell
|
69
|
+
# Table::Cell < String
|
70
|
+
row['operating_system'].downcase == 'centos'
|
71
|
+
|
72
|
+
# include_any?
|
73
|
+
c = row['Col1'] # c = 'CHECK'
|
74
|
+
c.include_any?(%w(AA EC DD))) # true
|
75
|
+
|
76
|
+
### Meta data
|
77
|
+
# Set format for one column
|
78
|
+
t.set_metadata('Tax', {format: '0.0%'})
|
58
79
|
|
80
|
+
# Extract metadata
|
81
|
+
t.metadata['Tax'] # {format: '0.0%'}
|
82
|
+
|
83
|
+
# Set format for multiple columns
|
84
|
+
t.set_metadata(*%w(Income Tax Dept), {format: '#,##0'})
|
85
|
+
|
86
|
+
# Add meta data during add_column
|
87
|
+
t.add_column('Tax', {format: '0.0%'}){|row| 0.25}
|
88
|
+
|
59
89
|
### Publish
|
60
90
|
# Export as array
|
61
91
|
t.to_a
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'write_xlsx'
|
2
|
+
require_relative 'table'
|
2
3
|
|
3
4
|
|
4
5
|
module TableTransform
|
@@ -7,6 +8,7 @@ module TableTransform
|
|
7
8
|
class ExcelCreator
|
8
9
|
def initialize(filename)
|
9
10
|
@workbook = WriteXLSX.new(filename)
|
11
|
+
@formats = {}
|
10
12
|
end
|
11
13
|
|
12
14
|
def create!
|
@@ -14,7 +16,8 @@ module TableTransform
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def add_tab(name, table)
|
17
|
-
|
19
|
+
set_formats(table.metadata) if table.is_a?(TableTransform::Table)
|
20
|
+
create_table(name, table)
|
18
21
|
end
|
19
22
|
|
20
23
|
#################################
|
@@ -35,8 +38,26 @@ module TableTransform
|
|
35
38
|
res.map! { |x| [x, max_width].min }
|
36
39
|
end
|
37
40
|
|
38
|
-
def
|
41
|
+
def set_formats(metadata)
|
42
|
+
metadata.each{|_,v|
|
43
|
+
f = v[:format]
|
44
|
+
@formats[f] ||= @workbook.add_format(:num_format => f) unless f.nil?
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_column_metadata(metadata, formats)
|
49
|
+
res = []
|
50
|
+
metadata.each{ |header_name, data|
|
51
|
+
data_dup = data.dup
|
52
|
+
data_dup[:format] = formats[data_dup[:format]] unless data_dup[:format].nil? #replace str format with excel representation
|
53
|
+
res << {header: header_name}.merge(data_dup)
|
54
|
+
}
|
55
|
+
res
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_table(name, table)
|
39
59
|
worksheet = @workbook.add_worksheet(name)
|
60
|
+
data = table.to_a
|
40
61
|
return if data.nil? or data.empty? # Create empty worksheet if no data
|
41
62
|
|
42
63
|
auto_filter = true
|
@@ -51,7 +72,7 @@ module TableTransform
|
|
51
72
|
:name => name.tr(' ', '_'),
|
52
73
|
:data => data,
|
53
74
|
:autofilter => auto_filter ? 1 : 0,
|
54
|
-
:columns =>
|
75
|
+
:columns => create_column_metadata(table.metadata, @formats)
|
55
76
|
}
|
56
77
|
)
|
57
78
|
|
@@ -25,25 +25,43 @@ module TableTransform
|
|
25
25
|
# @throws if column size for each row match
|
26
26
|
def initialize(rows)
|
27
27
|
raise 'Table required to have at least a header row' if (rows.nil? or rows.empty?)
|
28
|
+
|
28
29
|
@data_rows = rows.clone
|
29
|
-
|
30
|
-
@column_indexes = create_column_name_binding(
|
30
|
+
header = @data_rows.shift
|
31
|
+
@column_indexes = create_column_name_binding(header)
|
32
|
+
@metadata = header.zip( Array.new(header.size){{}} ).to_h
|
31
33
|
|
32
|
-
validate_header_uniqueness
|
34
|
+
validate_header_uniqueness(header)
|
33
35
|
validate_column_size
|
34
36
|
end
|
35
37
|
|
38
|
+
# Sets metadata for given columns
|
39
|
+
# Example:
|
40
|
+
# set_metadata('Col1', {format: '#,##0'})
|
41
|
+
def set_metadata(*columns, metadata)
|
42
|
+
validate_column_names(*columns)
|
43
|
+
validate_metadata_tags(metadata)
|
44
|
+
|
45
|
+
columns.each{|col| @metadata[col] = metadata.clone}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns meta data as Hash with header name as key
|
49
|
+
def metadata
|
50
|
+
@metadata.clone
|
51
|
+
end
|
52
|
+
|
36
53
|
def << (hash_values)
|
37
54
|
@data_rows << create_row(hash_values)
|
38
55
|
self
|
39
56
|
end
|
40
57
|
|
41
58
|
# Add two tables
|
42
|
-
# @throws if header do not match
|
59
|
+
# @throws if header or meta data do not match
|
43
60
|
def +(table)
|
44
61
|
t2 = table.to_a
|
45
62
|
t2_header = t2.shift
|
46
|
-
raise 'Tables cannot be added due to header mismatch' if @
|
63
|
+
raise 'Tables cannot be added due to header mismatch' if @metadata.keys != t2_header
|
64
|
+
raise 'Tables cannot be added due to meta data mismatch' if @metadata != table.metadata
|
47
65
|
TableTransform::Table.new(self.to_a + t2)
|
48
66
|
end
|
49
67
|
|
@@ -56,29 +74,33 @@ module TableTransform
|
|
56
74
|
# @returns array of data arrays including header row
|
57
75
|
def to_a
|
58
76
|
res = @data_rows.clone
|
59
|
-
res.unshift @
|
77
|
+
res.unshift @metadata.keys.clone
|
60
78
|
end
|
61
79
|
|
62
80
|
# @returns new table with specified columns specified in given header
|
63
81
|
def extract(header)
|
64
|
-
|
65
|
-
|
82
|
+
validate_column_names(*header)
|
83
|
+
selected_cols = @column_indexes.values_at(*header)
|
84
|
+
t = Table.new( @data_rows.inject([header]) {|res, row| (res << row.values_at(*selected_cols))} )
|
85
|
+
t.metadata = t.metadata.keys.zip(@metadata.values_at(*header)).to_h
|
86
|
+
t
|
66
87
|
end
|
67
88
|
|
68
89
|
# @returns new table with rows that match given block
|
69
90
|
def filter
|
70
|
-
Table.new( @data_rows.select {|row| yield Row.new(@column_indexes, row)}.unshift @
|
91
|
+
Table.new( @data_rows.select {|row| yield Row.new(@column_indexes, row)}.unshift @metadata.keys.clone )
|
71
92
|
end
|
72
93
|
|
73
94
|
#adds a column with given name to the far right of the table
|
74
95
|
#@throws if given column name already exists
|
75
|
-
def add_column(name)
|
76
|
-
raise "Column '#{name}' already exists" if @
|
77
|
-
@
|
96
|
+
def add_column(name, metadata = {})
|
97
|
+
raise "Column '#{name}' already exists" if @metadata.keys.include?(name)
|
98
|
+
@metadata[name] = {}
|
78
99
|
@data_rows.each{|x|
|
79
100
|
x << (yield Row.new(@column_indexes, x))
|
80
101
|
}
|
81
102
|
@column_indexes[name] = @column_indexes.size
|
103
|
+
set_metadata(name, metadata)
|
82
104
|
self # enable chaining
|
83
105
|
end
|
84
106
|
|
@@ -87,32 +109,21 @@ module TableTransform
|
|
87
109
|
@data_rows.each{|r|
|
88
110
|
r[index] = yield Row.new(@column_indexes, r)
|
89
111
|
}
|
112
|
+
|
90
113
|
self # enable chaining
|
91
114
|
end
|
92
115
|
|
93
116
|
def delete_column(*names)
|
94
|
-
|
95
|
-
|
96
|
-
delete_indexes.each{|i| @header.delete_at(i)}
|
117
|
+
validate_column_names(*names)
|
118
|
+
names.each{|n| @metadata.delete(n)}
|
97
119
|
|
98
|
-
selected_cols = @
|
120
|
+
selected_cols = @column_indexes.values_at(*@metadata.keys)
|
99
121
|
@data_rows.map!{|row| row.values_at(*selected_cols)}
|
100
122
|
|
101
|
-
@column_indexes = create_column_name_binding(@
|
123
|
+
@column_indexes = create_column_name_binding(@metadata.keys)
|
102
124
|
self
|
103
125
|
end
|
104
126
|
|
105
|
-
# @throws unless all header names are unique
|
106
|
-
private def validate_header_uniqueness
|
107
|
-
dup = @header.select{ |e| @header.count(e) > 1 }.uniq
|
108
|
-
raise "Column #{dup.map{|x| "'#{x}'"}.join(' and ')} not unique" if dup.size > 0
|
109
|
-
end
|
110
|
-
|
111
|
-
# @throws unless all rows have same number of elements
|
112
|
-
private def validate_column_size
|
113
|
-
@data_rows.each_with_index {|x, index| raise "Column size mismatch. On row #{index+1}. Size #{x.size} expected to be #{@header.size}" if @header.size != x.size}
|
114
|
-
end
|
115
|
-
|
116
127
|
class Row
|
117
128
|
|
118
129
|
def initialize(cols, row)
|
@@ -136,13 +147,44 @@ module TableTransform
|
|
136
147
|
end
|
137
148
|
end
|
138
149
|
|
150
|
+
protected
|
151
|
+
attr_writer :metadata
|
152
|
+
|
139
153
|
private
|
140
154
|
def create_column_name_binding(header_row)
|
141
155
|
header_row.map.with_index{ |x, i| [x, i] }.to_h
|
142
156
|
end
|
143
157
|
|
144
158
|
def create_row(hash_values)
|
145
|
-
@
|
159
|
+
@metadata.keys.inject([]) { |row, col| row << hash_values.fetch(col){raise "Value for column '#{col}' could not be found"} }
|
160
|
+
end
|
161
|
+
|
162
|
+
# @throws unless all header names are unique
|
163
|
+
def validate_header_uniqueness(header)
|
164
|
+
dup = header.select{ |e| header.count(e) > 1 }.uniq
|
165
|
+
raise "Column(s) not unique: #{dup.map{|x| "'#{x}'"}.join(', ')}" if dup.size > 0
|
166
|
+
end
|
167
|
+
|
168
|
+
# @throws unless all rows have same number of elements
|
169
|
+
def validate_column_size
|
170
|
+
@data_rows.each_with_index {|x, index| raise "Column size mismatch. On row #{index+1}. Size #{x.size} expected to be #{@metadata.size}" if @metadata.size != x.size}
|
171
|
+
end
|
172
|
+
|
173
|
+
def validate_column_names(*names)
|
174
|
+
diff = names - @metadata.keys
|
175
|
+
raise raise "No column with name '#{diff.first}' exists" if diff.size > 0
|
176
|
+
end
|
177
|
+
|
178
|
+
def validate_metadata_tags(metadata)
|
179
|
+
raise 'Metadata must be a hash' unless metadata.is_a?(Hash)
|
180
|
+
metadata.each { |k, v|
|
181
|
+
case k
|
182
|
+
when :format
|
183
|
+
raise "Meta tag 'format' expected to be a non-empty string" unless v.is_a?(String) && !v.empty?
|
184
|
+
else
|
185
|
+
raise "Unknown meta data tag '#{k}'"
|
186
|
+
end
|
187
|
+
}
|
146
188
|
end
|
147
189
|
end
|
148
190
|
end
|
data/table_transform.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ['Jonas Lantto']
|
10
10
|
spec.email = ['j@lantto.net']
|
11
11
|
|
12
|
-
spec.summary = %q{
|
13
|
-
spec.description = %q{}
|
12
|
+
spec.summary = %q{Transforms csv like data to Excel}
|
13
|
+
spec.description = %q{Utility to work with csv type data in a name safe environment with utilities to transform data}
|
14
14
|
spec.homepage = 'https://github.com/jonas-lantto/table_transform'
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
@@ -19,9 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.
|
23
|
-
|
24
|
-
spec.add_development_dependency '
|
25
|
-
spec.add_development_dependency '
|
26
|
-
spec.add_development_dependency '
|
22
|
+
spec.required_ruby_version = '>= 2.1'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
26
|
+
spec.add_development_dependency 'write_xlsx', '~> 0.83'
|
27
|
+
spec.add_development_dependency 'roo', '~> 2.3'
|
28
|
+
spec.add_development_dependency 'codeclimate-test-reporter','~> 0.5.0'
|
27
29
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_transform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.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-02-
|
11
|
+
date: 2016-02-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.10'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.10'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,7 +66,22 @@ dependencies:
|
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
68
|
version: '2.3'
|
83
|
-
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: codeclimate-test-reporter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.5.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.5.0
|
83
|
+
description: Utility to work with csv type data in a name safe environment with utilities
|
84
|
+
to transform data
|
84
85
|
email:
|
85
86
|
- j@lantto.net
|
86
87
|
executables: []
|
@@ -115,7 +116,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
115
116
|
requirements:
|
116
117
|
- - ">="
|
117
118
|
- !ruby/object:Gem::Version
|
118
|
-
version: '
|
119
|
+
version: '2.1'
|
119
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
121
|
requirements:
|
121
122
|
- - ">="
|
@@ -126,6 +127,5 @@ rubyforge_project:
|
|
126
127
|
rubygems_version: 2.4.8
|
127
128
|
signing_key:
|
128
129
|
specification_version: 4
|
129
|
-
summary:
|
130
|
-
to transform data
|
130
|
+
summary: Transforms csv like data to Excel
|
131
131
|
test_files: []
|