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,508 @@
1
+
2
+ require 'rubygems'
3
+ require 'rexml/document'
4
+ require 'fileutils'
5
+ require 'zip/zipfilesystem'
6
+ require 'date'
7
+ require 'base64'
8
+ require 'cgi'
9
+
10
+ class Openoffice < GenericSpreadsheet
11
+
12
+ @@nr = 0
13
+
14
+ # initialization and opening of a spreadsheet file
15
+ # values for packed: :zip
16
+ def initialize(filename, packed=nil, file_warning=:error) #, create = false)
17
+ @file_warning = file_warning
18
+ super()
19
+ @tmpdir = "oo_"+$$.to_s
20
+ @tmpdir = File.join(ENV['ROO_TMP'], @tmpdir) if ENV['ROO_TMP']
21
+ unless File.exists?(@tmpdir)
22
+ FileUtils::mkdir(@tmpdir)
23
+ end
24
+ filename = open_from_uri(filename) if filename[0,7] == "http://"
25
+ filename = unzip(filename) if packed and packed == :zip
26
+ begin
27
+ file_type_check(filename,'.ods','an openoffice')
28
+ #if create and ! File.exists?(filename)
29
+ # self.create_openoffice(filename)
30
+ #end
31
+ @cells_read = Hash.new
32
+ #TODO: @cells_read[:default] = false
33
+ @filename = filename
34
+ unless File.file?(@filename)
35
+ raise IOError, "file #{@filename} does not exist"
36
+ end
37
+ @@nr += 1
38
+ @file_nr = @@nr
39
+ extract_content
40
+ file = File.new(File.join(@tmpdir, @file_nr.to_s+"_roo_content.xml"))
41
+ @doc = REXML::Document.new file
42
+ file.close
43
+ ensure
44
+ #if ENV["roo_local"] != "thomas-p"
45
+ FileUtils::rm_r(@tmpdir)
46
+ #end
47
+ end
48
+ @default_sheet = nil
49
+ # no need to set default_sheet if there is only one sheet in the document
50
+ if self.sheets.size == 1
51
+ @default_sheet = self.sheets.first
52
+ end
53
+ @cell = Hash.new
54
+ @cell_type = Hash.new
55
+ @formula = Hash.new
56
+ @first_row = Hash.new
57
+ @last_row = Hash.new
58
+ @first_column = Hash.new
59
+ @last_column = Hash.new
60
+ @style = Hash.new
61
+ @style_defaults = Hash.new { |h,k| h[k] = [] }
62
+ @style_definitions = Hash.new
63
+ @header_line = 1
64
+ end
65
+
66
+ # creates a new empty openoffice-spreadsheet file
67
+ def create_openoffice(filename) #:nodoc:
68
+ #TODO: a better way for creating the file contents
69
+ # now you have to call mkbase64...rb to create an include file with all
70
+ # the empty files in an openoffice zip-file
71
+ load 'base64include.rb'
72
+ # puts @@empty_spreadsheet
73
+ f = File.open(filename,'wb')
74
+ f.print(Base64.decode64(@@empty_spreadsheet))
75
+ f.close
76
+ end
77
+
78
+ # Returns the content of a spreadsheet-cell.
79
+ # (1,1) is the upper left corner.
80
+ # (1,1), (1,'A'), ('A',1), ('a',1) all refers to the
81
+ # cell at the first line and first row.
82
+ def cell(row, col, sheet=nil)
83
+ sheet = @default_sheet unless sheet
84
+ read_cells(sheet) unless @cells_read[sheet]
85
+ row,col = normalize(row,col)
86
+ if celltype(row,col,sheet) == :date
87
+ yyyy,mm,dd = @cell[sheet][[row,col]].split('-')
88
+ return Date.new(yyyy.to_i,mm.to_i,dd.to_i)
89
+ end
90
+ @cell[sheet][[row,col]]
91
+ end
92
+
93
+ # Returns the formula at (row,col).
94
+ # Returns nil if there is no formula.
95
+ # The method #formula? checks if there is a formula.
96
+ def formula(row,col,sheet=nil)
97
+ sheet = @default_sheet unless sheet
98
+ read_cells(sheet) unless @cells_read[sheet]
99
+ row,col = normalize(row,col)
100
+ if @formula[sheet][[row,col]] == nil
101
+ return nil
102
+ else
103
+ return @formula[sheet][[row,col]]["oooc:".length..-1]
104
+ end
105
+ end
106
+
107
+ # true, if there is a formula
108
+ def formula?(row,col,sheet=nil)
109
+ sheet = @default_sheet unless sheet
110
+ read_cells(sheet) unless @cells_read[sheet]
111
+ row,col = normalize(row,col)
112
+ formula(row,col) != nil
113
+ end
114
+
115
+ class Font
116
+ attr_accessor :bold, :italic, :underline
117
+
118
+ def bold?
119
+ @bold == 'bold'
120
+ end
121
+
122
+ def italic?
123
+ @italic == 'italic'
124
+ end
125
+
126
+ def underline?
127
+ @underline != nil
128
+ end
129
+ end
130
+
131
+ # Given a cell, return the cell's style
132
+ def font(row, col, sheet=nil)
133
+ sheet = @default_sheet unless sheet
134
+ read_cells(sheet) unless @cells_read[sheet]
135
+ row,col = normalize(row,col)
136
+ style_name = @style[sheet][[row,col]] || @style_defaults[sheet][col - 1] || 'Default'
137
+ @style_definitions[style_name]
138
+ end
139
+
140
+ # set a cell to a certain value
141
+ # (this will not be saved back to the spreadsheet file!)
142
+ def set(row,col,value,sheet=nil) #:nodoc:
143
+ sheet = @default_sheet unless sheet
144
+ read_cells(sheet) unless @cells_read[sheet]
145
+ row,col = normalize(row,col)
146
+ set_value(row,col,value,sheet)
147
+ if value.class == Fixnum
148
+ set_type(row,col,:float,sheet)
149
+ elsif value.class == String
150
+ set_type(row,col,:string,sheet)
151
+ elsif value.class == Float
152
+ set_type(row,col,:string,sheet)
153
+ else
154
+ raise ArgumentError, "Type for "+value.to_s+" not set"
155
+ end
156
+ end
157
+
158
+ # returns the type of a cell:
159
+ # * :float
160
+ # * :string
161
+ # * :date
162
+ # * :percentage
163
+ # * :formula
164
+ # * :time
165
+ # * :datetime
166
+ def celltype(row,col,sheet=nil)
167
+ sheet = @default_sheet unless sheet
168
+ read_cells(sheet) unless @cells_read[sheet]
169
+ row,col = normalize(row,col)
170
+ if @formula[sheet][[row,col]]
171
+ return :formula
172
+ else
173
+ @cell_type[sheet][[row,col]]
174
+ end
175
+ end
176
+
177
+ # returns an array of sheet names in the spreadsheet
178
+ def sheets
179
+ return_sheets = []
180
+ oo_document_count = 0
181
+ @doc.each_element do |oo_document|
182
+ oo_document_count += 1
183
+ oo_element_count = 0
184
+ oo_document.each_element do |oo_element|
185
+ oo_element_count += 1
186
+ if oo_element.name == "body"
187
+ oo_element.each_element do |be|
188
+ if be.name == "spreadsheet"
189
+ be.each_element do |se|
190
+ if se.name == "table"
191
+ return_sheets << se.attributes['name']
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ return_sheets
200
+ end
201
+
202
+ # version of the openoffice document
203
+ # at 2007 this is always "1.0"
204
+ def officeversion
205
+ oo_version
206
+ @officeversion
207
+ end
208
+
209
+ # shows the internal representation of all cells
210
+ # mainly for debugging purposes
211
+ def to_s(sheet=nil)
212
+ sheet = @default_sheet unless sheet
213
+ read_cells(sheet) unless @cells_read[sheet]
214
+ @cell[sheet].inspect
215
+ end
216
+
217
+ # save spreadsheet
218
+ def save #:nodoc:
219
+ 42
220
+ end
221
+
222
+ # returns each formula in the selected sheet as an array of elements
223
+ # [row, col, formula]
224
+ def formulas(sheet=nil)
225
+ theformulas = Array.new
226
+ sheet = @default_sheet unless sheet
227
+ read_cells(sheet) unless @cells_read[sheet]
228
+ first_row(sheet).upto(last_row(sheet)) {|row|
229
+ first_column(sheet).upto(last_column(sheet)) {|col|
230
+ if formula?(row,col,sheet)
231
+ f = [row, col, formula(row,col,sheet)]
232
+ theformulas << f
233
+ end
234
+ }
235
+ }
236
+ theformulas
237
+ end
238
+
239
+ private
240
+
241
+ # read the version of the OO-Version
242
+ def oo_version
243
+ @doc.each_element do |oo_document|
244
+ @officeversion = oo_document.attributes['version']
245
+ end
246
+ end
247
+
248
+ # helper function to set the internal representation of cells
249
+ def set_cell_values(sheet,x,y,i,v,vt,formula,tr,str_v,style_name)
250
+ key = [y,x+i]
251
+ @cell_type[sheet] = {} unless @cell_type[sheet]
252
+ @cell_type[sheet][key] = Openoffice.oo_type_2_roo_type(vt)
253
+ @formula[sheet] = {} unless @formula[sheet]
254
+ @formula[sheet][key] = formula if formula
255
+ @cell[sheet] = {} unless @cell[sheet]
256
+ @style[sheet] = {} unless @style[sheet]
257
+ @style[sheet][key] = style_name
258
+ case @cell_type[sheet][key]
259
+ when :float
260
+ @cell[sheet][key] = v.to_f
261
+ when :string
262
+ @cell[sheet][key] = str_v
263
+ when :date
264
+ if tr.attributes['date-value'].size != "XXXX-XX-XX".size
265
+ #-- dann ist noch eine Uhrzeit vorhanden
266
+ #-- "1961-11-21T12:17:18"
267
+ @cell[sheet][key] = DateTime.parse(tr.attributes['date-value'])
268
+ @cell_type[sheet][key] = :datetime
269
+ else
270
+ @cell[sheet][key] = tr.attributes['date-value']
271
+ end
272
+ when :percentage
273
+ @cell[sheet][key] = v.to_f
274
+ when :time
275
+ hms = v.split(':')
276
+ @cell[sheet][key] = hms[0].to_i*3600 + hms[1].to_i*60 + hms[2].to_i
277
+ else
278
+ @cell[sheet][key] = v
279
+ end
280
+ end
281
+
282
+ # read all cells in the selected sheet
283
+ #--
284
+ # the following construct means '4 blanks'
285
+ # some content <text:s text:c="3"/>
286
+ #++
287
+ def read_cells(sheet=nil)
288
+ sheet = @default_sheet unless sheet
289
+ sheet_found = false
290
+ raise ArgumentError, "Error: sheet '#{sheet||'nil'}' not valid" if @default_sheet == nil and sheet==nil
291
+ raise RangeError unless self.sheets.include? sheet
292
+ oo_document_count = 0
293
+ @doc.each_element do |oo_document|
294
+ # @officeversion = oo_document.attributes['version']
295
+ oo_document_count += 1
296
+ oo_element_count = 0
297
+ oo_document.each_element do |oo_element|
298
+ oo_element_count += 1
299
+ if oo_element.name == "body"
300
+ oo_element.each_element do |be|
301
+ if be.name == "spreadsheet"
302
+ be.each_element do |se|
303
+ if se.name == "table"
304
+ if se.attributes['name']==sheet
305
+ sheet_found = true
306
+ x=1
307
+ y=1
308
+ se.each_element do |te|
309
+ if te.name == "table-column"
310
+ rep = te.attributes["number-columns-repeated"]
311
+ @style_defaults[sheet] << te.attributes["default-cell-style-name"]
312
+ elsif te.name == "table-row"
313
+ if te.attributes['number-rows-repeated']
314
+ skip_y = te.attributes['number-rows-repeated'].to_i
315
+ y = y + skip_y - 1 # minus 1 because this line will be counted as a line element
316
+ end
317
+ te.each_element do |tr|
318
+ if tr.name == 'table-cell'
319
+ skip = tr.attributes['number-columns-repeated']
320
+ formula = tr.attributes['formula']
321
+ vt = tr.attributes['value-type']
322
+ v = tr.attributes['value']
323
+ style_name = tr.attributes['style-name']
324
+ if vt == 'string'
325
+ str_v = ''
326
+ # insert \n if there is more than one paragraph
327
+ para_count = 0
328
+ tr.each_element do |str|
329
+ if str.name == 'p'
330
+ v = str.text
331
+ str_v += "\n" if para_count > 0
332
+ para_count += 1
333
+ if str.children.size > 1
334
+ str_v = children_to_string(str.children)
335
+ else
336
+ str.children.each {|child|
337
+ str_v = str_v + child.to_s #.text
338
+ }
339
+ end
340
+ str_v.gsub!(/&apos;/,"'") # special case not supported by unescapeHTML
341
+ str_v = CGI.unescapeHTML(str_v)
342
+ end # == 'p'
343
+ end
344
+ elsif vt == 'time'
345
+ tr.each_element do |str|
346
+ if str.name == 'p'
347
+ v = str.text
348
+ end
349
+ end
350
+ elsif vt == '' or vt == nil
351
+ #
352
+ elsif vt == 'date'
353
+ #
354
+ elsif vt == 'percentage'
355
+ #
356
+ elsif vt == 'float'
357
+ #
358
+ elsif vt == 'boolean'
359
+ v = tr.attributes['boolean-value']
360
+ #
361
+ else
362
+ # raise "unknown type #{vt}"
363
+ end
364
+ if skip
365
+ if v != nil or tr.attributes['date-value']
366
+ 0.upto(skip.to_i-1) do |i|
367
+ set_cell_values(sheet,x,y,i,v,vt,formula,tr,str_v,style_name)
368
+ end
369
+ end
370
+ x += (skip.to_i - 1)
371
+ end # if skip
372
+ set_cell_values(sheet,x,y,0,v,vt,formula,tr,str_v,style_name)
373
+ x += 1
374
+ end
375
+ end
376
+ y += 1
377
+ x = 1
378
+ end
379
+ end
380
+ end # sheet
381
+ end
382
+ end
383
+ end
384
+ end
385
+ elsif oo_element.name == "automatic-styles"
386
+ read_styles(oo_element)
387
+ end
388
+ end
389
+ end
390
+ if !sheet_found
391
+ raise RangeError
392
+ end
393
+ @cells_read[sheet] = true
394
+ end
395
+
396
+ def read_styles(style_elements)
397
+ @style_definitions['Default'] = Openoffice::Font.new
398
+ style_elements.each do |style|
399
+ next unless style.name == 'style'
400
+ style_name = style.attributes['name']
401
+ style.each do |properties|
402
+ font = Openoffice::Font.new
403
+ font.bold = properties.attributes['font-weight']
404
+ font.italic = properties.attributes['font-style']
405
+ font.underline = properties.attributes['text-underline-style']
406
+ @style_definitions[style_name] = font
407
+ end
408
+ end
409
+ end
410
+
411
+ # Checks if the default_sheet exists. If not an RangeError exception is
412
+ # raised
413
+ def check_default_sheet
414
+ sheet_found = false
415
+ raise ArgumentError, "Error: default_sheet not set" if @default_sheet == nil
416
+ @doc.each_element do |oo_document|
417
+ oo_element_count = 0
418
+ oo_document.each_element do |oo_element|
419
+ oo_element_count += 1
420
+ if oo_element.name == "body"
421
+ oo_element.each_element do |be|
422
+ if be.name == "spreadsheet"
423
+ be.each_element do |se|
424
+ if se.name == "table"
425
+ if se.attributes['name'] == @default_sheet
426
+ sheet_found = true
427
+ end # sheet
428
+ end
429
+ end
430
+ end
431
+ end
432
+ end
433
+ end
434
+ end
435
+ if ! sheet_found
436
+ raise RangeError, "sheet '#{@default_sheet}' not found"
437
+ end
438
+ end
439
+
440
+ def process_zipfile(zip, path='')
441
+ if zip.file.file? path
442
+ if path == "content.xml"
443
+ open(File.join(@tmpdir, @file_nr.to_s+'_roo_content.xml'),'wb') {|f|
444
+ f << zip.read(path)
445
+ }
446
+ end
447
+ else
448
+ unless path.empty?
449
+ path += '/'
450
+ end
451
+ zip.dir.foreach(path) do |filename|
452
+ process_zipfile(zip, path+filename)
453
+ end
454
+ end
455
+ end
456
+
457
+ def extract_content
458
+ Zip::ZipFile.open(@filename) do |zip|
459
+ process_zipfile(zip)
460
+ end
461
+ end
462
+
463
+ def set_value(row,col,value,sheet=nil)
464
+ sheet = @default_value unless sheet
465
+ @cell[sheet][[row,col]] = value
466
+ end
467
+
468
+ def set_type(row,col,type,sheet=nil)
469
+ sheet = @default_value unless sheet
470
+ @cell_type[sheet][[row,col]] = type
471
+ end
472
+
473
+ A_ROO_TYPE = {
474
+ "float" => :float,
475
+ "string" => :string,
476
+ "date" => :date,
477
+ "percentage" => :percentage,
478
+ "time" => :time,
479
+ }
480
+
481
+ def Openoffice.oo_type_2_roo_type(ootype)
482
+ return A_ROO_TYPE[ootype]
483
+ end
484
+
485
+ # helper method to convert compressed spaces and other elements within
486
+ # an text into a string
487
+ def children_to_string(children)
488
+ result = ''
489
+ children.each {|child|
490
+ if child.class == REXML::Text
491
+ result = result + child.to_s
492
+ else
493
+ if child.name == 's'
494
+ compressed_spaces = child.attributes['c'].to_i
495
+ # no explicit number means a count of 1:
496
+ if compressed_spaces == 0
497
+ compressed_spaces = 1
498
+ end
499
+ result = result + " "*compressed_spaces
500
+ else
501
+ result = result + child.to_s
502
+ end
503
+ end
504
+ }
505
+ result
506
+ end
507
+
508
+ end # class
@@ -0,0 +1,81 @@
1
+ def spreadsheet(spreadsheet, sheets, options={})
2
+ coordinates = true # default
3
+ o=""
4
+ if options[:coordinates] != nil
5
+ o << ":coordinates uebergeben: #{options[:coordinates]}"
6
+ coordinates = options[:coordinates]
7
+ end
8
+ if options[:bgcolor]
9
+ bgcolor = options[:bgcolor]
10
+ else
11
+ bgcolor = false
12
+ end
13
+
14
+ sheets.each { |sheet|
15
+ @rspreadsheet.default_sheet = sheet
16
+ linenumber = @rspreadsheet.first_row(sheet)
17
+ if options[:first_row]
18
+ linenumber += (options[:first_row]-1)
19
+ end
20
+ o << '<table border="0" cellspacing="1" cellpadding="1">'
21
+ if options[:first_row]
22
+ first_row = options[:first_row]
23
+ end
24
+ if options[:last_row]
25
+ last_row = options[:last_row]
26
+ end
27
+ if options[:first_column]
28
+ first_column = options[:first_column]
29
+ end
30
+ if options[:last_column]
31
+ last_column = options[:last_column]
32
+ end
33
+ first_row = @rspreadsheet.first_row(sheet) unless first_row
34
+ last_row = @rspreadsheet.last_row(sheet) unless last_row
35
+ first_column = @rspreadsheet.first_column(sheet) unless first_column
36
+ last_column = @rspreadsheet.last_column(sheet) unless last_column
37
+ if coordinates
38
+ o << " <tr>"
39
+ o << " <td>&nbsp;</td>"
40
+ @rspreadsheet.first_column(sheet).upto(@rspreadsheet.last_column(sheet)) {|c|
41
+ if c < first_column or c > last_column
42
+ next
43
+ end
44
+ o << " <td>"
45
+ o << " <b>#{GenericSpreadsheet.number_to_letter(c)}</b>"
46
+ o << " </td>"
47
+ }
48
+ o << "</tr>"
49
+ end
50
+ @rspreadsheet.first_row.upto(@rspreadsheet.last_row) do |y|
51
+ if first_row and (y < first_row or y > last_row)
52
+ next
53
+ end
54
+ o << "<tr>"
55
+ if coordinates
56
+ o << "<td><b>#{linenumber.to_s}</b></td>"
57
+ end
58
+ linenumber += 1
59
+ @rspreadsheet.first_column(sheet).upto(@rspreadsheet.last_column(sheet)) do |x|
60
+ if x < first_column or x > last_column
61
+ next
62
+ end
63
+ if bgcolor
64
+ o << "<td bgcolor=\"#{bgcolor}\">"
65
+ else
66
+ o << '<td bgcolor="lightgreen">'
67
+ end
68
+ if @rspreadsheet.cell(y,x).to_s.empty?
69
+ o << "&nbsp;"
70
+ else
71
+ o << "#{@rspreadsheet.cell(y,x)}"
72
+ end
73
+ o << "</td>"
74
+ end
75
+ o << "</tr>"
76
+ end
77
+ o << "</table>"
78
+ } # each sheet
79
+ return o
80
+ end
81
+
@@ -0,0 +1,9 @@
1
+ module Roo #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 2
5
+ TINY = 3
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/roo.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Roo
2
+ end
3
+
4
+ require 'roo/version'
5
+ # require 'roo/spreadsheetparser' TODO:
6
+ require 'roo/generic_spreadsheet'
7
+ require 'roo/openoffice'
8
+ require 'roo/excel'
9
+ require 'roo/excelx'
10
+ require 'roo/google'
11
+ require 'roo/roo_rails_helper'