roo 2.0.1 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -0
  3. data/.github/ISSUE_TEMPLATE +10 -0
  4. data/.gitignore +4 -0
  5. data/.travis.yml +10 -6
  6. data/CHANGELOG.md +116 -1
  7. data/Gemfile +3 -4
  8. data/Gemfile_ruby2 +30 -0
  9. data/Guardfile +1 -2
  10. data/README.md +56 -22
  11. data/Rakefile +1 -1
  12. data/lib/roo/base.rb +108 -245
  13. data/lib/roo/constants.rb +5 -0
  14. data/lib/roo/csv.rb +94 -87
  15. data/lib/roo/errors.rb +11 -0
  16. data/lib/roo/excelx/cell/base.rb +94 -0
  17. data/lib/roo/excelx/cell/boolean.rb +27 -0
  18. data/lib/roo/excelx/cell/date.rb +28 -0
  19. data/lib/roo/excelx/cell/datetime.rb +111 -0
  20. data/lib/roo/excelx/cell/empty.rb +19 -0
  21. data/lib/roo/excelx/cell/number.rb +87 -0
  22. data/lib/roo/excelx/cell/string.rb +19 -0
  23. data/lib/roo/excelx/cell/time.rb +43 -0
  24. data/lib/roo/excelx/cell.rb +33 -4
  25. data/lib/roo/excelx/comments.rb +33 -0
  26. data/lib/roo/excelx/coordinate.rb +12 -0
  27. data/lib/roo/excelx/extractor.rb +3 -4
  28. data/lib/roo/excelx/format.rb +64 -0
  29. data/lib/roo/excelx/shared.rb +32 -0
  30. data/lib/roo/excelx/shared_strings.rb +124 -4
  31. data/lib/roo/excelx/sheet.rb +12 -7
  32. data/lib/roo/excelx/sheet_doc.rb +108 -97
  33. data/lib/roo/excelx/styles.rb +1 -1
  34. data/lib/roo/excelx.rb +61 -103
  35. data/lib/roo/formatters/base.rb +15 -0
  36. data/lib/roo/formatters/csv.rb +84 -0
  37. data/lib/roo/formatters/matrix.rb +23 -0
  38. data/lib/roo/formatters/xml.rb +31 -0
  39. data/lib/roo/formatters/yaml.rb +40 -0
  40. data/lib/roo/libre_office.rb +1 -2
  41. data/lib/roo/link.rb +21 -2
  42. data/lib/roo/open_office.rb +468 -523
  43. data/lib/roo/spreadsheet.rb +3 -1
  44. data/lib/roo/tempdir.rb +21 -0
  45. data/lib/roo/utils.rb +7 -7
  46. data/lib/roo/version.rb +1 -1
  47. data/lib/roo.rb +8 -3
  48. data/roo.gemspec +2 -1
  49. data/spec/helpers.rb +5 -0
  50. data/spec/lib/roo/base_spec.rb +229 -0
  51. data/spec/lib/roo/csv_spec.rb +19 -0
  52. data/spec/lib/roo/excelx_spec.rb +97 -11
  53. data/spec/lib/roo/openoffice_spec.rb +18 -1
  54. data/spec/lib/roo/spreadsheet_spec.rb +20 -0
  55. data/spec/lib/roo/utils_spec.rb +1 -1
  56. data/spec/spec_helper.rb +5 -5
  57. data/test/all_ss.rb +12 -11
  58. data/test/excelx/cell/test_base.rb +63 -0
  59. data/test/excelx/cell/test_boolean.rb +36 -0
  60. data/test/excelx/cell/test_date.rb +38 -0
  61. data/test/excelx/cell/test_datetime.rb +45 -0
  62. data/test/excelx/cell/test_empty.rb +7 -0
  63. data/test/excelx/cell/test_number.rb +74 -0
  64. data/test/excelx/cell/test_string.rb +28 -0
  65. data/test/excelx/cell/test_time.rb +30 -0
  66. data/test/formatters/test_csv.rb +119 -0
  67. data/test/formatters/test_matrix.rb +76 -0
  68. data/test/formatters/test_xml.rb +78 -0
  69. data/test/formatters/test_yaml.rb +20 -0
  70. data/test/helpers/test_accessing_files.rb +60 -0
  71. data/test/helpers/test_comments.rb +43 -0
  72. data/test/helpers/test_formulas.rb +9 -0
  73. data/test/helpers/test_labels.rb +103 -0
  74. data/test/helpers/test_sheets.rb +55 -0
  75. data/test/helpers/test_styles.rb +62 -0
  76. data/test/roo/test_base.rb +182 -0
  77. data/test/roo/test_csv.rb +60 -0
  78. data/test/roo/test_excelx.rb +325 -0
  79. data/test/roo/test_libre_office.rb +9 -0
  80. data/test/roo/test_open_office.rb +289 -0
  81. data/test/test_helper.rb +116 -18
  82. data/test/test_roo.rb +362 -2088
  83. metadata +70 -4
  84. data/test/test_generic_spreadsheet.rb +0 -237
@@ -51,7 +51,7 @@ module Roo
51
51
  xfs.children.map do |xf|
52
52
  xf['numFmtId']
53
53
  end
54
- end
54
+ end.compact
55
55
  end
56
56
 
57
57
  def extract_num_fmts
data/lib/roo/excelx.rb CHANGED
@@ -1,10 +1,19 @@
1
1
  require 'nokogiri'
2
2
  require 'zip/filesystem'
3
3
  require 'roo/link'
4
+ require 'roo/tempdir'
4
5
  require 'roo/utils'
6
+ require 'forwardable'
7
+ require 'set'
5
8
 
6
9
  module Roo
7
10
  class Excelx < Roo::Base
11
+ extend Roo::Tempdir
12
+ extend Forwardable
13
+
14
+ ERROR_VALUES = %w(#N/A #REF! #NAME? #DIV/0! #NULL! #VALUE! #NUM!).to_set
15
+
16
+ require 'roo/excelx/shared'
8
17
  require 'roo/excelx/workbook'
9
18
  require 'roo/excelx/shared_strings'
10
19
  require 'roo/excelx/styles'
@@ -13,68 +22,10 @@ module Roo
13
22
  require 'roo/excelx/relationships'
14
23
  require 'roo/excelx/comments'
15
24
  require 'roo/excelx/sheet_doc'
16
-
17
- module Format
18
- EXCEPTIONAL_FORMATS = {
19
- 'h:mm am/pm' => :date,
20
- 'h:mm:ss am/pm' => :date
21
- }
22
-
23
- STANDARD_FORMATS = {
24
- 0 => 'General'.freeze,
25
- 1 => '0'.freeze,
26
- 2 => '0.00'.freeze,
27
- 3 => '#,##0'.freeze,
28
- 4 => '#,##0.00'.freeze,
29
- 9 => '0%'.freeze,
30
- 10 => '0.00%'.freeze,
31
- 11 => '0.00E+00'.freeze,
32
- 12 => '# ?/?'.freeze,
33
- 13 => '# ??/??'.freeze,
34
- 14 => 'mm-dd-yy'.freeze,
35
- 15 => 'd-mmm-yy'.freeze,
36
- 16 => 'd-mmm'.freeze,
37
- 17 => 'mmm-yy'.freeze,
38
- 18 => 'h:mm AM/PM'.freeze,
39
- 19 => 'h:mm:ss AM/PM'.freeze,
40
- 20 => 'h:mm'.freeze,
41
- 21 => 'h:mm:ss'.freeze,
42
- 22 => 'm/d/yy h:mm'.freeze,
43
- 37 => '#,##0 ;(#,##0)'.freeze,
44
- 38 => '#,##0 ;[Red](#,##0)'.freeze,
45
- 39 => '#,##0.00;(#,##0.00)'.freeze,
46
- 40 => '#,##0.00;[Red](#,##0.00)'.freeze,
47
- 45 => 'mm:ss'.freeze,
48
- 46 => '[h]:mm:ss'.freeze,
49
- 47 => 'mmss.0'.freeze,
50
- 48 => '##0.0E+0'.freeze,
51
- 49 => '@'.freeze
52
- }
53
-
54
- def to_type(format)
55
- format = format.to_s.downcase
56
- if (type = EXCEPTIONAL_FORMATS[format])
57
- type
58
- elsif format.include?('#')
59
- :float
60
- elsif !format.match(/d+(?![\]])/).nil? || format.include?('y')
61
- if format.include?('h') || format.include?('s')
62
- :datetime
63
- else
64
- :date
65
- end
66
- elsif format.include?('h') || format.include?('s')
67
- :time
68
- elsif format.include?('%')
69
- :percentage
70
- else
71
- :float
72
- end
73
- end
74
-
75
- module_function :to_type
76
- end
25
+ require 'roo/excelx/coordinate'
26
+ require 'roo/excelx/format'
77
27
 
28
+ delegate [:styles, :workbook, :shared_strings, :rels_files, :sheet_files, :comments_files] => :@shared
78
29
  ExceedsMaxError = Class.new(StandardError)
79
30
 
80
31
  # initialization and opening of a spreadsheet file
@@ -87,16 +38,22 @@ module Roo
87
38
  cell_max = options.delete(:cell_max)
88
39
  sheet_options = {}
89
40
  sheet_options[:expand_merged_ranges] = (options[:expand_merged_ranges] || false)
41
+ sheet_options[:no_hyperlinks] = (options[:no_hyperlinks] || false)
90
42
 
91
43
  unless is_stream?(filename_or_stream)
92
- file_type_check(filename_or_stream, '.xlsx', 'an Excel-xlsx', file_warning, packed)
93
- basename = File.basename(filename_or_stream)
44
+ file_type_check(filename_or_stream, %w[.xlsx .xlsm], 'an Excel 2007', file_warning, packed)
45
+ basename = find_basename(filename_or_stream)
94
46
  end
95
47
 
96
- @tmpdir = make_tmpdir(basename, options[:tmpdir_root])
48
+ # NOTE: Create temp directory and allow Ruby to cleanup the temp directory
49
+ # when the object is garbage collected. Initially, the finalizer was
50
+ # created in the Roo::Tempdir module, but that led to a segfault
51
+ # when testing in Ruby 2.4.0.
52
+ @tmpdir = self.class.make_tempdir(self, basename, options[:tmpdir_root])
53
+ ObjectSpace.define_finalizer(self, self.class.finalize(object_id))
54
+
55
+ @shared = Shared.new(@tmpdir)
97
56
  @filename = local_filename(filename_or_stream, @tmpdir, packed)
98
- @comments_files = []
99
- @rels_files = []
100
57
  process_zipfile(@filename || filename_or_stream)
101
58
 
102
59
  @sheet_names = workbook.sheets.map do |sheet|
@@ -106,7 +63,7 @@ module Roo
106
63
  end.compact
107
64
  @sheets = []
108
65
  @sheets_by_name = Hash[@sheet_names.map.with_index do |sheet_name, n|
109
- @sheets[n] = Sheet.new(sheet_name, @rels_files[n], @sheet_files[n], @comments_files[n], styles, shared_strings, workbook, sheet_options)
66
+ @sheets[n] = Sheet.new(sheet_name, @shared, n, sheet_options)
110
67
  [sheet_name, @sheets[n]]
111
68
  end]
112
69
 
@@ -116,9 +73,9 @@ module Roo
116
73
  end
117
74
 
118
75
  super
119
- rescue => e # clean up any temp files, but only if an error was raised
120
- close
121
- raise e
76
+ rescue
77
+ self.class.finalize_tempdirs(object_id)
78
+ raise
122
79
  end
123
80
 
124
81
  def method_missing(method, *args)
@@ -187,7 +144,7 @@ module Roo
187
144
  def set(row, col, value, sheet = nil) #:nodoc:
188
145
  key = normalize(row, col)
189
146
  cell_type = cell_type_by_value(value)
190
- sheet_for(sheet).cells[key] = Cell.new(value, cell_type, nil, cell_type, value, nil, nil, nil, Cell::Coordinate.new(row, col))
147
+ sheet_for(sheet).cells[key] = Cell.new(value, cell_type, nil, cell_type, value, nil, nil, nil, Coordinate.new(row, col))
191
148
  end
192
149
 
193
150
  # Returns the formula at (row,col).
@@ -239,14 +196,21 @@ module Roo
239
196
  # Note: this is only available within the Excelx class
240
197
  def excelx_type(row, col, sheet = nil)
241
198
  key = normalize(row, col)
242
- safe_send(sheet_for(sheet).cells[key], :excelx_type)
199
+ safe_send(sheet_for(sheet).cells[key], :cell_type)
243
200
  end
244
201
 
245
202
  # returns the internal value of an excelx cell
246
203
  # Note: this is only available within the Excelx class
247
204
  def excelx_value(row, col, sheet = nil)
248
205
  key = normalize(row, col)
249
- safe_send(sheet_for(sheet).cells[key], :excelx_value)
206
+ safe_send(sheet_for(sheet).cells[key], :cell_value)
207
+ end
208
+
209
+ # returns the internal value of an excelx cell
210
+ # Note: this is only available within the Excelx class
211
+ def formatted_value(row, col, sheet = nil)
212
+ key = normalize(row, col)
213
+ safe_send(sheet_for(sheet).cells[key], :formatted_value)
250
214
  end
251
215
 
252
216
  # returns the internal format of an excel cell
@@ -259,8 +223,8 @@ module Roo
259
223
  sheet = sheet_for(sheet)
260
224
  key = normalize(row, col)
261
225
  cell = sheet.cells[key]
262
- !cell || !cell.value || (cell.type == :string && cell.value.empty?) \
263
- || (row < sheet.first_row || row > sheet.last_row || col < sheet.first_column || col > sheet.last_column)
226
+ !cell || cell.empty? ||
227
+ (row < sheet.first_row || row > sheet.last_row || col < sheet.first_column || col > sheet.last_column)
264
228
  end
265
229
 
266
230
  # shows the internal representation of all cells
@@ -321,7 +285,12 @@ module Roo
321
285
  # Yield an array of Excelx::Cell
322
286
  # Takes options for sheet, pad_cells, and max_rows
323
287
  def each_row_streaming(options = {})
324
- sheet_for(options.delete(:sheet)).each_row(options) { |row| yield row }
288
+ sheet = sheet_for(options.delete(:sheet))
289
+ if block_given?
290
+ sheet.each_row(options) { |row| yield row }
291
+ else
292
+ sheet.to_enum(:each_row, options)
293
+ end
325
294
  end
326
295
 
327
296
  private
@@ -394,11 +363,14 @@ module Roo
394
363
  end
395
364
  end
396
365
 
366
+ # Extracts the sheets in order, but it will ignore sheets that are not
367
+ # worksheets.
397
368
  def extract_sheets_in_order(entries, sheet_ids, sheets, tmpdir)
398
- sheet_ids.each_with_index do |id, i|
369
+ (sheet_ids & sheets.keys).each_with_index do |id, i|
399
370
  name = sheets[id]
400
- entry = entries.find { |e| e.name =~ /#{name}$/ }
371
+ entry = entries.find { |e| "/#{e.name}" =~ /#{name}$/ }
401
372
  path = "#{tmpdir}/roo_sheet#{i + 1}"
373
+ sheet_files << path
402
374
  @sheet_files << path
403
375
  entry.extract(path)
404
376
  end
@@ -409,19 +381,13 @@ module Roo
409
381
  @sheet_files = []
410
382
 
411
383
  unless is_stream?(zipfilename_or_stream)
412
- process_zipfile_entries Zip::File.open(zipfilename_or_stream).to_a.sort_by(&:name)
384
+ zip_file = Zip::File.open(zipfilename_or_stream)
413
385
  else
414
- stream = Zip::InputStream.open zipfilename_or_stream
415
- begin
416
- entries = []
417
- while (entry = stream.get_next_entry)
418
- entries << entry
419
- end
420
- process_zipfile_entries entries
421
- ensure
422
- stream.close
423
- end
386
+ zip_file = Zip::CentralDirectory.new
387
+ zip_file.read_from_stream zipfilename_or_stream
424
388
  end
389
+
390
+ process_zipfile_entries zip_file.to_a.sort_by(&:name)
425
391
  end
426
392
 
427
393
  def process_zipfile_entries(entries)
@@ -458,31 +424,23 @@ module Roo
458
424
  # sheet's comment file is in the sheet1.xml.rels file. SEE
459
425
  # ECMA-376 12.3.3 in "Ecma Office Open XML Part 1".
460
426
  nr = Regexp.last_match[1].to_i
461
- @comments_files[nr - 1] = "#{@tmpdir}/roo_comments#{nr}"
427
+ comments_files[nr - 1] = "#{@tmpdir}/roo_comments#{nr}"
428
+ when %r{chartsheets/_rels/sheet([0-9]+).xml.rels$}
429
+ # NOTE: Chart sheet relationship files were interfering with
430
+ # worksheets.
431
+ nil
462
432
  when /sheet([0-9]+).xml.rels$/
463
433
  # FIXME: Roo seems to use sheet[\d].xml.rels for hyperlinks only, but
464
434
  # it also stores the location for sharedStrings, comments,
465
435
  # drawings, etc.
466
436
  nr = Regexp.last_match[1].to_i
467
- @rels_files[nr - 1] = "#{@tmpdir}/roo_rels#{nr}"
437
+ rels_files[nr - 1] = "#{@tmpdir}/roo_rels#{nr}"
468
438
  end
469
439
 
470
440
  entry.extract(path) if path
471
441
  end
472
442
  end
473
443
 
474
- def styles
475
- @styles ||= Styles.new(File.join(@tmpdir, 'roo_styles.xml'))
476
- end
477
-
478
- def shared_strings
479
- @shared_strings ||= SharedStrings.new(File.join(@tmpdir, 'roo_sharedStrings.xml'))
480
- end
481
-
482
- def workbook
483
- @workbook ||= Workbook.new(File.join(@tmpdir, 'roo_workbook.xml'))
484
- end
485
-
486
444
  def safe_send(object, method, *args)
487
445
  object.send(method, *args) if object && object.respond_to?(method)
488
446
  end
@@ -0,0 +1,15 @@
1
+ module Roo
2
+ module Formatters
3
+ module Base
4
+ # converts an integer value to a time string like '02:05:06'
5
+ def integer_to_timestring(content)
6
+ h = (content / 3600.0).floor
7
+ content -= h * 3600
8
+ m = (content / 60.0).floor
9
+ content -= m * 60
10
+ s = content
11
+ Kernel.format("%02d:%02d:%02d", h, m, s)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,84 @@
1
+ module Roo
2
+ module Formatters
3
+ module CSV
4
+ def to_csv(filename = nil, separator = ",", sheet = default_sheet)
5
+ if filename
6
+ File.open(filename, "w") do |file|
7
+ write_csv_content(file, sheet, separator)
8
+ end
9
+ true
10
+ else
11
+ sio = ::StringIO.new
12
+ write_csv_content(sio, sheet, separator)
13
+ sio.rewind
14
+ sio.read
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ # Write all cells to the csv file. File can be a filename or nil. If the
21
+ # file argument is nil the output goes to STDOUT
22
+ def write_csv_content(file = nil, sheet = nil, separator = ",")
23
+ file ||= STDOUT
24
+ return unless first_row(sheet) # The sheet is empty
25
+
26
+ 1.upto(last_row(sheet)) do |row|
27
+ 1.upto(last_column(sheet)) do |col|
28
+ # TODO: use CSV.generate_line
29
+ file.print(separator) if col > 1
30
+ file.print cell_to_csv(row, col, sheet)
31
+ end
32
+ file.print("\n")
33
+ end
34
+ end
35
+
36
+ # The content of a cell in the csv output
37
+ def cell_to_csv(row, col, sheet)
38
+ return "" if empty?(row, col, sheet)
39
+
40
+ onecell = cell(row, col, sheet)
41
+
42
+ case celltype(row, col, sheet)
43
+ when :string
44
+ %("#{onecell.gsub('"', '""')}") unless onecell.empty?
45
+ when :boolean
46
+ # TODO: this only works for excelx
47
+ onecell = self.sheet_for(sheet).cells[[row, col]].formatted_value
48
+ %("#{onecell.gsub('"', '""').downcase}")
49
+ when :float, :percentage
50
+ if onecell == onecell.to_i
51
+ onecell.to_i.to_s
52
+ else
53
+ onecell.to_s
54
+ end
55
+ when :formula
56
+ case onecell
57
+ when String
58
+ %("#{onecell.gsub('"', '""')}") unless onecell.empty?
59
+ when Integer
60
+ onecell.to_s
61
+ when Float
62
+ if onecell == onecell.to_i
63
+ onecell.to_i.to_s
64
+ else
65
+ onecell.to_s
66
+ end
67
+ when Date, DateTime, TrueClass, FalseClass
68
+ onecell.to_s
69
+ else
70
+ fail "unhandled onecell-class #{onecell.class}"
71
+ end
72
+ when :date, :datetime
73
+ onecell.to_s
74
+ when :time
75
+ integer_to_timestring(onecell)
76
+ when :link
77
+ %("#{onecell.url.gsub('"', '""')}")
78
+ else
79
+ fail "unhandled celltype #{celltype(row, col, sheet)}"
80
+ end || ""
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,23 @@
1
+ module Roo
2
+ module Formatters
3
+ module Matrix
4
+ # returns a matrix object from the whole sheet or a rectangular area of a sheet
5
+ def to_matrix(from_row = nil, from_column = nil, to_row = nil, to_column = nil, sheet = default_sheet)
6
+ require 'matrix'
7
+
8
+ return ::Matrix.empty unless first_row
9
+
10
+ from_row ||= first_row(sheet)
11
+ to_row ||= last_row(sheet)
12
+ from_column ||= first_column(sheet)
13
+ to_column ||= last_column(sheet)
14
+
15
+ ::Matrix.rows(from_row.upto(to_row).map do |row|
16
+ from_column.upto(to_column).map do |col|
17
+ cell(row, col, sheet)
18
+ end
19
+ end)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ # returns an XML representation of all sheets of a spreadsheet file
2
+ module Roo
3
+ module Formatters
4
+ module XML
5
+ def to_xml
6
+ Nokogiri::XML::Builder.new do |xml|
7
+ xml.spreadsheet do
8
+ sheets.each do |sheet|
9
+ self.default_sheet = sheet
10
+ xml.sheet(name: sheet) do |x|
11
+ if first_row && last_row && first_column && last_column
12
+ # sonst gibt es Fehler bei leeren Blaettern
13
+ first_row.upto(last_row) do |row|
14
+ first_column.upto(last_column) do |col|
15
+ next if empty?(row, col)
16
+
17
+ x.cell(cell(row, col),
18
+ row: row,
19
+ column: col,
20
+ type: celltype(row, col))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end.to_xml
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ module Roo
2
+ module Formatters
3
+ module YAML
4
+ # returns a rectangular area (default: all cells) as yaml-output
5
+ # you can add additional attributes with the prefix parameter like:
6
+ # oo.to_yaml({"file"=>"flightdata_2007-06-26", "sheet" => "1"})
7
+ def to_yaml(prefix = {}, from_row = nil, from_column = nil, to_row = nil, to_column = nil, sheet = default_sheet)
8
+ # return an empty string if there is no first_row, i.e. the sheet is empty
9
+ return "" unless first_row
10
+
11
+ from_row ||= first_row(sheet)
12
+ to_row ||= last_row(sheet)
13
+ from_column ||= first_column(sheet)
14
+ to_column ||= last_column(sheet)
15
+
16
+ result = "--- \n"
17
+ from_row.upto(to_row) do |row|
18
+ from_column.upto(to_column) do |col|
19
+ next if empty?(row, col, sheet)
20
+
21
+ result << "cell_#{row}_#{col}: \n"
22
+ prefix.each do|k, v|
23
+ result << " #{k}: #{v} \n"
24
+ end
25
+ result << " row: #{row} \n"
26
+ result << " col: #{col} \n"
27
+ result << " celltype: #{celltype(row, col, sheet)} \n"
28
+ value = cell(row, col, sheet)
29
+ if celltype(row, col, sheet) == :time
30
+ value = integer_to_timestring(value)
31
+ end
32
+ result << " value: #{value} \n"
33
+ end
34
+ end
35
+
36
+ result
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,4 @@
1
1
  require 'roo/open_office'
2
2
 
3
3
  # LibreOffice is just an alias for Roo::OpenOffice class
4
- class Roo::LibreOffice < Roo::OpenOffice
5
- end
4
+ Roo::LibreOffice = Roo::OpenOffice
data/lib/roo/link.rb CHANGED
@@ -1,9 +1,28 @@
1
+ require 'uri'
2
+
1
3
  module Roo
2
4
  class Link < String
5
+ # FIXME: Roo::Link inherits from String. A link cell is_a?(Roo::Link). **It is
6
+ # the only situation where a cells `value` is always a String**. Link
7
+ # cells have a nifty `to_uri` method, but this method isn't easily
8
+ # reached. (e.g. `sheet.sheet_for(nil).cells[[row,column]]).value.to_uri`;
9
+ # `sheet.hyperlink(row, column)` doesn't use `to_uri`).
10
+ #
11
+ # 1. Add different types of links (String, Numeric, Date, DateTime, etc.)
12
+ # 2. Remove Roo::Link.
13
+ # 3. Don't inherit the string and pass the cell's value.
14
+ #
15
+ # I don't know the historical reasons for the Roo::Link, but right now
16
+ # it seems uneccessary. I'm in favor of keeping it just in case.
17
+ #
18
+ # I'm also in favor of passing the cell's value to Roo::Link. The
19
+ # cell.value's class would still be Roo::Link, but the value itself
20
+ # would depend on what type of cell it is (Numeric, Date, etc.).
21
+ #
3
22
  attr_reader :href
4
- alias :url :href
23
+ alias_method :url, :href
5
24
 
6
- def initialize(href='', text=href)
25
+ def initialize(href = '', text = href)
7
26
  super(text)
8
27
  @href = href
9
28
  end