workbook 0.4.6.0 → 0.4.7

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +14 -15
  4. data/lib/workbook.rb +22 -11
  5. data/lib/workbook/book.rb +47 -25
  6. data/lib/workbook/cell.rb +20 -26
  7. data/lib/workbook/generatetypes.rb +14 -0
  8. data/lib/workbook/modules/cache.rb +52 -0
  9. data/lib/workbook/modules/{table_diff_sort.rb → diff_sort.rb} +64 -16
  10. data/lib/workbook/modules/raw_objects_storage.rb +7 -2
  11. data/lib/workbook/readers/ods_reader.rb +1 -1
  12. data/lib/workbook/readers/xls_reader.rb +55 -55
  13. data/lib/workbook/readers/xls_shared.rb +47 -0
  14. data/lib/workbook/readers/xlsx_reader.rb +34 -153
  15. data/lib/workbook/row.rb +47 -4
  16. data/lib/workbook/sheet.rb +4 -0
  17. data/lib/workbook/table.rb +36 -16
  18. data/lib/workbook/types/Date.rb +9 -0
  19. data/lib/workbook/types/False.rb +0 -0
  20. data/lib/workbook/types/FalseClass.rb +9 -0
  21. data/lib/workbook/types/Nil.rb +0 -0
  22. data/lib/workbook/types/NilClass.rb +9 -0
  23. data/lib/workbook/types/Numeric.rb +9 -0
  24. data/lib/workbook/types/String.rb +9 -0
  25. data/lib/workbook/types/Time.rb +9 -0
  26. data/lib/workbook/types/True.rb +0 -0
  27. data/lib/workbook/types/TrueClass.rb +9 -0
  28. data/lib/workbook/version.rb +1 -1
  29. data/lib/workbook/writers/html_writer.rb +40 -18
  30. data/lib/workbook/writers/xls_writer.rb +47 -5
  31. data/lib/workbook/writers/xlsx_writer.rb +123 -0
  32. data/test/artifacts/bigtable.xls +0 -0
  33. data/test/artifacts/bigtable.xlsx +0 -0
  34. data/test/artifacts/simple_sheet.xlsx +0 -0
  35. data/test/artifacts/simple_sheet_many_sheets.xls +0 -0
  36. data/test/test_book.rb +50 -2
  37. data/test/test_cell.rb +1 -1
  38. data/test/test_format.rb +8 -0
  39. data/test/test_modules_cache.rb +68 -0
  40. data/test/test_modules_table_diff_sort.rb +12 -1
  41. data/test/test_readers_xls_reader.rb +6 -0
  42. data/test/test_readers_xlsx_reader.rb +10 -9
  43. data/test/test_row.rb +65 -8
  44. data/test/test_sheet.rb +8 -0
  45. data/test/test_table.rb +48 -0
  46. data/test/test_writers_html_writer.rb +18 -8
  47. data/test/test_writers_xls_writer.rb +90 -0
  48. data/test/test_writers_xlsx_writer.rb +153 -0
  49. data/workbook.gemspec +9 -7
  50. metadata +71 -31
@@ -11,8 +11,7 @@ module Workbook
11
11
 
12
12
  # Returns true if there is a template for a certain class, otherwise false
13
13
  def has_raw_for? raw_object_class
14
- raws.each { |tc,t| return true if tc == raw_object_class}
15
- return false
14
+ available_raws.include? raw_object_class
16
15
  end
17
16
 
18
17
  # Returns raw data stored for a type of raw object (if available)
@@ -27,6 +26,12 @@ module Workbook
27
26
  @raws = {}
28
27
  end
29
28
 
29
+ # Lists the classes for which raws are available
30
+ # @return Array<Object> array with the classes available
31
+ def available_raws
32
+ raws.keys
33
+ end
34
+
30
35
  # Return all raw data references
31
36
  def raws
32
37
  @raws = {} unless defined? @raws
@@ -10,7 +10,7 @@ module Workbook
10
10
  file_obj = file_obj.path if file_obj.is_a? File
11
11
  content = ""
12
12
  styles = ""
13
- Zip::ZipFile.open(file_obj) do |zipfile|
13
+ Zip::File.open(file_obj) do |zipfile|
14
14
  zipfile.entries.each do |file|
15
15
  styles = zipfile.read(file.name) if file.name == "styles.xml"
16
16
  content = zipfile.read(file.name) if file.name == "content.xml"
@@ -1,9 +1,12 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'spreadsheet'
3
+ require 'workbook/readers/xls_shared'
4
+
3
5
 
4
6
  module Workbook
5
7
  module Readers
6
8
  module XlsReader
9
+ include Workbook::Readers::XlsShared
7
10
 
8
11
  def load_xls file_obj
9
12
  begin
@@ -27,63 +30,66 @@ module Workbook
27
30
  number_of_worksheets = xls_spreadsheet.worksheets.count
28
31
  (0..number_of_worksheets-1).each do |si|
29
32
  xls_sheet = xls_spreadsheet.worksheets[si]
30
- begin
31
- number_of_rows = xls_spreadsheet.worksheets[si].count
32
- s = create_or_open_sheet_at(si)
33
- (0..number_of_rows-1).each do |ri|
34
- xls_row = xls_sheet.row(ri)
35
- r = s.table.create_or_open_row_at(ri)
36
- col_widths = xls_sheet.columns.collect{|c| c.width if c}
37
- xls_row.each_with_index do |xls_cell,ci|
33
+ if [:visible, :hidden, nil].include? xls_sheet.visibility # don't read :strong_hidden sheets, symmetrical to the writer
34
+ begin
35
+ number_of_rows = xls_sheet.count
36
+ s = create_or_open_sheet_at(si)
37
+ s.name = xls_sheet.name
38
+ (0..number_of_rows-1).each do |ri|
39
+ xls_row = xls_sheet.row(ri)
40
+ r = s.table.create_or_open_row_at(ri)
41
+ col_widths = xls_sheet.columns.collect{|c| c.width if c}
42
+ xls_row.each_with_index do |xls_cell,ci|
38
43
 
39
- begin
40
- r[ci] = Workbook::Cell.new xls_cell
41
- r[ci].parse!
42
- rescue ArgumentError => e
43
- if e.message.match('not a Spreadsheet::Formula')
44
- v = xls_cell.value
45
- if v.class == Float and xls_row.format(ci).date?
46
- xls_row[ci] = v
47
- v = xls_row.datetime(ci)
48
- end
49
- if v.is_a? Spreadsheet::Excel::Error
50
- v = "----!"
44
+ begin
45
+ r[ci] = Workbook::Cell.new xls_cell
46
+ r[ci].parse!
47
+ rescue ArgumentError => e
48
+ if e.message.match('not a Spreadsheet::Formula')
49
+ v = xls_cell.value
50
+ if v.class == Float and xls_row.format(ci).date?
51
+ xls_row[ci] = v
52
+ v = xls_row.datetime(ci)
53
+ end
54
+ if v.is_a? Spreadsheet::Excel::Error
55
+ v = "----!"
56
+ end
57
+ r[ci] = Workbook::Cell.new v
58
+ elsif e.message.match('not a Spreadsheet::Link')
59
+ r[ci] = Workbook::Cell.new xls_cell.to_s
60
+ elsif e.message.match('not a Spreadsheet::Link')
61
+ r[ci] = Workbook::Cell.new xls_cell.to_s
62
+ elsif e.message.match('not a Spreadsheet::Excel::Error')
63
+ r[ci] = "._."
64
+ else
65
+ r[ci] = "._." # raise e (we're going to be silent for now)
51
66
  end
52
- r[ci] = Workbook::Cell.new v
53
- elsif e.message.match('not a Spreadsheet::Link')
54
- r[ci] = Workbook::Cell.new xls_cell.to_s
55
- elsif e.message.match('not a Spreadsheet::Link')
56
- r[ci] = Workbook::Cell.new xls_cell.to_s
57
- elsif e.message.match('not a Spreadsheet::Excel::Error')
58
- r[ci] = "._."
59
- else
60
- r[ci] = "._." # raise e (we're going to be silent for now)
61
67
  end
62
- end
63
- xls_format = xls_row.format(ci)
64
- col_width = nil
68
+ xls_format = xls_row.format(ci)
69
+ col_width = nil
65
70
 
66
- if ri == 0
67
- col_width = col_widths[ci]
68
- end
69
- f = template.create_or_find_format_by "object_id_#{xls_format.object_id}",col_width
70
- f[:width]= col_width
71
- f[:rotation] = xls_format.rotation if xls_format.rotation
72
- f[:background_color] = xls_color_to_html_hex(xls_format.pattern_fg_color)
73
- f[:number_format] = ms_formatting_to_strftime(xls_format.number_format)
74
- f[:text_direction] = xls_format.text_direction
75
- f[:font_family] = "#{xls_format.font.name}, #{xls_format.font.family}"
76
- f[:font_weight] = xls_format.font.weight
77
- f[:color] = xls_color_to_html_hex(xls_format.font.color)
71
+ if ri == 0
72
+ col_width = col_widths[ci]
73
+ end
74
+ f = template.create_or_find_format_by "object_id_#{xls_format.object_id}",col_width
75
+ f[:width]= col_width
76
+ f[:rotation] = xls_format.rotation if xls_format.rotation
77
+ f[:background_color] = xls_color_to_html_hex(xls_format.pattern_fg_color)
78
+ f[:number_format] = ms_formatting_to_strftime(xls_format.number_format)
79
+ f[:text_direction] = xls_format.text_direction
80
+ f[:font_family] = "#{xls_format.font.name}, #{xls_format.font.family}"
81
+ f[:font_weight] = xls_format.font.weight
82
+ f[:color] = xls_color_to_html_hex(xls_format.font.color)
78
83
 
79
- f.add_raw xls_format
84
+ f.add_raw xls_format
80
85
 
81
- r[ci].format = f
86
+ r[ci].format = f
87
+ end
82
88
  end
89
+ rescue TypeError
90
+ puts "WARNING: Failed at worksheet (#{si})... ignored"
91
+ #ignore SpreadsheetGem can be buggy...
83
92
  end
84
- rescue TypeError
85
- puts "WARNING: Failed at worksheet (#{si})... ignored"
86
- #ignore SpreadsheetGem can be buggy...
87
93
  end
88
94
  end
89
95
  end
@@ -92,12 +98,6 @@ module Workbook
92
98
  def xls_color_to_html_hex color_sym
93
99
  Workbook::Book::XLS_COLORS[color_sym] ? Workbook::Book::XLS_COLORS[color_sym] : "#000000"
94
100
  end
95
-
96
- def ms_formatting_to_strftime ms_nr_format
97
- ms_nr_format = ms_nr_format.downcase
98
- return nil if ms_nr_format == 'general'
99
- ms_nr_format.gsub('yyyy','%Y').gsub('dddd','%A').gsub('mmmm','%B').gsub('ddd','%a').gsub('mmm','%b').gsub('yy','%y').gsub('dd','%d').gsub('mm','%m').gsub('y','%y').gsub('%%y','%y').gsub('d','%e').gsub('%%e','%d').gsub('m','%m').gsub('%%m','%m').gsub(';@','').gsub('\\','')
100
- end
101
101
  end
102
102
  end
103
103
  end
@@ -2,6 +2,53 @@
2
2
  module Workbook
3
3
  module Readers
4
4
  module XlsShared
5
+ def ms_formatting_to_strftime ms_nr_format
6
+ if ms_nr_format
7
+ ms_nr_format = ms_nr_format.to_s.downcase
8
+ return nil if ms_nr_format == 'general' or ms_nr_format == ""
9
+ translation_table = {
10
+ 'yyyy'=>'%Y',
11
+ 'dddd'=>'%A',
12
+ 'mmmm'=>'%B',
13
+ 'ddd'=>'%a',
14
+ 'mmm'=>'%b',
15
+ 'yy'=>'%y',
16
+ 'dd'=>'%d',
17
+ 'mm'=>'%m',
18
+ 'y'=>'%y',
19
+ '%%y'=>'%y',
20
+ 'd'=>'%e',
21
+ '%%e'=>'%d',
22
+ 'm'=>'%m',
23
+ '%%m'=>'%m',
24
+ ';@'=>'',
25
+ '\\'=>''
26
+ }
27
+ translation_table.each{|k,v| ms_nr_format.gsub!(k,v) }
28
+ ms_nr_format
29
+ end
30
+ end
31
+
32
+ # Attempt to convert html-hex color value to xls color number
33
+ #
34
+ # @param [String] hex color
35
+ # @return [String] xls color
36
+ def html_color_to_xls_color hex
37
+ Workbook::Readers::XlsShared::XLS_COLORS.each do |k,v|
38
+ return k if (v == hex or (hex and hex != "" and k == hex.to_sym))
39
+ end
40
+ return nil
41
+ end
42
+
43
+ # Converts standard (ruby/C++/unix/...) strftime formatting to MS's formatting
44
+ #
45
+ # @param [String, nil] numberformat (nil returns nil)
46
+ # @return [String, nil]
47
+ def strftime_to_ms_format numberformat
48
+ return nil if numberformat.nil?
49
+ return numberformat.gsub('%Y','yyyy').gsub('%A','dddd').gsub('%B','mmmm').gsub('%a','ddd').gsub('%b','mmm').gsub('%y','yy').gsub('%d','dd').gsub('%m','mm').gsub('%y','y').gsub('%y','%%y').gsub('%e','d')
50
+ end
51
+
5
52
  XLS_COLORS = {:xls_color_1=>'#000000',
6
53
  :xls_color_2=>'#FFFFFF',
7
54
  :xls_color_3=>'#FF0000',
@@ -1,136 +1,19 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'rubyXL'
3
+ require 'workbook/readers/xls_shared'
3
4
 
4
- # Monkeypatching rubyXL, pull request submitted: https://github.com/gilt/rubyXL/pull/47
5
- module RubyXL
6
- class Workbook
7
-
8
- # Improves upon date format detection
9
- def is_date_format?(num_fmt)
10
- num_fmt = num_fmt.to_s
11
- num_fmt.downcase!
12
- skip_chars = ['$', '-', '+', '/', '(', ')', ':', ' ']
13
- num_chars = ['0', '#', '?']
14
- non_date_formats = ['0.00e+00', '##0.0e+0', 'general', '@']
15
- date_chars = ['y','m','d','h','s']
16
-
17
- state = 0
18
- s = ''
19
- num_fmt.split(//).each do |c|
20
- if state == 0
21
- if c == '"'
22
- state = 1
23
- elsif ['\\', '_', '*'].include?(c)
24
- state = 2
25
- elsif skip_chars.include?(c)
26
- next
27
- else
28
- s << c
29
- end
30
- elsif state == 1
31
- if c == '"'
32
- state = 0
33
- end
34
- elsif state == 2
35
- state = 0
36
- end
37
- end
38
- s.gsub!(/\[[^\]]*\]/, '')
39
- if non_date_formats.include?(s)
40
- return false
41
- end
42
- separator = ';'
43
- got_sep = 0
44
- date_count = 0
45
- num_count = 0
46
- s.split(//).each do |c|
47
- if date_chars.include?(c)
48
- date_count += 1
49
- elsif num_chars.include?(c)
50
- num_count += 1
51
- elsif c == separator
52
- got_sep = 1
53
- end
54
- end
55
- if date_count > 0 && num_count == 0
56
- return true
57
- elsif num_count > 0 && date_count == 0
58
- return false
59
- elsif date_count
60
- # ambiguous result
61
- elsif got_sep == 0
62
- # constant result
63
- end
64
- return date_count > num_count
65
- end
66
- end
67
- end
68
- # end monkey patch submitted
69
-
70
- module RubyXL
71
- class Workbook
72
- def num_fmts_by_id
73
- return @num_fmts_hash unless @num_fmts_hash.nil?
74
- @num_fmts_hash={1=>{:attributes=>{:formatCode=>'0'}},
75
- 2=>{:attributes=>{:formatCode=>'0.00'}},
76
- 3=>{:attributes=>{:formatCode=>'#, ##0'}},
77
- 4=>{:attributes=>{:formatCode=>'#, ##0.00'}},
78
- 5=>{:attributes=>{:formatCode=>'$#, ##0_);($#, ##0)'}},
79
- 6=>{:attributes=>{:formatCode=>'$#, ##0_);[Red]($#, ##0)'}},
80
- 7=>{:attributes=>{:formatCode=>'$#, ##0.00_);($#, ##0.00)'}},
81
- 8=>{:attributes=>{:formatCode=>'$#, ##0.00_);[Red]($#, ##0.00)'}},
82
- 9=>{:attributes=>{:formatCode=>'0%'}},
83
- 10=>{:attributes=>{:formatCode=>'0.00%'}},
84
- 11=>{:attributes=>{:formatCode=>'0.00E+00'}},
85
- 12=>{:attributes=>{:formatCode=>'# ?/?'}},
86
- 13=>{:attributes=>{:formatCode=>'# ??/??'}},
87
- 14=>{:attributes=>{:formatCode=>'m/d/yyyy'}},
88
- 15=>{:attributes=>{:formatCode=>'d-mmm-yy'}},
89
- 16=>{:attributes=>{:formatCode=>'d-mmm'}},
90
- 17=>{:attributes=>{:formatCode=>'mmm-yy'}},
91
- 18=>{:attributes=>{:formatCode=>'h:mm AM/PM'}},
92
- 19=>{:attributes=>{:formatCode=>'h:mm:ss AM/PM'}},
93
- 20=>{:attributes=>{:formatCode=>'h:mm'}},
94
- 21=>{:attributes=>{:formatCode=>'h:mm:ss'}},
95
- 22=>{:attributes=>{:formatCode=>'m/d/yyyy h:mm'}},
96
- 37=>{:attributes=>{:formatCode=>'#, ##0_);(#, ##0)'}},
97
- 38=>{:attributes=>{:formatCode=>'#, ##0_);[Red](#, ##0)'}},
98
- 39=>{:attributes=>{:formatCode=>'#, ##0.00_);(#, ##0.00)'}},
99
- 40=>{:attributes=>{:formatCode=>'#, ##0.00_);[Red](#, ##0.00)'}},
100
- 45=>{:attributes=>{:formatCode=>'mm:ss'}},
101
- 46=>{:attributes=>{:formatCode=>'[h]:mm:ss'}},
102
- 47=>{:attributes=>{:formatCode=>'mm:ss.0'}},
103
- 48=>{:attributes=>{:formatCode=>'##0.0E+0'}},
104
- 49=>{:attributes=>{:formatCode=>'@'}}}
105
- if num_fmts and num_fmts[:numFmt]
106
- num_fmts[:numFmt].each do |num_fmt|
107
- @num_fmts_hash[num_fmt[:attributes][:numFmtId]]=num_fmt
108
- end
109
- end
110
- return @num_fmts_hash
111
- end
112
- end
113
- end
114
-
115
- # other monkey patch
116
- module RubyXL
117
- class Cell
118
- def number_format
119
- if !@value.is_a?(String)
120
- if @workbook.num_fmts_by_id
121
- num_fmt_id = xf_id()[:numFmtId]
122
- tmp_num_fmt = @workbook.num_fmts_by_id[num_fmt_id]
123
- return (tmp_num_fmt &&tmp_num_fmt[:attributes] && tmp_num_fmt[:attributes][:formatCode]) ? tmp_num_fmt[:attributes][:formatCode] : nil
124
- end
125
- end
126
- end
127
- end
128
- end
129
- # end of monkey patch
130
5
 
131
6
  module Workbook
132
7
  module Readers
133
8
  module XlsxReader
9
+ include Workbook::Readers::XlsShared
10
+
11
+ # Load method for .xlsm files, an office open file format, hence compatible with .xlsx (it emphasizes that it contains macros)
12
+ #
13
+ # @param [String, File] file_obj a string with a reference to the file to be written to
14
+ def load_xlsm file_obj
15
+ self.load_xlsx file_obj
16
+ end
134
17
  def load_xlsx file_obj
135
18
  file_obj = file_obj.path if file_obj.is_a? File
136
19
  sp = RubyXL::Parser.parse(file_obj)
@@ -149,43 +32,41 @@ module Workbook
149
32
  rescue
150
33
  # Column widths couldn't be read, no big deal...
151
34
  end
35
+
152
36
  worksheet.each_with_index do |row, ri|
153
- r = s.table.create_or_open_row_at(ri)
37
+ if row
38
+ r = s.table.create_or_open_row_at(ri)
39
+ row.cells.each_with_index do |cell,ci|
40
+ if cell.nil?
41
+ r[ci] = Workbook::Cell.new nil
42
+ else
43
+ r[ci] = Workbook::Cell.new cell.value
44
+ r[ci].parse!
45
+ xls_format = cell.style_index
46
+ col_width = nil
154
47
 
155
- row.each_with_index do |cell,ci|
156
- if cell.nil?
157
- r[ci] = Workbook::Cell.new nil
158
- else
159
- r[ci] = Workbook::Cell.new cell.value
160
- r[ci].parse!
161
- xls_format = cell.style_index
162
- col_width = nil
48
+ if ri == 0
49
+ col_width = col_widths[ci]
50
+ end
51
+ f = template.create_or_find_format_by "style_index_#{cell.style_index}", col_width
52
+ f[:width]= col_width
53
+ background_color = cell.fill_color
54
+ background_color = (background_color.length == 8) ? background_color[2..8] : background_color #ignoring alpha for now.
55
+ f[:background_color] = "##{background_color}"
163
56
 
164
- if ri == 0
165
- col_width = col_widths[ci]
166
- end
167
- f = template.create_or_find_format_by "style_index_#{cell.style_index}", col_width
168
- f[:width]= col_width
169
- f[:background_color] = "##{cell.fill_color}"
170
- f[:number_format] = ms_formatting_to_strftime(cell.number_format)
171
- f[:font_family] = cell.font_name
172
- f[:color] = "##{cell.font_color}"
57
+ f[:number_format] = ms_formatting_to_strftime(cell.number_format)
58
+ # f[:font_family] = cell.font_name
59
+ # f[:color] = "##{cell.font_color}"
173
60
 
174
- f.add_raw xls_format
61
+ f.add_raw xls_format
175
62
 
176
- r[ci].format = f
63
+ r[ci].format = f
64
+ end
177
65
  end
178
66
  end
179
67
  end
180
68
  end
181
69
  end
182
- def ms_formatting_to_strftime ms_nr_format
183
- if ms_nr_format
184
- ms_nr_format = ms_nr_format.to_s.downcase
185
- return nil if ms_nr_format == 'general' or ms_nr_format == ""
186
- ms_nr_format.gsub('yyyy','%Y').gsub('dddd','%A').gsub('mmmm','%B').gsub('ddd','%a').gsub('mmm','%b').gsub('yy','%y').gsub('dd','%d').gsub('mm','%m').gsub('y','%y').gsub('%%y','%y').gsub('d','%e').gsub('%%e','%d').gsub('m','%m').gsub('%%m','%m').gsub(';@','').gsub('\\','')
187
- end
188
- end
189
70
  end
190
71
  end
191
72
  end
data/lib/workbook/row.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  module Workbook
3
4
  class Row < Array
5
+ include Workbook::Modules::Cache
6
+
4
7
  alias_method :compare_without_header, :<=>
5
8
  attr_accessor :placeholder # The placeholder attribute is used in compares (corresponds to newly created or removed lines (depending which side you're on)
6
9
  attr_accessor :format
@@ -57,13 +60,45 @@ module Workbook
57
60
  end
58
61
  end
59
62
 
63
+ # Add cell
64
+ # @param [Workbook::Cell, Numeric,String,Time,Date,TrueClass,FalseClass,NilClass] cell or value to add
65
+ def push(cell)
66
+ cell = Workbook::Cell.new(cell) unless cell.class == Workbook::Cell
67
+ super(cell)
68
+ end
69
+
70
+ # Add cell
71
+ # @param [Workbook::Cell, Numeric,String,Time,Date,TrueClass,FalseClass,NilClass] cell or value to add
72
+ def <<(cell)
73
+ cell = Workbook::Cell.new(cell) unless cell.class == Workbook::Cell
74
+ super(cell)
75
+ end
76
+
77
+ # plus
78
+ # @param [Workbook::Row, Array] row to add
79
+ # @return [Workbook::Row] a new row, not linked to the table
80
+ def +(row)
81
+ rv = super(row)
82
+ rv = Workbook::Row.new(rv) unless rv.class == Workbook::Row
83
+ return rv
84
+ end
85
+
86
+ # concat
87
+ # @param [Workbook::Row, Array] row to add
88
+ # @return [self] self
89
+ def concat(row)
90
+ row = Workbook::Row.new(row) unless row.class == Workbook::Row
91
+ super(row)
92
+ end
93
+
94
+
60
95
  # Overrides normal Array's []-function with support for symbols that identify a column based on the header-values
61
96
  #
62
97
  # @example Lookup using fixnum or header value encoded as symbol
63
98
  # row[1] #=> <Cell value="a">
64
99
  # row[:a] #=> <Cell value="a">
65
100
  #
66
- # @param [Fixnum, Symbol] index_or_hash
101
+ # @param [Fixnum, Symbol, String] index_or_hash that identifies the column (strings are converted to symbols)
67
102
  # @return [Workbook::Cell, nil]
68
103
  def [](index_or_hash)
69
104
  if index_or_hash.is_a? Symbol
@@ -73,6 +108,9 @@ module Workbook
73
108
  rescue NoMethodError
74
109
  end
75
110
  return rv
111
+ elsif index_or_hash.is_a? String
112
+ symbolized = Workbook::Cell.new(index_or_hash).to_sym
113
+ self[symbolized]
76
114
  else
77
115
  if index_or_hash
78
116
  return to_a[index_or_hash]
@@ -82,17 +120,20 @@ module Workbook
82
120
 
83
121
  # Overrides normal Array's []=-function with support for symbols that identify a column based on the header-values
84
122
  #
85
- # @example Lookup using fixnum or header value encoded as symbol
123
+ # @example Lookup using fixnum or header value encoded as symbol (strings are converted to symbols)
86
124
  # row[1] #=> <Cell value="a">
87
125
  # row[:a] #=> <Cell value="a">
88
126
  #
89
- # @param [Fixnum, Symbol] index_or_hash
127
+ # @param [Fixnum, Symbol, String] index_or_hash that identifies the column
90
128
  # @param [String, Fixnum, NilClass, Date, DateTime, Time, Float] value
91
129
  # @return [Workbook::Cell, nil]
92
130
  def []= (index_or_hash, value)
93
131
  index = index_or_hash
94
132
  if index_or_hash.is_a? Symbol
95
133
  index = table_header_keys.index(index_or_hash)
134
+ elsif index_or_hash.is_a? String
135
+ symbolized = Workbook::Cell.new(index_or_hash).to_sym
136
+ index = table_header_keys.index(symbolized)
96
137
  end
97
138
 
98
139
  value_celled = Workbook::Cell.new
@@ -144,7 +185,9 @@ module Workbook
144
185
  # Converts a row to an array of symbol representations of the row content, see also: Workbook::Cell#to_sym
145
186
  # @return [Array<Symbol>] returns row as an array of symbols
146
187
  def to_symbols
147
- collect{|c| c.to_sym}
188
+ fetch_cache(:to_symbols){
189
+ collect{|c| c.to_sym}
190
+ }
148
191
  end
149
192
 
150
193
  # Converts the row to an array of Workbook::Cell's