table_transform 0.3.0 → 0.4.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: 91c96009ab39b8d65825644044ebd64e75b85994
4
- data.tar.gz: 7f6c16447953380214f627ce735bbe90bedcd1dd
3
+ metadata.gz: 9eeed52b5a7aa9db9eea7f0e0f27a40ff2b932be
4
+ data.tar.gz: bad8620c79b09929f105d54894b4df931a8b778b
5
5
  SHA512:
6
- metadata.gz: 3cf48bcd2ed716bc0c0c2c00d43f8d6d2bd6165f790c75bfb20e496a2aa7afe223012f1a91495d0d77c36973c42c9f6ccfcb5620efc5e7f79a72ae57a1427bf3
7
- data.tar.gz: f3e61219d7987ef67a8070cd6e11228c0ab138a7475d2890e20a12e2c03737bf0fca5522b44804ab21f22505d0ad805b40f951d672cf2853217ced602481764c
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(data, auto_filter_correct = true, max_width = 100)
28
- return [] if data.empty?
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
- res = Array.new(data.first.map { |x| x.to_s.size + auto_filter_size_correction })
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(data, auto_filter)
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| @metadata.delete(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)
@@ -1,3 +1,3 @@
1
1
  module TableTransform
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -1,3 +1,4 @@
1
1
  require 'table_transform/version'
2
2
  require 'table_transform/table'
3
+ require 'table_transform/formula_helper'
3
4
  require 'table_transform/excel_creator'
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.3.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-02-29 00:00:00.000000000 Z
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