workbook 0.4.6.0 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
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