hmcgowan-roo 1.2.4

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 (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