table_transform 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18bd452f05254c417d3deb30c03d61c9595d3494
4
- data.tar.gz: d998ab542023204734aca739c5caf91538307e91
3
+ metadata.gz: 49ec7d69f22f2697f744cbfde782c2d5bdf176ec
4
+ data.tar.gz: 9528fc0373c003fb1be323e00cea5718328e4a9e
5
5
  SHA512:
6
- metadata.gz: fb3c761f20419bccf2a954c05ee50b0baf01886bc87df0808071cf44147e6c8050bee7b8d0613b058a7f152dbe8ce6e02b4cd74ac08164e0dff3590028060bad
7
- data.tar.gz: 55bf3224205284a9b32904fa7f5d507be845878bd4eaf0f983a17e26456d6cccb24f1420a7df697866223b2df2964150404285e1db6497efe7904464172bcc1c
6
+ metadata.gz: d4001db9c5ccb63aedf406320d0e5accec5b8f788a247dc75f6981a1dbbdf205d29d56667bee5d0575302635177cba0b47beb863f6c44f57977fb9dd9762a2fc
7
+ data.tar.gz: 0e1697d357bcb66f417102b049d02bdd0d6ceb68d8fe1d4f60112d0a5c01746f9bba1a8c9edcd8dd1d71ef690fbd34cf206850020fabb5d0cd242dcae1834e55
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.6.0][] (2016-03-20)
10
+ * Added rename table column
11
+ * Harmonization of how to work with properties, Table and Column
12
+ * Deprecated old metadata functions (to be removed)
13
+
9
14
  ## [0.5.0][] (2016-03-13)
10
15
  * Table properties added
11
16
 
@@ -28,8 +33,9 @@ TableTransform is still in pre-release state. This means that its APIs and behav
28
33
  * Initial release including Table
29
34
 
30
35
  [Semver]: http://semver.org
31
- [Unreleased]: https://github.com/jonas-lantto/table_transform/compare/v0.5.0...HEAD
32
- [0.4.0]: https://github.com/jonas-lantto/table_transform/compare/v0.4.0...v0.5.0
36
+ [Unreleased]: https://github.com/jonas-lantto/table_transform/compare/v0.6.0...HEAD
37
+ [0.6.0]: https://github.com/jonas-lantto/table_transform/compare/v0.5.0...v0.6.0
38
+ [0.5.0]: https://github.com/jonas-lantto/table_transform/compare/v0.4.0...v0.5.0
33
39
  [0.4.0]: https://github.com/jonas-lantto/table_transform/compare/v0.3.0...v0.4.0
34
40
  [0.3.0]: https://github.com/jonas-lantto/table_transform/compare/v0.2.0...v0.3.0
35
41
  [0.2.0]: https://github.com/jonas-lantto/table_transform/compare/v0.1.0...v0.2.0
data/README.md CHANGED
@@ -46,6 +46,9 @@ Or install it yourself as:
46
46
  # Will remove given columns. One or several can be specified.
47
47
  t.delete_column('Name', 'Address')
48
48
 
49
+ # Rename column
50
+ r.rename_column('Age', 'Years')
51
+
49
52
  # Create a new Table with given column in specified order
50
53
  t.extract(%w(Length Name))
51
54
 
@@ -73,22 +76,38 @@ A row can access its values by column name e.g. `row['Name']`
73
76
  c = row['Col1'] # c = 'CHECK'
74
77
  c.include_any?(%w(AA EC DD))) # true
75
78
 
76
- ### Table properties
79
+ ### Properties
80
+ There are two categories of properties, Table and Column<br/>
81
+ Same interface for interacting with properties
82
+
83
+ # Update - add or update :name property in existing p
84
+ p.update({name: 'xxx'})
85
+
86
+ # Reset - resets p with given property, clearing previous properties
87
+ p.reset({name: 'xxx'})
88
+
89
+ # Delete
90
+ p.delete(:name)
91
+
92
+ # Each
93
+ p.each{|x| ...}
94
+
95
+ # Access
96
+ p[:name]
97
+
98
+ #### Table properties
77
99
  # Set table name - override default name
78
100
  t.table_properties.update({name: 'Table1'})
79
101
 
80
102
  # Set turn on/off auto filter button
81
103
  t.table_properties.update({auto_filter: false})
82
104
 
83
- ### Meta data
105
+ #### Column properties
84
106
  # Set format for one column
85
- t.set_metadata('Tax', {format: '0.0%'})
107
+ t.column_properties['Tax'].update({format: '0.0%'})
86
108
 
87
- # Extract metadata
88
- t.metadata['Tax'] # {format: '0.0%'}
89
-
90
- # Set format for multiple columns
91
- t.set_metadata(*%w(Income Tax Dept), {format: '#,##0'})
109
+ # Extract properties
110
+ t.column_properties['Tax'] # {format: '0.0%'}
92
111
 
93
112
  # Add meta data during add_column
94
113
  t.add_column('Tax', {format: '0.0%'}){|row| 0.25}
@@ -16,7 +16,7 @@ module TableTransform
16
16
  end
17
17
 
18
18
  def add_tab(name, table)
19
- set_formats(table.metadata) if table.is_a?(TableTransform::Table)
19
+ set_formats(table.column_properties) if table.is_a?(TableTransform::Table)
20
20
  create_table(name, table)
21
21
  end
22
22
 
@@ -46,7 +46,7 @@ module TableTransform
46
46
 
47
47
  auto_filter_size_correction = auto_filter_correct ? 3 : 0
48
48
  res = Array.new(data.first.map { |name|
49
- [name.to_s.size + auto_filter_size_correction, format_column_size(table.metadata[name][:format])].max
49
+ [name.to_s.size + auto_filter_size_correction, format_column_size(table.column_properties[name][:format])].max
50
50
  })
51
51
  data.each { |row|
52
52
  row.each_with_index { |cell, column_no|
@@ -56,21 +56,29 @@ module TableTransform
56
56
  res.map! { |x| [x, max_width].min }
57
57
  end
58
58
 
59
- def set_formats(metadata)
60
- metadata.each{|_,v|
59
+ def set_formats(column_properties)
60
+ # find all :formats across all columns
61
+ column_properties.each{|_,v|
61
62
  f = v[:format]
62
63
  @formats[f] ||= @workbook.add_format(:num_format => f) unless f.nil?
63
64
  }
64
65
  end
65
66
 
66
- def create_column_metadata(metadata, formats, formulas)
67
+ def create_column_metadata(column_properties, formats, formulas)
67
68
  res = []
68
- metadata.each{ |header_name, data|
69
- data_dup = data.dup
70
- data_dup[:format] = formats[data_dup[:format]] unless data_dup[:format].nil? #replace str format with excel representation
69
+ column_properties.each{ |header_name, data|
70
+ col_props = TableTransform::Properties.new data.to_h
71
+
72
+ #format (replace str format with excel representation)
73
+ col_props.update({format: formats[col_props[:format]]}) unless col_props[:format].nil?
74
+
75
+ #formula
71
76
  formula = formulas[header_name]
72
- data_dup.merge!({formula: formula}) unless formula.nil?
73
- res << {header: header_name}.merge(data_dup)
77
+ col_props.update({formula: formula}) unless formula.nil?
78
+
79
+ #header
80
+ col_props.update({header: header_name})
81
+ res << col_props.to_h
74
82
  }
75
83
  res
76
84
  end
@@ -80,8 +88,8 @@ module TableTransform
80
88
  data = table.to_a
81
89
  return if data.nil? or data.empty? # Create empty worksheet if no data
82
90
 
83
- properties = ExcelCreator::default_properties(name).update(table.table_properties.to_h)
84
- col_width = ExcelCreator::column_width(table, properties[:auto_filter])
91
+ table_properties = ExcelCreator::default_properties(name).update(table.table_properties.to_h)
92
+ col_width = ExcelCreator::column_width(table, table_properties[:auto_filter])
85
93
 
86
94
  header = data.shift
87
95
  data << [nil] * header.count if data.empty? # Add extra row if empty
@@ -89,10 +97,10 @@ module TableTransform
89
97
  worksheet.add_table(
90
98
  0, 0, data.count, header.count - 1,
91
99
  {
92
- :name => properties[:name],
100
+ :name => table_properties[:name],
93
101
  :data => data,
94
- :autofilter => properties[:auto_filter] ? 1 : 0,
95
- :columns => create_column_metadata(table.metadata, @formats, table.formulas)
102
+ :autofilter => table_properties[:auto_filter] ? 1 : 0,
103
+ :columns => create_column_metadata(table.column_properties, @formats, table.formulas)
96
104
  }
97
105
  )
98
106
 
@@ -1,10 +1,14 @@
1
+ require 'forwardable'
1
2
 
2
3
  module TableTransform
3
4
 
4
5
  class Properties
6
+ extend Forwardable
7
+ def_delegators :@props, :delete, :each, :[]
8
+
5
9
  def initialize(init_properties = {})
6
10
  validate(init_properties)
7
- @props = init_properties
11
+ @props = init_properties.clone
8
12
  end
9
13
 
10
14
  def validate(properties)
@@ -20,18 +24,11 @@ module TableTransform
20
24
  @props.merge! properties
21
25
  end
22
26
 
23
- def delete(prop_key)
24
- @props.delete(prop_key)
25
- end
26
-
27
27
  def reset(properties)
28
28
  validate(properties)
29
29
  @props = properties
30
30
  end
31
31
 
32
- def [](prop_key)
33
- raise "Property '#{prop_key}' does not exist" unless @props.include? prop_key
34
- @props[prop_key]
35
- end
36
32
  end
33
+
37
34
  end
@@ -11,6 +11,7 @@ module TableTransform
11
11
  class Table
12
12
  attr_reader :formulas
13
13
  attr_reader :table_properties
14
+ attr_reader :column_properties
14
15
 
15
16
  def self.create_from_file(file_name, sep = ',')
16
17
  rows = CSV.read(file_name, { :col_sep => sep })
@@ -33,9 +34,10 @@ module TableTransform
33
34
  @data_rows = rows.clone
34
35
  header = @data_rows.shift
35
36
  @column_indexes = create_column_name_binding(header)
36
- @metadata = header.zip( Array.new(header.size){{}} ).to_h
37
37
  @formulas = {}
38
38
  @table_properties = TableProperties.new(table_properties)
39
+ @column_properties = Hash.new{|hash, key| raise "No column with name '#{key}' exists"}
40
+ create_column_properties(*header,{})
39
41
 
40
42
  validate_header_uniqueness(header)
41
43
  validate_column_size
@@ -45,19 +47,22 @@ module TableTransform
45
47
  # Example:
46
48
  # set_metadata('Col1', {format: '#,##0'})
47
49
  def set_metadata(*columns, metadata)
48
- validate_column_names(*columns)
49
- validate_metadata_tags(metadata)
50
-
51
- columns.each{|col| @metadata[col] = metadata.clone}
50
+ warn 'set_metadata is deprecated. Use column_properties[] instead'
51
+ columns.each{|c| @column_properties[c].reset(metadata)}
52
52
  end
53
53
 
54
54
  # Returns meta data as Hash with header name as key
55
55
  def metadata
56
- @metadata.clone
56
+ warn 'metadata is deprecated. Use column_properties[] instead'
57
+ res = Hash.new
58
+ @column_properties.each{|k, v|
59
+ res.merge! ({k => v.to_h})
60
+ }
61
+ res
57
62
  end
58
63
 
59
- def add_column_formula(column, formula, metadata = {})
60
- add_column(column, metadata){nil}
64
+ def add_column_formula(column, formula, column_properties = {})
65
+ add_column(column, column_properties){nil}
61
66
  @formulas[column] = formula
62
67
  self # self chaining
63
68
  end
@@ -68,12 +73,12 @@ module TableTransform
68
73
  end
69
74
 
70
75
  # Add two tables
71
- # @throws if header or meta data do not match
76
+ # @throws if header or properties do not match
72
77
  def +(table)
73
78
  t2 = table.to_a
74
79
  t2_header = t2.shift
75
- raise 'Tables cannot be added due to header mismatch' unless @metadata.keys == t2_header
76
- raise 'Tables cannot be added due to meta data mismatch' unless @metadata == table.metadata
80
+ raise 'Tables cannot be added due to header mismatch' unless @column_properties.keys == t2_header
81
+ raise 'Tables cannot be added due to column properties mismatch' unless column_properties_eql? table.column_properties
77
82
  raise 'Tables cannot be added due to table properties mismatch' unless @table_properties.to_h == table.table_properties.to_h
78
83
  TableTransform::Table.new(self.to_a + t2)
79
84
  end
@@ -87,34 +92,33 @@ module TableTransform
87
92
  # @returns array of data arrays including header row
88
93
  def to_a
89
94
  res = @data_rows.clone
90
- res.unshift @metadata.keys.clone
95
+ res.unshift @column_properties.keys.clone
91
96
  end
92
97
 
93
98
  # @returns new table with specified columns specified in given header
94
99
  def extract(header)
95
- validate_column_names(*header)
100
+ validate_column_exist(*header)
96
101
  selected_cols = @column_indexes.values_at(*header)
97
102
  t = Table.new( @data_rows.inject([header]) {|res, row| (res << row.values_at(*selected_cols))}, @table_properties.to_h )
98
- t.metadata = t.metadata.keys.zip(@metadata.values_at(*header)).to_h
103
+ header.each{|h| t.column_properties[h].reset(@column_properties[h].to_h)}
99
104
  t.formulas = header.zip(@formulas.values_at(*header)).to_h
100
105
  t
101
106
  end
102
107
 
103
108
  # @returns new table with rows that match given block
104
109
  def filter
105
- Table.new( (@data_rows.select {|row| yield Row.new(@column_indexes, row)}.unshift @metadata.keys.clone), @table_properties.to_h )
110
+ Table.new( (@data_rows.select {|row| yield Row.new(@column_indexes, row)}.unshift @column_properties.keys.clone), @table_properties.to_h )
106
111
  end
107
112
 
108
113
  #adds a column with given name to the far right of the table
109
114
  #@throws if given column name already exists
110
- def add_column(name, metadata = {})
111
- raise "Column '#{name}' already exists" if @metadata.keys.include?(name)
112
- @metadata[name] = {}
115
+ def add_column(name, column_properties = {})
116
+ validate_column_absence(name)
117
+ create_column_properties(name, column_properties)
113
118
  @data_rows.each{|x|
114
119
  x << (yield Row.new(@column_indexes, x))
115
120
  }
116
121
  @column_indexes[name] = @column_indexes.size
117
- set_metadata(name, metadata)
118
122
  self # enable chaining
119
123
  end
120
124
 
@@ -129,19 +133,28 @@ module TableTransform
129
133
  end
130
134
 
131
135
  def delete_column(*names)
132
- validate_column_names(*names)
136
+ validate_column_exist(*names)
133
137
  names.each{|n|
134
- @metadata.delete(n)
138
+ @column_properties.delete(n)
135
139
  @formulas.delete(n)
136
140
  }
137
141
 
138
- selected_cols = @column_indexes.values_at(*@metadata.keys)
142
+ selected_cols = @column_indexes.values_at(*@column_properties.keys)
139
143
  @data_rows.map!{|row| row.values_at(*selected_cols)}
140
144
 
141
- @column_indexes = create_column_name_binding(@metadata.keys)
145
+ @column_indexes = create_column_name_binding(@column_properties.keys)
142
146
  self
143
147
  end
144
148
 
149
+ def rename_column(from, to)
150
+ validate_column_exist(from)
151
+ validate_column_absence(to)
152
+
153
+ @column_properties = @column_properties.map{|k,v| [k == from ? to : k, v] }.to_h
154
+ @formulas = @formulas.map{|k,v| [k == from ? to : k, v] }.to_h
155
+ @column_indexes = create_column_name_binding(@column_properties.keys)
156
+ end
157
+
145
158
  # Table row
146
159
  # Columns within row can be referenced by name, e.g. row['name']
147
160
  class Row
@@ -185,9 +198,23 @@ module TableTransform
185
198
  end
186
199
  end
187
200
 
201
+ # Column properties
202
+ class ColumnProperties < TableTransform::Properties
203
+ def validate(properties)
204
+ super
205
+ properties.each { |k, v|
206
+ case k
207
+ when :format
208
+ raise "Column property 'format' expected to be a non-empty string" unless v.is_a?(String) && !v.empty?
209
+ else
210
+ raise "Unknown column property '#{k}'"
211
+ end
212
+ }
213
+ end
214
+ end
215
+
188
216
 
189
217
  protected
190
- attr_writer :metadata
191
218
  attr_writer :formulas
192
219
 
193
220
  private
@@ -196,7 +223,16 @@ module TableTransform
196
223
  end
197
224
 
198
225
  def create_row(hash_values)
199
- @metadata.keys.inject([]) { |row, col| row << hash_values.fetch(col){raise "Value for column '#{col}' could not be found"} }
226
+ @column_properties.keys.inject([]) { |row, col| row << hash_values.fetch(col){raise "Value for column '#{col}' could not be found"} }
227
+ end
228
+
229
+ def create_column_properties(*header, properties)
230
+ header.each{|key| @column_properties.store(key, TableTransform::Table::ColumnProperties.new(properties))}
231
+ end
232
+
233
+ def column_properties_eql?(column_properties)
234
+ return false unless @column_properties.size == column_properties.size
235
+ @column_properties.each{|key, prop| return false unless prop.to_h == column_properties[key].to_h}
200
236
  end
201
237
 
202
238
  # @throws unless all header names are unique
@@ -207,24 +243,17 @@ module TableTransform
207
243
 
208
244
  # @throws unless all rows have same number of elements
209
245
  def validate_column_size
210
- @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}
246
+ @data_rows.each_with_index {|x, index| raise "Column size mismatch. On row #{index+1}. Size #{x.size} expected to be #{@column_properties.size}" if @column_properties.size != x.size}
211
247
  end
212
248
 
213
- def validate_column_names(*names)
214
- diff = names - @metadata.keys
249
+ def validate_column_exist(*names)
250
+ diff = names - @column_properties.keys
215
251
  raise raise "No column with name '#{diff.first}' exists" if diff.size > 0
216
252
  end
217
253
 
218
- def validate_metadata_tags(metadata)
219
- raise 'Metadata must be a hash' unless metadata.is_a?(Hash)
220
- metadata.each { |k, v|
221
- case k
222
- when :format
223
- raise "Meta tag 'format' expected to be a non-empty string" unless v.is_a?(String) && !v.empty?
224
- else
225
- raise "Unknown meta data tag '#{k}'"
226
- end
227
- }
254
+ def validate_column_absence(name)
255
+ raise "Column '#{name}' already exists" if @column_properties.keys.include?(name)
228
256
  end
257
+
229
258
  end
230
259
  end
@@ -1,3 +1,3 @@
1
1
  module TableTransform
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  end
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.5.0
4
+ version: 0.6.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-03-13 00:00:00.000000000 Z
11
+ date: 2016-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake