hmcgowan-roo 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/History.txt +225 -0
  2. data/README.txt +43 -0
  3. data/lib/roo/excel.rb +455 -0
  4. data/lib/roo/excelx.rb +654 -0
  5. data/lib/roo/generic_spreadsheet.rb +636 -0
  6. data/lib/roo/google.rb +411 -0
  7. data/lib/roo/openoffice.rb +508 -0
  8. data/lib/roo/roo_rails_helper.rb +81 -0
  9. data/lib/roo/version.rb +9 -0
  10. data/lib/roo.rb +11 -0
  11. data/test/Bibelbund.csv +3741 -0
  12. data/test/Bibelbund.ods +0 -0
  13. data/test/Bibelbund.xls +0 -0
  14. data/test/Bibelbund.xlsx +0 -0
  15. data/test/Bibelbund1.ods +0 -0
  16. data/test/bbu.ods +0 -0
  17. data/test/bbu.xls +0 -0
  18. data/test/bbu.xlsx +0 -0
  19. data/test/bode-v1.ods.zip +0 -0
  20. data/test/bode-v1.xls.zip +0 -0
  21. data/test/boolean.ods +0 -0
  22. data/test/boolean.xls +0 -0
  23. data/test/boolean.xlsx +0 -0
  24. data/test/borders.ods +0 -0
  25. data/test/borders.xls +0 -0
  26. data/test/borders.xlsx +0 -0
  27. data/test/bug-row-column-fixnum-float.xls +0 -0
  28. data/test/datetime.ods +0 -0
  29. data/test/datetime.xls +0 -0
  30. data/test/datetime.xlsx +0 -0
  31. data/test/emptysheets.ods +0 -0
  32. data/test/emptysheets.xls +0 -0
  33. data/test/false_encoding.xls +0 -0
  34. data/test/formula.ods +0 -0
  35. data/test/formula.xls +0 -0
  36. data/test/formula.xlsx +0 -0
  37. data/test/html-escape.ods +0 -0
  38. data/test/no_spreadsheet_file.txt +1 -0
  39. data/test/numbers1.csv +18 -0
  40. data/test/numbers1.ods +0 -0
  41. data/test/numbers1.xls +0 -0
  42. data/test/numbers1.xlsx +0 -0
  43. data/test/numbers1_excel.csv +18 -0
  44. data/test/only_one_sheet.ods +0 -0
  45. data/test/only_one_sheet.xls +0 -0
  46. data/test/only_one_sheet.xlsx +0 -0
  47. data/test/ric.ods +0 -0
  48. data/test/simple_spreadsheet.ods +0 -0
  49. data/test/simple_spreadsheet.xls +0 -0
  50. data/test/simple_spreadsheet.xlsx +0 -0
  51. data/test/simple_spreadsheet_from_italo.ods +0 -0
  52. data/test/simple_spreadsheet_from_italo.xls +0 -0
  53. data/test/style.ods +0 -0
  54. data/test/style.xls +0 -0
  55. data/test/style.xlsx +0 -0
  56. data/test/test_helper.rb +19 -0
  57. data/test/test_roo.rb +4946 -0
  58. data/test/time-test.csv +2 -0
  59. data/test/time-test.ods +0 -0
  60. data/test/time-test.xls +0 -0
  61. data/test/time-test.xlsx +0 -0
  62. metadata +225 -0
@@ -0,0 +1,636 @@
1
+ require 'rubygems'
2
+ require 'builder'
3
+
4
+ # Base class for all other types of spreadsheets
5
+ class GenericSpreadsheet
6
+
7
+ attr_reader :default_sheet
8
+
9
+ # sets the line with attribute names (default: 1)
10
+ attr_accessor :header_line
11
+
12
+ def initialize
13
+ end
14
+
15
+ # set the working sheet in the document
16
+ def default_sheet=(sheet)
17
+ if sheet.kind_of? Fixnum
18
+ if sheet >= 0 and sheet <= sheets.length
19
+ sheet = self.sheets[sheet-1]
20
+ else
21
+ raise RangeError
22
+ end
23
+ elsif sheet.kind_of?(String)
24
+ raise RangeError if ! self.sheets.include?(sheet)
25
+ else
26
+ raise TypeError, "what are you trying to set as default sheet?"
27
+ end
28
+ @default_sheet = sheet
29
+ check_default_sheet
30
+ @first_row[sheet] = @last_row[sheet] = @first_column[sheet] = @last_column[sheet] = nil
31
+ @cells_read[sheet] = false
32
+ end
33
+
34
+ # first non-empty column as a letter
35
+ def first_column_as_letter(sheet=nil)
36
+ GenericSpreadsheet.number_to_letter(first_column(sheet))
37
+ end
38
+
39
+ # last non-empty column as a letter
40
+ def last_column_as_letter(sheet=nil)
41
+ GenericSpreadsheet.number_to_letter(last_column(sheet))
42
+ end
43
+
44
+ # returns the number of the first non-empty row
45
+ def first_row(sheet=nil)
46
+ if sheet == nil
47
+ sheet = @default_sheet
48
+ end
49
+ read_cells(sheet) unless @cells_read[sheet]
50
+ if @first_row[sheet]
51
+ return @first_row[sheet]
52
+ end
53
+ impossible_value = 999_999 # more than a spreadsheet can hold
54
+ result = impossible_value
55
+ @cell[sheet].each_pair {|key,value|
56
+ y,x = key # _to_string(key).split(',')
57
+ y = y.to_i
58
+ result = [result, y].min if value
59
+ } if @cell[sheet]
60
+ result = nil if result == impossible_value
61
+ @first_row[sheet] = result
62
+ result
63
+ end
64
+
65
+ # returns the number of the last non-empty row
66
+ def last_row(sheet=nil)
67
+ sheet = @default_sheet unless sheet
68
+ read_cells(sheet) unless @cells_read[sheet]
69
+ if @last_row[sheet]
70
+ return @last_row[sheet]
71
+ end
72
+ impossible_value = 0
73
+ result = impossible_value
74
+ @cell[sheet].each_pair {|key,value|
75
+ y,x = key # _to_string(key).split(',')
76
+ y = y.to_i
77
+ result = [result, y].max if value
78
+ } if @cell[sheet]
79
+ result = nil if result == impossible_value
80
+ @last_row[sheet] = result
81
+ result
82
+ end
83
+
84
+ # returns the number of the first non-empty column
85
+ def first_column(sheet=nil)
86
+ if sheet == nil
87
+ sheet = @default_sheet
88
+ end
89
+ read_cells(sheet) unless @cells_read[sheet]
90
+ if @first_column[sheet]
91
+ return @first_column[sheet]
92
+ end
93
+ impossible_value = 999_999 # more than a spreadsheet can hold
94
+ result = impossible_value
95
+ @cell[sheet].each_pair {|key,value|
96
+ y,x = key # _to_string(key).split(',')
97
+ x = x # .to_i
98
+ result = [result, x].min if value
99
+ } if @cell[sheet]
100
+ result = nil if result == impossible_value
101
+ @first_column[sheet] = result
102
+ result
103
+ end
104
+
105
+ # returns the number of the last non-empty column
106
+ def last_column(sheet=nil)
107
+ sheet = @default_sheet unless sheet
108
+ read_cells(sheet) unless @cells_read[sheet]
109
+ if @last_column[sheet]
110
+ return @last_column[sheet]
111
+ end
112
+ impossible_value = 0
113
+ result = impossible_value
114
+ @cell[sheet].each_pair {|key,value|
115
+ y,x = key # _to_string(key).split(',')
116
+ x = x.to_i
117
+ result = [result, x].max if value
118
+ } if @cell[sheet]
119
+ result = nil if result == impossible_value
120
+ @last_column[sheet] = result
121
+ result
122
+ end
123
+
124
+ # returns a rectangular area (default: all cells) as yaml-output
125
+ # you can add additional attributes with the prefix parameter like:
126
+ # oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
127
+ def to_yaml(prefix={}, from_row=nil, from_column=nil, to_row=nil, to_column=nil,sheet=nil)
128
+ sheet = @default_sheet unless sheet
129
+ result = "--- \n"
130
+ (from_row||first_row(sheet)).upto(to_row||last_row(sheet)) do |row|
131
+ (from_column||first_column(sheet)).upto(to_column||last_column(sheet)) do |col|
132
+ unless empty?(row,col,sheet)
133
+ result << "cell_#{row}_#{col}: \n"
134
+ prefix.each {|k,v|
135
+ result << " #{k}: #{v} \n"
136
+ }
137
+ result << " row: #{row} \n"
138
+ result << " col: #{col} \n"
139
+ result << " celltype: #{self.celltype(row,col,sheet)} \n"
140
+ if self.celltype(row,col,sheet) == :time
141
+ result << " value: #{GenericSpreadsheet.integer_to_timestring( self.cell(row,col,sheet))} \n"
142
+ else
143
+ result << " value: #{self.cell(row,col,sheet)} \n"
144
+ end
145
+ end
146
+ end
147
+ end
148
+ result
149
+ end
150
+
151
+ # write the current spreadsheet to stdout or into a file
152
+ def to_csv(filename=nil,sheet=nil)
153
+ sheet = @default_sheet unless sheet
154
+ if filename
155
+ file = File.open(filename,"w") # do |file|
156
+ write_csv_content(file,sheet)
157
+ file.close
158
+ else
159
+ write_csv_content(STDOUT,sheet)
160
+ end
161
+ true
162
+ end
163
+
164
+ # find a row either by row number or a condition
165
+ # Caution: this works only within the default sheet -> set default_sheet before you call this method
166
+ # (experimental. see examples in the test_roo.rb file)
167
+ def find(*args) # :nodoc
168
+ result_array = false
169
+ args.each {|arg,val|
170
+ if arg.class == Hash
171
+ arg.each { |hkey,hval|
172
+ if hkey == :array and hval == true
173
+ result_array = true
174
+ end
175
+ }
176
+ end
177
+ }
178
+ column_with = {}
179
+ 1.upto(last_column) do |col|
180
+ column_with[cell(@header_line,col)] = col
181
+ end
182
+ result = Array.new
183
+ #-- id
184
+ if args[0].class == Fixnum
185
+ rownum = args[0]
186
+ if @header_line
187
+ tmp = {}
188
+ else
189
+ tmp = []
190
+ end
191
+ 1.upto(self.row(rownum).size) {|j|
192
+ x = ''
193
+ column_with.each { |key,val|
194
+ if val == j
195
+ x = key
196
+ end
197
+ }
198
+ if @header_line
199
+ tmp[x] = cell(rownum,j)
200
+ else
201
+ tmp[j-1] = cell(rownum,j)
202
+ end
203
+
204
+ }
205
+ if @header_line
206
+ result = [ tmp ]
207
+ else
208
+ result = tmp
209
+ end
210
+ #-- :all
211
+ elsif args[0] == :all
212
+ if args[1].class == Hash
213
+ args[1].each {|key,val|
214
+ if key == :conditions
215
+ column_with = {}
216
+ 1.upto(last_column) do |col|
217
+ column_with[cell(@header_line,col)] = col
218
+ end
219
+ conditions = val
220
+ first_row.upto(last_row) do |i|
221
+ # are all conditions met?
222
+ found = 1
223
+ conditions.each { |key,val|
224
+ if cell(i,column_with[key]) == val
225
+ found *= 1
226
+ else
227
+ found *= 0
228
+ end
229
+ }
230
+ if found > 0
231
+ tmp = {}
232
+ 1.upto(self.row(i).size) {|j|
233
+ x = ''
234
+ column_with.each { |key,val|
235
+ if val == j
236
+ x = key
237
+ end
238
+ }
239
+ tmp[x] = cell(i,j)
240
+ }
241
+ if result_array
242
+ result << self.row(i)
243
+ else
244
+ result << tmp
245
+ end
246
+ end
247
+ end
248
+ end # :conditions
249
+ }
250
+ end
251
+ end
252
+ result
253
+ end
254
+
255
+ # returns all values in this row as an array
256
+ # row numbers are 1,2,3,... like in the spreadsheet
257
+ def row(rownumber,sheet=nil)
258
+ sheet = @default_sheet unless sheet
259
+ read_cells(sheet) unless @cells_read[sheet]
260
+ result = []
261
+ tmp_arr = []
262
+ @cell[sheet].each_pair {|key,value|
263
+ y,x = key # _to_string(key).split(',')
264
+ x = x.to_i
265
+ y = y.to_i
266
+ if y == rownumber
267
+ tmp_arr[x] = value
268
+ end
269
+ }
270
+ result = tmp_arr[1..-1]
271
+ while result && result[-1] == nil
272
+ result = result[0..-2]
273
+ end
274
+ result
275
+ end
276
+
277
+ # returns all values in this column as an array
278
+ # column numbers are 1,2,3,... like in the spreadsheet
279
+ def column(columnnumber,sheet=nil)
280
+ if columnnumber.class == String
281
+ columnnumber = Excel.letter_to_number(columnnumber)
282
+ end
283
+ sheet = @default_sheet unless sheet
284
+ read_cells(sheet) unless @cells_read[sheet]
285
+ result = []
286
+ first_row(sheet).upto(last_row(sheet)) do |row|
287
+ result << cell(row,columnnumber,sheet)
288
+ end
289
+ result
290
+ end
291
+
292
+ # reopens and read a spreadsheet document
293
+ def reload
294
+ ds = @default_sheet
295
+ initialize(@filename) if self.class == Openoffice or
296
+ self.class == Excel
297
+ initialize(@spreadsheetkey,@user,@password) if self.class == Google
298
+ self.default_sheet = ds
299
+ #@first_row = @last_row = @first_column = @last_column = nil
300
+ end
301
+
302
+ # true if cell is empty
303
+ def empty?(row, col, sheet=nil)
304
+ sheet = @default_sheet unless sheet
305
+ read_cells(sheet) unless @cells_read[sheet] or self.class == Excel
306
+ row,col = normalize(row,col)
307
+ return true unless cell(row, col, sheet)
308
+ return true if celltype(row, col, sheet) == :string && cell(row, col, sheet).empty?
309
+ return true if row < first_row(sheet) || row > last_row(sheet) || col < first_column(sheet) || col > last_column(sheet)
310
+ false
311
+ end
312
+
313
+ # recursively removes the current temporary directory
314
+ # this is only needed if you work with zipped files or files via the web
315
+ def remove_tmp
316
+ if File.exists?(@tmpdir)
317
+ FileUtils::rm_r(@tmpdir)
318
+ end
319
+ end
320
+
321
+ # Returns information of the spreadsheet document and all sheets within
322
+ # this document.
323
+ def info
324
+ result = "File: #{File.basename(@filename)}\n"+
325
+ "Number of sheets: #{sheets.size}\n"+
326
+ "Sheets: #{sheets.map{|sheet| sheet+", "}.to_s[0..-3]}\n"
327
+ n = 1
328
+ sheets.each {|sheet|
329
+ self.default_sheet = sheet
330
+ result << "Sheet " + n.to_s + ":\n"
331
+ unless first_row
332
+ result << " - empty -"
333
+ else
334
+ result << " First row: #{first_row}\n"
335
+ result << " Last row: #{last_row}\n"
336
+ result << " First column: #{GenericSpreadsheet.number_to_letter(first_column)}\n"
337
+ result << " Last column: #{GenericSpreadsheet.number_to_letter(last_column)}"
338
+ end
339
+ result << "\n" if sheet != sheets.last
340
+ n += 1
341
+ }
342
+ result
343
+ end
344
+
345
+ def to_xml
346
+ xml_document = ''
347
+ xml = Builder::XmlMarkup.new(:target => xml_document, :indent => 2)
348
+ xml.instruct! :xml, :version =>"1.0", :encoding => "utf-8"
349
+ xml.spreadsheet {
350
+ self.sheets.each do |sheet|
351
+ self.default_sheet = sheet
352
+ xml.sheet(:name => sheet) { |x|
353
+ if first_row and last_row and first_column and last_column
354
+ # sonst gibt es Fehler bei leeren Blaettern
355
+ first_row.upto(last_row) do |row|
356
+ first_column.upto(last_column) do |col|
357
+ unless empty?(row,col)
358
+ x.cell(cell(row,col),
359
+ :row =>row,
360
+ :column => col,
361
+ :type => celltype(row,col))
362
+ end
363
+ end
364
+ end
365
+ end
366
+ }
367
+ end
368
+ }
369
+ xml_document
370
+ end
371
+
372
+ protected
373
+
374
+ def file_type_check(filename, ext, name)
375
+ new_expression = {
376
+ '.ods' => 'Openoffice.new',
377
+ '.xls' => 'Excel.new',
378
+ '.xlsx' => 'Excelx.new',
379
+ }
380
+ case ext
381
+ when '.ods', '.xls', '.xlsx'
382
+ correct_class = "use #{new_expression[ext]} to handle #{ext} spreadsheet files"
383
+ else
384
+ raise "unknown file type: #{ext}"
385
+ end
386
+ if File.extname(filename).downcase != ext
387
+ case @file_warning
388
+ when :error
389
+ warn correct_class
390
+ raise TypeError, "#{filename} is not #{name} file"
391
+ when :warning
392
+ warn "are you sure, this is #{name} spreadsheet file?"
393
+ warn correct_class
394
+ when :ignore
395
+ # ignore
396
+ else
397
+ raise "#{@file_warning} illegal state of file_warning"
398
+ end
399
+ end
400
+ end
401
+
402
+ # konvertiert einen Key in der Form "12,45" (=row,column) in
403
+ # ein Array mit numerischen Werten ([12,45])
404
+ # Diese Methode ist eine temp. Loesung, um zu erforschen, ob der
405
+ # Zugriff mit numerischen Keys schneller ist.
406
+ def key_to_num(str)
407
+ r,c = str.split(',')
408
+ r = r.to_i
409
+ c = c.to_i
410
+ [r,c]
411
+ end
412
+
413
+ # siehe: key_to_num
414
+ def key_to_string(arr)
415
+ "#{arr[0]},#{arr[1]}"
416
+ end
417
+
418
+ private
419
+
420
+ # converts cell coordinate to numeric values of row,col
421
+ def normalize(row,col)
422
+ if row.class == String
423
+ if col.class == Fixnum
424
+ # ('A',1):
425
+ # ('B', 5) -> (5, 2)
426
+ row, col = col, row
427
+ else
428
+ raise ArgumentError
429
+ end
430
+ end
431
+ if col.class == String
432
+ col = GenericSpreadsheet.letter_to_number(col)
433
+ end
434
+ return row,col
435
+ end
436
+
437
+ # def open_from_uri(uri)
438
+ # require 'open-uri' ;
439
+ # tempfilename = File.join(@tmpdir, File.basename(uri))
440
+ # f = File.open(tempfilename,"wb")
441
+ # begin
442
+ # open(uri) do |net|
443
+ # f.write(net.read)
444
+ # end
445
+ # rescue
446
+ # raise "could not open #{uri}"
447
+ # end
448
+ # f.close
449
+ # File.join(@tmpdir, File.basename(uri))
450
+ # end
451
+
452
+ # OpenURI::HTTPError
453
+ # def open_from_uri(uri)
454
+ # require 'open-uri'
455
+ # #existiert URL?
456
+ # r = Net::HTTP.get_response(URI.parse(uri))
457
+ # raise "URL nicht verfuegbar" unless r.is_a? Net::HTTPOK
458
+ # tempfilename = File.join(@tmpdir, File.basename(uri))
459
+ # f = File.open(tempfilename,"wb")
460
+ # open(uri) do |net|
461
+ # f.write(net.read)
462
+ # end
463
+ # # rescue
464
+ # # raise "could not open #{uri}"
465
+ # # end
466
+ # f.close
467
+ # File.join(@tmpdir, File.basename(uri))
468
+ # end
469
+
470
+ def open_from_uri(uri)
471
+ require 'open-uri'
472
+ response = ''
473
+ begin
474
+ open(uri, "User-Agent" => "Ruby/#{RUBY_VERSION}") { |net|
475
+ response = net.read
476
+ tempfilename = File.join(@tmpdir, File.basename(uri))
477
+ f = File.open(tempfilename,"wb")
478
+ f.write(response)
479
+ f.close
480
+ }
481
+ rescue OpenURI::HTTPError
482
+ raise "could not open #{uri}"
483
+ end
484
+ File.join(@tmpdir, File.basename(uri))
485
+ end
486
+
487
+ def open_from_stream(stream)
488
+ tempfilename = File.join(@tmpdir, "spreadsheet")
489
+ f = File.open(tempfilename,"wb")
490
+ f.write(stream[7..-1])
491
+ f.close
492
+ File.join(@tmpdir, "spreadsheet")
493
+ end
494
+
495
+ # convert a number to something like 'AB' (1 => 'A', 2 => 'B', ...)
496
+ def self.number_to_letter(n)
497
+ letters=""
498
+ while n > 0
499
+ num = n%26
500
+ letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[num-1,1] + letters
501
+ n = n.div(26)
502
+ end
503
+ letters
504
+ end
505
+
506
+ # convert letters like 'AB' to a number ('A' => 1, 'B' => 2, ...)
507
+ def self.letter_to_number(letters)
508
+ result = 0
509
+ while letters && letters.length > 0
510
+ character = letters[0,1].upcase
511
+ num = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(character)
512
+ raise ArgumentError, "invalid column character '#{letters[0,1]}'" if num == nil
513
+ num += 1
514
+ result = result * 26 + num
515
+ letters = letters[1..-1]
516
+ end
517
+ result
518
+ end
519
+
520
+ def unzip(filename)
521
+ ret = nil
522
+ Zip::ZipFile.open(filename) do |zip|
523
+ ret = process_zipfile_packed zip
524
+ end
525
+ ret
526
+ end
527
+
528
+ # check if default_sheet was set and exists in sheets-array
529
+ def check_default_sheet
530
+ sheet_found = false
531
+ raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
532
+ if sheets.index(@default_sheet)
533
+ sheet_found = true
534
+ end
535
+ if ! sheet_found
536
+ raise RangeError, "sheet '#{@default_sheet}' not found"
537
+ end
538
+ #raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
539
+ end
540
+
541
+ def process_zipfile_packed(zip, path='')
542
+ ret=nil
543
+ if zip.file.file? path
544
+ # extract and return filename
545
+ @tmpdir = "oo_"+$$.to_s
546
+ unless File.exists?(@tmpdir)
547
+ FileUtils::mkdir(@tmpdir)
548
+ end
549
+ file = File.open(File.join(@tmpdir, path),"wb")
550
+ file.write(zip.read(path))
551
+ file.close
552
+ return File.join(@tmpdir, path)
553
+ else
554
+ unless path.empty?
555
+ path += '/'
556
+ end
557
+ zip.dir.foreach(path) do |filename|
558
+ ret = process_zipfile_packed(zip, path + filename)
559
+ end
560
+ end
561
+ ret
562
+ end
563
+
564
+ def write_csv_content(file=nil,sheet=nil)
565
+ file = STDOUT unless file
566
+ if first_row(sheet) # sheet is not empty
567
+ # first_row(sheet).upto(last_row(sheet)) do |row|
568
+ 1.upto(last_row(sheet)) do |row|
569
+ 1.upto(last_column(sheet)) do |col|
570
+ file.print(",") if col > 1
571
+ onecell = cell(row,col,sheet)
572
+ onecelltype = celltype(row,col,sheet)
573
+ file.print one_cell_output(onecelltype,onecell,empty?(row,col,sheet))
574
+ end
575
+ file.print("\n")
576
+ end # sheet not empty
577
+ end
578
+ end
579
+
580
+ def one_cell_output(onecelltype,onecell,empty)
581
+ str = ""
582
+ if empty
583
+ str += ''
584
+ else
585
+ case onecelltype
586
+ when :string
587
+ if onecell == ""
588
+ str << ''
589
+ else
590
+ onecell.gsub!(/"/,'""')
591
+ str << ('"'+onecell+'"')
592
+ end
593
+ when :float,:percentage
594
+ if onecell == onecell.to_i
595
+ str << onecell.to_i.to_s
596
+ else
597
+ str << onecell.to_s
598
+ end
599
+ when :formula
600
+ if onecell.class == String
601
+ if onecell == ""
602
+ str << ''
603
+ else
604
+ onecell.gsub!(/"/,'""')
605
+ str << '"'+onecell+'"'
606
+ end
607
+ elsif onecell.class == Float
608
+ if onecell == onecell.to_i
609
+ str << onecell.to_i.to_s
610
+ else
611
+ str << onecell.to_s
612
+ end
613
+ else
614
+ raise "unhandled onecell-class "+onecell.class.to_s
615
+ end
616
+ when :date
617
+ str << onecell.to_s
618
+ when :time
619
+ str << GenericSpreadsheet.integer_to_timestring(onecell)
620
+ else
621
+ raise "unhandled celltype "+onecelltype.to_s
622
+ end
623
+ end
624
+ str
625
+ end
626
+
627
+ # converts an integer value to a time string like '02:05:06'
628
+ def self.integer_to_timestring(content)
629
+ h = (content/3600.0).floor
630
+ content = content - h*3600
631
+ m = (content/60.0).floor
632
+ content = content - m*60
633
+ s = content
634
+ sprintf("%02d:%02d:%02d",h,m,s)
635
+ end
636
+ end