roo 2.0.1 → 2.7.1

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